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
@@ -4,12 +4,11 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
6
  exports.register = function () {
7
- const plugin = this;
8
7
 
9
- plugin.load_quarantine_ini();
8
+ this.load_quarantine_ini();
10
9
 
11
- plugin.register_hook('queue', 'quarantine');
12
- plugin.register_hook('queue_outbound', 'quarantine');
10
+ this.register_hook('queue', 'quarantine');
11
+ this.register_hook('queue_outbound', 'quarantine');
13
12
  }
14
13
 
15
14
  exports.hook_init_master = function (next, server) {
@@ -19,9 +18,8 @@ exports.hook_init_master = function (next, server) {
19
18
  }
20
19
 
21
20
  exports.load_quarantine_ini = function () {
22
- const plugin = this;
23
- plugin.cfg = plugin.config.get('quarantine.ini', () => {
24
- plugin.load_quarantine_ini();
21
+ this.cfg = this.config.get('quarantine.ini', () => {
22
+ this.load_quarantine_ini();
25
23
  })
26
24
  }
27
25
 
@@ -34,33 +32,26 @@ const zeroPad = exports.zeroPad = (n, digits) => {
34
32
  }
35
33
 
36
34
  exports.clean_tmp_directory = function (next) {
37
- // At start-up; delete any files in the temporary directory
38
- // NOTE: This is deliberately syncronous to ensure that this
39
- // is completed prior to any messages being received.
40
- const plugin = this;
41
- const tmp_dir = path.join(plugin.get_base_dir(), 'tmp');
35
+ const tmp_dir = path.join(this.get_base_dir(), 'tmp');
42
36
 
43
37
  if (fs.existsSync(tmp_dir)) {
44
38
  const dirent = fs.readdirSync(tmp_dir);
45
- plugin.loginfo(`Removing temporary files from: ${tmp_dir}`);
46
- for (let i=0; i<dirent.length; i++) {
47
- fs.unlinkSync(path.join(tmp_dir, dirent[i]));
39
+ this.loginfo(`Removing temporary files from: ${tmp_dir}`);
40
+ for (const element of dirent) {
41
+ fs.unlinkSync(path.join(tmp_dir, element));
48
42
  }
49
43
  }
50
44
  next();
51
45
  }
52
46
 
53
47
  function wants_quarantine (connection) {
54
- if (connection.notes.quarantine)
55
- return connection.notes.quarantine;
48
+ const { notes, transaction } = connection ?? {}
56
49
 
57
- if (connection.transaction.notes.quarantine)
58
- return connection.transaction.notes.quarantine;
50
+ if (notes.quarantine) return notes.quarantine;
59
51
 
60
- if (connection.transaction.notes.get('queue.wants') === 'quarantine')
61
- return true;
52
+ if (transaction.notes.quarantine) return transaction.notes.quarantine;
62
53
 
63
- return false;
54
+ return transaction.notes.get('queue.wants') === 'quarantine';
64
55
  }
65
56
 
66
57
  exports.get_base_dir = function () {
@@ -69,19 +60,17 @@ exports.get_base_dir = function () {
69
60
  }
70
61
 
71
62
  exports.init_quarantine_dir = function (done) {
72
- const plugin = this;
73
- const tmp_dir = path.join(plugin.get_base_dir(), 'tmp');
63
+ const tmp_dir = path.join(this.get_base_dir(), 'tmp');
74
64
  fs.promises.mkdir(tmp_dir, { recursive: true })
75
- .then(made => plugin.loginfo(`created ${tmp_dir}`))
76
- .catch(err => plugin.logerror(`Unable to create ${tmp_dir}`))
65
+ .then(made => this.loginfo(`created ${tmp_dir}`))
66
+ .catch(err => this.logerror(`Unable to create ${tmp_dir}`))
77
67
  .finally(done);
78
68
  }
79
69
 
80
70
  exports.quarantine = function (next, connection) {
81
- const plugin = this;
82
71
 
83
72
  const quarantine = wants_quarantine(connection);
84
- plugin.logdebug(`quarantine: ${quarantine}`);
73
+ this.logdebug(`quarantine: ${quarantine}`);
85
74
  if (!quarantine) return next();
86
75
 
87
76
  // Calculate date in YYYYMMDD format
@@ -96,8 +85,10 @@ exports.quarantine = function (next, connection) {
96
85
  subdir = path.join(quarantine, yyyymmdd);
97
86
  }
98
87
 
99
- const txn = connection.transaction;
100
- const base_dir = plugin.get_base_dir();
88
+ const txn = connection?.transaction;
89
+ if (!txn) return next();
90
+
91
+ const base_dir = this.get_base_dir();
101
92
  const msg_dir = path.join(base_dir, subdir);
102
93
  const tmp_path = path.join(base_dir, 'tmp', txn.uuid);
103
94
  const msg_path = path.join(msg_dir, txn.uuid);
@@ -109,25 +100,25 @@ exports.quarantine = function (next, connection) {
109
100
  // final destination.
110
101
  fs.promises.mkdir(msg_dir, { recursive: true })
111
102
  .catch(err => {
112
- connection.logerror(plugin, `Error creating directory: ${msg_dir}`);
103
+ connection.logerror(this, `Error creating directory: ${msg_dir}`);
113
104
  next();
114
105
  })
115
106
  .then(ok => {
116
107
  const ws = fs.createWriteStream(tmp_path);
117
108
 
118
109
  ws.on('error', err => {
119
- connection.logerror(plugin, `Error writing quarantine file: ${err.message}`);
110
+ connection.logerror(this, `Error writing quarantine file: ${err.message}`);
120
111
  return next();
121
112
  });
122
113
  ws.on('close', () => {
123
114
  fs.link(tmp_path, msg_path, err => {
124
115
  if (err) {
125
- connection.logerror(plugin, `Error writing quarantine file: ${err}`);
116
+ connection.logerror(this, `Error writing quarantine file: ${err}`);
126
117
  }
127
118
  else {
128
119
  // Add a note to where we stored the message
129
120
  txn.notes.quarantined = msg_path;
130
- txn.results.add(plugin, { pass: msg_path, emit: true });
121
+ txn.results.add(this, { pass: msg_path, emit: true });
131
122
  // Now delete the temporary file
132
123
  fs.unlink(tmp_path, () => {});
133
124
  }
@@ -20,6 +20,8 @@ exports.register = function () {
20
20
 
21
21
  //Actual magic of publishing message to rabbit when email comes happen here.
22
22
  exports.hook_queue = (next, connection) => {
23
+ if (!connection?.transaction) return next();
24
+
23
25
  //Calling the get_data method and when it gets the data on callback, publish the message to queue with routing key.
24
26
  connection.transaction.message_stream.get_data(buffere => {
25
27
  const exchangeData = exports.exchangeMapping[exchangeName + queueName]
@@ -51,11 +53,10 @@ exports.hook_queue = (next, connection) => {
51
53
 
52
54
  //This initializes the connection to rabbitmq server, It reads values from rabbitmq.ini file in config directory.
53
55
  exports.init_rabbitmq_server = function () {
54
- const plugin = this;
55
56
  // this is called during init of rabbitmq
56
57
 
57
58
  //Read the config file rabbitmq
58
- const config = plugin.config.get('rabbitmq.ini');
59
+ const config = this.config.get('rabbitmq.ini');
59
60
  //Just putting the defaults
60
61
  const options = {};
61
62
  let confirm = true;
@@ -11,20 +11,20 @@ exports.register = function () {
11
11
  }
12
12
 
13
13
  exports.rabbitmq_queue = function (next, connection) {
14
- const plugin = this;
14
+ if (!connection?.transaction) return next();
15
+
15
16
  connection.transaction.message_stream.get_data(str => {
16
- if (channel && channel.sendToQueue(queue, str, {deliveryMode})) {
17
+ if (channel?.sendToQueue(queue, str, {deliveryMode})) {
17
18
  return next(OK);
18
19
  }
19
20
  else {
20
- plugin.logerror("Failed to queue to rabbitmq");
21
+ this.logerror("Failed to queue to rabbitmq");
21
22
  return next();
22
23
  }
23
24
  });
24
25
  }
25
26
 
26
27
  exports.init_amqp_connection = function () {
27
- const plugin = this;
28
28
  const cfg = this.config.get("rabbitmq.ini").rabbitmq;
29
29
 
30
30
  const protocol = cfg.protocol || "amqp";
@@ -43,30 +43,30 @@ exports.init_amqp_connection = function () {
43
43
 
44
44
  amqp.connect(`${protocol}://${encodeURIComponent(user)}:${encodeURIComponent(password)}@${host}:${port}${vhost}`, (err, conn) => {
45
45
  if (err) {
46
- plugin.logerror(`Connection to rabbitmq failed: ${err}`);
46
+ this.logerror(`Connection to rabbitmq failed: ${err}`);
47
47
  return;
48
48
  }
49
49
  // TODO: if !confirm conn.createChannel...
50
50
  conn.createConfirmChannel((err2, ch) => {
51
51
  if (err2) {
52
- plugin.logerror(`Error creating rabbitmq channel: ${err2}`);
52
+ this.logerror(`Error creating rabbitmq channel: ${err2}`);
53
53
  return conn.close();
54
54
  }
55
55
  ch.assertExchange(exchangeName, exchangeType, {durable}, (err3, ok) => {
56
56
  if (err3) {
57
- plugin.logerror(`Error asserting rabbitmq exchange: ${err3}`);
57
+ this.logerror(`Error asserting rabbitmq exchange: ${err3}`);
58
58
  return conn.close();
59
59
  }
60
60
  ch.assertQueue(queueName,
61
61
  {durable, autoDelete},
62
62
  (err4, ok2) => {
63
63
  if (err4) {
64
- plugin.logerror(`Error asserting rabbitmq queue: ${err4}`);
64
+ this.logerror(`Error asserting rabbitmq queue: ${err4}`);
65
65
  return conn.close();
66
66
  }
67
67
  queue = ok2.queue;
68
68
  channel = ch;
69
- plugin.register_hook('queue', 'rabbitmq_queue');
69
+ this.register_hook('queue', 'rabbitmq_queue');
70
70
  }
71
71
  );
72
72
  });
@@ -6,14 +6,15 @@ exports.register = function () {
6
6
  }
7
7
 
8
8
  exports.load_flat_ini = function () {
9
- const plugin = this;
10
- plugin.cfg = plugin.config.get('smtp_bridge.ini', () => {
11
- plugin.load_flat_ini();
9
+ this.cfg = this.config.get('smtp_bridge.ini', () => {
10
+ this.load_flat_ini();
12
11
  });
13
12
  }
14
13
 
15
14
  exports.hook_data_post = (next, connection) => {
16
- const txn = connection.transaction;
15
+ const txn = connection?.transaction;
16
+ if (!txn) return next();
17
+
17
18
  // Copy auth notes to transaction notes so they're available in hmail.todo.notes
18
19
  txn.notes.auth_user = connection.notes.auth_user;
19
20
  txn.notes.auth_passwd = connection.notes.auth_passwd;
@@ -9,33 +9,35 @@ const url = require('url');
9
9
  const smtp_client_mod = require('./smtp_client');
10
10
 
11
11
  exports.register = function () {
12
- const plugin = this;
13
- plugin.load_errs = [];
12
+ this.load_errs = [];
14
13
 
15
- plugin.load_smtp_forward_ini();
14
+ this.load_smtp_forward_ini();
16
15
 
17
- if (plugin.load_errs.length > 0) return;
16
+ if (this.load_errs.length > 0) return;
18
17
 
19
- if (plugin.cfg.main.check_sender) {
20
- plugin.register_hook('mail', 'check_sender');
18
+ if (this.cfg.main.check_sender) {
19
+ this.register_hook('mail', 'check_sender');
21
20
  }
22
21
 
23
- if (plugin.cfg.main.check_recipient) {
24
- plugin.register_hook('rcpt', 'check_recipient');
22
+ if (this.cfg.main.check_recipient) {
23
+ this.register_hook('rcpt', 'check_recipient');
25
24
  }
26
25
 
27
- plugin.register_hook('queue', 'queue_forward');
26
+ this.register_hook('queue', 'queue_forward');
27
+
28
+ if (this.cfg.main.enable_outbound) {
29
+ this.register_hook('queue_outbound', 'queue_forward');
30
+ }
28
31
 
29
- plugin.register_hook('get_mx', 'get_mx'); // for relaying outbound messages
32
+ this.register_hook('get_mx', 'get_mx'); // for relaying outbound messages
30
33
  }
31
34
 
32
35
  exports.load_smtp_forward_ini = function () {
33
- const plugin = this;
34
36
 
35
- plugin.cfg = plugin.config.get('smtp_forward.ini', {
37
+ this.cfg = this.config.get('smtp_forward.ini', {
36
38
  booleans: [
37
39
  '-main.enable_tls',
38
- '+main.enable_outbound',
40
+ '-main.enable_outbound',
39
41
  'main.one_message_per_rcpt',
40
42
  '-main.check_sender',
41
43
  '-main.check_recipient',
@@ -44,93 +46,86 @@ exports.load_smtp_forward_ini = function () {
44
46
  ],
45
47
  },
46
48
  () => {
47
- plugin.load_smtp_forward_ini();
49
+ this.load_smtp_forward_ini();
48
50
  });
49
-
50
- if (plugin.cfg.main.enable_outbound) {
51
- plugin.lognotice('outbound enabled, will default to disabled in Haraka v3 (see #1472)');
52
- }
53
51
  }
54
52
 
55
53
  exports.get_config = function (conn) {
56
- const plugin = this;
57
54
 
58
- if (!conn.transaction) return plugin.cfg.main;
55
+ if (!conn.transaction) return this.cfg.main;
59
56
 
60
57
  let dom;
61
- if (plugin.cfg.main.domain_selector === 'mail_from') {
62
- if (!conn.transaction.mail_from) return plugin.cfg.main;
58
+ if (this.cfg.main.domain_selector === 'mail_from') {
59
+ if (!conn.transaction.mail_from) return this.cfg.main;
63
60
  dom = conn.transaction.mail_from.host;
64
61
  }
65
62
  else {
66
- if (!conn.transaction.rcpt_to[0]) return plugin.cfg.main;
63
+ if (!conn.transaction.rcpt_to[0]) return this.cfg.main;
67
64
  dom = conn.transaction.rcpt_to[0].host;
68
65
  }
69
66
 
70
- if (!dom) return plugin.cfg.main;
71
- if (!plugin.cfg[dom]) return plugin.cfg.main; // no specific route
67
+ if (!dom) return this.cfg.main;
68
+ if (!this.cfg[dom]) return this.cfg.main; // no specific route
72
69
 
73
- return plugin.cfg[dom];
70
+ return this.cfg[dom];
74
71
  }
75
72
 
76
73
  exports.is_outbound_enabled = function (dom_cfg) {
77
- const plugin = this;
78
74
 
79
75
  if ('enable_outbound' in dom_cfg) return dom_cfg.enable_outbound; // per-domain flag
80
76
 
81
- return plugin.cfg.main.enable_outbound; // follow the global configuration
77
+ return this.cfg.main.enable_outbound; // follow the global configuration
82
78
  };
83
79
 
84
80
  exports.check_sender = function (next, connection, params) {
85
- const plugin = this;
86
- if (!connection.transaction) return;
87
- const txn = connection.transaction;
81
+ const txn = connection?.transaction;
82
+ if (!txn) return;
88
83
 
89
84
  const email = params[0].address();
90
85
  if (!email) {
91
- txn.results.add(plugin, {skip: 'mail_from.null', emit: true});
86
+ txn.results.add(this, {skip: 'mail_from.null', emit: true});
92
87
  return next();
93
88
  }
94
89
 
95
90
  const domain = params[0].host.toLowerCase();
96
- if (!plugin.cfg[domain]) return next();
91
+ if (!this.cfg[domain]) return next();
97
92
 
98
93
  // domain is defined in smtp_forward.ini
99
94
  txn.notes.local_sender = true;
100
95
 
101
96
  if (!connection.relaying) {
102
- txn.results.add(plugin, {fail: 'mail_from!spoof'});
97
+ txn.results.add(this, {fail: 'mail_from!spoof'});
103
98
  return next(DENY, "Spoofed MAIL FROM");
104
99
  }
105
100
 
106
- txn.results.add(plugin, {pass: 'mail_from'});
101
+ txn.results.add(this, {pass: 'mail_from'});
107
102
  return next();
108
103
  }
109
104
 
110
105
  exports.set_queue = function (connection, queue_wanted, domain) {
111
- const plugin = this;
112
106
 
113
- let dom_cfg = plugin.cfg[domain];
107
+ let dom_cfg = this.cfg[domain];
114
108
  if (dom_cfg === undefined) dom_cfg = {};
115
109
 
116
- if (!queue_wanted) queue_wanted = dom_cfg.queue || plugin.cfg.main.queue;
110
+ if (!queue_wanted) queue_wanted = dom_cfg.queue || this.cfg.main.queue;
117
111
  if (!queue_wanted) return true;
118
112
 
119
- let dst_host = dom_cfg.host || plugin.cfg.main.host;
113
+
114
+ let dst_host = dom_cfg.host || this.cfg.main.host;
120
115
  if (dst_host) dst_host = `smtp://${dst_host}`;
121
116
 
122
- const notes = connection.transaction.notes;
117
+ const notes = connection?.transaction?.notes;
118
+ if (!notes) return false;
123
119
  if (!notes.get('queue.wants')) {
124
120
  notes.set('queue.wants', queue_wanted);
125
- if (dst_host) {
126
- notes.set('queue.next_hop', dst_host);
127
- }
121
+ if (dst_host) notes.set('queue.next_hop', dst_host);
128
122
  return true;
129
123
  }
130
124
 
131
125
  // multiple recipients with same destination
132
126
  if (notes.get('queue.wants') === queue_wanted) {
133
127
  if (!dst_host) return true;
128
+
134
129
  const next_hop = notes.get('queue.next_hop');
135
130
  if (!next_hop) return true;
136
131
  if (next_hop === dst_host) return true;
@@ -141,49 +136,47 @@ exports.set_queue = function (connection, queue_wanted, domain) {
141
136
  }
142
137
 
143
138
  exports.check_recipient = function (next, connection, params) {
144
- const plugin = this;
145
- const txn = connection.transaction;
139
+ const txn = connection?.transaction;
146
140
  if (!txn) return;
147
141
 
148
142
  const rcpt = params[0];
149
143
  if (!rcpt.host) {
150
- txn.results.add(plugin, {skip: 'rcpt!domain'});
144
+ txn.results.add(this, {skip: 'rcpt!domain'});
151
145
  return next();
152
146
  }
153
147
 
154
148
  if (connection.relaying && txn.notes.local_sender) {
155
- plugin.set_queue(connection, 'outbound');
156
- txn.results.add(plugin, {pass: 'relaying local_sender'});
149
+ this.set_queue(connection, 'outbound');
150
+ txn.results.add(this, {pass: 'relaying local_sender'});
157
151
  return next(OK);
158
152
  }
159
153
 
160
154
  const domain = rcpt.host.toLowerCase();
161
- if (plugin.cfg[domain] !== undefined) {
162
- if (plugin.set_queue(connection, 'smtp_forward', domain)) {
163
- txn.results.add(plugin, {pass: 'rcpt_to'});
155
+ if (this.cfg[domain] !== undefined) {
156
+ if (this.set_queue(connection, 'smtp_forward', domain)) {
157
+ txn.results.add(this, {pass: 'rcpt_to'});
164
158
  return next(OK);
165
159
  }
166
- txn.results.add(plugin, {pass: 'rcpt_to.split'});
160
+ txn.results.add(this, {pass: 'rcpt_to.split'});
167
161
  return next(DENYSOFT, "Split transaction, retry soon");
168
162
  }
169
163
 
170
164
  // the MAIL FROM domain is not local and neither is the RCPT TO
171
165
  // Another RCPT plugin may vouch for this recipient.
172
- txn.results.add(plugin, {msg: 'rcpt!local'});
166
+ txn.results.add(this, {msg: 'rcpt!local'});
173
167
  return next();
174
168
  }
175
169
 
176
170
  exports.auth = function (cfg, connection, smtp_client) {
177
- const plugin = this;
178
171
 
179
- connection.loginfo(plugin, `Configuring authentication for SMTP server ${cfg.host}:${cfg.port}`);
172
+ connection.loginfo(this, `Configuring authentication for SMTP server ${cfg.host}:${cfg.port}`);
180
173
  smtp_client.on('capabilities', () => {
181
- connection.loginfo(plugin, 'capabilities received');
174
+ connection.loginfo(this, 'capabilities received');
182
175
 
183
176
  if ('secured' in smtp_client) {
184
- connection.loginfo(plugin, 'secured is pending');
177
+ connection.loginfo(this, 'secured is pending');
185
178
  if (smtp_client.secured === false) {
186
- connection.loginfo(plugin, "Waiting for STARTTLS to complete. AUTH postponed");
179
+ connection.loginfo(this, "Waiting for STARTTLS to complete. AUTH postponed");
187
180
  return;
188
181
  }
189
182
  }
@@ -194,18 +187,19 @@ exports.auth = function (cfg, connection, smtp_client) {
194
187
  }
195
188
 
196
189
  if (cfg.auth_type === 'plain') {
197
- connection.loginfo(plugin, `Authenticating with AUTH PLAIN ${cfg.auth_user}`);
190
+ connection.loginfo(this, `Authenticating with AUTH PLAIN ${cfg.auth_user}`);
198
191
  smtp_client.send_command('AUTH', `PLAIN ${base64(`\0${cfg.auth_user}\0${cfg.auth_pass}`)}`);
192
+ return
199
193
  }
200
- else if (cfg.auth_type === 'login') {
194
+
195
+ if (cfg.auth_type === 'login') {
201
196
  smtp_client.authenticating = true;
202
197
  smtp_client.authenticated = false;
203
198
 
204
- connection.loginfo(plugin, `Authenticating with AUTH LOGIN ${cfg.auth_user}`);
199
+ connection.loginfo(this, `Authenticating with AUTH LOGIN ${cfg.auth_user}`);
205
200
  smtp_client.send_command('AUTH', 'LOGIN');
206
201
  smtp_client.on('auth', () => {
207
- //TODO: nothing?
208
-
202
+ // do nothing
209
203
  });
210
204
  smtp_client.on('auth_username', () => {
211
205
  smtp_client.send_command(base64(cfg.auth_user));
@@ -218,16 +212,15 @@ exports.auth = function (cfg, connection, smtp_client) {
218
212
  }
219
213
 
220
214
  exports.forward_enabled = function (conn, dom_cfg) {
221
- const plugin = this;
222
215
 
223
216
  const q_wants = conn.transaction.notes.get('queue.wants');
224
217
  if (q_wants && q_wants !== 'smtp_forward') {
225
- conn.logdebug(plugin, `skipping, unwanted (${q_wants})`);
218
+ conn.logdebug(this, `skipping, unwanted (${q_wants})`);
226
219
  return false;
227
220
  }
228
221
 
229
- if (conn.relaying && !plugin.is_outbound_enabled(dom_cfg)) {
230
- conn.logdebug(plugin, 'skipping, outbound disabled');
222
+ if (conn.relaying && !this.is_outbound_enabled(dom_cfg)) {
223
+ conn.logdebug(this, 'skipping, outbound disabled');
231
224
  return false;
232
225
  }
233
226
 
@@ -236,30 +229,29 @@ exports.forward_enabled = function (conn, dom_cfg) {
236
229
 
237
230
  exports.queue_forward = function (next, connection) {
238
231
  const plugin = this;
239
- const conn = connection;
240
- const txn = connection.transaction;
232
+ const txn = connection?.transaction;
241
233
 
242
- const cfg = plugin.get_config(conn);
243
- if (!plugin.forward_enabled(conn, cfg)) return next();
234
+ const cfg = plugin.get_config(connection);
235
+ if (!plugin.forward_enabled(connection, cfg)) return next();
244
236
 
245
- smtp_client_mod.get_client_plugin(plugin, conn, cfg, (err, smtp_client) => {
237
+ smtp_client_mod.get_client_plugin(plugin, connection, cfg, (err, smtp_client) => {
246
238
  smtp_client.next = next;
247
239
 
248
240
  let rcpt = 0;
249
241
 
250
- if (cfg.auth_user) plugin.auth(cfg, conn, smtp_client);
242
+ if (cfg.auth_user) plugin.auth(cfg, connection, smtp_client);
251
243
 
252
- conn.loginfo(plugin, `forwarding to ${
244
+ connection.loginfo(plugin, `forwarding to ${
253
245
  cfg.forwarding_host_pool ? 'host_pool' : `${cfg.host}:${cfg.port}`}`
254
246
  );
255
247
 
256
248
  function get_rs () {
257
- if (connection.transaction) return txn.results;
258
- return conn.results;
249
+ if (txn) return txn.results;
250
+ return connection.results;
259
251
  }
260
252
 
261
253
  function dead_sender () {
262
- if (smtp_client.is_dead_sender(plugin, conn)) {
254
+ if (smtp_client.is_dead_sender(plugin, connection)) {
263
255
  get_rs().add(plugin, { err: 'dead sender' });
264
256
  return true;
265
257
  }
@@ -267,7 +259,7 @@ exports.queue_forward = function (next, connection) {
267
259
  }
268
260
 
269
261
  function send_rcpt () {
270
- if (dead_sender()) return;
262
+ if (dead_sender() || !txn) return;
271
263
  if (rcpt === txn.rcpt_to.length) {
272
264
  smtp_client.send_command('DATA');
273
265
  return;
@@ -293,7 +285,8 @@ exports.queue_forward = function (next, connection) {
293
285
  });
294
286
 
295
287
  smtp_client.on('dot', () => {
296
- if (dead_sender()) return;
288
+ if (dead_sender() || !txn) return;
289
+
297
290
  get_rs().add(plugin, { pass: smtp_client.response });
298
291
  if (rcpt < txn.rcpt_to.length) {
299
292
  smtp_client.send_command('RSET');
@@ -304,12 +297,12 @@ exports.queue_forward = function (next, connection) {
304
297
  });
305
298
 
306
299
  smtp_client.on('rset', () => {
307
- if (dead_sender()) return;
300
+ if (dead_sender() || !txn) return;
308
301
  smtp_client.send_command('MAIL', `FROM:${txn.mail_from}`);
309
302
  });
310
303
 
311
304
  smtp_client.on('bad_code', (code, msg) => {
312
- if (dead_sender()) return;
305
+ if (dead_sender() || !txn) return;
313
306
  smtp_client.call_next(((code && code[0] === '5') ? DENY : DENYSOFT),
314
307
  msg);
315
308
  smtp_client.release();
@@ -333,18 +326,17 @@ exports.get_mx_next_hop = next_hop => {
333
326
  }
334
327
 
335
328
  exports.get_mx = function (next, hmail, domain) {
336
- const plugin = this;
337
329
 
338
330
  // hmail.todo not defined in tests.
339
331
  if (hmail.todo.notes.next_hop) {
340
- return next(OK, plugin.get_mx_next_hop(hmail.todo.notes.next_hop));
332
+ return next(OK, this.get_mx_next_hop(hmail.todo.notes.next_hop));
341
333
  }
342
334
 
343
- const dom = plugin.cfg.main.domain_selector === 'mail_from' ? hmail.todo.mail_from.host.toLowerCase() : domain.toLowerCase();
344
- const cfg = plugin.cfg[dom];
335
+ const dom = this.cfg.main.domain_selector === 'mail_from' ? hmail.todo.mail_from.host.toLowerCase() : domain.toLowerCase();
336
+ const cfg = this.cfg[dom];
345
337
 
346
338
  if (cfg === undefined) {
347
- plugin.logdebug(`using DNS MX for: ${domain}`);
339
+ this.logdebug(`using DNS MX for: ${domain}`);
348
340
  return next();
349
341
  }
350
342
 
@@ -355,14 +347,14 @@ exports.get_mx = function (next, hmail, domain) {
355
347
 
356
348
  const mx = {
357
349
  priority: 0,
358
- exchange: cfg.host || plugin.cfg.main.host,
359
- port: cfg.port || plugin.cfg.main.port || 25,
350
+ exchange: cfg.host || this.cfg.main.host,
351
+ port: cfg.port || this.cfg.main.port || 25,
360
352
  }
361
353
 
362
354
  // apply auth/mx options
363
355
  mx_opts.forEach(o => {
364
356
  if (cfg[o] === undefined) return;
365
- mx[o] = plugin.cfg[dom][o];
357
+ mx[o] = this.cfg[dom][o];
366
358
  })
367
359
 
368
360
  return next(OK, mx);