Haraka 3.1.0 → 3.1.2

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 (160) hide show
  1. package/.prettierignore +4 -0
  2. package/CONTRIBUTORS.md +5 -5
  3. package/Changes.md +69 -50
  4. package/Plugins.md +3 -1
  5. package/README.md +1 -1
  6. package/bin/haraka +475 -478
  7. package/config/outbound.ini +3 -0
  8. package/connection.js +1072 -1108
  9. package/docs/Connection.md +29 -30
  10. package/docs/CoreConfig.md +38 -39
  11. package/docs/CustomReturnCodes.md +0 -1
  12. package/docs/HAProxy.md +2 -2
  13. package/docs/Header.md +1 -1
  14. package/docs/Logging.md +29 -5
  15. package/docs/Outbound.md +93 -78
  16. package/docs/Plugins.md +103 -108
  17. package/docs/Transaction.md +49 -51
  18. package/docs/Tutorial.md +127 -143
  19. package/docs/deprecated/access.md +0 -1
  20. package/docs/deprecated/backscatterer.md +2 -3
  21. package/docs/deprecated/connect.rdns_access.md +18 -27
  22. package/docs/deprecated/data.headers.md +0 -1
  23. package/docs/deprecated/data.nomsgid.md +1 -2
  24. package/docs/deprecated/data.noreceived.md +1 -2
  25. package/docs/deprecated/data.rfc5322_header_checks.md +1 -2
  26. package/docs/deprecated/dkim_sign.md +13 -17
  27. package/docs/deprecated/dkim_verify.md +9 -17
  28. package/docs/deprecated/dnsbl.md +36 -38
  29. package/docs/deprecated/dnswl.md +41 -43
  30. package/docs/deprecated/lookup_rdns.strict.md +21 -34
  31. package/docs/deprecated/mail_from.access.md +17 -25
  32. package/docs/deprecated/mail_from.blocklist.md +9 -12
  33. package/docs/deprecated/mail_from.nobounces.md +1 -2
  34. package/docs/deprecated/rcpt_to.access.md +20 -27
  35. package/docs/deprecated/rcpt_to.blocklist.md +10 -13
  36. package/docs/deprecated/rcpt_to.routes.md +0 -1
  37. package/docs/deprecated/rdns.regexp.md +13 -15
  38. package/docs/plugins/aliases.md +89 -89
  39. package/docs/plugins/auth/auth_bridge.md +5 -7
  40. package/docs/plugins/auth/auth_ldap.md +11 -14
  41. package/docs/plugins/auth/auth_proxy.md +10 -12
  42. package/docs/plugins/auth/auth_vpopmaild.md +5 -6
  43. package/docs/plugins/auth/flat_file.md +4 -4
  44. package/docs/plugins/block_me.md +3 -3
  45. package/docs/plugins/data.signatures.md +1 -2
  46. package/docs/plugins/delay_deny.md +3 -4
  47. package/docs/plugins/max_unrecognized_commands.md +4 -4
  48. package/docs/plugins/prevent_credential_leaks.md +6 -6
  49. package/docs/plugins/process_title.md +18 -18
  50. package/docs/plugins/queue/deliver.md +2 -3
  51. package/docs/plugins/queue/discard.md +4 -4
  52. package/docs/plugins/queue/lmtp.md +1 -3
  53. package/docs/plugins/queue/qmail-queue.md +7 -9
  54. package/docs/plugins/queue/quarantine.md +16 -21
  55. package/docs/plugins/queue/rabbitmq.md +8 -11
  56. package/docs/plugins/queue/rabbitmq_amqplib.md +43 -39
  57. package/docs/plugins/queue/smtp_bridge.md +7 -10
  58. package/docs/plugins/queue/smtp_forward.md +42 -34
  59. package/docs/plugins/queue/smtp_proxy.md +30 -29
  60. package/docs/plugins/queue/test.md +1 -3
  61. package/docs/plugins/rcpt_to.in_host_list.md +6 -6
  62. package/docs/plugins/rcpt_to.max_count.md +1 -1
  63. package/docs/plugins/record_envelope_addresses.md +3 -3
  64. package/docs/plugins/reseed_rng.md +6 -6
  65. package/docs/plugins/status.md +9 -8
  66. package/docs/plugins/tarpit.md +7 -11
  67. package/docs/plugins/tls.md +12 -17
  68. package/docs/plugins/toobusy.md +4 -4
  69. package/docs/plugins/xclient.md +3 -3
  70. package/docs/tutorials/Migrating_from_v1_to_v2.md +19 -41
  71. package/docs/tutorials/SettingUpOutbound.md +6 -9
  72. package/endpoint.js +35 -38
  73. package/eslint.config.mjs +22 -19
  74. package/haraka.js +42 -47
  75. package/host_pool.js +75 -79
  76. package/http/html/404.html +45 -49
  77. package/http/html/index.html +39 -28
  78. package/http/package.json +2 -4
  79. package/line_socket.js +27 -28
  80. package/logger.js +182 -201
  81. package/outbound/client_pool.js +34 -27
  82. package/outbound/config.js +64 -59
  83. package/outbound/fsync_writestream.js +24 -25
  84. package/outbound/hmail.js +888 -835
  85. package/outbound/index.js +194 -187
  86. package/outbound/qfile.js +49 -52
  87. package/outbound/queue.js +197 -190
  88. package/outbound/timer_queue.js +41 -43
  89. package/outbound/tls.js +68 -61
  90. package/outbound/todo.js +11 -11
  91. package/package.json +38 -33
  92. package/plugins/.eslintrc.yaml +0 -1
  93. package/plugins/auth/auth_base.js +123 -127
  94. package/plugins/auth/auth_bridge.js +7 -7
  95. package/plugins/auth/auth_proxy.js +121 -126
  96. package/plugins/auth/auth_vpopmaild.js +84 -85
  97. package/plugins/auth/flat_file.js +18 -17
  98. package/plugins/block_me.js +31 -31
  99. package/plugins/data.signatures.js +13 -13
  100. package/plugins/delay_deny.js +65 -61
  101. package/plugins/prevent_credential_leaks.js +23 -23
  102. package/plugins/process_title.js +125 -128
  103. package/plugins/profile.js +5 -5
  104. package/plugins/queue/deliver.js +3 -3
  105. package/plugins/queue/discard.js +13 -14
  106. package/plugins/queue/lmtp.js +16 -17
  107. package/plugins/queue/qmail-queue.js +54 -55
  108. package/plugins/queue/quarantine.js +68 -70
  109. package/plugins/queue/rabbitmq.js +80 -87
  110. package/plugins/queue/rabbitmq_amqplib.js +75 -54
  111. package/plugins/queue/smtp_bridge.js +16 -16
  112. package/plugins/queue/smtp_forward.js +175 -179
  113. package/plugins/queue/smtp_proxy.js +69 -71
  114. package/plugins/queue/test.js +9 -9
  115. package/plugins/rcpt_to.host_list_base.js +30 -34
  116. package/plugins/rcpt_to.in_host_list.js +19 -19
  117. package/plugins/record_envelope_addresses.js +4 -4
  118. package/plugins/reseed_rng.js +4 -4
  119. package/plugins/status.js +90 -97
  120. package/plugins/tarpit.js +25 -14
  121. package/plugins/tls.js +68 -68
  122. package/plugins/toobusy.js +21 -23
  123. package/plugins/xclient.js +51 -53
  124. package/plugins.js +276 -293
  125. package/rfc1869.js +30 -35
  126. package/server.js +308 -299
  127. package/smtp_client.js +244 -228
  128. package/test/.eslintrc.yaml +0 -1
  129. package/test/connection.js +127 -134
  130. package/test/endpoint.js +53 -47
  131. package/test/fixtures/line_socket.js +12 -12
  132. package/test/fixtures/util_hmailitem.js +89 -85
  133. package/test/host_pool.js +90 -92
  134. package/test/installation/plugins/base_plugin.js +2 -2
  135. package/test/installation/plugins/folder_plugin/index.js +2 -3
  136. package/test/installation/plugins/inherits.js +3 -3
  137. package/test/installation/plugins/load_first.js +2 -3
  138. package/test/installation/plugins/plugin.js +1 -3
  139. package/test/installation/plugins/tls.js +2 -4
  140. package/test/logger.js +135 -116
  141. package/test/outbound/hmail.js +49 -35
  142. package/test/outbound/index.js +118 -101
  143. package/test/outbound/qfile.js +51 -53
  144. package/test/outbound_bounce_net_errors.js +84 -69
  145. package/test/outbound_bounce_rfc3464.js +235 -165
  146. package/test/plugins/auth/auth_base.js +420 -279
  147. package/test/plugins/auth/auth_vpopmaild.js +38 -39
  148. package/test/plugins/queue/smtp_forward.js +126 -104
  149. package/test/plugins/rcpt_to.host_list_base.js +85 -67
  150. package/test/plugins/rcpt_to.in_host_list.js +159 -112
  151. package/test/plugins/status.js +71 -64
  152. package/test/plugins/tls.js +37 -34
  153. package/test/plugins.js +97 -92
  154. package/test/rfc1869.js +19 -26
  155. package/test/server.js +293 -272
  156. package/test/smtp_client.js +180 -176
  157. package/test/tls_socket.js +62 -66
  158. package/test/transaction.js +159 -160
  159. package/tls_socket.js +331 -333
  160. package/transaction.js +129 -137
package/outbound/hmail.js CHANGED
@@ -1,151 +1,157 @@
1
- 'use strict';
2
-
3
- const events = require('node:events');
4
- const fs = require('node:fs');
5
- const dns = require('node:dns');
6
- const net = require('node:net');
7
- const path = require('node:path');
8
-
9
- const { Address } = require('address-rfc2821');
10
- const config = require('haraka-config');
11
- const constants = require('haraka-constants');
12
- const DSN = require('haraka-dsn');
13
- const message = require('haraka-email-message')
14
- const net_utils = require('haraka-net-utils');
15
- const Notes = require('haraka-notes');
16
- const utils = require('haraka-utils');
17
-
18
- const logger = require('../logger');
19
- const plugins = require('../plugins');
20
-
21
- const client_pool = require('./client_pool');
22
- const _qfile = require('./qfile');
23
- const outbound = require('./index');
24
- const obtls = require('./tls');
25
-
26
- const FsyncWriteStream = require('./fsync_writestream');
27
-
28
- let queue_dir;
29
- let temp_fail_queue;
30
- let delivery_queue;
1
+ 'use strict'
2
+
3
+ const events = require('node:events')
4
+ const fs = require('node:fs')
5
+ const dns = require('node:dns')
6
+ const net = require('node:net')
7
+ const path = require('node:path')
8
+
9
+ const { Address } = require('address-rfc2821')
10
+ const config = require('haraka-config')
11
+ const constants = require('haraka-constants')
12
+ const DSN = require('haraka-dsn')
13
+ const message = require('haraka-email-message')
14
+ const net_utils = require('haraka-net-utils')
15
+ const Notes = require('haraka-notes')
16
+ const utils = require('haraka-utils')
17
+
18
+ const logger = require('../logger')
19
+ const plugins = require('../plugins')
20
+
21
+ const client_pool = require('./client_pool')
22
+ const _qfile = require('./qfile')
23
+ const outbound = require('./index')
24
+ const obtls = require('./tls')
25
+
26
+ const FsyncWriteStream = require('./fsync_writestream')
27
+
28
+ let queue_dir
29
+ let temp_fail_queue
30
+ let delivery_queue
31
31
  setImmediate(() => {
32
- const queuelib = require('./queue');
33
- queue_dir = queuelib.queue_dir;
34
- temp_fail_queue = queuelib.temp_fail_queue;
35
- delivery_queue = queuelib.delivery_queue;
36
- });
32
+ const queuelib = require('./queue')
33
+ queue_dir = queuelib.queue_dir
34
+ temp_fail_queue = queuelib.temp_fail_queue
35
+ delivery_queue = queuelib.delivery_queue
36
+ })
37
37
 
38
- const obc = require('./config');
38
+ const obc = require('./config')
39
39
 
40
40
  /////////////////////////////////////////////////////////////////////////////
41
41
  // HMailItem - encapsulates an individual outbound mail item
42
42
 
43
- function dummy_func () {}
43
+ function dummy_func() {}
44
44
 
