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/index.js CHANGED
@@ -1,337 +1,344 @@
1
- 'use strict';
2
-
3
- const fs = require('node:fs');
4
- const path = require('node:path');
5
-
6
- const { Address } = require('address-rfc2821');
7
- const config = require('haraka-config');
8
- const constants = require('haraka-constants');
9
- const net_utils = require('haraka-net-utils');
10
- const utils = require('haraka-utils');
11
- const ResultStore = require('haraka-results');
12
-
13
- const logger = require('../logger');
14
- const trans = require('../transaction');
15
- const plugins = require('../plugins');
16
- const FsyncWriteStream = require('./fsync_writestream');
17
-
18
- const obc = require('./config');
19
- const queuelib = require('./queue');
20
- const HMailItem = require('./hmail');
21
- const TODOItem = require('./todo');
22
- const _qfile = exports.qfile = require('./qfile');
23
-
24
- const { queue_dir, temp_fail_queue, delivery_queue } = queuelib;
25
-
26
- const smtp_ini = config.get('smtp.ini', { booleans: [ '+headers.add_received' ] })
27
-
28
- exports.temp_fail_queue = temp_fail_queue;
29
- exports.delivery_queue = delivery_queue;
30
-
31
- exports.name = 'outbound';
32
- exports.net_utils = net_utils;
33
- exports.config = config;
34
-
35
- const qlfns = ['get_stats', 'list_queue', 'stat_queue', 'scan_queue_pids', 'flush_queue',
36
- 'load_pid_queue', 'ensure_queue_dir', 'load_queue', 'stats'
1
+ 'use strict'
2
+
3
+ const fs = require('node:fs')
4
+ const path = require('node:path')
5
+
6
+ const { Address } = require('address-rfc2821')
7
+ const config = require('haraka-config')
8
+ const constants = require('haraka-constants')
9
+ const net_utils = require('haraka-net-utils')
10
+ const utils = require('haraka-utils')
11
+ const ResultStore = require('haraka-results')
12
+
13
+ const logger = require('../logger')
14
+ const trans = require('../transaction')
15
+ const plugins = require('../plugins')
16
+ const FsyncWriteStream = require('./fsync_writestream')
17
+
18
+ const obc = require('./config')
19
+ const queuelib = require('./queue')
20
+ const HMailItem = require('./hmail')
21
+ const TODOItem = require('./todo')
22
+ const _qfile = (exports.qfile = require('./qfile'))
23
+
24
+ const { queue_dir, temp_fail_queue, delivery_queue } = queuelib
25
+
26
+ const smtp_ini = config.get('smtp.ini', {
27
+ booleans: ['+headers.add_received'],
28
+ })
29
+
30
+ exports.temp_fail_queue = temp_fail_queue
31
+ exports.delivery_queue = delivery_queue
32
+
33
+ exports.name = 'outbound'
34
+ exports.net_utils = net_utils
35
+ exports.config = config
36
+
37
+ const qlfns = [
38
+ 'get_stats',
39
+ 'list_queue',
40
+ 'stat_queue',
41
+ 'scan_queue_pids',
42
+ 'flush_queue',
43
+ 'load_pid_queue',
44
+ 'ensure_queue_dir',
45
+ 'load_queue',
46
+ 'stats',
37
47
  ]
38
48
  for (const n of qlfns) {
39
- exports[n] = queuelib[n];
49
+ exports[n] = queuelib[n]
40
50
  }
41
51
 
42
- process.on('message', msg => {
52
+ process.on('message', (msg) => {
43
53
  if (!msg.event) return
44
54
 
45
55
  if (msg.event === 'outbound.load_pid_queue') {
46
- exports.load_pid_queue(msg.data);
47
- return;
56
+ exports.load_pid_queue(msg.data)
57
+ return
48
58
  }
49
59
  if (msg.event === 'outbound.flush_queue') {
50
- exports.flush_queue(msg.domain, process.pid);
51
- return;
60
+ exports.flush_queue(msg.domain, process.pid)
61
+ return
52
62
  }
53
63
  if (msg.event === 'outbound.shutdown') {
54
- logger.info(exports, "Shutting down temp fail queue");
55
- temp_fail_queue.shutdown();
56
- return;
64
+ logger.info(exports, 'Shutting down temp fail queue')
65
+ temp_fail_queue.shutdown()
66
+ return
57
67
  }
58
68
  // ignores the message
59
- });
69
+ })
60
70
 
61
71
  exports.send_email = function (from, to, contents, next, options = {}) {
72
+ const dot_stuffed = options.dot_stuffed ?? false
73
+ const notes = options.notes ?? null
74
+ const origin = options.origin ?? exports
62
75
 
63
- const dot_stuffed = options.dot_stuffed ?? false;
64
- const notes = options.notes ?? null;
65
- const origin = options.origin ?? exports;
76
+ logger.info('Sending email via params', origin)
66
77
 
67
- logger.info("Sending email via params", origin);
78
+ const transaction = trans.createTransaction(null, smtp_ini)
68
79
 
69
- const transaction = trans.createTransaction(null, smtp_ini);
70
-
71
- logger.info(`Created transaction: ${transaction.uuid}`, origin);
80
+ logger.info(`Created transaction: ${transaction.uuid}`, origin)
72
81
 
73
82
  // Adding notes passed as parameter
74
- if (notes) transaction.notes = notes;
83
+ if (notes) transaction.notes = notes
75
84
 
76
85
  // set MAIL FROM address, and parse if it's not an Address object
77
86
  if (from instanceof Address) {
78
- transaction.mail_from = from;
79
- }
80
- else {
87
+ transaction.mail_from = from
88
+ } else {
81
89
  try {
82
- from = new Address(from);
83
- }
84
- catch (err) {
85
- return next(constants.deny, `Malformed from: ${err}`);
90
+ from = new Address(from)
91
+ } catch (err) {
92
+ return next(constants.deny, `Malformed from: ${err}`)
86
93
  }
87
- transaction.mail_from = from;
94
+ transaction.mail_from = from
88
95
  }
89
96
 
90
97
  // Make sure to is an array
91
- if (!(Array.isArray(to))) to = [ to ];
98
+ if (!Array.isArray(to)) to = [to]
92
99
 
93
100
  if (to.length === 0) {
94
- return next(constants.deny, "No recipients for email");
101
+ return next(constants.deny, 'No recipients for email')
95
102
  }
96
103
 
97
104
  // Set RCPT TO's, and parse each if it's not an Address object.
98
- for (let i=0,l=to.length; i < l; i++) {
105
+ for (let i = 0, l = to.length; i < l; i++) {
99
106
  if (!(to[i] instanceof Address)) {
100
107
  try {
101
- to[i] = new Address(to[i]);
102
- }
103
- catch (err) {
104
- return next(constants.deny,
105
- `Malformed to address (${to[i]}): ${err}`);
108
+ to[i] = new Address(to[i])
109
+ } catch (err) {
110
+ return next(constants.deny, `Malformed to address (${to[i]}): ${err}`)
106
111
  }
107
112
  }
108
113
  }
109
114
 
110
- transaction.rcpt_to = to;
115
+ transaction.rcpt_to = to
111
116
 
112
117
  // Set data_lines to lines in contents
113
118
  if (typeof contents == 'string') {
114
- let match;
119
+ let match
115
120
  while ((match = utils.line_regexp.exec(contents))) {
116
- let line = match[1];
117
- line = line.replace(/\r?\n?$/, '\r\n'); // make sure it ends in \r\n
118
- if (dot_stuffed === false && line.length >= 3 && line.substr(0,1) === '.') {
119
- line = `.${line}`;
121
+ let line = match[1]
122
+ line = line.replace(/\r?\n?$/, '\r\n') // make sure it ends in \r\n
123
+ if (dot_stuffed === false && line.length >= 3 && line.substr(0, 1) === '.') {
124
+ line = `.${line}`
120
125
  }
121
- transaction.add_data(Buffer.from(line));
122
- contents = contents.substr(match[1].length);
126
+ transaction.add_data(Buffer.from(line))
127
+ contents = contents.substr(match[1].length)
123
128
  if (contents.length === 0) {
124
- break;
129
+ break
125
130
  }
126
131
  }
127
- }
128
- else {
132
+ } else {
129
133
  // Assume a stream
130
- return stream_line_reader(contents, transaction, err => {
134
+ return stream_line_reader(contents, transaction, (err) => {
131
135
  if (err) {
132
- return next(constants.denysoft, `Error from stream line reader: ${err}`);
136
+ return next(constants.denysoft, `Error from stream line reader: ${err}`)
133
137
  }
134
- exports.send_trans_email(transaction, next);
135
- });
138
+ exports.send_trans_email(transaction, next)
139
+ })
136
140
  }
137
141
 
138
- transaction.message_stream.add_line_end();
142
+ transaction.message_stream.add_line_end()
139
143
 
140
144
  // Allow for the removal of Message-Id and/or Date headers which
141
145
  // is useful when resending mail from a quarantine.
142
146
  if (options.remove_msgid) {
143
- transaction.remove_header('Message-Id');
147
+ transaction.remove_header('Message-Id')
144
148
  }
145
149
  if (options.remove_date) {
146
- transaction.remove_header('Date');
150
+ transaction.remove_header('Date')
147
151
  }
148
152
 
149
- this.send_trans_email(transaction, next);
153
+ this.send_trans_email(transaction, next)
150
154
  }
151
155
 
152
- function stream_line_reader (stream, transaction, cb) {
153
- let current_data = '';
154
- function process_data (data) {
155
- current_data += data.toString();
156
- let results;
156
+ function stream_line_reader(stream, transaction, cb) {
157
+ let current_data = ''
158
+ function process_data(data) {
159
+ current_data += data.toString()
160
+ let results
157
161
  while ((results = utils.line_regexp.exec(current_data))) {
158
- const this_line = results[1];
159
- current_data = current_data.slice(this_line.length);
162
+ const this_line = results[1]
163
+ current_data = current_data.slice(this_line.length)
160
164
  if (!(current_data.length || this_line.length)) {
161
- return;
165
+ return
162
166
  }
163
- transaction.add_data(Buffer.from(this_line));
167
+ transaction.add_data(Buffer.from(this_line))
164
168
  }
165
169
  }
166
170
 
167
- function process_end () {
171
+ function process_end() {
168
172
  if (current_data.length) {
169
- transaction.add_data(Buffer.from(current_data));
173
+ transaction.add_data(Buffer.from(current_data))
170
174
  }
171
- current_data = '';
172
- transaction.message_stream.add_line_end();
173
- cb();
175
+ current_data = ''
176
+ transaction.message_stream.add_line_end()
177
+ cb()
174
178
  }
175
179
 
176
- stream.on('data', process_data);
177
- stream.once('end', process_end);
178
- stream.once('error', cb);
180
+ stream.on('data', process_data)
181
+ stream.once('end', process_end)
182
+ stream.once('error', cb)
179
183
  }
180
184
 
181
- function get_deliveries (transaction) {
182
- const deliveries = [];
185
+ function get_deliveries(transaction) {
186
+ const deliveries = []
183
187
 
184
188
  if (obc.cfg.always_split) {
185
- logger.debug(exports, "always split");
189
+ logger.debug(exports, 'always split')
186
190
  for (const rcpt of transaction.rcpt_to) {
187
- deliveries.push({domain: rcpt.host, rcpts: [ rcpt ]});
191
+ deliveries.push({ domain: rcpt.host, rcpts: [rcpt] })
188
192
  }
189
- return deliveries;
193
+ return deliveries
190
194
  }
191
195
 
192
196
  // First get each domain
193
- const recips = {};
194
- transaction.rcpt_to.forEach(rcpt => {
195
- const domain = rcpt.host;
196
- if (!recips[domain]) { recips[domain] = []; }
197
- recips[domain].push(rcpt);
198
- });
199
- Object.keys(recips).forEach(domain => {
200
- deliveries.push({domain, 'rcpts': recips[domain]});
201
- });
202
- return deliveries;
197
+ const recips = {}
198
+ transaction.rcpt_to.forEach((rcpt) => {
199
+ const domain = rcpt.host
200
+ if (!recips[domain]) {
201
+ recips[domain] = []
202
+ }
203
+ recips[domain].push(rcpt)
204
+ })
205
+ Object.keys(recips).forEach((domain) => {
206
+ deliveries.push({ domain, rcpts: recips[domain] })
207
+ })
208
+ return deliveries
203
209
  }
204
210
 
205
211
  exports.send_trans_email = function (transaction, next) {
206
-
207
212
  // add potentially missing headers
208
213
  if (!transaction.header.get_all('Message-Id').length) {
209
- logger.info(exports, "Adding missing Message-Id header");
210
- transaction.add_header('Message-Id', `<${transaction.uuid}@${net_utils.get_primary_host_name()}>`);
214
+ logger.info(exports, 'Adding missing Message-Id header')
215
+ transaction.add_header('Message-Id', `<${transaction.uuid}@${net_utils.get_primary_host_name()}>`)
211
216
  }
212
217
  if (transaction.header.get('Message-Id') === '<>') {
213
- logger.info(exports, "Replacing empty Message-Id header");
214
- transaction.remove_header('Message-Id');
215
- transaction.add_header('Message-Id', `<${transaction.uuid}@${net_utils.get_primary_host_name()}>`);
218
+ logger.info(exports, 'Replacing empty Message-Id header')
219
+ transaction.remove_header('Message-Id')
220
+ transaction.add_header('Message-Id', `<${transaction.uuid}@${net_utils.get_primary_host_name()}>`)
216
221
  }
217
222
  if (!transaction.header.get_all('Date').length) {
218
- logger.info(exports, "Adding missing Date header");
219
- transaction.add_header('Date', utils.date_to_str(new Date()));
223
+ logger.info(exports, 'Adding missing Date header')
224
+ transaction.add_header('Date', utils.date_to_str(new Date()))
220
225
  }
221
226
 
222
227
  if (obc.cfg.received_header !== 'disabled') {
223
- transaction.add_leading_header('Received', `(${obc.cfg.received_header}); ${utils.date_to_str(new Date())}`);
228
+ transaction.add_leading_header('Received', `(${obc.cfg.received_header}); ${utils.date_to_str(new Date())}`)
224
229
  }
225
230
 
226
- const connection = { transaction };
231
+ const connection = { transaction }
227
232
 
228
- logger.add_log_methods(connection);
233
+ logger.add_log_methods(connection)
229
234
  if (!transaction.results) {
230
- logger.debug(exports, 'adding results store');
231
- transaction.results = new ResultStore(connection);
235
+ logger.debug(exports, 'adding results store')
236
+ transaction.results = new ResultStore(connection)
232
237
  }
233
238
 
234
239
  connection.pre_send_trans_email_respond = async (retval) => {
235
- const deliveries = get_deliveries(transaction);
236
- const hmails = [];
237
- const ok_paths = [];
240
+ const deliveries = get_deliveries(transaction)
241
+ const hmails = []
242
+ const ok_paths = []
238
243
 
239
- let todo_index = 1;
244
+ let todo_index = 1
240
245
 
241
246
  try {
242
247
  for (const deliv of deliveries) {
243
- const todo = new TODOItem(deliv.domain, deliv.rcpts, transaction);
244
- todo.uuid = `${todo.uuid}.${todo_index}`;
245
- todo_index++;
246
- await this.process_delivery(ok_paths, todo, hmails);
248
+ const todo = new TODOItem(deliv.domain, deliv.rcpts, transaction)
249
+ todo.uuid = `${todo.uuid}.${todo_index}`
250
+ todo_index++
251
+ await this.process_delivery(ok_paths, todo, hmails)
247
252
  }
248
- }
249
- catch (err) {
250
- for (let i=0, l=ok_paths.length; i<l; i++) {
251
- fs.unlink(ok_paths[i], () => {});
253
+ } catch (err) {
254
+ for (let i = 0, l = ok_paths.length; i < l; i++) {
255
+ fs.unlink(ok_paths[i], () => {})
252
256
  }
253
- transaction.results.add({ name: 'outbound'}, { err });
254
- if (next) next(constants.denysoft, err);
255
- return;
257
+ transaction.results.add({ name: 'outbound' }, { err })
258
+ if (next) next(constants.denysoft, err)
259
+ return
256
260
  }
257
261
 
258
262
  for (const hmail of hmails) {
259
- delivery_queue.push(hmail);
263
+ delivery_queue.push(hmail)
260
264
  }
261
265
 
262
- transaction.results.add({ name: 'outbound'}, { pass: "queued" });
263
- if (next) next(constants.ok, `Message Queued (${transaction.uuid})`);
266
+ transaction.results.add({ name: 'outbound' }, { pass: 'queued' })
267
+ if (next) next(constants.ok, `Message Queued (${transaction.uuid})`)
264
268
  }
265
269
 
266
- plugins.run_hooks('pre_send_trans_email', connection);
270
+ plugins.run_hooks('pre_send_trans_email', connection)
267
271
  }
268
272
 
269
273
  exports.process_delivery = function (ok_paths, todo, hmails) {
270
274
  return new Promise((resolve, reject) => {
271
-
272
- logger.info(exports, `Transaction delivery for domain: ${todo.domain}`);
273
- const fname = _qfile.name();
274
- const tmp_path = path.join(queue_dir, `${_qfile.platformDOT}${fname}`);
275
- const ws = new FsyncWriteStream(tmp_path, { flags: constants.WRITE_EXCL });
275
+ logger.info(exports, `Transaction delivery for domain: ${todo.domain}`)
276
+ const fname = _qfile.name()
277
+ const tmp_path = path.join(queue_dir, `${_qfile.platformDOT}${fname}`)
278
+ const ws = new FsyncWriteStream(tmp_path, {
279
+ flags: constants.WRITE_EXCL,
280
+ })
276
281
 
277
282
  ws.on('close', () => {
278
- const dest_path = path.join(queue_dir, fname);
279
- fs.rename(tmp_path, dest_path, err => {
283
+ const dest_path = path.join(queue_dir, fname)
284
+ fs.rename(tmp_path, dest_path, (err) => {
280
285
  if (err) {
281
- logger.error(exports, `Unable to rename tmp file!: ${err}`);
282
- fs.unlink(tmp_path, () => {});
283
- reject("Queue error");
284
- }
285
- else {
286
- hmails.push(new HMailItem (fname, dest_path, todo.notes));
287
- ok_paths.push(dest_path);
288
- resolve();
286
+ logger.error(exports, `Unable to rename tmp file!: ${err}`)
287
+ fs.unlink(tmp_path, () => {})
288
+ reject('Queue error')
289
+ } else {
290
+ hmails.push(new HMailItem(fname, dest_path, todo.notes))
291
+ ok_paths.push(dest_path)
292
+ resolve()
289
293
  }
290
294
  })
291
295
  })
292
296
 
293
- ws.on('error', err => {
294
- logger.error(exports, `Unable to write queue file (${fname}): ${err}`);
295
- ws.destroy();
296
- fs.unlink(tmp_path, () => {});
297
- reject("Queueing failed");
297
+ ws.on('error', (err) => {
298
+ logger.error(exports, `Unable to write queue file (${fname}): ${err}`)
299
+ ws.destroy()
300
+ fs.unlink(tmp_path, () => {})
301
+ reject('Queueing failed')
298
302
  })
299
303
 
300
304
  this.build_todo(todo, ws, () => {
301
- todo.message_stream.pipe(ws, { dot_stuffing: true });
302
- });
305
+ // SUNSET: dot_stuffing was renamed to dot_stuffed, remove it after 2026-01
306
+ todo.message_stream.pipe(ws, {
307
+ dot_stuffing: true,
308
+ dot_stuffed: false,
309
+ })
310
+ })
303
311
  })
304
312
  }
305
313
 
306
314
  exports.build_todo = (todo, ws, write_more) => {
307
-
308
315
  const todo_str = `\n${JSON.stringify(todo, exclude_from_json, '\t')}\n`
309
316
  const todo_len = Buffer.byteLength(todo_str)
310
317
 
311
- const buf = Buffer.alloc(4 + todo_len);
312
- buf.writeUInt32BE(todo_len, 0);
313
- buf.write(todo_str, 4);
318
+ const buf = Buffer.alloc(4 + todo_len)
319
+ buf.writeUInt32BE(todo_len, 0)
320
+ buf.write(todo_str, 4)
314
321
 
315
- const continue_writing = ws.write(buf);
322
+ const continue_writing = ws.write(buf)
316
323
  if (continue_writing) {
317
- process.nextTick(write_more);
324
+ process.nextTick(write_more)
318
325
  return
319
326
  }
320
327
 
321
- ws.once('drain', write_more);
328
+ ws.once('drain', write_more)
322
329
  }
323
330
 
324
331
  // Replacer function to exclude items from the queue file header
325
- function exclude_from_json (key, value) {
332
+ function exclude_from_json(key, value) {
326
333
  switch (key) {
327
334
  case 'message_stream':
328
- return undefined;
335
+ return undefined
329
336
  default:
330
- return value;
337
+ return value
331
338
  }
332
339
  }
333
340
 
334
341
  // exported for testability
335
- exports.TODOItem = TODOItem;
342
+ exports.TODOItem = TODOItem
336
343
 
337
- exports.HMailItem = HMailItem;
344
+ exports.HMailItem = HMailItem