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/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,72 @@ 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 record of res) {
314
306
  if (!record.includes('p=')) {
315
- self.debug(`${self.identity}: ignoring TXT record: ${record}`);
307
+ this.debug(`${this.identity}: ignoring TXT record: ${record}`);
316
308
  continue;
317
309
  }
318
- self.debug(`${self.identity}: got DNS record: ${record}`);
310
+ this.debug(`${this.identity}: got DNS record: ${record}`);
319
311
  const rec = record.replace(/\r?\n/g, '').replace(/\s+/g,'');
320
312
  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];
313
+ for (const element of split) {
314
+ const split2 = element.split('=');
315
+ if (split2[0]) this.dns_fields[split2[0]] = split2[1];
324
316
  }
325
317
 
326
318
  // Validate
327
- if (!self.dns_fields.v || self.dns_fields.v !== 'DKIM1') {
328
- return self.result('invalid version', 'invalid');
319
+ if (!this.dns_fields.v || this.dns_fields.v !== 'DKIM1') {
320
+ return this.result('invalid version', 'invalid');
329
321
  }
330
- if (self.dns_fields.g) {
331
- if (self.dns_fields.g !== '*') {
332
- let s = self.dns_fields.g;
322
+ if (this.dns_fields.g) {
323
+ if (this.dns_fields.g !== '*') {
324
+ let s = this.dns_fields.g;
333
325
  // Escape any special regexp characters
334
326
  s = s.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
335
327
  // Make * a non-greedy match against anything except @
336
328
  s = s.replace('\\*','[^@]*?');
337
329
  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');
330
+ this.debug(`${this.identity}: matching ${this.dns_fields.g} against i=${this.fields.i} regexp=${reg.toString()}`);
331
+ if (!reg.test(this.fields.i)) {
332
+ return this.result('inapplicable key', 'invalid');
341
333
  }
342
334
  }
343
335
  }
344
336
  else {
345
- return self.result('inapplicable key', 'invalid');
337
+ return this.result('inapplicable key', 'invalid');
346
338
  }
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');
339
+ if (this.dns_fields.h) {
340
+ const hashes = this.dns_fields.h.split(':');
341
+ for (const hashElement of hashes) {
342
+ const hash = hashElement.trim();
343
+ if (!this.fields.a.includes(hash)) {
344
+ return this.result('inappropriate hash algorithm', 'invalid');
353
345
  }
354
346
  }
355
347
  }
356
- if (self.dns_fields.k) {
357
- if (!self.fields.a.includes(self.dns_fields.k)) {
358
- return self.result('inappropriate key type', 'invalid');
348
+ if (this.dns_fields.k) {
349
+ if (!this.fields.a.includes(this.dns_fields.k)) {
350
+ return this.result('inappropriate key type', 'invalid');
359
351
  }
360
352
  }
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();
353
+ if (this.dns_fields.t) {
354
+ const flags = this.dns_fields.t.split(':');
355
+ for (const flagElement of flags) {
356
+ const flag = flagElement.trim();
365
357
  if (flag === 'y') {
366
358
  // Test mode
367
- self.test_mode = true;
359
+ this.test_mode = true;
368
360
  }
369
361
  else if (flag === 's') {
370
362
  // 'i' and 'd' domain much match exactly
371
- let i = self.fields.i
363
+ let { i } = this.fields
372
364
  i = i.substr(i.indexOf('@')+1, i.length)
373
365
  if (i.toLowerCase() !== this.fields.d.toLowerCase()) {
374
366
  return this.result('i/d selector domain mismatch (t=s)', 'invalid')
@@ -376,26 +368,27 @@ class DKIMObject {
376
368
  }
377
369
  }
378
370
  }
379
- if (!self.dns_fields.p) return self.result('key revoked', 'invalid');
371
+ if (!this.dns_fields.p) return this.result('key revoked', 'invalid');
380
372
 
381
373
  // 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')
374
+ this.public_key = `-----BEGIN PUBLIC KEY-----\r\n${
375
+
376
+ this.dns_fields.p.replace(/(.{1,76})/g, '$1\r\n')
384
377
  }-----END PUBLIC KEY-----\r\n`;
385
378
 
386
379
  let verified;
387
380
  try {
388
- verified = self.verifier.verify(self.public_key, self.fields.b, 'base64');
389
- self.debug(`${self.identity}: verified=${verified}`);
381
+ verified = this.verifier.verify(this.public_key, this.fields.b, 'base64');
382
+ this.debug(`${this.identity}: verified=${verified}`);
390
383
  }
391
384
  catch (e) {
392
- self.debug(`${self.identity}: verification error: ${e.message}`);
393
- return self.result('verification error', 'invalid');
385
+ this.debug(`${this.identity}: verification error: ${e.message}`);
386
+ return this.result('verification error', 'invalid');
394
387
  }
395
- return self.result(null, ((verified) ? 'pass' : 'fail'));
388
+ return this.result(null, ((verified) ? 'pass' : 'fail'));
396
389
  }
397
390
  // We didn't find a valid DKIM record for this signature
398
- self.result('no key for signature', 'invalid');
391
+ this.result('no key for signature', 'invalid');
399
392
  });
400
393
  }
401
394
 
@@ -508,9 +501,9 @@ class DKIMVerifyStream extends Stream {
508
501
 
509
502
  // Set the overall result based on this precedence order
510
503
  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];
504
+ for (const element of rr) {
505
+ if (!self.result || (self.result && self.result !== element && result.result === element)) {
506
+ self.result = element;
514
507
  }
515
508
  }
516
509
  }
@@ -543,14 +536,14 @@ class DKIMVerifyStream extends Stream {
543
536
  if (!this._in_body) {
544
537
  this._in_body = true;
545
538
  // Parse the headers
546
- for (let h=0; h<this.headers.length; h++) {
547
- const match = /^([^: ]+):\s*((:?.|[\r\n])*)/.exec(this.headers[h]);
539
+ for (const header of this.headers) {
540
+ const match = /^([^: ]+):\s*((:?.|[\r\n])*)/.exec(header);
548
541
  if (!match) continue;
549
542
  const header_name = match[1];
550
543
  if (!header_name) continue;
551
544
  const hn = header_name.toLowerCase();
552
545
  if (!this.header_idx[hn]) this.header_idx[hn] = [];
553
- this.header_idx[hn].push(this.headers[h]);
546
+ this.header_idx[hn].push(header);
554
547
  }
555
548
  if (!this.header_idx['dkim-signature']) {
556
549
  this._no_signatures_found = true;
@@ -563,8 +556,8 @@ class DKIMVerifyStream extends Stream {
563
556
  const dkim_headers = this.header_idx['dkim-signature'];
564
557
  this.debug(`Found ${dkim_headers.length} DKIM signatures`);
565
558
  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));
559
+ for (const dkimHeader of dkim_headers) {
560
+ this.dkim_objects.push(new DKIMObject(dkimHeader, this.header_idx, callback, this.opts));
568
561
  }
569
562
  if (this.pending === 0) {
570
563
  process.nextTick(() => {
@@ -587,8 +580,8 @@ class DKIMVerifyStream extends Stream {
587
580
  }
588
581
  }
589
582
  else {
590
- for (let e=0; e<this.dkim_objects.length; e++) {
591
- this.dkim_objects[e].add_body_line(line);
583
+ for (const dkimObject of this.dkim_objects) {
584
+ dkimObject.add_body_line(line);
592
585
  }
593
586
  }
594
587
  if (once) {
@@ -606,13 +599,12 @@ class DKIMVerifyStream extends Stream {
606
599
 
607
600
  end (buf) {
608
601
  this.handle_buf(((buf) ? buf : null));
609
- for (let d=0; d<this.dkim_objects.length; d++) {
610
- this.dkim_objects[d].end();
602
+ for (const dkimObject of this.dkim_objects) {
603
+ dkimObject.end();
611
604
  }
612
605
  if (this.pending === 0 && this._no_signatures_found === false) {
613
- const self = this;
614
606
  process.nextTick(() => {
615
- self.cb(null, self.result, self.results);
607
+ this.cb(null, this.result, this.results);
616
608
  });
617
609
  }
618
610
  }
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}`;