45
45
  class HMailItem extends events.EventEmitter {
46
- constructor (filename, filePath, notes) {
47
- super();
48
-
49
- const parts = _qfile.parts(filename);
50
- if (!parts) throw new Error(`Bad filename: ${filename}`);
51
-
52
- this.cfg = obc.cfg;
53
- this.obtls = obtls;
54
- this.name = 'outbound';
55
- this.path = filePath;
56
- this.filename = filename;
57
- this.next_process = parts.next_attempt;
58
- this.num_failures = parts.attempts;
59
- this.pid = parts.pid;
60
- this.notes = notes || new Notes();
61
- this.refcount = 1;
62
- this.todo = null;
63
- this.file_size = 0;
64
- this.next_cb = dummy_func;
65
- this.bounce_error = null;
66
- this.hook = null;
67
- this.size_file();
46
+ constructor(filename, filePath, notes) {
47
+ super()
48
+
49
+ const parts = _qfile.parts(filename)
50
+ if (!parts) throw new Error(`Bad filename: ${filename}`)
51
+
52
+ this.cfg = obc.cfg
53
+ this.obtls = obtls
54
+ this.name = 'outbound'
55
+ this.path = filePath
56
+ this.filename = filename
57
+ this.next_process = parts.next_attempt
58
+ this.num_failures = parts.attempts
59
+ this.pid = parts.pid
60
+ this.notes = notes || new Notes()
61
+ this.refcount = 1
62
+ this.todo = null
63
+ this.file_size = 0
64
+ this.next_cb = dummy_func
65
+ this.bounce_error = null
66
+ this.hook = null
67
+ this.size_file()
68
68
  }
69
69
 
70
- data_stream () {
71
- return fs.createReadStream(this.path, {start: this.data_start, end: this.file_size});
70
+ data_stream() {
71
+ return fs.createReadStream(this.path, {
72
+ start: this.data_start,
73
+ end: this.file_size,
74
+ })
72
75
  }
73
76
 
74
- size_file () {
77
+ size_file() {
75
78
  fs.stat(this.path, (err, stats) => {
76
79
  if (err) {
77
80
  // we are fucked... guess I need somewhere for this to go
78
- this.logerror(`Error obtaining file size: ${err}`);
79
- this.temp_fail("Error obtaining file size");
81
+ this.logerror(`Error obtaining file size: ${err}`)
82
+ this.temp_fail('Error obtaining file size')
80
83
  return
81
84
  }
82
85
  if (stats.size === 0) {
83
- this.logerror(`Error reading queue file ${this.filename}: zero bytes`);
84
- this.emit('error', `Error reading queue file ${this.filename}: zero bytes`);
86
+ this.logerror(`Error reading queue file ${this.filename}: zero bytes`)
87
+ this.emit('error', `Error reading queue file ${this.filename}: zero bytes`)
85
88
  return
86
89
  }
87
90
 
88
- this.file_size = stats.size;
89
- this.read_todo();
90
- });
91
+ this.file_size = stats.size
92
+ this.read_todo()
93
+ })
91
94
  }
92
95
 
93
- read_todo () {
94
- this._stream_bytes_from(this.path, {start: 0, end: 3}, (err, bytes) => {
96
+ read_todo() {
97
+ this._stream_bytes_from(this.path, { start: 0, end: 3 }, (err, bytes) => {
95
98
  if (err) {
96
- const errMsg = `Error reading queue file ${this.filename}: ${err}`;
97
- this.logerror(errMsg);
98
- this.temp_fail(errMsg);
99
+ const errMsg = `Error reading queue file ${this.filename}: ${err}`
100
+ this.logerror(errMsg)
101
+ this.temp_fail(errMsg)
99
102
  return
100
103
  }
101
104
 
102
- const todo_len = bytes.readUInt32BE(0);
103
- this.logdebug(`todo header length: ${todo_len}`);
104
- this.data_start = todo_len + 4;
105
+ const todo_len = bytes.readUInt32BE(0)
106
+ this.logdebug(`todo header length: ${todo_len}`)
107
+ this.data_start = todo_len + 4
105
108
 
106
- this._stream_bytes_from(this.path, {start: 4, end: todo_len + 3}, (err2, todo_bytes) => {
109
+ this._stream_bytes_from(this.path, { start: 4, end: todo_len + 3 }, (err2, todo_bytes) => {
107
110
  if (todo_bytes.length !== todo_len) {
108
- const wrongLength = `Didn't find right amount of data in todo!: ${err2} ${this.path}`;
109
- this.logcrit(wrongLength);
111
+ const wrongLength = `Didn't find right amount of data in todo!: ${err2} ${this.path}`
112
+ this.logcrit(wrongLength)
110
113
  fs.rename(this.path, path.join(queue_dir, `error.${this.filename}`), (err3) => {
111
114
  if (err3) {
112
- this.logerror(`Error creating (error.${this.filename}): ${err3}`);
115
+ this.logerror(`Error creating (error.${this.filename}): ${err3}`)
113
116
  }
114
- });
115
- this.emit('error', wrongLength); // Note nothing picks this up yet
117
+ })
118
+ this.emit('error', wrongLength) // Note nothing picks this up yet
116
119
  return
117
120
  }
118
121
 
119
122
  // we read everything
120
123
  const todo_json = todo_bytes.toString().trim()
121
- const last_char = todo_json.charAt(todo_json.length - 1);
124
+ const last_char = todo_json.charAt(todo_json.length - 1)
122
125
  if (last_char !== '}') {
123
- this.emit('error', `invalid todo header end char: ${last_char} at pos ${todo_len} of ${this.filename}`)
126
+ this.emit(
127
+ 'error',
128
+ `invalid todo header end char: ${last_char} at pos ${todo_len} of ${this.filename}`,
129
+ )
124
130
  return
125
131
  }
126
- this.todo = JSON.parse(todo_json);
127
- this.todo.mail_from = new Address (this.todo.mail_from);
128
- this.todo.rcpt_to = this.todo.rcpt_to.map(a => new Address (a));
129
- this.todo.notes = new Notes(this.todo.notes);
130
- this.emit('ready');
131
- });
132
- });
132
+ this.todo = JSON.parse(todo_json)
133
+ this.todo.mail_from = new Address(this.todo.mail_from)
134
+ this.todo.rcpt_to = this.todo.rcpt_to.map((a) => new Address(a))
135
+ this.todo.notes = new Notes(this.todo.notes)
136
+ this.emit('ready')
137
+ })
138
+ })
133
139
  }
134
140
 
135
- _stream_bytes_from (file_path, opts, done) {
141
+ _stream_bytes_from(file_path, opts, done) {
136
142
  if (opts.encoding !== undefined) {
137
143
  // passing an encoding to fs.createReadStream will change the type of data returned
138
144
  // ex: instead of returning a buffer, it may return a String, which will cause
139
145
  // Buffer.concat to barf. There's a reason this function has 'bytes' in the name
140
- done(new Error("Thar be dragons here! Encode/decode on the result of this function"))
146
+ done(new Error('Thar be dragons here! Encode/decode on the result of this function'))
141
147
  return
142
148
  }
143
149
 
144
- const stream = fs.createReadStream(file_path, opts);
150
+ const stream = fs.createReadStream(file_path, opts)
145
151
 
146
152
  stream.on('error', done)
147
153
 
148
- let raw_bytes = Buffer.alloc(0);
154
+ let raw_bytes = Buffer.alloc(0)
149
155
  stream.on('data', (data) => {
150
156
  raw_bytes = Buffer.concat([raw_bytes, data])
151
157
  })
@@ -155,786 +161,826 @@ class HMailItem extends events.EventEmitter {
155
161
  })
156
162
  }
157
163
 
158
- send () {
164
+ send() {
159
165
  if (obc.cfg.disabled) {
160
166
  // try again in 1 second if delivery is disabled
161
- this.logdebug("delivery disabled temporarily. Retrying in 1s.");
162
- setTimeout(() => { this.send(); }, 1000);
163
- return;
167
+ this.logdebug('delivery disabled temporarily. Retrying in 1s.')
168
+ setTimeout(() => {
169
+ this.send()
170
+ }, 1000)
171
+ return
164
172
  }
165
173
 
166
174
  if (!this.todo) {
167
- this.once('ready', () => { this._send(); });
168
- }
169
- else {
170
- this._send();
175
+ this.once('ready', () => {
176
+ this._send()
177
+ })
178
+ } else {
179
+ this._send()
171
180
  }
172
181
  }
173
182
 
174
- _send () {
175
- plugins.run_hooks('send_email', this);
183
+ _send() {
184
+ plugins.run_hooks('send_email', this)
176
185
  }
177
186
 
178
- send_email_respond (retval, delay_seconds) {
187
+ send_email_respond(retval, delay_seconds) {
179
188
  if (retval === constants.delay) {
180
189
  // Try again in 'delay' seconds.
181
- this.logdebug(`Delivery of this email delayed for ${delay_seconds} seconds`);
182
- this.next_cb();
183
- temp_fail_queue.add(this.filename, delay_seconds * 1000, () => { delivery_queue.push(this); });
184
- }
185
- else {
186
- this.logdebug(`Sending mail: ${this.filename}`);
187
- this.get_mx();
190
+ this.logdebug(`Delivery of this email delayed for ${delay_seconds} seconds`)
191
+ this.next_cb()
192
+ temp_fail_queue.add(this.filename, delay_seconds * 1000, () => {
193
+ delivery_queue.push(this)
194
+ })
195
+ } else {
196
+ this.logdebug(`Sending mail: ${this.filename}`)
197
+ this.get_mx()
188
198
  }
189
199
  }
190
200
 
191
- get_mx () {
192
- const { domain } = this.todo;
193
- plugins.run_hooks('get_mx', this, domain);
201
+ get_mx() {
202
+ const { domain } = this.todo
203
+ plugins.run_hooks('get_mx', this, domain)
194
204
  }
195
205
 
196
- async get_mx_respond (retval, mx) {
206
+ async get_mx_respond(retval, mx) {
197
207
  switch (retval) {
198
208
  case constants.ok: {
199
- this.logdebug(`MX from Plugin: ${this.todo.domain} => 0 ${JSON.stringify(mx)}`);
200
- let mx_list;
209
+ this.logdebug(`MX from Plugin: ${this.todo.domain} => 0 ${JSON.stringify(mx)}`)
210
+ let mx_list
201
211
  if (Array.isArray(mx)) {
202
- mx_list = mx.map(m => new net_utils.HarakaMx(m));
203
- }
204
- else {
205
- mx_list = [new net_utils.HarakaMx(mx)];
212
+ mx_list = mx.map((m) => new net_utils.HarakaMx(m))
213
+ } else {
214
+ mx_list = [new net_utils.HarakaMx(mx)]
206
215
  }
207
- return this.found_mx(mx_list);
216
+ return this.found_mx(mx_list)
208
217
  }
209
218
  case constants.deny:
210
- this.logwarn(`get_mx plugin returned DENY: ${mx}`);
219
+ this.logwarn(`get_mx plugin returned DENY: ${mx}`)
211
220
  for (const rcpt of this.todo.rcpt_to) {
212
- this.extend_rcpt_with_dsn(rcpt, DSN.addr_bad_dest_system(`No MX for ${this.todo.domain}`));
221
+ this.extend_rcpt_with_dsn(rcpt, DSN.addr_bad_dest_system(`No MX for ${this.todo.domain}`))
213
222
  }
214
- return this.bounce(`No MX for ${this.todo.domain}`);
223
+ return this.bounce(`No MX for ${this.todo.domain}`)
215
224
  case constants.denysoft:
216
- this.logwarn(`get_mx plugin returned DENYSOFT: ${mx}`);
225
+ this.logwarn(`get_mx plugin returned DENYSOFT: ${mx}`)
217
226
  for (const rcpt of this.todo.rcpt_to) {
218
- this.extend_rcpt_with_dsn(rcpt, DSN.addr_bad_dest_system(`Temporary MX lookup error for ${this.todo.domain}`, 450));
227
+ this.extend_rcpt_with_dsn(
228
+ rcpt,
229
+ DSN.addr_bad_dest_system(`Temporary MX lookup error for ${this.todo.domain}`, 450),
230
+ )
219
231
  }
220
- return this.temp_fail(`Temporary MX lookup error for ${this.todo.domain}`);
232
+ return this.temp_fail(`Temporary MX lookup error for ${this.todo.domain}`)
221
233
  }
222
234
 
223
235
  // none of the above return codes, drop through to DNS
224
236
  try {
225
- const exchanges = await net_utils.get_mx(this.todo.domain);
226
-
237
+ const exchanges = await net_utils.get_mx(this.todo.domain)
238
+
227
239
  if (exchanges.length) {
228
240
  this.found_mx(this.sort_mx(exchanges))
229
- }
230
- else {
241
+ } else {
231
242
  for (const rcpt of this.todo.rcpt_to) {
232
- this.extend_rcpt_with_dsn(rcpt, DSN.addr_bad_dest_system(`Nowhere to deliver mail to for domain: ${this.todo.domain}`))
243
+ this.extend_rcpt_with_dsn(
244
+ rcpt,
245
+ DSN.addr_bad_dest_system(`Nowhere to deliver mail to for domain: ${this.todo.domain}`),
246
+ )
233
247
  }
234
- this.bounce(`Nowhere to deliver mail to for domain: ${this.todo.domain}`);
248
+ this.bounce(`Nowhere to deliver mail to for domain: ${this.todo.domain}`)
235
249
  }
236
250
  } catch (e) {
237
- this.get_mx_error(e);
251
+ this.get_mx_error(e)
238
252
  }
239
253
  }
240
254
 
241
- get_mx_error (err) {
242
- this.lognotice(`MX Lookup for ${this.todo.domain} failed: ${err}`);
255
+ get_mx_error(err) {
256
+ this.lognotice(`MX Lookup for ${this.todo.domain} failed: ${err}`)
243
257
 
244
258
  if (err.code === dns.NXDOMAIN || err.code === dns.NOTFOUND) {
245
259
  for (const rcpt of this.todo.rcpt_to) {
246
- this.extend_rcpt_with_dsn(rcpt, DSN.addr_bad_dest_system(`No Such Domain: ${this.todo.domain}`));
260
+ this.extend_rcpt_with_dsn(rcpt, DSN.addr_bad_dest_system(`No Such Domain: ${this.todo.domain}`))
247
261
  }
248
- this.bounce(`No Such Domain: ${this.todo.domain}`);
249
- }
250
- else {
262
+ this.bounce(`No Such Domain: ${this.todo.domain}`)
263
+ } else {
251
264
  // every other error is transient
252
265
  for (const rcpt of this.todo.rcpt_to) {
253
- this.extend_rcpt_with_dsn(rcpt, DSN.addr_unspecified(`DNS lookup failure: ${this.todo.domain}`));
266
+ this.extend_rcpt_with_dsn(rcpt, DSN.addr_unspecified(`DNS lookup failure: ${this.todo.domain}`))
254
267
  }
255
- this.temp_fail(`DNS lookup failure: ${err}`);
268
+ this.temp_fail(`DNS lookup failure: ${err}`)
256
269
  }
257
270
  }
258
271
 
259
- async found_mx (mxs) {
260
-
272
+ async found_mx(mxs) {
261
273
  // support draft-delany-nullmx-02
262
274
  if (mxs.length === 1 && mxs[0].priority === 0 && mxs[0].exchange === '') {
263
275
  for (const rcpt of this.todo.rcpt_to) {
264
- this.extend_rcpt_with_dsn(rcpt, DSN.addr_bad_dest_system(`Domain ${this.todo.domain} sends and receives no email (NULL MX)`));
276
+ this.extend_rcpt_with_dsn(
277
+ rcpt,
278
+ DSN.addr_bad_dest_system(`Domain ${this.todo.domain} sends and receives no email (NULL MX)`),
279
+ )
265
280
  }
266
- return this.bounce(`Domain ${this.todo.domain} sends and receives no email (NULL MX)`);
281
+ return this.bounce(`Domain ${this.todo.domain} sends and receives no email (NULL MX)`)
267
282
  }
268
283
 
269
284
  // resolves the MX hostnames to IPs
270
- this.mxlist = await net_utils.resolve_mx_hosts(mxs);
285
+ this.mxlist = await net_utils.resolve_mx_hosts(mxs)
286
+
287
+ switch (obc.cfg.inet_prefer) {
288
+ case 'v4':
289
+ this.mxlist = [
290
+ ...this.mxlist.filter((mx) => !net.isIP(mx.exchange) || net.isIPv4(mx.exchange)),
291
+ ...this.mxlist.filter((mx) => net.isIPv6(mx.exchange)),
292
+ ]
293
+ break
294
+ case 'v6':
295
+ this.mxlist = [
296
+ ...this.mxlist.filter((mx) => !net.isIP(mx.exchange) || net.isIPv6(mx.exchange)),
297
+ ...this.mxlist.filter((mx) => net.isIPv4(mx.exchange)),
298
+ ]
299
+ break
300
+ }
271
301
 
272
- this.try_deliver();
302
+ this.try_deliver()
273
303
  }
274
304
 
275
- async try_deliver () {
276
-
305
+ async try_deliver() {
277
306
  // are any MXs left?
278
307
  if (this.mxlist.length === 0) {
279
308
  for (const rcpt of this.todo.rcpt_to) {
280
- this.extend_rcpt_with_dsn(rcpt, DSN.addr_bad_dest_system(`Tried all MXs ${this.todo.domain}`));
309
+ this.extend_rcpt_with_dsn(rcpt, DSN.addr_bad_dest_system(`Tried all MXs ${this.todo.domain}`))
281
310
  }
282
- return this.temp_fail("Tried all MXs");
311
+ return this.temp_fail('Tried all MXs')
283
312
  }
284
313
 
285
- const mx = this.mxlist.shift();
314
+ const mx = this.mxlist.shift()
286
315
 
287
- if (!obc.cfg.local_mx_ok && mx.from_dns && await net_utils.is_local_host(mx.exchange)) {
316
+ if (!obc.cfg.local_mx_ok && mx.from_dns && (await net_utils.is_local_host(mx.exchange))) {
288
317
  this.loginfo(`MX ${mx.exchange} is local, skipping since local_mx_ok=false`)
289
- return this.try_deliver(); // try next MX
318
+ return this.try_deliver() // try next MX
290
319
  }
291
320
 
292
321
  this.force_tls = this.get_force_tls(mx)
293
322
 
294
323
  if (this.todo.notes.outbound_ip) {
295
- this.logerror(`notes.outbound_ip is deprecated. Use get_mx.bind instead!`);
296
- if (!mx.bind) mx.bind = this.todo.notes.outbound_ip;
324
+ this.logerror(`notes.outbound_ip is deprecated. Use get_mx.bind instead!`)
325
+ if (!mx.bind) mx.bind = this.todo.notes.outbound_ip
297
326
  }
298
327
 
299
328
  // Allow transaction notes to set outbound IP helo
300
329
  if (this.todo.notes.outbound_helo) {
301
- mx.bind_helo = this.todo.notes.outbound_helo;
330
+ mx.bind_helo = this.todo.notes.outbound_helo
302
331
  }
303
332
 
304
- const host = mx.path ? mx.path : mx.exchange;
333
+ const host = mx.path ? mx.path : mx.exchange
305
334
  const lmtp = mx.using_lmtp ? ' using LMTP' : ''
306
335
  if (!mx.port) mx.port = mx.using_lmtp ? 24 : 25
307
336
  const from_dns = mx.from_dns ? ' (via DNS)' : ''
308
337
 
309
- this.logdebug(`deliver: ${mx.bind_helo} -> ${host}${lmtp}${from_dns} (${delivery_queue.length()}) (${temp_fail_queue.length()})`)
338
+ this.logdebug(
339
+ `deliver: ${mx.bind_helo} -> ${host}${lmtp}${from_dns} (${delivery_queue.length()}) (${temp_fail_queue.length()})`,
340
+ )
310
341
  client_pool.get_client(mx, (err, socket) => {
311
342
  if (err) {
312
343
  if (/connection timed out|connect ECONNREFUSED/.test(err)) {
313
- logger.notice(this, `Failed to get socket: ${err}`);
314
- }
315
- else {
316
- logger.error(this, `Failed to get socket: ${err}`);
344
+ logger.notice(this, `Failed to get socket: ${err}`)
345
+ } else {
346
+ logger.error(this, `Failed to get socket: ${err}`)
317
347
  }
318
348
 
319
- return this.try_deliver(); // try next MX
349
+ return this.try_deliver() // try next MX
320
350
  }
321
- this.try_deliver_host_on_socket(mx, host, mx.port, socket);
322
- });
351
+ this.try_deliver_host_on_socket(mx, host, mx.port, socket)
352
+ })
323
353
  }
324
354
 
325
- try_deliver_host_on_socket (mx, host, port, socket) {
326
- const self = this;
327
- let processing_mail = true;
328
- let command = mx.using_lmtp ? 'connect_lmtp' : 'connect';
355
+ try_deliver_host_on_socket(mx, host, port, socket) {
356
+ const self = this
357
+ let processing_mail = true
358
+ let command = mx.using_lmtp ? 'connect_lmtp' : 'connect'
329
359
 
330
360
  for (const l of ['error', 'timeout', 'close', 'end']) {
331
- socket.removeAllListeners(l);
361
+ socket.removeAllListeners(l)
332
362
  }
333
363
 
334
364
  socket.once('timeout', function () {
335
- socket.emit('error', `socket timeout waiting on ${command}`);
336
- });
365
+ socket.emit('error', `socket timeout waiting on ${command}`)
366
+ })
337
367
 
338
- socket.once('error', err => {
368
+ socket.once('error', (err) => {
339
369
  if (!processing_mail) return
340
370
 
341
- self.logerror(`Ongoing connection failed to ${host}:${port} : ${err}`);
342
- processing_mail = false;
343
- client_pool.release_client(socket, mx);
344
- if (err.source === 'tls') // exception thrown from tls_socket during tls upgrade
345
- return obtls.mark_tls_nogo(host, () => { return self.try_deliver(); });
346
- self.try_deliver(); // try the next MX
371
+ self.logerror(`Ongoing connection failed to ${host}:${port} : ${err}`)
372
+ processing_mail = false
373
+ client_pool.release_client(socket, mx)
374
+ if (err.source === 'tls')
375
+ // exception thrown from tls_socket during tls upgrade
376
+ return obtls.mark_tls_nogo(host, () => {
377
+ return self.try_deliver()
378
+ })
379
+ self.try_deliver() // try the next MX
347
380
  })
348
381
 
349
382
  socket.once('close', () => {
350
383
  if (!processing_mail) return
351
384
 
352
- self.logerror(`Remote end ${host}:${port} closed connection while we were processing mail. Trying next MX.`);
353
- processing_mail = false;
354
- client_pool.release_client(socket, mx);
355
- self.try_deliver();
356
- });
385
+ self.logerror(`Remote end ${host}:${port} closed connection while we were processing mail. Trying next MX.`)
386
+ processing_mail = false
387
+ client_pool.release_client(socket, mx)
388
+ self.try_deliver()
389
+ })
357
390
 
358
391
  socket.once('end', () => {
359
- socket.writable = false;
360
- if (!processing_mail) client_pool.release_client(socket, mx);
392
+ socket.writable = false
393
+ if (!processing_mail) client_pool.release_client(socket, mx)
361
394
  })
362
395
 
363
- let response = [];
396
+ let response = []
364
397
 
365
- let recip_index = 0;
366
- const recipients = this.todo.rcpt_to;
367
- let lmtp_rcpt_idx = 0;
398
+ let recip_index = 0
399
+ const recipients = this.todo.rcpt_to
400
+ let lmtp_rcpt_idx = 0
368
401
 
369
- let last_recip = null;
370
- const ok_recips = [];
371
- const fail_recips = [];
372
- const bounce_recips = [];
373
- let secured = false;
374
- let authenticating = false;
375
- let authenticated = false;
402
+ let last_recip = null
403
+ const ok_recips = []
404
+ const fail_recips = []
405
+ const bounce_recips = []
406
+ let secured = false
407
+ let authenticating = false
408
+ let authenticated = false
376
409
  let smtp_properties = {
377
- "tls": false,
378
- "max_size": 0,
379
- "eightbitmime": false,
380
- "enh_status_codes": false,
381
- "auth": [],
382
- };
383
-
384
- const send_command = socket.send_command = (cmd, data) => {
410
+ tls: false,
411
+ max_size: 0,
412
+ eightbitmime: false,
413
+ enh_status_codes: false,
414
+ auth: [],
415
+ }
416
+
417
+ const send_command = (socket.send_command = (cmd, data) => {
385
418
  if (!socket.writable) {
386
- self.logerror("Socket writability went away");
419
+ self.logerror('Socket writability went away')
387
420
  if (processing_mail) {
388
- processing_mail = false;
389
- client_pool.release_client(socket, mx);
390
- return self.try_deliver();
421
+ processing_mail = false
422
+ client_pool.release_client(socket, mx)
423
+ return self.try_deliver()
391
424
  }
392
- return;
425
+ return
393
426
  }
394
427
  if (self.force_tls && !['EHLO', 'LHLO', 'STARTTLS'].includes(cmd.toUpperCase()) && !socket.isSecure()) {
395
428
  // For safety against programming mistakes
396
- self.logerror("Blocking attempt to send unencrypted data to forced TLS socket. This message indicates a programming error in the software.");
397
- processing_mail = false;
398
- client_pool.release_client(socket, mx);
399
- return;
429
+ self.logerror(
430
+ 'Blocking attempt to send unencrypted data to forced TLS socket. This message indicates a programming error in the software.',
431
+ )
432
+ processing_mail = false
433
+ client_pool.release_client(socket, mx)
434
+ return
400
435
  }
401
436
 
402
- let line = `${cmd}${data ? ` ${data}` : ''}`;
437
+ let line = `${cmd}${data ? ` ${data}` : ''}`
403
438
  if (cmd === 'dot' || cmd === 'dot_lmtp') {
404
- line = '.';
439
+ line = '.'
405
440
  }
406
- if (authenticating) cmd = 'auth';
407
- self.logprotocol(`C: ${line}`);
408
- socket.write(`${line}\r\n`, "utf8", err => {
441
+ if (authenticating) cmd = 'auth'
442
+ self.logprotocol(`C: ${line}`)
443
+ socket.write(`${line}\r\n`, 'utf8', (err) => {
409
444
  if (err) {
410
- self.logcrit(`Socket write failed unexpectedly: ${err}`);
445
+ self.logcrit(`Socket write failed unexpectedly: ${err}`)
411
446
  // We may want to release client here - but I want to get this
412
447
  // line of code in before we do that so we might see some logging
413
448
  // in case of errors.
414
449
  // client_pool.release_client(socket, mx);
415
450
  }
416
- });
417
- command = cmd.toLowerCase();
418
- response = [];
419
- };
451
+ })
452
+ command = cmd.toLowerCase()
453
+ response = []
454
+ })
420
455
 
421
- function set_ehlo_props () {
456
+ function set_ehlo_props() {
422
457
  for (let i = 0, l = response.length; i < l; i++) {
423
- const r = response[i];
458
+ const r = response[i]
424
459
  if (r.toUpperCase() === '8BITMIME') {
425
- smtp_properties.eightbitmime = true;
426
- }
427
- else if (r.toUpperCase() === 'STARTTLS') {
428
- smtp_properties.tls = true;
429
- }
430
- else if (r.toUpperCase() === 'ENHANCEDSTATUSCODES') {
431
- smtp_properties.enh_status_codes = true;
432
- }
433
- else if (r.toUpperCase() === 'SMTPUTF8') {
434
- smtp_properties.smtp_utf8 = true;
435
- }
436
- else {
460
+ smtp_properties.eightbitmime = true
461
+ } else if (r.toUpperCase() === 'STARTTLS') {
462
+ smtp_properties.tls = true
463
+ } else if (r.toUpperCase() === 'ENHANCEDSTATUSCODES') {
464
+ smtp_properties.enh_status_codes = true
465
+ } else if (r.toUpperCase() === 'SMTPUTF8') {
466
+ smtp_properties.smtp_utf8 = true
467
+ } else {
437
468
  // Check for SIZE parameter and limit
438
- let matches = r.match(/^SIZE\s+(\d+)$/);
469
+ let matches = r.match(/^SIZE\s+(\d+)$/)
439
470
  if (matches) {
440
- smtp_properties.max_size = matches[1];
471
+ smtp_properties.max_size = matches[1]
441
472
  }
442
473
  // Check for AUTH
443
- matches = r.match(/^AUTH\s+(.+)$/);
474
+ matches = r.match(/^AUTH\s+(.+)$/)
444
475
  if (matches) {
445
- smtp_properties.auth = matches[1].split(/\s+/);
476
+ smtp_properties.auth = matches[1].split(/\s+/)
446
477
  }
447
478
  }
448
479
  }
449
480
  }
450
481
 
451
- function get_reverse_path_with_params () {
482
+ function get_reverse_path_with_params() {
452
483
  const rp = self.todo.mail_from.format(!smtp_properties.smtp_utf8)
453
484
  let rp_params = ''
454
485
  if (smtp_properties.smtp_utf8 && has_non_ascii(rp)) rp_params += ' SMTPUTF8'
455
486
  return `FROM:${rp}${rp_params}`
456
487
  }
457
488
 
458
- function has_non_ascii (string) {
459
- return [...string].some(char => char.charCodeAt(0) > 127)
489
+ function has_non_ascii(string) {
490
+ return [...string].some((char) => char.charCodeAt(0) > 127)
460
491
  }
461
492
 
462
- function auth_and_mail_phase () {
463
- if (!authenticated && (mx.auth_user && mx.auth_pass)) {
493
+ function auth_and_mail_phase() {
494
+ if (!authenticated && mx.auth_user && mx.auth_pass) {
464
495
  // We have AUTH credentials to send for this domain
465
496
 
466
497
  if (!(Array.isArray(smtp_properties.auth) && smtp_properties.auth.length)) {
467
498
  // AUTH not offered
468
- self.logwarn(`AUTH configured for domain ${self.todo.domain} but host ${host} did not advertise AUTH capability`);
499
+ self.logwarn(
500
+ `AUTH configured for domain ${self.todo.domain} but host ${host} did not advertise AUTH capability`,
501
+ )
469
502
  // Try and send the message without authentication
470
- return send_command('MAIL', get_reverse_path_with_params());
503
+ return send_command('MAIL', get_reverse_path_with_params())
471
504
  }
472
505
 
473
506
  if (!mx.auth_type) {
474
507
  // User hasn't specified an authentication type, so we pick one
475
508
  // We'll prefer CRAM-MD5 as it's the most secure that we support.
476
509
  if (smtp_properties.auth.includes('CRAM-MD5')) {
477
- mx.auth_type = 'CRAM-MD5';
510
+ mx.auth_type = 'CRAM-MD5'
478
511
  }
479
512
  // PLAIN requires less round-trips compared to LOGIN
480
513
  else if (smtp_properties.auth.includes('PLAIN')) {
481
514
  // PLAIN requires less round trips compared to LOGIN
482
515
  // So we'll make this our 2nd pick.
483
- mx.auth_type = 'PLAIN';
484
- }
485
- else if (smtp_properties.auth.includes('LOGIN')) {
486
- mx.auth_type = 'LOGIN';
516
+ mx.auth_type = 'PLAIN'
517
+ } else if (smtp_properties.auth.includes('LOGIN')) {
518
+ mx.auth_type = 'LOGIN'
487
519
  }
488
520
  }
489
521
 
490
522
  if (!mx.auth_type || (mx.auth_type && !smtp_properties.auth.includes(mx.auth_type.toUpperCase()))) {
491
523
  // No compatible authentication types offered by the server
492
- self.logwarn(`AUTH configured for domain ${self.todo.domain} but host ${host}did not offer any compatible types${(mx.auth_type) ? ` (requested: ${mx.auth_type})` : ''} (offered: ${smtp_properties.auth.join(',')})`);
524
+ self.logwarn(
525
+ `AUTH configured for domain ${self.todo.domain} but host ${host}did not offer any compatible types${mx.auth_type ? ` (requested: ${mx.auth_type})` : ''} (offered: ${smtp_properties.auth.join(',')})`,
526
+ )
493
527
  // Proceed without authentication
494
- return send_command('MAIL', get_reverse_path_with_params());
528
+ return send_command('MAIL', get_reverse_path_with_params())
495
529
  }
496
530
 
497
531
  switch (mx.auth_type.toUpperCase()) {
498
532
  case 'PLAIN':
499
- return send_command('AUTH', `PLAIN ${utils.base64(`\0${mx.auth_user}\0${mx.auth_pass}`)}`);
533
+ return send_command('AUTH', `PLAIN ${utils.base64(`\0${mx.auth_user}\0${mx.auth_pass}`)}`)
500
534
  case 'LOGIN':
501
- authenticating = true;
502
- return send_command('AUTH', 'LOGIN');
535
+ authenticating = true
536
+ return send_command('AUTH', 'LOGIN')
503
537
  case 'CRAM-MD5':
504
- authenticating = true;
505
- return send_command('AUTH', 'CRAM-MD5');
538
+ authenticating = true
539
+ return send_command('AUTH', 'CRAM-MD5')
506
540
  default:
507
541
  // Unsupported AUTH type
508
- self.logwarn(`Unsupported authentication type ${mx.auth_type.toUpperCase()} requested for domain ${self.todo.domain}`);
509
- return send_command('MAIL', get_reverse_path_with_params());
542
+ self.logwarn(
543
+ `Unsupported authentication type ${mx.auth_type.toUpperCase()} requested for domain ${self.todo.domain}`,
544
+ )
545
+ return send_command('MAIL', get_reverse_path_with_params())
510
546
  }
511
547
  }
512
548
 
513
- return send_command('MAIL', get_reverse_path_with_params());
549
+ return send_command('MAIL', get_reverse_path_with_params())
514
550
  }
515
551
 
516
552
  // IMPORTANT: do STARTTLS before AUTH for security
517
- function process_ehlo_data () {
518
- set_ehlo_props();
553
+ function process_ehlo_data() {
554
+ set_ehlo_props()
519
555
 
520
- if (secured) return auth_and_mail_phase(); // TLS already negotiated
556
+ if (secured) return auth_and_mail_phase() // TLS already negotiated
521
557
 
522
558
  if (self.force_tls) {
523
- self.logdebug(`Using TLS for domain: ${self.todo.domain}, host: ${host}`);
559
+ self.logdebug(`Using TLS for domain: ${self.todo.domain}, host: ${host}`)
524
560
 
525
561
  if (!obc.cfg.enable_tls || !smtp_properties.tls) {
526
562
  // Prevent further use of the non-securable socket
527
- processing_mail = false;
528
- socket.write("QUIT\r\n", "utf8"); // courtesy
529
- socket.end();
530
- client_pool.release_client(socket, mx);
531
- return self.temp_fail(`No TLS available but required by configuration.`);
563
+ processing_mail = false
564
+ socket.write('QUIT\r\n', 'utf8') // courtesy
565
+ socket.end()
566
+ client_pool.release_client(socket, mx)
567
+ return self.temp_fail(`No TLS available but required by configuration.`)
532
568
  }
533
569
 
534
570
  socket.once('secure', () => {
535
571
  // Set this flag so we don't try STARTTLS again if it
536
572
  // is incorrectly offered at EHLO once we are secured.
537
- secured = true;
538
- send_command(mx.using_lmtp ? 'LHLO' : 'EHLO', mx.bind_helo);
539
- });
540
- return send_command('STARTTLS');
573
+ secured = true
574
+ send_command(mx.using_lmtp ? 'LHLO' : 'EHLO', mx.bind_helo)
575
+ })
576
+ return send_command('STARTTLS')
541
577
  }
542
- if (!obc.cfg.enable_tls) return auth_and_mail_phase(); // TLS not enabled
543
- if (!smtp_properties.tls) return auth_and_mail_phase(); // TLS not advertised by remote
578
+ if (!obc.cfg.enable_tls) return auth_and_mail_phase() // TLS not enabled
579
+ if (!smtp_properties.tls) return auth_and_mail_phase() // TLS not advertised by remote
544
580
 
545
581
  if (obtls.cfg === undefined) {
546
- self.logerror(`Oops, TLS config not loaded yet!`);
547
- return auth_and_mail_phase(); // no outbound TLS config
582
+ self.logerror(`Oops, TLS config not loaded yet!`)
583
+ return auth_and_mail_phase() // no outbound TLS config
548
584
  }
549
585
 
550
586
  // TLS is configured and available
551
587
 
552
588
  // TLS exclude lists checks for MX host or remote domain
553
- if (net_utils.ip_in_list(obtls.cfg.no_tls_hosts, host)) return auth_and_mail_phase();
554
- if (net_utils.ip_in_list(obtls.cfg.no_tls_hosts, self.todo.domain)) return auth_and_mail_phase();
589
+ if (net_utils.ip_in_list(obtls.cfg.no_tls_hosts, host)) return auth_and_mail_phase()
590
+ if (net_utils.ip_in_list(obtls.cfg.no_tls_hosts, self.todo.domain)) return auth_and_mail_phase()
555
591
 
556
592
  // Check Redis and skip for hosts that failed past TLS upgrade
557
- return obtls.check_tls_nogo(host,
558
- () => { // Clear to GO
559
- self.logdebug(`Trying TLS for domain: ${self.todo.domain}, host: ${host}`);
593
+ return obtls.check_tls_nogo(
594
+ host,
595
+ () => {
596
+ // Clear to GO
597
+ self.logdebug(`Trying TLS for domain: ${self.todo.domain}, host: ${host}`)
560
598
 
561
599
  socket.once('secure', () => {
562
600
  // Set this flag so we don't try STARTTLS again if it
563
601
  // is incorrectly offered at EHLO once we are secured.
564
- secured = true;
565
- send_command(mx.using_lmtp ? 'LHLO' : 'EHLO', mx.bind_helo);
566
- });
567
- return send_command('STARTTLS');
602
+ secured = true
603
+ send_command(mx.using_lmtp ? 'LHLO' : 'EHLO', mx.bind_helo)
604
+ })
605
+ return send_command('STARTTLS')
568
606
  },
569
- (when) => { // No GO
570
- self.loginfo(`TLS disabled for ${host} because it was marked as non-TLS on ${when}`);
571
- return auth_and_mail_phase();
572
- }
573
- );
607
+ (when) => {
608
+ // No GO
609
+ self.loginfo(`TLS disabled for ${host} because it was marked as non-TLS on ${when}`)
610
+ return auth_and_mail_phase()
611
+ },
612
+ )
574
613
  }
575
614
 
576
- let fp_called = false;
615
+ let fp_called = false
577
616
 
578
- function finish_processing_mail (success) {
617
+ function finish_processing_mail(success) {
579
618
  if (fp_called) {
580
- return self.logerror(`finish_processing_mail called multiple times! Stack: ${(new Error()).stack}`);
619
+ return self.logerror(`finish_processing_mail called multiple times! Stack: ${new Error().stack}`)
581
620
  }
582
- fp_called = true;
621
+ fp_called = true
583
622
  if (fail_recips.length) {
584
- self.refcount++;
585
- self.split_to_new_recipients(fail_recips, "Some recipients temporarily failed", hmail => {
586
- self.discard();
587
- hmail.temp_fail(`Some recipients temp failed: ${fail_recips.join(', ')}`, { fail_recips, mx });
588
- });
623
+ self.refcount++
624
+ self.split_to_new_recipients(fail_recips, 'Some recipients temporarily failed', (hmail) => {
625
+ self.discard()
626
+ hmail.temp_fail(`Some recipients temp failed: ${fail_recips.join(', ')}`, {
627
+ fail_recips,
628
+ mx,
629
+ })
630
+ })
589
631
  }
590
632
  if (bounce_recips.length) {
591
- self.refcount++;
592
- self.split_to_new_recipients(bounce_recips, "Some recipients rejected", hmail => {
593
- self.discard();
594
- hmail.bounce(`Some recipients failed: ${bounce_recips.join(', ')}`, { bounce_recips, mx });
595
- });
633
+ self.refcount++
634
+ self.split_to_new_recipients(bounce_recips, 'Some recipients rejected', (hmail) => {
635
+ self.discard()
636
+ hmail.bounce(`Some recipients failed: ${bounce_recips.join(', ')}`, {
637
+ bounce_recips,
638
+ mx,
639
+ })
640
+ })
596
641
  }
597
- processing_mail = false;
642
+ processing_mail = false
598
643
  if (success) {
599
- const reason = response.join(' ');
644
+ const reason = response.join(' ')
600
645
 
601
646
  let hostname = mx.exchange
602
647
  if (net.isIP(hostname) && mx.from_dns && !net.isIP(mx.from_dns)) {
603
648
  hostname = mx.from_dns
604
649
  }
605
650
 
606
- self.delivered(host, port, (mx.using_lmtp ? 'LMTP' : 'SMTP'), hostname,
607
- reason, ok_recips, fail_recips, bounce_recips, secured, authenticated);
608
- }
609
- else {
610
- self.discard();
651
+ self.delivered(
652
+ host,
653
+ port,
654
+ mx.using_lmtp ? 'LMTP' : 'SMTP',
655
+ hostname,
656
+ reason,
657
+ ok_recips,
658
+ fail_recips,
659
+ bounce_recips,
660
+ secured,
661
+ authenticated,
662
+ )
663
+ } else {
664
+ self.discard()
611
665
  }
612
666
 
613
- send_command('QUIT');
667
+ send_command('QUIT')
614
668
  }
615
669
 
616
- socket.on('line', line => {
670
+ socket.on('line', (line) => {
617
671
  if (!processing_mail && command !== 'rset') {
618
672
  if (command !== 'quit') {
619
- self.logprotocol(`Received data after stopping processing: ${line}`);
673
+ self.logprotocol(`Received data after stopping processing: ${line}`)
620
674
  }
621
- return;
675
+ return
622
676
  }
623
- self.logprotocol(`S: ${line}`);
624
- const matches = smtp_regexp.exec(line);
677
+ self.logprotocol(`S: ${line}`)
678
+ const matches = smtp_regexp.exec(line)
625
679
  if (!matches) {
626
680
  // Unrecognized response.
627
- self.logerror(`Unrecognized response from upstream server: ${line}`);
628
- processing_mail = false;
681
+ self.logerror(`Unrecognized response from upstream server: ${line}`)
682
+ processing_mail = false
629
683
  // Release back to the pool and instruct it to terminate this connection
630
- client_pool.release_client(socket, mx);
631
- self.todo.rcpt_to.forEach(rcpt => {
632
- self.extend_rcpt_with_dsn(rcpt, DSN.proto_invalid_command(`Unrecognized response from upstream server: ${line}`));
633
- });
634
- self.bounce(`Unrecognized response from upstream server: ${line}`, {mx});
635
- return;
684
+ client_pool.release_client(socket, mx)
685
+ self.todo.rcpt_to.forEach((rcpt) => {
686
+ self.extend_rcpt_with_dsn(
687
+ rcpt,
688
+ DSN.proto_invalid_command(`Unrecognized response from upstream server: ${line}`),
689
+ )
690
+ })
691
+ self.bounce(`Unrecognized response from upstream server: ${line}`, { mx })
692
+ return
636
693
  }
637
694
 
638
- let reason;
639
- const code = matches[1];
640
- const cont = matches[2];
641
- const extc = matches[3];
642
- const rest = matches[4];
643
- response.push(rest);
644
- if (cont !== ' ') return;
695
+ let reason
696
+ const code = matches[1]
697
+ const cont = matches[2]
698
+ const extc = matches[3]
699
+ const rest = matches[4]
700
+ response.push(rest)
701
+ if (cont !== ' ') return
645
702
 
646
703
  if (code.match(/^2/)) {
647
704
  // Successful command, fall through
648
- }
649
- else if (code.match(/^3/) && command !== 'data') {
705
+ } else if (code.match(/^3/) && command !== 'data') {
650
706
  if (authenticating) {
651
- const resp = response.join(' ');
707
+ const resp = response.join(' ')
652
708
  switch (mx.auth_type.toUpperCase()) {
653
709
  case 'LOGIN':
654
710
  if (resp === 'VXNlcm5hbWU6') {
655
711
  // Username:
656
- return send_command(utils.base64(mx.auth_user));
657
- }
658
- else if (resp === 'UGFzc3dvcmQ6') {
712
+ return send_command(utils.base64(mx.auth_user))
713
+ } else if (resp === 'UGFzc3dvcmQ6') {
659
714
  // Password:
660
- return send_command(utils.base64(mx.auth_pass));
715
+ return send_command(utils.base64(mx.auth_pass))
661
716
  }
662
- break;
717
+ break
663
718
  case 'CRAM-MD5':
664
719
  // The response is our challenge
665
- return send_command(cram_md5_response(mx.auth_user, mx.auth_pass, resp));
720
+ return send_command(cram_md5_response(mx.auth_user, mx.auth_pass, resp))
666
721
  default:
667
722
  // This shouldn't happen...
668
723
  }
669
724
  }
670
725
  // Error
671
- reason = response.join(' ');
672
- recipients.forEach(rcpt => {
673
- rcpt.dsn_action = 'delayed';
674
- rcpt.dsn_smtp_code = code;
675
- rcpt.dsn_smtp_extc = extc;
676
- rcpt.dsn_status = extc;
677
- rcpt.dsn_smtp_response = response.join(' ');
678
- rcpt.dsn_remote_mta = mx.exchange;
679
- });
680
- send_command('QUIT');
681
- processing_mail = false;
682
- return self.temp_fail(`Upstream error: ${code} ${(extc) ? `${extc} ` : ''}${reason}`);
683
- }
684
- else if (code.match(/^4/)) {
685
- authenticating = false;
726
+ reason = response.join(' ')
727
+ recipients.forEach((rcpt) => {
728
+ rcpt.dsn_action = 'delayed'
729
+ rcpt.dsn_smtp_code = code
730
+ rcpt.dsn_smtp_extc = extc
731
+ rcpt.dsn_status = extc
732
+ rcpt.dsn_smtp_response = response.join(' ')
733
+ rcpt.dsn_remote_mta = mx.exchange
734
+ })
735
+ send_command('QUIT')
736
+ processing_mail = false
737
+ return self.temp_fail(`Upstream error: ${code} ${extc ? `${extc} ` : ''}${reason}`)
738
+ } else if (code.match(/^4/)) {
739
+ authenticating = false
686
740
  if (/^rcpt/.test(command) || command === 'dot_lmtp') {
687
- if (command === 'dot_lmtp') last_recip = ok_recips.shift();
741
+ if (command === 'dot_lmtp') last_recip = ok_recips.shift()
688
742
  // this recipient was rejected
689
- reason = `${code} ${(extc) ? `${extc} ` : ''}${response.join(' ')}`;
690
- self.lognotice(`recipient ${last_recip} deferred: ${reason}`);
691
- last_recip.reason = reason;
692
-
693
- last_recip.dsn_action = 'delayed';
694
- last_recip.dsn_smtp_code = code;
695
- last_recip.dsn_smtp_extc = extc;
696
- last_recip.dsn_status = extc;
697
- last_recip.dsn_smtp_response = response.join(' ');
698
- last_recip.dsn_remote_mta = mx.exchange;
699
-
700
- fail_recips.push(last_recip);
743
+ reason = `${code} ${extc ? `${extc} ` : ''}${response.join(' ')}`
744
+ self.lognotice(`recipient ${last_recip} deferred: ${reason}`)
745
+ last_recip.reason = reason
746
+
747
+ last_recip.dsn_action = 'delayed'
748
+ last_recip.dsn_smtp_code = code
749
+ last_recip.dsn_smtp_extc = extc
750
+ last_recip.dsn_status = extc
751
+ last_recip.dsn_smtp_response = response.join(' ')
752
+ last_recip.dsn_remote_mta = mx.exchange
753
+
754
+ fail_recips.push(last_recip)
701
755
  if (command === 'dot_lmtp') {
702
- response = [];
756
+ response = []
703
757
  if (ok_recips.length === 0) {
704
- return finish_processing_mail(false);
758
+ return finish_processing_mail(false)
705
759
  }
706
760
  }
761
+ } else if (processing_mail) {
762
+ reason = response.join(' ')
763
+ recipients.forEach((rcpt) => {
764
+ rcpt.dsn_action = 'delayed'
765
+ rcpt.dsn_smtp_code = code
766
+ rcpt.dsn_smtp_extc = extc
767
+ rcpt.dsn_status = extc
768
+ rcpt.dsn_smtp_response = response.join(' ')
769
+ rcpt.dsn_remote_mta = mx.exchange
770
+ })
771
+ send_command('QUIT')
772
+ processing_mail = false
773
+ return self.temp_fail(`Upstream error: ${code} ${extc ? `${extc} ` : ''}${reason}`)
774
+ } else {
775
+ reason = response.join(' ')
776
+ self.lognotice(`Error - but not processing mail: ${code} ${extc ? `${extc} ` : ''}${reason}`)
777
+ return client_pool.release_client(socket, mx)
707
778
  }
708
- else if (processing_mail) {
709
- reason = response.join(' ');
710
- recipients.forEach(rcpt => {
711
- rcpt.dsn_action = 'delayed';
712
- rcpt.dsn_smtp_code = code;
713
- rcpt.dsn_smtp_extc = extc;
714
- rcpt.dsn_status = extc;
715
- rcpt.dsn_smtp_response = response.join(' ');
716
- rcpt.dsn_remote_mta = mx.exchange;
717
- });
718
- send_command('QUIT');
719
- processing_mail = false;
720
- return self.temp_fail(`Upstream error: ${code} ${(extc) ? `${extc} ` : ''}${reason}`);
721
- }
722
- else {
723
- reason = response.join(' ');
724
- self.lognotice(`Error - but not processing mail: ${code} ${((extc) ? `${extc} ` : '')}${reason}`);
725
- return client_pool.release_client(socket, mx);
726
- }
727
- }
728
- else if (code.match(/^5/)) {
729
- authenticating = false;
779
+ } else if (code.match(/^5/)) {
780
+ authenticating = false
730
781
  if (command === 'ehlo') {
731
782
  // EHLO command was rejected; fall-back to HELO
732
- return send_command('HELO', mx.bind_helo);
783
+ return send_command('HELO', mx.bind_helo)
733
784
  }
734
785
  if (command === 'rset') {
735
786
  // Broken server doesn't accept RSET, terminate the connection
736
- return client_pool.release_client(socket, mx);
787
+ return client_pool.release_client(socket, mx)
737
788
  }
738
- reason = `${code} ${(extc) ? `${extc} ` : ''}${response.join(' ')}`;
789
+ reason = `${code} ${extc ? `${extc} ` : ''}${response.join(' ')}`
739
790
  if (/^rcpt/.test(command) || command === 'dot_lmtp') {
740
- if (command === 'dot_lmtp') last_recip = ok_recips.shift();
741
- self.lognotice(`recipient ${last_recip} rejected: ${reason}`);
742
- last_recip.reason = reason;
743
-
744
- last_recip.dsn_action = 'failed';
745
- last_recip.dsn_smtp_code = code;
746
- last_recip.dsn_smtp_extc = extc;
747
- last_recip.dsn_status = extc;
748
- last_recip.dsn_smtp_response = response.join(' ');
749
- last_recip.dsn_remote_mta = mx.exchange;
750
-
751
- bounce_recips.push(last_recip);
791
+ if (command === 'dot_lmtp') last_recip = ok_recips.shift()
792
+ self.lognotice(`recipient ${last_recip} rejected: ${reason}`)
793
+ last_recip.reason = reason
794
+
795
+ last_recip.dsn_action = 'failed'
796
+ last_recip.dsn_smtp_code = code
797
+ last_recip.dsn_smtp_extc = extc
798
+ last_recip.dsn_status = extc
799
+ last_recip.dsn_smtp_response = response.join(' ')
800
+ last_recip.dsn_remote_mta = mx.exchange
801
+
802
+ bounce_recips.push(last_recip)
752
803
  if (command === 'dot_lmtp') {
753
- response = [];
804
+ response = []
754
805
  if (ok_recips.length === 0) {
755
- return finish_processing_mail(false);
806
+ return finish_processing_mail(false)
756
807
  }
757
808
  }
758
- }
759
- else {
760
- recipients.forEach(rcpt => {
761
- rcpt.dsn_action = 'failed';
762
- rcpt.dsn_smtp_code = code;
763
- rcpt.dsn_smtp_extc = extc;
764
- rcpt.dsn_status = extc;
765
- rcpt.dsn_smtp_response = response.join(' ');
766
- rcpt.dsn_remote_mta = mx.exchange;
767
- });
768
- send_command('QUIT');
769
- processing_mail = false;
770
- return self.bounce(reason, { mx });
809
+ } else {
810
+ recipients.forEach((rcpt) => {
811
+ rcpt.dsn_action = 'failed'
812
+ rcpt.dsn_smtp_code = code
813
+ rcpt.dsn_smtp_extc = extc
814
+ rcpt.dsn_status = extc
815
+ rcpt.dsn_smtp_response = response.join(' ')
816
+ rcpt.dsn_remote_mta = mx.exchange
817
+ })
818
+ send_command('QUIT')
819
+ processing_mail = false
820
+ return self.bounce(reason, { mx })
771
821
  }
772
822
  }
773
823
 
774
824
  switch (command) {
775
825
  case 'connect':
776
- send_command('EHLO', mx.bind_helo);
777
- break;
826
+ send_command('EHLO', mx.bind_helo)
827
+ break
778
828
  case 'connect_lmtp':
779
- send_command('LHLO', mx.bind_helo);
780
- break;
829
+ send_command('LHLO', mx.bind_helo)
830
+ break
781
831
  case 'lhlo':
782
832
  case 'ehlo':
783
- process_ehlo_data();
784
- break;
833
+ process_ehlo_data()
834
+ break
785
835
  case 'starttls': {
786
- const tls_options = obtls.get_tls_options(mx);
787
- if (self.force_tls) tls_options.rejectUnauthorized = true;
836
+ const tls_options = obtls.get_tls_options(mx)
837
+ if (self.force_tls) tls_options.rejectUnauthorized = true
788
838
 
789
- smtp_properties = {};
839
+ smtp_properties = {}
790
840
  socket.upgrade(tls_options, (authorized, verifyError, cert, cipher) => {
791
841
  const loginfo = {
792
- verified: authorized
793
- };
842
+ verified: authorized,
843
+ }
794
844
  if (cipher) {
795
- loginfo.cipher = cipher.name;
796
- loginfo.version = cipher.version;
845
+ loginfo.cipher = cipher.name
846
+ loginfo.version = cipher.version
797
847
  }
798
- if (verifyError) loginfo.error = verifyError;
848
+ if (verifyError) loginfo.error = verifyError
799
849
  if (cert?.subject) {
800
- loginfo.cn = cert.subject.CN;
801
- loginfo.organization = cert.subject.O;
850
+ loginfo.cn = cert.subject.CN
851
+ loginfo.organization = cert.subject.O
802
852
  }
803
- if (cert?.issuer) loginfo.issuer = cert.issuer.O;
804
- if (cert?.valid_to) loginfo.expires = cert.valid_to;
805
- if (cert?.fingerprint) loginfo.fingerprint = cert.fingerprint;
806
- self.loginfo('secured', loginfo);
853
+ if (cert?.issuer) loginfo.issuer = cert.issuer.O
854
+ if (cert?.valid_to) loginfo.expires = cert.valid_to
855
+ if (cert?.fingerprint) loginfo.fingerprint = cert.fingerprint
856
+ self.loginfo('secured', loginfo)
807
857
 
808
858
  if (self.force_tls && !authorized) {
809
- processing_mail = false;
810
- socket.end();
811
- self.temp_fail('Host failed TLS verification required by configuration.');
812
- client_pool.release_client(socket, mx);
859
+ processing_mail = false
860
+ socket.end()
861
+ self.temp_fail('Host failed TLS verification required by configuration.')
862
+ client_pool.release_client(socket, mx)
813
863
  }
814
- });
815
- break;
864
+ })
865
+ break
816
866
  }
817
867
  case 'auth':
818
- authenticating = false;
819
- authenticated = true;
820
- send_command('MAIL', get_reverse_path_with_params());
821
- break;
868
+ authenticating = false
869
+ authenticated = true
870
+ send_command('MAIL', get_reverse_path_with_params())
871
+ break
822
872
  case 'helo':
823
- send_command('MAIL', get_reverse_path_with_params());
824
- break;
873
+ send_command('MAIL', get_reverse_path_with_params())
874
+ break
825
875
  case 'mail':
826
- last_recip = recipients[recip_index];
827
- recip_index++;
828
- send_command('RCPT', `TO:${last_recip.format(!smtp_properties.smtp_utf8)}`);
829
- break;
876
+ last_recip = recipients[recip_index]
877
+ recip_index++
878
+ send_command('RCPT', `TO:${last_recip.format(!smtp_properties.smtp_utf8)}`)
879
+ break
830
880
  case 'rcpt':
831
881
  if (last_recip && code.match(/^250/)) {
832
- ok_recips.push(last_recip);
882
+ ok_recips.push(last_recip)
833
883
  }
834
- if (recip_index === recipients.length) { // End of RCPT TOs
884
+ if (recip_index === recipients.length) {
885
+ // End of RCPT TOs
835
886
  if (ok_recips.length > 0) {
836
- send_command('DATA');
887
+ send_command('DATA')
888
+ } else {
889
+ finish_processing_mail(false)
837
890
  }
838
- else {
839
- finish_processing_mail(false);
840
- }
841
- }
842
- else {
843
- last_recip = recipients[recip_index];
844
- recip_index++;
845
- send_command('RCPT', `TO:${last_recip.format(!smtp_properties.smtp_utf8)}`);
891
+ } else {
892
+ last_recip = recipients[recip_index]
893
+ recip_index++
894
+ send_command('RCPT', `TO:${last_recip.format(!smtp_properties.smtp_utf8)}`)
846
895
  }
847
- break;
896
+ break
848
897
  case 'data': {
849
- const data_stream = self.data_stream();
850
- data_stream.on('data', data => {
851
- self.logdata(`C: ${data}`);
852
- });
853
- data_stream.on('error', err => {
854
- self.logerror(`Reading from the data stream failed: ${err}`);
855
- });
898
+ const data_stream = self.data_stream()
899
+ data_stream.on('data', (data) => {
900
+ self.logdata(`C: ${data}`)
901
+ })
902
+ data_stream.on('error', (err) => {
903
+ self.logerror(`Reading from the data stream failed: ${err}`)
904
+ })
856
905
  data_stream.on('end', () => {
857
- send_command(mx.using_lmtp ? 'dot_lmtp' : 'dot');
858
- });
859
- data_stream.pipe(socket, {end: false});
860
- break;
906
+ send_command(mx.using_lmtp ? 'dot_lmtp' : 'dot')
907
+ })
908
+ data_stream.pipe(socket, { end: false })
909
+ break
861
910
  }
862
911
  case 'dot':
863
- finish_processing_mail(true);
864
- break;
912
+ finish_processing_mail(true)
913
+ break
865
914
  case 'dot_lmtp':
866
- if (code.match(/^2/)) lmtp_rcpt_idx++;
915
+ if (code.match(/^2/)) lmtp_rcpt_idx++
867
916
  if (lmtp_rcpt_idx === ok_recips.length) {
868
- finish_processing_mail(true);
917
+ finish_processing_mail(true)
869
918
  }
870
- break;
919
+ break
871
920
  case 'quit':
872
921
  case 'rset':
873
- client_pool.release_client(socket, mx);
874
- break;
922
+ client_pool.release_client(socket, mx)
923
+ break
875
924
  default:
876
925
  // should never get here - means we did something
877
926
  // wrong.
878
- throw new Error(`Unknown command: ${command}`);
927
+ throw new Error(`Unknown command: ${command}`)
879
928
  }
880
- });
929
+ })
881
930
 
882
931
  if (socket.__fromPool) {
883
- logger.debug(this, 'got socket, trying to deliver');
884
- secured = socket.isEncrypted();
885
- logger.debug(this, `got ${secured ? 'TLS ' : '' }socket, trying to deliver`);
886
- send_command('MAIL', get_reverse_path_with_params());
932
+ logger.debug(this, 'got socket, trying to deliver')
933
+ secured = socket.isEncrypted()
934
+ logger.debug(this, `got ${secured ? 'TLS ' : ''}socket, trying to deliver`)
935
+ send_command('MAIL', get_reverse_path_with_params())
887
936
  }
888
937
  }
889
938
 
890
- extend_rcpt_with_dsn (rcpt, dsn) {
891
- rcpt.dsn_code = dsn.code;
892
- rcpt.dsn_msg = dsn.msg;
893
- rcpt.dsn_status = `${dsn.cls}.${dsn.sub}.${dsn.det}`;
939
+ extend_rcpt_with_dsn(rcpt, dsn) {
940
+ rcpt.dsn_code = dsn.code
941
+ rcpt.dsn_msg = dsn.msg
942
+ rcpt.dsn_status = `${dsn.cls}.${dsn.sub}.${dsn.det}`
894
943
  if (dsn.cls == 4) {
895
- rcpt.dsn_action = 'delayed';
896
- }
897
- else if (dsn.cls == 5) {
898
- rcpt.dsn_action = 'failed';
944
+ rcpt.dsn_action = 'delayed'
945
+ } else if (dsn.cls == 5) {
946
+ rcpt.dsn_action = 'failed'
899
947
  }
900
948
  }
901
949
 
902
- populate_bounce_message (from, to, reason, cb) {
903
-
904
- let buf = '';
905
- const original_header_lines = [];
906
- let headers_done = false;
907
- const header = new message.Header();
950
+ populate_bounce_message(from, to, reason, cb) {
951
+ let buf = ''
952
+ const original_header_lines = []
953
+ let headers_done = false
954
+ const header = new message.Header()
908
955
 
909
956
  try {
910
- const data_stream = this.data_stream();
911
- data_stream.on('data', data => {
957
+ const data_stream = this.data_stream()
958
+ data_stream.on('data', (data) => {
912
959
  if (headers_done === false) {
913
- buf += data;
914
- let results;
960
+ buf += data
961
+ let results
915
962
  while ((results = utils.line_regexp.exec(buf))) {
916
- const this_line = results[1];
963
+ const this_line = results[1]
917
964
  if (this_line === '\n' || this_line == '\r\n') {
918
- headers_done = true;
919
- break;
965
+ headers_done = true
966
+ break
920
967
  }
921
- buf = buf.slice(this_line.length);
922
- original_header_lines.push(this_line);
968
+ buf = buf.slice(this_line.length)
969
+ original_header_lines.push(this_line)
923
970
  }
924
971
  }
925
- });
972
+ })
926
973
  data_stream.on('end', () => {
927
974
  if (original_header_lines.length > 0) {
928
- header.parse(original_header_lines);
975
+ header.parse(original_header_lines)
929
976
  }
930
- this.populate_bounce_message_with_headers(from, to, reason, header, cb);
931
- });
932
- data_stream.on('error', err => {
933
- cb(err);
934
- });
935
- }
936
- catch (err) {
937
- this.populate_bounce_message_with_headers(from, to, reason, header, cb);
977
+ this.populate_bounce_message_with_headers(from, to, reason, header, cb)
978
+ })
979
+ data_stream.on('error', (err) => {
980
+ cb(err)
981
+ })
982
+ } catch (err) {
983
+ this.populate_bounce_message_with_headers(from, to, reason, header, cb)
938
984
  }
939
985
  }
940
986
 
@@ -960,509 +1006,516 @@ class HMailItem extends events.EventEmitter {
960
1006
  * @param header
961
1007
  * @param cb - a callback for fn(err, message_body_lines)
962
1008
  */
963
- populate_bounce_message_with_headers (from, to, reason, header, cb) {
964
- const CRLF = '\r\n';
1009
+ populate_bounce_message_with_headers(from, to, reason, header, cb) {
1010
+ const CRLF = '\r\n'
965
1011
 
966
- const originalMessageId = header.get('Message-Id');
1012
+ const originalMessageId = header.get('Message-Id')
967
1013
 
968
- const bounce_msg_ = config.get('outbound.bounce_message', 'data');
969
- const bounce_msg_html_ = config.get('outbound.bounce_message_html', 'data');
970
- const bounce_msg_image_ = config.get('outbound.bounce_message_image', 'data');
1014
+ const bounce_msg_ = config.get('outbound.bounce_message', 'data')
1015
+ const bounce_msg_html_ = config.get('outbound.bounce_message_html', 'data')
1016
+ const bounce_msg_image_ = config.get('outbound.bounce_message_image', 'data')
971
1017
 
972
- const bounce_header_lines = [];
973
- const bounce_body_lines = [];
974
- const bounce_html_lines = [];
975
- const bounce_image_lines = [];
976
- let bounce_headers_done = false;
1018
+ const bounce_header_lines = []
1019
+ const bounce_body_lines = []
1020
+ const bounce_html_lines = []
1021
+ const bounce_image_lines = []
1022
+ let bounce_headers_done = false
977
1023
 
978
1024
  const values = {
979
1025
  date: utils.date_to_str(new Date()),
980
- me: net_utils.get_primary_host_name(),
1026
+ me: net_utils.get_primary_host_name(),
981
1027
  from,
982
1028
  to,
983
1029
  subject: header.get_decoded('Subject').trim(),
984
1030
  recipients: this.todo.rcpt_to.join(', '),
985
1031
  reason,
986
- extended_reason: this.todo.rcpt_to.map(recip => {
987
- if (recip.reason) {
988
- return `${recip.original}: ${recip.reason}`;
989
- }
990
- }).join('\n'),
1032
+ extended_reason: this.todo.rcpt_to
1033
+ .map((recip) => {
1034
+ if (recip.reason) {
1035
+ return `${recip.original}: ${recip.reason}`
1036
+ }
1037
+ })
1038
+ .join('\n'),
991
1039
  pid: process.pid,
992
1040
  msgid: `<${utils.uuid()}@${net_utils.get_primary_host_name()}>`,
993
- };
1041
+ }
994
1042
 
995
- bounce_msg_.forEach(line => {
996
- line = line.replace(/\{(\w+)\}/g, (i, word) => values[word] || '?');
1043
+ bounce_msg_.forEach((line) => {
1044
+ line = line.replace(/\{(\w+)\}/g, (i, word) => values[word] || '?')
997
1045
 
998
1046
  if (bounce_headers_done == false && line == '') {
999
- bounce_headers_done = true;
1000
- }
1001
- else if (bounce_headers_done == false) {
1002
- bounce_header_lines.push(line);
1047
+ bounce_headers_done = true
1048
+ } else if (bounce_headers_done == false) {
1049
+ bounce_header_lines.push(line)
1050
+ } else if (bounce_headers_done == true) {
1051
+ bounce_body_lines.push(line)
1003
1052
  }
1004
- else if (bounce_headers_done == true) {
1005
- bounce_body_lines.push(line);
1006
- }
1007
- });
1053
+ })
1008
1054
 
1009
1055
  const escaped_chars = {
1010
- "&": "amp",
1011
- "<": "lt",
1012
- ">": "gt",
1056
+ '&': 'amp',
1057
+ '<': 'lt',
1058
+ '>': 'gt',
1013
1059
  '"': 'quot',
1014
1060
  "'": 'apos',
1015
- "\r": '#10',
1016
- "\n": '#13'
1017
- };
1018
- const escape_pattern = new RegExp(`[${Object.keys(escaped_chars).join('')}]`, 'g');
1061
+ '\r': '#10',
1062
+ '\n': '#13',
1063
+ }
1064
+ const escape_pattern = new RegExp(`[${Object.keys(escaped_chars).join('')}]`, 'g')
1019
1065
 
1020
- bounce_msg_html_.forEach(line => {
1066
+ bounce_msg_html_.forEach((line) => {
1021
1067
  line = line.replace(/\{(\w+)\}/g, (i, word) => {
1022
1068
  if (word in values) {
1023
- return String(values[word]).replace(escape_pattern, m => `&${escaped_chars[m]};`);
1069
+ return String(values[word]).replace(escape_pattern, (m) => `&${escaped_chars[m]};`)
1070
+ } else {
1071
+ return '?'
1024
1072
  }
1025
- else {
1026
- return '?';
1027
- }
1028
- });
1073
+ })
1029
1074
 
1030
- bounce_html_lines.push(line);
1031
- });
1075
+ bounce_html_lines.push(line)
1076
+ })
1032
1077
 
1033
- bounce_msg_image_.forEach(line => {
1078
+ bounce_msg_image_.forEach((line) => {
1034
1079
  bounce_image_lines.push(line)
1035
- });
1080
+ })
1036
1081
 
1037
- const boundary = `boundary_${utils.uuid()}`;
1038
- const bounce_body = [];
1082
+ const boundary = `boundary_${utils.uuid()}`
1083
+ const bounce_body = []
1039
1084
 
1040
- bounce_header_lines.forEach(line => {
1041
- bounce_body.push(`${line}${CRLF}`);
1042
- });
1043
- bounce_body.push(`Content-Type: multipart/report; report-type=delivery-status;${CRLF} boundary="${boundary}"${CRLF}`);
1085
+ bounce_header_lines.forEach((line) => {
1086
+ bounce_body.push(`${line}${CRLF}`)
1087
+ })
1088
+ bounce_body.push(
1089
+ `Content-Type: multipart/report; report-type=delivery-status;${CRLF} boundary="${boundary}"${CRLF}`,
1090
+ )
1044
1091
  // Adding references to original msg id
1045
1092
  if (originalMessageId != '') {
1046
- bounce_body.push(`References: ${originalMessageId.replace(/(\r?\n)*$/, '')}${CRLF}`);
1093
+ bounce_body.push(`References: ${originalMessageId.replace(/(\r?\n)*$/, '')}${CRLF}`)
1047
1094
  }
1048
1095
 
1049
- bounce_body.push(CRLF);
1050
- bounce_body.push(`This is a MIME-encapsulated message.${CRLF}`);
1051
- bounce_body.push(CRLF);
1096
+ bounce_body.push(CRLF)
1097
+ bounce_body.push(`This is a MIME-encapsulated message.${CRLF}`)
1098
+ bounce_body.push(CRLF)
1052
1099
 
1053
- let boundary_incr = '';
1100
+ let boundary_incr = ''
1054
1101
  if (bounce_html_lines.length > 1) {
1055
- boundary_incr = 'a';
1056
- bounce_body.push(`--${boundary}${CRLF}`);
1057
- bounce_body.push(`Content-Type: multipart/related; boundary="${boundary}${boundary_incr}"${CRLF}`);
1058
- bounce_body.push(CRLF);
1059
- bounce_body.push(`--${boundary}${boundary_incr}${CRLF}`);
1060
- boundary_incr = 'b';
1061
- bounce_body.push(`Content-Type: multipart/alternative; boundary="${boundary}${boundary_incr}"${CRLF}`);
1062
- bounce_body.push(CRLF);
1102
+ boundary_incr = 'a'
1103
+ bounce_body.push(`--${boundary}${CRLF}`)
1104
+ bounce_body.push(`Content-Type: multipart/related; boundary="${boundary}${boundary_incr}"${CRLF}`)
1105
+ bounce_body.push(CRLF)
1106
+ bounce_body.push(`--${boundary}${boundary_incr}${CRLF}`)
1107
+ boundary_incr = 'b'
1108
+ bounce_body.push(`Content-Type: multipart/alternative; boundary="${boundary}${boundary_incr}"${CRLF}`)
1109
+ bounce_body.push(CRLF)
1063
1110
  }
1064
1111
 
1065
- bounce_body.push(`--${boundary}${boundary_incr}${CRLF}`);
1066
- bounce_body.push(`Content-Type: text/plain; charset=us-ascii${CRLF}`);
1067
- bounce_body.push(CRLF);
1068
- bounce_body_lines.forEach(line => {
1069
- bounce_body.push(`${line}${CRLF}`);
1070
- });
1071
- bounce_body.push(CRLF);
1112
+ bounce_body.push(`--${boundary}${boundary_incr}${CRLF}`)
1113
+ bounce_body.push(`Content-Type: text/plain; charset=us-ascii${CRLF}`)
1114
+ bounce_body.push(CRLF)
1115
+ bounce_body_lines.forEach((line) => {
1116
+ bounce_body.push(`${line}${CRLF}`)
1117
+ })
1118
+ bounce_body.push(CRLF)
1072
1119
 
1073
1120
  if (bounce_html_lines.length > 1) {
1074
- bounce_body.push(`--${boundary}${boundary_incr}${CRLF}`);
1075
- bounce_body.push(`Content-Type: text/html; charset=us-ascii${CRLF}`);
1076
- bounce_body.push(CRLF);
1077
- bounce_html_lines.forEach(line => {
1078
- bounce_body.push(`${line}${CRLF}`);
1079
- });
1080
- bounce_body.push(CRLF);
1081
- bounce_body.push(`--${boundary}${boundary_incr}--${CRLF}`);
1121
+ bounce_body.push(`--${boundary}${boundary_incr}${CRLF}`)
1122
+ bounce_body.push(`Content-Type: text/html; charset=us-ascii${CRLF}`)
1123
+ bounce_body.push(CRLF)
1124
+ bounce_html_lines.forEach((line) => {
1125
+ bounce_body.push(`${line}${CRLF}`)
1126
+ })
1127
+ bounce_body.push(CRLF)
1128
+ bounce_body.push(`--${boundary}${boundary_incr}--${CRLF}`)
1082
1129
 
1083
1130
  if (bounce_image_lines.length > 1) {
1084
- boundary_incr = 'a';
1085
- bounce_body.push(`--${boundary}${boundary_incr}${CRLF}`);
1131
+ boundary_incr = 'a'
1132
+ bounce_body.push(`--${boundary}${boundary_incr}${CRLF}`)
1086
1133
  //bounce_body.push(`Content-Type: text/html; charset=us-ascii${CRLF}`);
1087
1134
  //bounce_body.push(CRLF);
1088
- bounce_image_lines.forEach(line => {
1089
- bounce_body.push(`${line}${CRLF}`);
1090
- });
1091
- bounce_body.push(CRLF);
1092
- bounce_body.push(`--${boundary}${boundary_incr}--${CRLF}`);
1135
+ bounce_image_lines.forEach((line) => {
1136
+ bounce_body.push(`${line}${CRLF}`)
1137
+ })
1138
+ bounce_body.push(CRLF)
1139
+ bounce_body.push(`--${boundary}${boundary_incr}--${CRLF}`)
1093
1140
  }
1094
1141
  }
1095
1142
 
1096
- bounce_body.push(`--${boundary}${CRLF}`);
1097
- bounce_body.push(`Content-type: message/delivery-status${CRLF}`);
1098
- bounce_body.push(CRLF);
1143
+ bounce_body.push(`--${boundary}${CRLF}`)
1144
+ bounce_body.push(`Content-type: message/delivery-status${CRLF}`)
1145
+ bounce_body.push(CRLF)
1099
1146
  if (originalMessageId != '') {
1100
- bounce_body.push(`Original-Envelope-Id: ${originalMessageId.replace(/(\r?\n)*$/, '')}${CRLF}`);
1147
+ bounce_body.push(`Original-Envelope-Id: ${originalMessageId.replace(/(\r?\n)*$/, '')}${CRLF}`)
1101
1148
  }
1102
- bounce_body.push(`Reporting-MTA: dns;${net_utils.get_primary_host_name()}${CRLF}`);
1149
+ bounce_body.push(`Reporting-MTA: dns;${net_utils.get_primary_host_name()}${CRLF}`)
1103
1150
  if (this.todo.queue_time) {
1104
- bounce_body.push(`Arrival-Date: ${utils.date_to_str(new Date(this.todo.queue_time))}${CRLF}`);
1151
+ bounce_body.push(`Arrival-Date: ${utils.date_to_str(new Date(this.todo.queue_time))}${CRLF}`)
1105
1152
  }
1106
- this.todo.rcpt_to.forEach(rcpt_to => {
1107
- bounce_body.push(CRLF);
1108
- bounce_body.push(`Final-Recipient: rfc822;${rcpt_to.address()}${CRLF}`);
1109
- let dsn_action = null;
1153
+ this.todo.rcpt_to.forEach((rcpt_to) => {
1154
+ bounce_body.push(CRLF)
1155
+ bounce_body.push(`Final-Recipient: rfc822;${rcpt_to.address()}${CRLF}`)
1156
+ let dsn_action = null
1110
1157
  if (rcpt_to.dsn_action) {
1111
- dsn_action = rcpt_to.dsn_action;
1112
- }
1113
- else if (rcpt_to.dsn_code) {
1158
+ dsn_action = rcpt_to.dsn_action
1159
+ } else if (rcpt_to.dsn_code) {
1114
1160
  if (/^5/.exec(rcpt_to.dsn_code)) {
1115
- dsn_action = 'failed';
1116
- }
1117
- else if (/^4/.exec(rcpt_to.dsn_code)) {
1118
- dsn_action = 'delayed';
1161
+ dsn_action = 'failed'
1162
+ } else if (/^4/.exec(rcpt_to.dsn_code)) {
1163
+ dsn_action = 'delayed'
1164
+ } else if (/^2/.exec(rcpt_to.dsn_code)) {
1165
+ dsn_action = 'delivered'
1119
1166
  }
1120
- else if (/^2/.exec(rcpt_to.dsn_code)) {
1121
- dsn_action = 'delivered';
1122
- }
1123
- }
1124
- else if (rcpt_to.dsn_smtp_code) {
1167
+ } else if (rcpt_to.dsn_smtp_code) {
1125
1168
  if (/^5/.exec(rcpt_to.dsn_smtp_code)) {
1126
- dsn_action = 'failed';
1127
- }
1128
- else if (/^4/.exec(rcpt_to.dsn_smtp_code)) {
1129
- dsn_action = 'delayed';
1130
- }
1131
- else if (/^2/.exec(rcpt_to.dsn_smtp_code)) {
1132
- dsn_action = 'delivered';
1169
+ dsn_action = 'failed'
1170
+ } else if (/^4/.exec(rcpt_to.dsn_smtp_code)) {
1171
+ dsn_action = 'delayed'
1172
+ } else if (/^2/.exec(rcpt_to.dsn_smtp_code)) {
1173
+ dsn_action = 'delivered'
1133
1174
  }
1134
1175
  }
1135
1176
  if (dsn_action != null) {
1136
- bounce_body.push(`Action: ${dsn_action}${CRLF}`);
1177
+ bounce_body.push(`Action: ${dsn_action}${CRLF}`)
1137
1178
  }
1138
1179
  if (rcpt_to.dsn_status) {
1139
- let { dsn_status } = rcpt_to;
1180
+ let { dsn_status } = rcpt_to
1140
1181
  if (rcpt_to.dsn_code || rcpt_to.dsn_msg) {
1141
- dsn_status += " (";
1182
+ dsn_status += ' ('
1142
1183
  if (rcpt_to.dsn_code) {
1143
- dsn_status += rcpt_to.dsn_code;
1184
+ dsn_status += rcpt_to.dsn_code
1144
1185
  }
1145
1186
  if (rcpt_to.dsn_code || rcpt_to.dsn_msg) {
1146
- dsn_status += " ";
1187
+ dsn_status += ' '
1147
1188
  }
1148
1189
  if (rcpt_to.dsn_msg) {
1149
- dsn_status += rcpt_to.dsn_msg;
1190
+ dsn_status += rcpt_to.dsn_msg
1150
1191
  }
1151
- dsn_status += ")";
1192
+ dsn_status += ')'
1152
1193
  }
1153
- bounce_body.push(`Status: ${dsn_status}${CRLF}`);
1194
+ bounce_body.push(`Status: ${dsn_status}${CRLF}`)
1154
1195
  }
1155
1196
  if (rcpt_to.dsn_remote_mta) {
1156
- bounce_body.push(`Remote-MTA: ${rcpt_to.dsn_remote_mta}${CRLF}`);
1197
+ bounce_body.push(`Remote-MTA: ${rcpt_to.dsn_remote_mta}${CRLF}`)
1157
1198
  }
1158
- let diag_code = null;
1199
+ let diag_code = null
1159
1200
  if (rcpt_to.dsn_smtp_code || rcpt_to.dsn_smtp_extc || rcpt_to.dsn_smtp_response) {
1160
- diag_code = "smtp;";
1201
+ diag_code = 'smtp;'
1161
1202
  if (rcpt_to.dsn_smtp_code) {
1162
- diag_code += `${rcpt_to.dsn_smtp_code} `;
1203
+ diag_code += `${rcpt_to.dsn_smtp_code} `
1163
1204
  }
1164
1205
  if (rcpt_to.dsn_smtp_extc) {
1165
- diag_code += `${rcpt_to.dsn_smtp_extc} `;
1206
+ diag_code += `${rcpt_to.dsn_smtp_extc} `
1166
1207
  }
1167
1208
  if (rcpt_to.dsn_smtp_response) {
1168
- diag_code += `${rcpt_to.dsn_smtp_response} `;
1209
+ diag_code += `${rcpt_to.dsn_smtp_response} `
1169
1210
  }
1170
1211
  }
1171
1212
  if (diag_code != null) {
1172
- bounce_body.push(`Diagnostic-Code: ${diag_code}${CRLF}`);
1213
+ bounce_body.push(`Diagnostic-Code: ${diag_code}${CRLF}`)
1173
1214
  }
1174
- });
1175
- bounce_body.push(CRLF);
1176
-
1177
- bounce_body.push(`--${boundary}${CRLF}`);
1178
- bounce_body.push(`Content-Description: Undelivered Message Headers${CRLF}`);
1179
- bounce_body.push(`Content-Type: text/rfc822-headers${CRLF}`);
1180
- bounce_body.push(CRLF);
1181
- header.header_list.forEach(line => {
1182
- bounce_body.push(line);
1183
- });
1184
- bounce_body.push(CRLF);
1215
+ })
1216
+ bounce_body.push(CRLF)
1217
+
1218
+ bounce_body.push(`--${boundary}${CRLF}`)
1219
+ bounce_body.push(`Content-Description: Undelivered Message Headers${CRLF}`)
1220
+ bounce_body.push(`Content-Type: text/rfc822-headers${CRLF}`)
1221
+ bounce_body.push(CRLF)
1222
+ header.header_list.forEach((line) => {
1223
+ bounce_body.push(line)
1224
+ })
1225
+ bounce_body.push(CRLF)
1185
1226
 
1186
- bounce_body.push(`--${boundary}--${CRLF}`);
1227
+ bounce_body.push(`--${boundary}--${CRLF}`)
1187
1228
 
1188
- cb(null, bounce_body);
1229
+ cb(null, bounce_body)
1189
1230
  }
1190
1231
 
1191
- bounce (err, opts) {
1192
- this.loginfo(`bouncing mail: ${err}`);
1232
+ bounce(err, opts) {
1233
+ this.loginfo(`bouncing mail: ${err}`)
1193
1234
  if (!this.todo) {
1194
- this.once('ready', () => { this._bounce(err, opts); });
1195
- return;
1235
+ this.once('ready', () => {
1236
+ this._bounce(err, opts)
1237
+ })
1238
+ return
1196
1239
  }
1197
- this._bounce(err, opts);
1240
+ this._bounce(err, opts)
1198
1241
  }
1199
1242
 
1200
- _bounce (err, opts) {
1201
- err = new Error(err);
1243
+ _bounce(err, opts) {
1244
+ err = new Error(err)
1202
1245
  if (opts) {
1203
- err.mx = opts.mx;
1204
- err.deferred_rcpt = opts.fail_recips;
1205
- err.bounced_rcpt = opts.bounce_recips;
1246
+ err.mx = opts.mx
1247
+ err.deferred_rcpt = opts.fail_recips
1248
+ err.bounced_rcpt = opts.bounce_recips
1206
1249
  }
1207
- this.bounce_error = err;
1208
- plugins.run_hooks("bounce", this, err);
1250
+ this.bounce_error = err
1251
+ plugins.run_hooks('bounce', this, err)
1209
1252
  }
1210
1253
 
1211
- bounce_respond (retval, msg) {
1254
+ bounce_respond(retval, msg) {
1212
1255
  if (retval !== constants.cont) {
1213
- this.loginfo(`Plugin responded with: ${retval}. Not sending bounce.`);
1214
- return this.discard(); // calls next_cb
1256
+ this.loginfo(`Plugin responded with: ${retval}. Not sending bounce.`)
1257
+ return this.discard() // calls next_cb
1215
1258
  }
1216
1259
 
1217
- const self = this;
1218
- const err = this.bounce_error;
1260
+ const self = this
1261
+ const err = this.bounce_error
1219
1262
 
1220
1263
  if (!this.todo.mail_from.user) {
1221
1264
  // double bounce - mail was already a bounce
1222
- return this.double_bounce("Mail was already a bounce");
1265
+ return this.double_bounce('Mail was already a bounce')
1223
1266
  }
1224
1267
 
1225
- const from = new Address ('<>');
1226
- const recip = new Address (this.todo.mail_from.user, this.todo.mail_from.host);
1268
+ const from = new Address('<>')
1269
+ const recip = new Address(this.todo.mail_from.user, this.todo.mail_from.host)
1227
1270
  this.populate_bounce_message(from, recip, err, function (err2, data_lines) {
1228
1271
  if (err2) {
1229
- return self.double_bounce(`Error populating bounce message: ${err2}`);
1272
+ return self.double_bounce(`Error populating bounce message: ${err2}`)
1230
1273
  }
1231
1274
 
1232
- outbound.send_email(from, recip, data_lines.join(''), (code, msg2) => {
1233
- if (code === constants.deny) {
1234
- // failed to even queue the mail
1235
- return self.double_bounce("Unable to queue the bounce message. Not sending bounce!");
1236
- }
1237
- self.discard();
1238
- }, {origin: this});
1239
- });
1275
+ outbound.send_email(
1276
+ from,
1277
+ recip,
1278
+ data_lines.join(''),
1279
+ (code, msg2) => {
1280
+ if (code === constants.deny) {
1281
+ // failed to even queue the mail
1282
+ return self.double_bounce('Unable to queue the bounce message. Not sending bounce!')
1283
+ }
1284
+ self.discard()
1285
+ },
1286
+ { origin: this },
1287
+ )
1288
+ })
1240
1289
  }
1241
1290
 
1242
- double_bounce (err) {
1243
- this.lognotice(`Double bounce: ${err}`);
1244
- fs.unlink(this.path, () => {});
1245
- this.next_cb();
1291
+ double_bounce(err) {
1292
+ this.lognotice(`Double bounce: ${err}`)
1293
+ fs.unlink(this.path, () => {})
1294
+ this.next_cb()
1246
1295
  // TODO: fill this in... ?
1247
1296
  // One strategy is perhaps log to an mbox file. What do other servers do?
1248
1297
  // Another strategy might be delivery "plugins" to cope with this.
1249
1298
  }
1250
1299
 
1251
- delivered (ip, port, mode, host, response, ok_recips, fail_recips, bounce_recips, secured, authenticated) {
1252
- const delay = (Date.now() - this.todo.queue_time)/1000;
1300
+ delivered(ip, port, mode, host, response, ok_recips, fail_recips, bounce_recips, secured, authenticated) {
1301
+ const delay = (Date.now() - this.todo.queue_time) / 1000
1253
1302
  this.lognotice({
1254
1303
  'delivered file': this.filename,
1255
- 'domain': this.todo.domain,
1304
+ domain: this.todo.domain,
1256
1305
  host,
1257
1306
  ip,
1258
1307
  port,
1259
1308
  mode,
1260
- 'tls': ((secured) ? 'Y' : 'N'),
1261
- 'auth': ((authenticated) ? 'Y' : 'N'),
1309
+ tls: secured ? 'Y' : 'N',
1310
+ auth: authenticated ? 'Y' : 'N',
1262
1311
  response,
1263
1312
  delay,
1264
- 'fails': this.num_failures,
1265
- 'rcpts': `${ok_recips.length}/${fail_recips.length}/${bounce_recips.length}`
1266
- });
1267
- plugins.run_hooks("delivered", this, [host, ip, response, delay, port, mode, ok_recips, secured, authenticated]);
1313
+ fails: this.num_failures,
1314
+ rcpts: `${ok_recips.length}/${fail_recips.length}/${bounce_recips.length}`,
1315
+ })
1316
+ plugins.run_hooks('delivered', this, [host, ip, response, delay, port, mode, ok_recips, secured, authenticated])
1268
1317
  }
1269
1318
 
1270
- discard () {
1271
- this.refcount--;
1319
+ discard() {
1320
+ this.refcount--
1272
1321
  if (this.refcount === 0) {
1273
1322
  // Remove the file.
1274
- fs.unlink(this.path, () => {});
1275
- this.next_cb();
1323
+ fs.unlink(this.path, () => {})
1324
+ this.next_cb()
1276
1325
  }
1277
1326
  }
1278
1327
 
1279
- convert_temp_failed_to_bounce (err, extra) {
1280
- this.todo.rcpt_to.forEach(rcpt_to => {
1281
- rcpt_to.dsn_action = 'failed';
1328
+ convert_temp_failed_to_bounce(err, extra) {
1329
+ this.todo.rcpt_to.forEach((rcpt_to) => {
1330
+ rcpt_to.dsn_action = 'failed'
1282
1331
  if (rcpt_to.dsn_status) {
1283
- rcpt_to.dsn_status = (`${rcpt_to.dsn_status}`).replace(/^4/, '5');
1332
+ rcpt_to.dsn_status = `${rcpt_to.dsn_status}`.replace(/^4/, '5')
1284
1333
  }
1285
- });
1286
- return this.bounce(err, extra);
1334
+ })
1335
+ return this.bounce(err, extra)
1287
1336
  }
1288
1337
 
1289
- temp_fail (err, extra) {
1290
- logger.debug(this, `Temp fail for: ${err}`);
1291
- this.num_failures++;
1338
+ temp_fail(err, extra) {
1339
+ logger.debug(this, `Temp fail for: ${err}`)
1340
+ this.num_failures++
1292
1341
 
1293
1342
  // Test for max failures which is configurable.
1294
- if (this.num_failures > (obc.cfg.temp_fail_intervals.length)) {
1295
- return this.convert_temp_failed_to_bounce(`Too many failures (${err})`, extra);
1343
+ if (this.num_failures > obc.cfg.temp_fail_intervals.length) {
1344
+ return this.convert_temp_failed_to_bounce(`Too many failures (${err})`, extra)
1296
1345
  }
1297
1346
 
1298
- const delay = obc.cfg.temp_fail_intervals[this.num_failures-1];
1347
+ const delay = obc.cfg.temp_fail_intervals[this.num_failures - 1]
1299
1348
 
1300
- plugins.run_hooks('deferred', this, {delay, err});
1349
+ plugins.run_hooks('deferred', this, { delay, err, ...(extra || {}) })
1301
1350
  }
1302
1351
 
1303
- deferred_respond (retval, msg, params) {
1352
+ deferred_respond(retval, msg, params) {
1304
1353
  if (retval !== constants.cont && retval !== constants.denysoft) {
1305
- this.loginfo(`plugin responded with: ${retval}. Not deferring. Deleting mail.`);
1306
- return this.discard(); // calls next_cb
1354
+ this.loginfo(`plugin responded with: ${retval}. Not deferring. Deleting mail.`)
1355
+ return this.discard() // calls next_cb
1307
1356
  }
1308
1357
 
1309
- let delay = params.delay * 1000;
1358
+ let delay = params.delay * 1000
1310
1359
 
1311
1360
  if (retval === constants.denysoft) {
1312
- delay = parseInt(msg, 10) * 1000;
1361
+ delay = parseInt(msg, 10) * 1000
1313
1362
  }
1314
1363
 
1315
- this.loginfo(`Temp failing ${this.filename} for ${delay/1000} seconds: ${params.err}`);
1316
- const parts = _qfile.parts(this.filename);
1317
- parts.next_attempt = Date.now() + delay;
1318
- parts.attempts = this.num_failures;
1319
- const new_filename = _qfile.name(parts);
1364
+ this.loginfo(`Temp failing ${this.filename} for ${delay / 1000} seconds: ${params.err}`)
1365
+ const parts = _qfile.parts(this.filename)
1366
+ parts.next_attempt = Date.now() + delay
1367
+ parts.attempts = this.num_failures
1368
+ const new_filename = _qfile.name(parts)
1320
1369
 
1321
- fs.rename(this.path, path.join(queue_dir, new_filename), err => {
1370
+ fs.rename(this.path, path.join(queue_dir, new_filename), (err) => {
1322
1371
  if (err) {
1323
- return this.bounce(`Error re-queueing email: ${err}`);
1372
+ return this.bounce(`Error re-queueing email: ${err}`)
1324
1373
  }
1325
1374
 
1326
- this.path = path.join(queue_dir, new_filename);
1327
- this.filename = new_filename;
1375
+ this.path = path.join(queue_dir, new_filename)
1376
+ this.filename = new_filename
1328
1377
 
1329
- this.next_cb();
1378
+ this.next_cb()
1330
1379
 
1331
- temp_fail_queue.add(this.filename, delay, () => { delivery_queue.push(this); });
1332
- });
1380
+ temp_fail_queue.add(this.filename, delay, () => {
1381
+ delivery_queue.push(this)
1382
+ })
1383
+ })
1333
1384
  }
1334
1385
 
1335
1386
  // The following handler impacts outgoing mail. It removes the queue file.
1336
- delivered_respond (retval, msg) {
1387
+ delivered_respond(retval, msg) {
1337
1388
  if (retval !== constants.cont && retval !== constants.ok) {
1338
- this.logwarn(
1339
- "delivered plugin responded",
1340
- { retval, msg }
1341
- );
1389
+ this.logwarn('delivered plugin responded', { retval, msg })
1342
1390
  }
1343
- this.discard();
1391
+ this.discard()
1344
1392
  }
1345
1393
 
1346
- get_force_tls (mx) {
1394
+ get_force_tls(mx) {
1347
1395
  if (!mx.exchange) return false
1348
1396
  if (!obtls.cfg.force_tls_hosts) return false
1349
1397
 
1350
1398
  if (net_utils.ip_in_list(obtls.cfg.force_tls_hosts, mx.exchange)) {
1351
- this.logdebug(`Forcing TLS for host ${mx.exchange}`);
1352
- return true;
1399
+ this.logdebug(`Forcing TLS for host ${mx.exchange}`)
1400
+ return true
1353
1401
  }
1354
1402
 
1355
1403
  if (mx.from_dns) {
1356
1404
  // the MX was looked up in DNS and already resolved to IP(s).
1357
1405
  // This checks the hostname.
1358
1406
  if (net_utils.ip_in_list(obtls.cfg.force_tls_hosts, mx.from_dns)) {
1359
- this.logdebug(`Forcing TLS for host ${mx.from_dns}`);
1360
- return true;
1407
+ this.logdebug(`Forcing TLS for host ${mx.from_dns}`)
1408
+ return true
1361
1409
  }
1362
1410
  }
1363
1411
 
1364
1412
  if (net_utils.ip_in_list(obtls.cfg.force_tls_hosts, this.todo.domain)) {
1365
- this.logdebug(`Forcing TLS for domain ${this.todo.domain}`);
1366
- return true;
1413
+ this.logdebug(`Forcing TLS for domain ${this.todo.domain}`)
1414
+ return true
1367
1415
  }
1368
1416
 
1369
1417
  return false
1370
1418
  }
1371
1419
 
1372
- sort_mx (mx_list) {
1420
+ sort_mx(mx_list) {
1373
1421
  // MXs must be sorted by priority.
1374
- const sorted = mx_list.sort((a,b) => a.priority - b.priority);
1422
+ const sorted = mx_list.sort((a, b) => a.priority - b.priority)
1375
1423
 
1376
1424
  // Matched priorities must be randomly shuffled.
1377
1425
  // This isn't a very good shuffle but it'll do for now.
1378
- for (let i=0,l=sorted.length-1; i<l; i++) {
1379
- if (sorted[i].priority === sorted[i+1].priority) {
1380
- if (Math.round(Math.random())) { // 0 or 1
1381
- const j = sorted[i];
1382
- sorted[i] = sorted[i+1];
1383
- sorted[i+1] = j;
1426
+ for (let i = 0, l = sorted.length - 1; i < l; i++) {
1427
+ if (sorted[i].priority === sorted[i + 1].priority) {
1428
+ if (Math.round(Math.random())) {
1429
+ // 0 or 1
1430
+ const j = sorted[i]
1431
+ sorted[i] = sorted[i + 1]
1432
+ sorted[i + 1] = j
1384
1433
  }
1385
1434
  }
1386
1435
  }
1387
1436
 
1388
- return sorted;
1437
+ return sorted
1389
1438
  }
1390
1439
 
1391
- split_to_new_recipients (recipients, response, cb) {
1392
- const hmail = this;
1440
+ split_to_new_recipients(recipients, response, cb) {
1441
+ const hmail = this
1393
1442
  if (recipients.length === hmail.todo.rcpt_to.length) {
1394
1443
  // Split to new for no reason - increase refcount and return self
1395
- hmail.refcount++;
1396
- return cb(hmail);
1444
+ hmail.refcount++
1445
+ return cb(hmail)
1397
1446
  }
1398
- const fname = _qfile.name();
1399
- const tmp_path = path.join(queue_dir, `${_qfile.platformDOT}${fname}`);
1400
- const ws = new FsyncWriteStream(tmp_path, { flags: constants.WRITE_EXCL });
1401
- function err_handler (err, location) {
1402
- logger.error(this, `Error while splitting to new recipients (${location}): ${err}`);
1403
- hmail.todo.rcpt_to.forEach(rcpt => {
1404
- hmail.extend_rcpt_with_dsn(rcpt, DSN.sys_unspecified(`Error splitting to new recipients: ${err}`));
1405
- });
1406
- hmail.bounce(`Error splitting to new recipients: ${err}`);
1447
+ const fname = _qfile.name()
1448
+ const tmp_path = path.join(queue_dir, `${_qfile.platformDOT}${fname}`)
1449
+ const ws = new FsyncWriteStream(tmp_path, {
1450
+ flags: constants.WRITE_EXCL,
1451
+ })
1452
+ function err_handler(err, location) {
1453
+ logger.error(this, `Error while splitting to new recipients (${location}): ${err}`)
1454
+ hmail.todo.rcpt_to.forEach((rcpt) => {
1455
+ hmail.extend_rcpt_with_dsn(rcpt, DSN.sys_unspecified(`Error splitting to new recipients: ${err}`))
1456
+ })
1457
+ hmail.bounce(`Error splitting to new recipients: ${err}`)
1407
1458
  }
1408
1459
 
1409
- ws.on('error', err => { err_handler(err, "tmp file writer");});
1460
+ ws.on('error', (err) => {
1461
+ err_handler(err, 'tmp file writer')
1462
+ })
1410
1463
 
1411
- let writing = false;
1464
+ let writing = false
1412
1465
 
1413
- function write_more () {
1414
- if (writing) return;
1415
- writing = true;
1416
- const rs = hmail.data_stream();
1417
- rs.pipe(ws, {end: false});
1418
- rs.on('error', err => {
1419
- err_handler(err, "hmail.data_stream reader");
1420
- });
1466
+ function write_more() {
1467
+ if (writing) return
1468
+ writing = true
1469
+ const rs = hmail.data_stream()
1470
+ rs.pipe(ws, { end: false })
1471
+ rs.on('error', (err) => {
1472
+ err_handler(err, 'hmail.data_stream reader')
1473
+ })
1421
1474
  rs.on('end', () => {
1422
1475
  ws.on('close', () => {
1423
- const dest_path = path.join(queue_dir, fname);
1424
- fs.rename(tmp_path, dest_path, err => {
1476
+ const dest_path = path.join(queue_dir, fname)
1477
+ fs.rename(tmp_path, dest_path, (err) => {
1425
1478
  if (err) {
1426
- err_handler(err, "tmp file rename");
1479
+ err_handler(err, 'tmp file rename')
1427
1480
  return
1428
1481
  }
1429
- const split_mail = new HMailItem (fname, dest_path, hmail.notes);
1482
+ const split_mail = new HMailItem(fname, dest_path, hmail.notes)
1430
1483
  split_mail.once('ready', () => {
1431
- cb(split_mail);
1432
- });
1433
- });
1434
- });
1435
- ws.destroySoon();
1436
- });
1484
+ cb(split_mail)
1485
+ })
1486
+ })
1487
+ })
1488
+ ws.destroySoon()
1489
+ })
1437
1490
  }
1438
1491
 
1439
- ws.on('error', err => {
1440
- logger.error(this, `Unable to write queue file (${fname}): ${err}`);
1441
- ws.destroy();
1442
- hmail.todo.rcpt_to.forEach(rcpt => {
1443
- hmail.extend_rcpt_with_dsn(rcpt, DSN.sys_unspecified(`Error re-queueing some recipients: ${err}`));
1444
- });
1445
- hmail.bounce(`Error re-queueing some recipients: ${err}`);
1446
- });
1447
-
1448
- const new_todo = JSON.parse(JSON.stringify(hmail.todo));
1449
- new_todo.rcpt_to = recipients;
1450
- outbound.build_todo(new_todo, ws, write_more);
1492
+ ws.on('error', (err) => {
1493
+ logger.error(this, `Unable to write queue file (${fname}): ${err}`)
1494
+ ws.destroy()
1495
+ hmail.todo.rcpt_to.forEach((rcpt) => {
1496
+ hmail.extend_rcpt_with_dsn(rcpt, DSN.sys_unspecified(`Error re-queueing some recipients: ${err}`))
1497
+ })
1498
+ hmail.bounce(`Error re-queueing some recipients: ${err}`)
1499
+ })
1500
+
1501
+ const new_todo = JSON.parse(JSON.stringify(hmail.todo))
1502
+ new_todo.rcpt_to = recipients
1503
+ outbound.build_todo(new_todo, ws, write_more)
1451
1504
  }
1452
1505
  }
1453
1506
 
1454
- module.exports = HMailItem;
1455
- module.exports.obtls = obtls;
1507
+ module.exports = HMailItem
1508
+ module.exports.obtls = obtls
1456
1509
 
1457
1510
  logger.add_log_methods(HMailItem)
1458
1511
 
1459
- const smtp_regexp = /^([2345]\d\d)([ -])#?(?:(\d\.\d\.\d)\s)?(.*)/;
1512
+ const smtp_regexp = /^([2345]\d\d)([ -])#?(?:(\d\.\d\.\d)\s)?(.*)/
1460
1513
 
1461
- function cram_md5_response (username, password, challenge) {
1462
- const crypto = require('crypto');
1463
- const c = utils.unbase64(challenge);
1464
- const hmac = crypto.createHmac('md5', password);
1465
- hmac.update(c);
1466
- const digest = hmac.digest('hex');
1467
- return utils.base64(`${username} ${digest}`);
1514
+ function cram_md5_response(username, password, challenge) {
1515
+ const crypto = require('crypto')
1516
+ const c = utils.unbase64(challenge)
1517
+ const hmac = crypto.createHmac('md5', password)
1518
+ hmac.update(c)
1519
+ const digest = hmac.digest('hex')
1520
+ return utils.base64(`${username} ${digest}`)
1468
1521
  }