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/dkim.js CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  const crypto = require('crypto');
4
4
  const dns = require('dns');
5
- const Stream = require('stream').Stream;
5
+ const { Stream } = require('stream');
6
6
  const utils = require('haraka-utils');
7
7
 
8
8
  //////////////////////
@@ -26,7 +26,7 @@ class Buf {
26
26
  if (!buf) buf = Buffer.from('');
27
27
  return buf;
28
28
  }
29
- if (buf && buf.length) {
29
+ if (buf?.length) {
30
30
  this.bar.push(buf);
31
31
  this.blen += buf.length;
32
32
  }
@@ -73,8 +73,8 @@ class DKIMObject {
73
73
  const [ , , dkim_signature] = /^([^:]+):\s*((?:.|[\r\n])*)$/.exec(header);
74
74
  const sig = dkim_signature.trim().replace(/\s+/g,'');
75
75
  const keys = sig.split(';');
76
- for (let k=0; k<keys.length; k++) {
77
- const key = keys[k].trim();
76
+ for (const keyElement of keys) {
77
+ const key = keyElement.trim();
78
78
  if (!key) continue; // skip empty keys
79
79
  const [ , key_name, key_value] = /^([^= ]+)=((?:.|[\r\n])+)$/.exec(key) || [];
80
80
  if (key_name) {
@@ -133,8 +133,8 @@ class DKIMObject {
133
133
 
134
134
  if (this.fields.h) {
135
135
  const headers = this.fields.h.split(':');
136
- for (let h=0; h<headers.length; h++) {
137
- this.signed_headers.push(headers[h].trim().toLowerCase());
136
+ for (const h of headers) {
137
+ this.signed_headers.push(h.trim().toLowerCase());
138
138
  }
139
139
  if (!this.signed_headers.includes('from')) {
140
140
  return this.result('from field not signed', 'invalid');
@@ -247,8 +247,7 @@ class DKIMObject {
247
247
  }
248
248
 
249
249
  // Now we canonicalize the specified headers
250
- for (let h=0; h<this.signed_headers.length; h++) {
251
- const header = this.signed_headers[h];
250
+ for (const header of this.signed_headers) {
252
251
  this.debug(`${this.identity}: canonicalize header: ${header}`);
253
252
  if (this.header_idx[header]) {
254
253
  // RFC 6376 section 5.4.2, read headers from bottom to top
@@ -281,12 +280,10 @@ class DKIMObject {
281
280
  our_sig = our_sig.replace(/\r\n$/,'');
282
281
  this.verifier.update(our_sig);
283
282
 
284
- // Do the DNS lookup to retrieve the public key
285
- const self = this;
286
283
  let timeout = false;
287
284
  const timer = setTimeout(() => {
288
285
  timeout = true;
289
- return self.result('DNS timeout', 'tempfail');
286
+ return this.result('DNS timeout', 'tempfail');
290
287
  }, this.timeout * 1000);
291
288
  const lookup = `${this.fields.s}._domainkey.${this.fields.d}`;
292
289
  this.debug(`${this.identity}: DNS lookup ${lookup} (timeout= ${this.timeout}s)`);
@@ -298,77 +295,73 @@ class DKIMObject {
298
295
  case dns.NOTFOUND:
299
296
  case dns.NODATA:
300
297
  case dns.NXDOMAIN:
301
- return self.result('no key for signature', 'invalid');
298
+ return this.result('no key for signature', 'invalid');
302
299
  default:
303
- self.debug(`${self.identity}: DNS lookup error: ${err.code}`);
304
- return self.result('key unavailable', 'tempfail');
300
+ this.debug(`${this.identity}: DNS lookup error: ${err.code}`);
301
+ return this.result('key unavailable', 'tempfail');
305
302
  }
306
303
  }
307
- if (!res) return self.result('no key for signature', 'invalid');
308
- for (let r=0; r<res.length; r++) {
309
- let record = res[r];
310
- // Node 0.11.x compatibility
311
- if (Array.isArray(record)) {
312
- record = record.join('');
313
- }
304
+ if (!res) return this.result('no key for signature', 'invalid');
305
+ for (const recordSegments of res) {
306
+ const record = recordSegments.join('');
314
307
  if (!record.includes('p=')) {
315
- self.debug(`${self.identity}: ignoring TXT record: ${record}`);
308
+ this.debug(`${this.identity}: ignoring TXT record: ${record}`);
316
309
  continue;
317
310
  }
318
- self.debug(`${self.identity}: got DNS record: ${record}`);
311
+ this.debug(`${this.identity}: got DNS record: ${record}`);
319
312
  const rec = record.replace(/\r?\n/g, '').replace(/\s+/g,'');
320
313
  const split = rec.split(';');
321
- for (let j=0; j<split.length; j++) {
322
- const split2 = split[j].split('=');
323
- if (split2[0]) self.dns_fields[split2[0]] = split2[1];
314
+ for (const element of split) {
315
+ const split2 = element.split('=');
316
+ if (split2[0]) this.dns_fields[split2[0]] = split2[1];
324
317
  }
325
318
 
326
319
  // Validate
327
- if (!self.dns_fields.v || self.dns_fields.v !== 'DKIM1') {
328
- return self.result('invalid version', 'invalid');
320
+ if (!this.dns_fields.v || this.dns_fields.v !== 'DKIM1') {
321
+ return this.result('invalid version', 'invalid');
329
322
  }
330
- if (self.dns_fields.g) {
331
- if (self.dns_fields.g !== '*') {
332
- let s = self.dns_fields.g;
323
+ if (this.dns_fields.g) {
324
+ if (this.dns_fields.g !== '*') {
325
+ let s = this.dns_fields.g;
333
326
  // Escape any special regexp characters
334
327
  s = s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
335
328
  // Make * a non-greedy match against anything except @
336
329
  s = s.replace('\\*','[^@]*?');
337
330
  const reg = new RegExp(`^${s}@`);
338
- self.debug(`${self.identity}: matching ${self.dns_fields.g} against i=${self.fields.i} regexp=${reg.toString()}`);
339
- if (!reg.test(self.fields.i)) {
340
- return self.result('inapplicable key', 'invalid');
331
+ this.debug(`${this.identity}: matching ${this.dns_fields.g} against i=${this.fields.i} regexp=${reg.toString()}`);
332
+ if (!reg.test(this.fields.i)) {
333
+ return this.result('inapplicable key', 'invalid');
341
334
  }
342
335
  }
343
336
  }
344
337
  else {
345
- return self.result('inapplicable key', 'invalid');
338
+ return this.result('inapplicable key', 'invalid');
346
339
  }
347
- if (self.dns_fields.h) {
348
- const hashes = self.dns_fields.h.split(':');
349
- for (let k=0; k<hashes.length; k++) {
350
- const hash = hashes[k].trim();
351
- if (!self.fields.a.includes(hash)) {
352
- return self.result('inappropriate hash algorithm', 'invalid');
340
+ if (this.dns_fields.h) {
341
+ const hashes = this.dns_fields.h.split(':');
342
+ for (const hashElement of hashes) {
343
+ const hash = hashElement.trim();
344
+ if (!this.fields.a.includes(hash)) {
345
+ return this.result('inappropriate hash algorithm', 'invalid');
353
346
  }
354
347
  }
355
348
  }
356
- if (self.dns_fields.k) {
357
- if (!self.fields.a.includes(self.dns_fields.k)) {
358
- return self.result('inappropriate key type', 'invalid');
349
+ if (this.dns_fields.k) {
350
+ if (!this.fields.a.includes(this.dns_fields.k)) {
351
+ return this.result('inappropriate key type', 'invalid');
359
352
  }
360
353
  }
361
- if (self.dns_fields.t) {
362
- const flags = self.dns_fields.t.split(':');
363
- for (let f=0; f<flags.length; f++) {
364
- const flag = flags[f].trim();
354
+ if (this.dns_fields.t) {
355
+ const flags = this.dns_fields.t.split(':');
356
+ for (const flagElement of flags) {
357
+ const flag = flagElement.trim();
365
358
  if (flag === 'y') {
366
359
  // Test mode
367
- self.test_mode = true;
360
+ this.test_mode = true;
368
361
  }
369
362
  else if (flag === 's') {
370
363
  // 'i' and 'd' domain much match exactly
371
- let i = self.fields.i
364
+ let { i } = this.fields
372
365
  i = i.substr(i.indexOf('@')+1, i.length)
373
366
  if (i.toLowerCase() !== this.fields.d.toLowerCase()) {
374
367
  return this.result('i/d selector domain mismatch (t=s)', 'invalid')
@@ -376,26 +369,27 @@ class DKIMObject {
376
369
  }
377
370
  }
378
371
  }
379
- if (!self.dns_fields.p) return self.result('key revoked', 'invalid');
372
+ if (!this.dns_fields.p) return this.result('key revoked', 'invalid');
380
373
 
381
374
  // crypto.verifier requires the key in PEM format
382
- self.public_key = `-----BEGIN PUBLIC KEY-----\r\n${
383
- self.dns_fields.p.replace(/(.{1,76})/g, '$1\r\n')
375
+ this.public_key = `-----BEGIN PUBLIC KEY-----\r\n${
376
+
377
+ this.dns_fields.p.replace(/(.{1,76})/g, '$1\r\n')
384
378
  }-----END PUBLIC KEY-----\r\n`;
385
379
 
386
380
  let verified;
387
381
  try {
388
- verified = self.verifier.verify(self.public_key, self.fields.b, 'base64');
389
- self.debug(`${self.identity}: verified=${verified}`);
382
+ verified = this.verifier.verify(this.public_key, this.fields.b, 'base64');
383
+ this.debug(`${this.identity}: verified=${verified}`);
390
384
  }
391
385
  catch (e) {
392
- self.debug(`${self.identity}: verification error: ${e.message}`);
393
- return self.result('verification error', 'invalid');
386
+ this.debug(`${this.identity}: verification error: ${e.message}`);
387
+ return this.result('verification error', 'invalid');
394
388
  }
395
- return self.result(null, ((verified) ? 'pass' : 'fail'));
389
+ return this.result(null, ((verified) ? 'pass' : 'fail'));
396
390
  }
397
391
  // We didn't find a valid DKIM record for this signature
398
- self.result('no key for signature', 'invalid');
392
+ this.result('no key for signature', 'invalid');
399
393
  });
400
394
  }
401
395
 
@@ -508,9 +502,9 @@ class DKIMVerifyStream extends Stream {
508
502
 
509
503
  // Set the overall result based on this precedence order
510
504
  const rr = ['pass','tempfail','fail','invalid','none'];
511
- for (let r=0; r<rr.length; r++) {
512
- if (!self.result || (self.result && self.result !== rr[r] && result.result === rr[r])) {
513
- self.result = rr[r];
505
+ for (const element of rr) {
506
+ if (!self.result || (self.result && self.result !== element && result.result === element)) {
507
+ self.result = element;
514
508
  }
515
509
  }
516
510
  }
@@ -543,14 +537,14 @@ class DKIMVerifyStream extends Stream {
543
537
  if (!this._in_body) {
544
538
  this._in_body = true;
545
539
  // Parse the headers
546
- for (let h=0; h<this.headers.length; h++) {
547
- const match = /^([^: ]+):\s*((:?.|[\r\n])*)/.exec(this.headers[h]);
540
+ for (const header of this.headers) {
541
+ const match = /^([^: ]+):\s*((:?.|[\r\n])*)/.exec(header);
548
542
  if (!match) continue;
549
543
  const header_name = match[1];
550
544
  if (!header_name) continue;
551
545
  const hn = header_name.toLowerCase();
552
546
  if (!this.header_idx[hn]) this.header_idx[hn] = [];
553
- this.header_idx[hn].push(this.headers[h]);
547
+ this.header_idx[hn].push(header);
554
548
  }
555
549
  if (!this.header_idx['dkim-signature']) {
556
550
  this._no_signatures_found = true;
@@ -563,8 +557,8 @@ class DKIMVerifyStream extends Stream {
563
557
  const dkim_headers = this.header_idx['dkim-signature'];
564
558
  this.debug(`Found ${dkim_headers.length} DKIM signatures`);
565
559
  this.pending = dkim_headers.length;
566
- for (let d=0; d<dkim_headers.length; d++) {
567
- this.dkim_objects.push(new DKIMObject(dkim_headers[d], this.header_idx, callback, this.opts));
560
+ for (const dkimHeader of dkim_headers) {
561
+ this.dkim_objects.push(new DKIMObject(dkimHeader, this.header_idx, callback, this.opts));
568
562
  }
569
563
  if (this.pending === 0) {
570
564
  process.nextTick(() => {
@@ -587,8 +581,8 @@ class DKIMVerifyStream extends Stream {
587
581
  }
588
582
  }
589
583
  else {
590
- for (let e=0; e<this.dkim_objects.length; e++) {
591
- this.dkim_objects[e].add_body_line(line);
584
+ for (const dkimObject of this.dkim_objects) {
585
+ dkimObject.add_body_line(line);
592
586
  }
593
587
  }
594
588
  if (once) {
@@ -606,13 +600,12 @@ class DKIMVerifyStream extends Stream {
606
600
 
607
601
  end (buf) {
608
602
  this.handle_buf(((buf) ? buf : null));
609
- for (let d=0; d<this.dkim_objects.length; d++) {
610
- this.dkim_objects[d].end();
603
+ for (const dkimObject of this.dkim_objects) {
604
+ dkimObject.end();
611
605
  }
612
606
  if (this.pending === 0 && this._no_signatures_found === false) {
613
- const self = this;
614
607
  process.nextTick(() => {
615
- self.cb(null, self.result, self.results);
608
+ this.cb(null, this.result, this.results);
616
609
  });
617
610
  }
618
611
  }
package/docs/Body.md CHANGED
@@ -1,22 +1 @@
1
- Body Object
2
- ===========
3
-
4
- The Body object gives you access to the textual body parts of an email.
5
-
6
- API
7
- ---
8
-
9
- * body.bodytext
10
-
11
- A String containing the body text. Note that HTML parts will have tags in-tact.
12
-
13
- * body.header
14
-
15
- The header of this MIME part. See the `Header Object` for details of the API.
16
-
17
- * body.children
18
-
19
- Any child MIME parts. For example a multipart/alternative mail will have a
20
- main body part with just the MIME preamble in (which is usually either empty,
21
- or reads something like "This is a multipart MIME message"), and two
22
- children, one text/plain and one text/html.
1
+ moved to [Body](https://github.com/haraka/email-message#body)
@@ -26,7 +26,7 @@ The list of plugins to load
26
26
  * listen\_host, port - the host and port to listen on (default: ::0 and 25)
27
27
  * listen - (default: [::0]:25) Comma separated IP:Port addresses to listen on
28
28
  * inactivity\_time - how long to let clients idle in seconds (default: 300)
29
- * nodes - specifies how many processes to fork. The string "cpus" will fork as many children as there are CPUs (default: 0, which disables cluster mode)
29
+ * nodes - specifies how many processes to fork. The string "cpus" will fork as many children as there are CPUs (default: 1, which enables cluster mode with a single process)
30
30
  * user - optionally a user to drop privileges to. Can be a string or UID.
31
31
  * group - optionally a group to drop privileges to. Can be a string or GID.
32
32
  * ignore\_bad\_plugins - If a plugin fails to compile by default Haraka will stop at load time.
@@ -136,4 +136,4 @@ The list of plugins to load
136
136
 
137
137
  * connection\_close\_message
138
138
 
139
- Defaults to `closing connection. Have a jolly good day.` can be overrridden with custom text
139
+ Defaults to `closing connection. Have a jolly good day.` can be overrridden with custom text
package/docs/Header.md CHANGED
@@ -1,47 +1 @@
1
- Header Object
2
- =============
3
-
4
- The Header object gives programmatic access to email headers. It is primarily
5
- used from `transaction.header` but also each MIME part of the `Body` will
6
- also have its own header object.
7
-
8
- API
9
- ---
10
-
11
- * header.get(key)
12
-
13
- Returns the header with the name `key`. If there are multiple headers with
14
- the given name (as is usually the case with "Received" for example) they will
15
- be concatenated together with "\n".
16
-
17
- * header.get\_all(key)
18
-
19
- Returns the headers with the name `key` as an array. Multi-valued headers
20
- will have multiple entries in the array.
21
-
22
- * header.get\_decoded(key)
23
-
24
- Works like `get(key)`, only it gives you headers decoded from any MIME encoding
25
- they may have used.
26
-
27
- * header.remove(key)
28
-
29
- Removes all headers with the given name. DO NOT USE. This is transparent to
30
- the transaction and it will not see the header(s) you removed. Instead use
31
- `transaction.remove_header(key)` which will also correct the data part of
32
- the email.
33
-
34
- * header.add(key, value)
35
-
36
- Adds a header with the given name and value. DO NOT USE. This is transparent
37
- to the transaction and it will not see the header you added. Instead use
38
- `transaction.add_header(key, value)` which will add the header to the data
39
- part of the email.
40
-
41
- * header.lines()
42
-
43
- Returns the entire header as a list of lines.
44
-
45
- * header.toString()
46
-
47
- Returns the entire header as a string.
1
+ moved to [Header](https://github.com/haraka/email-message#header)
package/docs/Outbound.md CHANGED
@@ -40,12 +40,9 @@ Default: true. Switch to false to disable TLS for outbound mail.
40
40
 
41
41
  This uses the same `tls_key.pem` and `tls_cert.pem` files that the `tls`
42
42
  plugin uses, along with other values in `tls.ini`. See the [tls plugin
43
- docs](http://haraka.github.io/manual/plugins/tls.html) for information on generating those
44
- files.
43
+ docs](http://haraka.github.io/manual/plugins/tls.html) for information on generating those files.
45
44
 
46
- Within `tls.ini` you can specify global options for the values `ciphers`, `minVersion`,
47
- `requestCert` and `rejectUnauthorized`, alternatively you can provide
48
- separate values by putting them under a key: `[outbound]`, such as:
45
+ Within `tls.ini` you can specify global options for the values `ciphers`, `minVersion`, `requestCert` and `rejectUnauthorized`, alternatively you can provide separate values by putting them under a key: `[outbound]`, such as:
49
46
 
50
47
  ```
51
48
  [outbound]
@@ -74,27 +71,6 @@ string is attached as a `Received` header to all outbound mail just before it is
74
71
 
75
72
  Timeout for connecting to remote servers. Default: 30s
76
73
 
77
- * `pool_timeout`
78
-
79
- Outbound mail uses "pooled" connections. An unused pool connection will send
80
- a QUIT after this time. Default: 50s
81
-
82
- Pooled connections means that a mail to a particular IP address will hold that
83
- connection open and use it the next time it is requested. This helps with
84
- large scale outbound mail. If you don't send lots of mail it is advised to
85
- lower the `pool_timeout` value since it may upset receiving mail servers.
86
-
87
- Setting this value to `0` will effectively disable the use of pools. You may
88
- wish to set this if you have a `get_mx` hook that picks outbound servers on
89
- a per-email basis (rather than per-domain).
90
-
91
- * `pool_concurrency_max`
92
-
93
- Set this to `0` to completely disable the pooling code.
94
-
95
- This value determines how many concurrent connections can be made to a single
96
- IP address (destination) in the pool. Default: 10 connections.
97
-
98
74
  * `local_mx_ok`
99
75
 
100
76
  Default: false. By default, outbound to a local IP is disabled, to avoid creating
@@ -105,13 +81,9 @@ This could be useful if you want to deliver mail to localhost on another port.
105
81
 
106
82
  Set this to specify the delay intervals to use between trying to re-send an email
107
83
  that has a temporary failure condition. The setting is a comma separated list of
108
- time spans and multipliers. The time span is a number followed by `s`, `m`, `h`, or `d`
109
- to represent seconds, minutes, hours, and days, respectively. The multiplier is an
110
- asterisk followed by an integer representing the number of times to repeat the interval.
84
+ time spans and multipliers. The time span is a number followed by `s`, `m`, `h`, or `d` to represent seconds, minutes, hours, and days, respectively. The multiplier is an asterisk followed by an integer representing the number of times to repeat the interval.
111
85
  For example, the entry `1m, 5m*2, 1h*3` results in an array of delay times of
112
- `[60,300,300,3600,3600,3600]` in seconds. The email will be bounced when the array
113
- runs out of intervals (the 7th failure in this case). Set this to `none` to bounce the
114
- email on the first temporary failure.
86
+ `[60,300,300,3600,3600,3600]` in seconds. The email will be bounced when the array runs out of intervals (the 7th failure in this case). Set this to `none` to bounce the email on the first temporary failure.
115
87
 
116
88
  ### outbound.bounce\_message
117
89
 
@@ -150,6 +122,8 @@ you may be interested in are:
150
122
  ** outbound_helo - the EHLO domain to use (again, do not set manually)
151
123
  * queue_time - the epoch milliseconds time when this mail was queued
152
124
  * uuid - the original transaction.uuid
125
+ * force_tls - if true, this mail will be sent over TLS or defer
126
+
153
127
 
154
128
  Outbound Mail Hooks
155
129
  -------------------
@@ -268,7 +242,7 @@ interface (or alias) on the local system.
268
242
 
269
243
  As described above the outbound IP can be set using the `bind` parameter
270
244
  and also the outbound helo for the IP can be set using the `bind_ehlo`
271
- parameter returned my the `get_mx` hook or during the reception of the message
245
+ parameter returned by the `get_mx` hook or during the reception of the message
272
246
  you can set a transaction note in a plugin to tell Haraka which outbound IP
273
247
  address you would like it to use when it tries to deliver the message:
274
248
 
@@ -301,9 +275,7 @@ The contents of the bounce message are configured by a file called
301
275
  contains several template entries wrapped in curly brackets. These will be
302
276
  populated as follows:
303
277
 
304
- Optional: Possibility to add HTML code (with optional image) to the bounce message is possible
305
- by adding the files `config/outbound.bounce_message_html`. An image can be attached
306
- to the mail by using `config/outbound.bounce_message_image`.
278
+ Optional: Possibility to add HTML code (with optional image) to the bounce message is possible by adding the files `config/outbound.bounce_message_html`. An image can be attached to the mail by using `config/outbound.bounce_message_image`.
307
279
 
308
280
  * pid - the current process id
309
281
  * date - the current date when the bounce occurred
package/endpoint.js CHANGED
@@ -41,7 +41,7 @@ class Endpoint {
41
41
  toString () {
42
42
  if (this.mode) return `${this.path}:${this.mode}`;
43
43
  if (this.path) return this.path;
44
- if (this.host.indexOf(':') >= 0) return `[${this.host}]:${this.port}`;
44
+ if (this.host.includes(':')) return `[${this.host}]:${this.port}`;
45
45
  return `${this.host}:${this.port}`;
46
46
  }
47
47
 
package/haraka.js CHANGED
@@ -54,7 +54,7 @@ signals.forEach((sig) => {
54
54
 
55
55
  logger.lognotice(`${sig} received`);
56
56
  logger.dump_and_exit(() => {
57
- if (server.cluster && server.cluster.isMaster) {
57
+ if (server.cluster?.isMaster) {
58
58
  server.performShutdown();
59
59
  }
60
60
  else if (!server.cluster) {
package/host_pool.js CHANGED
@@ -28,7 +28,6 @@ class HostPool {
28
28
  // takes a comma/space-separated list of ip:ports
29
29
  // 1.1.1.1:22, 3.3.3.3:44
30
30
  constructor (hostports_str, retry_secs) {
31
- const self = this;
32
31
  const hosts = (hostports_str || '')
33
32
  .trim()
34
33
  .split(/[\s,]+/)
@@ -42,11 +41,11 @@ class HostPool {
42
41
  port: splithost[1]
43
42
  };
44
43
  });
45
- self.hostports_str = hostports_str;
46
- self.hosts = utils.shuffle(hosts);
47
- self.dead_hosts = {}; // hostport => true/false
48
- self.last_i = 0; // the last one we checked
49
- self.retry_secs = retry_secs || 10;
44
+ this.hostports_str = hostports_str;
45
+ this.hosts = utils.shuffle(hosts);
46
+ this.dead_hosts = {}; // hostport => true/false
47
+ this.last_i = 0; // the last one we checked
48
+ this.retry_secs = retry_secs || 10;
50
49
  }
51
50
 
52
51
  /* failed
@@ -91,14 +90,12 @@ class HostPool {
91
90
  probe_dead_host (
92
91
  host, port, cb_if_still_dead, cb_if_alive
93
92
  ){
94
-
95
- const self = this;
96
93
  logger.loginfo(`probing dead host ${host}:${port}`);
97
94
 
98
95
  const connect_timeout_ms = 200; // keep it snappy
99
96
  let s;
100
97
  try {
101
- s = self.get_socket();
98
+ s = this.get_socket();
102
99
  s.setTimeout(connect_timeout_ms, () => {
103
100
  // nobody home, it's still dead
104
101
  s.destroy();
@@ -127,8 +124,7 @@ class HostPool {
127
124
  * so we can override in unit test
128
125
  */
129
126
  get_socket () {
130
- const s = new net.Socket();
131
- return s;
127
+ return new net.Socket();
132
128
  }
133
129
 
134
130
  /* get_host
@@ -151,7 +147,7 @@ class HostPool {
151
147
  for (let i = 0; i < this.hosts.length; ++i){
152
148
  let j = i + first_i;
153
149
  if (j >= this.hosts.length) {
154
- j = j - this.hosts.length;
150
+ j -= this.hosts.length;
155
151
  }
156
152
  host = this.hosts[j];
157
153
  const key = `${host.host}:${host.port}`;