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.
- package/.prettierignore +4 -0
- package/CONTRIBUTORS.md +5 -5
- package/Changes.md +69 -50
- package/Plugins.md +3 -1
- package/README.md +1 -1
- package/bin/haraka +475 -478
- package/config/outbound.ini +3 -0
- package/connection.js +1072 -1108
- package/docs/Connection.md +29 -30
- package/docs/CoreConfig.md +38 -39
- package/docs/CustomReturnCodes.md +0 -1
- package/docs/HAProxy.md +2 -2
- package/docs/Header.md +1 -1
- package/docs/Logging.md +29 -5
- package/docs/Outbound.md +93 -78
- package/docs/Plugins.md +103 -108
- package/docs/Transaction.md +49 -51
- package/docs/Tutorial.md +127 -143
- package/docs/deprecated/access.md +0 -1
- package/docs/deprecated/backscatterer.md +2 -3
- package/docs/deprecated/connect.rdns_access.md +18 -27
- package/docs/deprecated/data.headers.md +0 -1
- package/docs/deprecated/data.nomsgid.md +1 -2
- package/docs/deprecated/data.noreceived.md +1 -2
- package/docs/deprecated/data.rfc5322_header_checks.md +1 -2
- package/docs/deprecated/dkim_sign.md +13 -17
- package/docs/deprecated/dkim_verify.md +9 -17
- package/docs/deprecated/dnsbl.md +36 -38
- package/docs/deprecated/dnswl.md +41 -43
- package/docs/deprecated/lookup_rdns.strict.md +21 -34
- package/docs/deprecated/mail_from.access.md +17 -25
- package/docs/deprecated/mail_from.blocklist.md +9 -12
- package/docs/deprecated/mail_from.nobounces.md +1 -2
- package/docs/deprecated/rcpt_to.access.md +20 -27
- package/docs/deprecated/rcpt_to.blocklist.md +10 -13
- package/docs/deprecated/rcpt_to.routes.md +0 -1
- package/docs/deprecated/rdns.regexp.md +13 -15
- package/docs/plugins/aliases.md +89 -89
- package/docs/plugins/auth/auth_bridge.md +5 -7
- package/docs/plugins/auth/auth_ldap.md +11 -14
- package/docs/plugins/auth/auth_proxy.md +10 -12
- package/docs/plugins/auth/auth_vpopmaild.md +5 -6
- package/docs/plugins/auth/flat_file.md +4 -4
- package/docs/plugins/block_me.md +3 -3
- package/docs/plugins/data.signatures.md +1 -2
- package/docs/plugins/delay_deny.md +3 -4
- package/docs/plugins/max_unrecognized_commands.md +4 -4
- package/docs/plugins/prevent_credential_leaks.md +6 -6
- package/docs/plugins/process_title.md +18 -18
- package/docs/plugins/queue/deliver.md +2 -3
- package/docs/plugins/queue/discard.md +4 -4
- package/docs/plugins/queue/lmtp.md +1 -3
- package/docs/plugins/queue/qmail-queue.md +7 -9
- package/docs/plugins/queue/quarantine.md +16 -21
- package/docs/plugins/queue/rabbitmq.md +8 -11
- package/docs/plugins/queue/rabbitmq_amqplib.md +43 -39
- package/docs/plugins/queue/smtp_bridge.md +7 -10
- package/docs/plugins/queue/smtp_forward.md +42 -34
- package/docs/plugins/queue/smtp_proxy.md +30 -29
- package/docs/plugins/queue/test.md +1 -3
- package/docs/plugins/rcpt_to.in_host_list.md +6 -6
- package/docs/plugins/rcpt_to.max_count.md +1 -1
- package/docs/plugins/record_envelope_addresses.md +3 -3
- package/docs/plugins/reseed_rng.md +6 -6
- package/docs/plugins/status.md +9 -8
- package/docs/plugins/tarpit.md +7 -11
- package/docs/plugins/tls.md +12 -17
- package/docs/plugins/toobusy.md +4 -4
- package/docs/plugins/xclient.md +3 -3
- package/docs/tutorials/Migrating_from_v1_to_v2.md +19 -41
- package/docs/tutorials/SettingUpOutbound.md +6 -9
- package/endpoint.js +35 -38
- package/eslint.config.mjs +22 -19
- package/haraka.js +42 -47
- package/host_pool.js +75 -79
- package/http/html/404.html +45 -49
- package/http/html/index.html +39 -28
- package/http/package.json +2 -4
- package/line_socket.js +27 -28
- package/logger.js +182 -201
- package/outbound/client_pool.js +34 -27
- package/outbound/config.js +64 -59
- package/outbound/fsync_writestream.js +24 -25
- package/outbound/hmail.js +888 -835
- package/outbound/index.js +194 -187
- package/outbound/qfile.js +49 -52
- package/outbound/queue.js +197 -190
- package/outbound/timer_queue.js +41 -43
- package/outbound/tls.js +68 -61
- package/outbound/todo.js +11 -11
- package/package.json +38 -33
- package/plugins/.eslintrc.yaml +0 -1
- package/plugins/auth/auth_base.js +123 -127
- package/plugins/auth/auth_bridge.js +7 -7
- package/plugins/auth/auth_proxy.js +121 -126
- package/plugins/auth/auth_vpopmaild.js +84 -85
- package/plugins/auth/flat_file.js +18 -17
- package/plugins/block_me.js +31 -31
- package/plugins/data.signatures.js +13 -13
- package/plugins/delay_deny.js +65 -61
- package/plugins/prevent_credential_leaks.js +23 -23
- package/plugins/process_title.js +125 -128
- package/plugins/profile.js +5 -5
- package/plugins/queue/deliver.js +3 -3
- package/plugins/queue/discard.js +13 -14
- package/plugins/queue/lmtp.js +16 -17
- package/plugins/queue/qmail-queue.js +54 -55
- package/plugins/queue/quarantine.js +68 -70
- package/plugins/queue/rabbitmq.js +80 -87
- package/plugins/queue/rabbitmq_amqplib.js +75 -54
- package/plugins/queue/smtp_bridge.js +16 -16
- package/plugins/queue/smtp_forward.js +175 -179
- package/plugins/queue/smtp_proxy.js +69 -71
- package/plugins/queue/test.js +9 -9
- package/plugins/rcpt_to.host_list_base.js +30 -34
- package/plugins/rcpt_to.in_host_list.js +19 -19
- package/plugins/record_envelope_addresses.js +4 -4
- package/plugins/reseed_rng.js +4 -4
- package/plugins/status.js +90 -97
- package/plugins/tarpit.js +25 -14
- package/plugins/tls.js +68 -68
- package/plugins/toobusy.js +21 -23
- package/plugins/xclient.js +51 -53
- package/plugins.js +276 -293
- package/rfc1869.js +30 -35
- package/server.js +308 -299
- package/smtp_client.js +244 -228
- package/test/.eslintrc.yaml +0 -1
- package/test/connection.js +127 -134
- package/test/endpoint.js +53 -47
- package/test/fixtures/line_socket.js +12 -12
- package/test/fixtures/util_hmailitem.js +89 -85
- package/test/host_pool.js +90 -92
- package/test/installation/plugins/base_plugin.js +2 -2
- package/test/installation/plugins/folder_plugin/index.js +2 -3
- package/test/installation/plugins/inherits.js +3 -3
- package/test/installation/plugins/load_first.js +2 -3
- package/test/installation/plugins/plugin.js +1 -3
- package/test/installation/plugins/tls.js +2 -4
- package/test/logger.js +135 -116
- package/test/outbound/hmail.js +49 -35
- package/test/outbound/index.js +118 -101
- package/test/outbound/qfile.js +51 -53
- package/test/outbound_bounce_net_errors.js +84 -69
- package/test/outbound_bounce_rfc3464.js +235 -165
- package/test/plugins/auth/auth_base.js +420 -279
- package/test/plugins/auth/auth_vpopmaild.js +38 -39
- package/test/plugins/queue/smtp_forward.js +126 -104
- package/test/plugins/rcpt_to.host_list_base.js +85 -67
- package/test/plugins/rcpt_to.in_host_list.js +159 -112
- package/test/plugins/status.js +71 -64
- package/test/plugins/tls.js +37 -34
- package/test/plugins.js +97 -92
- package/test/rfc1869.js +19 -26
- package/test/server.js +293 -272
- package/test/smtp_client.js +180 -176
- package/test/tls_socket.js +62 -66
- package/test/transaction.js +159 -160
- package/tls_socket.js +331 -333
- package/transaction.js +129 -137
package/outbound/index.js
CHANGED
|
@@ -1,337 +1,344 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
const fs
|
|
4
|
-
const path
|
|
5
|
-
|
|
6
|
-
const { Address } = require('address-rfc2821')
|
|
7
|
-
const config
|
|
8
|
-
const constants
|
|
9
|
-
const net_utils
|
|
10
|
-
const utils
|
|
11
|
-
const ResultStore = require('haraka-results')
|
|
12
|
-
|
|
13
|
-
const logger
|
|
14
|
-
const trans
|
|
15
|
-
const plugins
|
|
16
|
-
const FsyncWriteStream = require('./fsync_writestream')
|
|
17
|
-
|
|
18
|
-
const obc
|
|
19
|
-
const queuelib
|
|
20
|
-
const HMailItem
|
|
21
|
-
const TODOItem
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
exports.
|
|
32
|
-
|
|
33
|
-
exports.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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,
|
|
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
|
-
|
|
64
|
-
const notes = options.notes ?? null;
|
|
65
|
-
const origin = options.origin ?? exports;
|
|
76
|
+
logger.info('Sending email via params', origin)
|
|
66
77
|
|
|
67
|
-
|
|
78
|
+
const transaction = trans.createTransaction(null, smtp_ini)
|
|
68
79
|
|
|
69
|
-
|
|
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
|
-
|
|
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 (!
|
|
98
|
+
if (!Array.isArray(to)) to = [to]
|
|
92
99
|
|
|
93
100
|
if (to.length === 0) {
|
|
94
|
-
return next(constants.deny,
|
|
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
|
-
|
|
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')
|
|
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
|
|
153
|
-
let current_data = ''
|
|
154
|
-
function process_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
|
|
182
|
-
const deliveries = []
|
|
185
|
+
function get_deliveries(transaction) {
|
|
186
|
+
const deliveries = []
|
|
183
187
|
|
|
184
188
|
if (obc.cfg.always_split) {
|
|
185
|
-
logger.debug(exports,
|
|
189
|
+
logger.debug(exports, 'always split')
|
|
186
190
|
for (const rcpt of transaction.rcpt_to) {
|
|
187
|
-
deliveries.push({domain: rcpt.host, rcpts: [
|
|
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]) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
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,
|
|
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,
|
|
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,
|
|
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
|
-
|
|
250
|
-
|
|
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:
|
|
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
|
-
|
|
273
|
-
const
|
|
274
|
-
const
|
|
275
|
-
|
|
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(
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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
|