Haraka 3.1.7 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/address.js +53 -0
- package/bin/haraka +1 -1
- package/connection.js +3 -3
- package/docs/Outbound.md +1 -1
- package/docs/Transaction.md +1 -1
- package/docs/plugins/status.md +21 -5
- package/outbound/hmail.js +2 -2
- package/outbound/index.js +1 -1
- package/outbound/queue.js +1 -1
- package/package.json +2 -3
- package/plugins/block_me.js +2 -2
- package/plugins/queue/smtp_forward.js +2 -2
- package/plugins/rcpt_to.host_list_base.js +1 -1
- package/plugins/record_envelope_addresses.js +2 -2
- package/plugins/status.js +34 -5
- package/test/connection.js +1 -1
- package/test/fixtures/util_hmailitem.js +1 -1
- package/test/outbound/index.js +2 -2
- package/test/plugins/auth/auth_base.js +1 -1
- package/test/plugins/block_me.js +1 -1
- package/test/plugins/queue/smtp_forward.js +1 -1
- package/test/plugins/rcpt_to.host_list_base.js +1 -1
- package/test/plugins/rcpt_to.in_host_list.js +1 -1
- package/test/plugins/record_envelope_addresses.js +2 -2
- package/test/plugins/status.js +71 -0
- package/test/smtp_client.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
|
|
4
4
|
|
|
5
5
|
### Unreleased
|
|
6
6
|
|
|
7
|
+
### [3.2.0] - 2026-05-NN
|
|
8
|
+
|
|
9
|
+
- fix(status): merge worker status into summary #3574
|
|
10
|
+
- dep: replace address-rfc282{1,2} with @haraka/email-address #3566
|
|
11
|
+
- change(BREAKING for some plugins), see https://github.com/haraka/Haraka/issues/3564
|
|
12
|
+
|
|
7
13
|
### [3.1.7] - 2026-05-19
|
|
8
14
|
|
|
9
15
|
- feat(smtp_forward,smtp_proxy): honor `tls.ini` `[main]` and plugin `[tls]`
|
|
@@ -1862,3 +1868,4 @@ config files.
|
|
|
1862
1868
|
[3.1.5]: https://github.com/haraka/Haraka/releases/tag/v3.1.5
|
|
1863
1869
|
[3.1.6]: https://github.com/haraka/Haraka/releases/tag/v3.1.6
|
|
1864
1870
|
[3.1.7]: https://github.com/haraka/Haraka/releases/tag/v3.1.7
|
|
1871
|
+
[3.2.0]: https://github.com/haraka/Haraka/releases/tag/v3.2.0
|
package/address.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// SUNSET 2027: Haraka core constructs envelope addresses with
|
|
4
|
+
// @haraka/email-address, whose `.address` / `.host` are string
|
|
5
|
+
// properties. Core itself — and a large body of bundled and
|
|
6
|
+
// third-party plugins — still use the historical address-rfc2821 /
|
|
7
|
+
// address-rfc2822 *method* contract (`addr.address()`, `addr.host()`).
|
|
8
|
+
//
|
|
9
|
+
// `asLegacy()` wraps each instance so both the new string-property API
|
|
10
|
+
// and the legacy callable form work during the transition. The wrap is
|
|
11
|
+
// idempotent, and `unwrapLegacy()` recovers the raw instance so the
|
|
12
|
+
// outbound queue's JSON re-hydration copies primitive string fields
|
|
13
|
+
// rather than the callable accessors.
|
|
14
|
+
//
|
|
15
|
+
// Once the ecosystem has migrated, delete this module, require
|
|
16
|
+
// `Address` straight from '@haraka/email-address', and drop the wrapper
|
|
17
|
+
// (see @haraka/email-address lib/legacy.js).
|
|
18
|
+
|
|
19
|
+
const { Address: BaseAddress, asLegacy, unwrapLegacy } = require('@haraka/email-address')
|
|
20
|
+
|
|
21
|
+
class Address extends BaseAddress {
|
|
22
|
+
constructor(...args) {
|
|
23
|
+
// never re-hydrate from a wrapped instance — copy raw strings
|
|
24
|
+
if (args.length) args[0] = unwrapLegacy(args[0])
|
|
25
|
+
// `new Address(user, host)` where `host` is another wrapped
|
|
26
|
+
// address's `.host` — the SUNSET-2027 callable accessor is
|
|
27
|
+
// `typeof 'function'`, which BaseAddress would mistake for an
|
|
28
|
+
// options object. Coerce it back to the primitive string.
|
|
29
|
+
if (args.length >= 2 && typeof args[1] === 'function') {
|
|
30
|
+
args[1] = String(args[1])
|
|
31
|
+
}
|
|
32
|
+
super(...args)
|
|
33
|
+
return asLegacy(this)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Preserve the address-rfc2821 wire shape so existing on-disk queue
|
|
37
|
+
// files stay byte-compatible across the upgrade and re-hydrate
|
|
38
|
+
// unchanged. @haraka/email-address additionally carries
|
|
39
|
+
// phrase/comment/group/opts, which are irrelevant to envelope
|
|
40
|
+
// addresses and must not leak into the persisted todo. SUNSET 2027.
|
|
41
|
+
toJSON() {
|
|
42
|
+
const out = {
|
|
43
|
+
original: this.original,
|
|
44
|
+
original_host: this.original_host,
|
|
45
|
+
host: this.host,
|
|
46
|
+
user: this.user,
|
|
47
|
+
}
|
|
48
|
+
if (this.is_utf8) out.is_utf8 = true
|
|
49
|
+
return out
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = { Address }
|
package/bin/haraka
CHANGED
|
@@ -406,7 +406,7 @@ if (parsed.version) {
|
|
|
406
406
|
plugins.load_plugins(parsed.test && parsed.test[0] !== 'all' ? parsed.test : null)
|
|
407
407
|
const Connection = require(path.join(base, 'connection'))
|
|
408
408
|
// var Transaction = require(path.join(base, "transaction"));
|
|
409
|
-
const Address = require('address
|
|
409
|
+
const Address = require('../address').Address
|
|
410
410
|
const Notes = require('haraka-notes')
|
|
411
411
|
const constants = require('haraka-constants')
|
|
412
412
|
const client = {
|
package/connection.js
CHANGED
|
@@ -12,7 +12,7 @@ const constants = require('haraka-constants')
|
|
|
12
12
|
const net_utils = require('haraka-net-utils')
|
|
13
13
|
const Notes = require('haraka-notes')
|
|
14
14
|
const utils = require('haraka-utils')
|
|
15
|
-
const { Address } = require('address
|
|
15
|
+
const { Address } = require('./address')
|
|
16
16
|
const ResultStore = require('haraka-results')
|
|
17
17
|
|
|
18
18
|
// Haraka libs
|
|
@@ -1039,7 +1039,7 @@ class Connection {
|
|
|
1039
1039
|
this.lognotice(dmsg, {
|
|
1040
1040
|
code: constants.translate(retval === constants.cont ? constants.ok : retval),
|
|
1041
1041
|
msg: msg || '',
|
|
1042
|
-
sender: this.transaction.mail_from.address
|
|
1042
|
+
sender: this.transaction.mail_from.address,
|
|
1043
1043
|
})
|
|
1044
1044
|
switch (retval) {
|
|
1045
1045
|
case constants.deny:
|
|
@@ -1089,7 +1089,7 @@ class Connection {
|
|
|
1089
1089
|
this.lognotice(dmsg, {
|
|
1090
1090
|
code: constants.translate(retval === constants.cont ? constants.ok : retval),
|
|
1091
1091
|
msg: msg || '',
|
|
1092
|
-
sender: this.transaction.mail_from.address
|
|
1092
|
+
sender: this.transaction.mail_from.address,
|
|
1093
1093
|
})
|
|
1094
1094
|
}
|
|
1095
1095
|
switch (retval) {
|
package/docs/Outbound.md
CHANGED
|
@@ -203,7 +203,7 @@ Options accepted by `send_email(from, to, contents, next, options)`:
|
|
|
203
203
|
|
|
204
204
|
To send an already-built `Transaction` directly, use `outbound.send_trans_email(transaction, next)`. This is what `send_email()` calls internally and fires the `pre_send_trans_email` hook.
|
|
205
205
|
|
|
206
|
-
<a name="fn1">1</a>: `Address` objects are [address
|
|
206
|
+
<a name="fn1">1</a>: `Address` objects are [@haraka/email-address](https://github.com/haraka/email-address) objects.
|
|
207
207
|
|
|
208
208
|
[url-tls]: plugins/tls.md
|
|
209
209
|
[url-harakamx]: https://github.com/haraka/haraka-net-utils?tab=readme-ov-file#harakamx
|
package/docs/Transaction.md
CHANGED
|
@@ -132,4 +132,4 @@ Append a banner to the end of the message. If `html` is omitted, each newline in
|
|
|
132
132
|
|
|
133
133
|
Register a filter applied to body parts. `ct_match` is either a regex matched against the content-type line, or a string matched as a prefix (e.g. `/^text\/html/` or `'text/plain'`). `filter` receives `(content_type, encoding, buffer)` and must return a `Buffer` with the replacement body (in the same encoding).
|
|
134
134
|
|
|
135
|
-
[address]: https://github.com/haraka/
|
|
135
|
+
[address]: https://github.com/haraka/email-address
|
package/docs/plugins/status.md
CHANGED
|
@@ -11,15 +11,31 @@ This plugin allows to get internal status of queues and pools with SMTP commands
|
|
|
11
11
|
|
|
12
12
|
```
|
|
13
13
|
< 220 example.com ESMTP Haraka ready
|
|
14
|
-
> STATUS QUEUE
|
|
14
|
+
> STATUS QUEUE INSPECT
|
|
15
15
|
< 211 {"delivery_queue":[],"temp_fail_queue":[]}
|
|
16
16
|
```
|
|
17
17
|
|
|
18
18
|
## Available commands list
|
|
19
19
|
|
|
20
|
-
- `STATUS POOL LIST` -
|
|
21
|
-
- `STATUS QUEUE STATS` - queue statistics in format "<in_progress>/<delivery_queue length>/<temp_fail_queue length>"
|
|
22
|
-
- `STATUS QUEUE LIST` - list of
|
|
23
|
-
- `STATUS QUEUE INSPECT` - returns content of
|
|
20
|
+
- `STATUS POOL LIST` - map of active outbound connection pools, keyed by `host:port`
|
|
21
|
+
- `STATUS QUEUE STATS` - queue statistics in format `"<in_progress>/<delivery_queue length>/<temp_fail_queue length>"`
|
|
22
|
+
- `STATUS QUEUE LIST` - list of queue files on disk with _uuid, domain, mail_from, rcpt_to_ attributes
|
|
23
|
+
- `STATUS QUEUE INSPECT` - returns merged content of `outbound.delivery_queue` and `outbound.temp_fail_queue` across all workers
|
|
24
24
|
- `STATUS QUEUE DISCARD file` - stop delivering email file
|
|
25
25
|
- `STATUS QUEUE PUSH file` - try to re-deliver email immediately
|
|
26
|
+
|
|
27
|
+
## Notes
|
|
28
|
+
|
|
29
|
+
### Live data only
|
|
30
|
+
|
|
31
|
+
`POOL LIST`, `QUEUE STATS`, and `QUEUE INSPECT` reflect live in-memory state. They show only messages currently being processed or waiting in the retry queue. `QUEUE LIST` reads queue files from disk and may show messages that have already been delivered if they haven't been cleaned up yet.
|
|
32
|
+
|
|
33
|
+
### Cluster mode
|
|
34
|
+
|
|
35
|
+
In cluster mode, `POOL LIST`, `QUEUE STATS`, and `QUEUE INSPECT` aggregate results from all worker processes into a single response:
|
|
36
|
+
|
|
37
|
+
- `POOL LIST` — pool maps from all workers are merged into one object
|
|
38
|
+
- `QUEUE STATS` — counters from all workers are summed into a single `"N/N/N"` string
|
|
39
|
+
- `QUEUE INSPECT` — `delivery_queue` and `temp_fail_queue` arrays from all workers are concatenated
|
|
40
|
+
|
|
41
|
+
`QUEUE LIST` always runs on the master process since it reads shared queue files from disk.
|
package/outbound/hmail.js
CHANGED
|
@@ -7,7 +7,7 @@ const dns = require('node:dns')
|
|
|
7
7
|
const net = require('node:net')
|
|
8
8
|
const path = require('node:path')
|
|
9
9
|
|
|
10
|
-
const { Address } = require('address
|
|
10
|
+
const { Address } = require('../address')
|
|
11
11
|
const config = require('haraka-config')
|
|
12
12
|
const constants = require('haraka-constants')
|
|
13
13
|
const DSN = require('haraka-dsn')
|
|
@@ -1148,7 +1148,7 @@ class HMailItem extends events.EventEmitter {
|
|
|
1148
1148
|
}
|
|
1149
1149
|
for (const rcpt_to of this.todo.rcpt_to) {
|
|
1150
1150
|
bounce_body.push(CRLF)
|
|
1151
|
-
bounce_body.push(`Final-Recipient: rfc822;${rcpt_to.address
|
|
1151
|
+
bounce_body.push(`Final-Recipient: rfc822;${rcpt_to.address}${CRLF}`)
|
|
1152
1152
|
let dsn_action = null
|
|
1153
1153
|
if (rcpt_to.dsn_action) {
|
|
1154
1154
|
dsn_action = rcpt_to.dsn_action
|
package/outbound/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fs = require('node:fs/promises')
|
|
4
4
|
const path = require('node:path')
|
|
5
5
|
|
|
6
|
-
const { Address } = require('address
|
|
6
|
+
const { Address } = require('../address')
|
|
7
7
|
const config = require('haraka-config')
|
|
8
8
|
const constants = require('haraka-constants')
|
|
9
9
|
const net_utils = require('haraka-net-utils')
|
package/outbound/queue.js
CHANGED
|
@@ -4,7 +4,7 @@ const child_process = require('node:child_process')
|
|
|
4
4
|
const fs = require('node:fs/promises')
|
|
5
5
|
const path = require('node:path')
|
|
6
6
|
|
|
7
|
-
const { Address } = require('address
|
|
7
|
+
const { Address } = require('../address')
|
|
8
8
|
const config = require('haraka-config')
|
|
9
9
|
|
|
10
10
|
const logger = require('../logger')
|
package/package.json
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"server",
|
|
10
10
|
"email"
|
|
11
11
|
],
|
|
12
|
-
"version": "3.
|
|
12
|
+
"version": "3.2.0",
|
|
13
13
|
"homepage": "http://haraka.github.io",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
@@ -20,8 +20,7 @@
|
|
|
20
20
|
"node": ">=20"
|
|
21
21
|
},
|
|
22
22
|
"dependencies": {
|
|
23
|
-
"address
|
|
24
|
-
"address-rfc2822": "~2.2.3",
|
|
23
|
+
"@haraka/email-address": "~3.1.3",
|
|
25
24
|
"haraka-config": "~1.6.0",
|
|
26
25
|
"haraka-constants": "~1.0.7",
|
|
27
26
|
"haraka-dsn": "~1.2.0",
|
package/plugins/block_me.js
CHANGED
|
@@ -25,12 +25,12 @@ exports.hook_data_post = function (next, connection) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
// Check recipient is the right one
|
|
28
|
-
if (connection.transaction.rcpt_to[0].address
|
|
28
|
+
if (connection.transaction.rcpt_to[0].address.toLowerCase() != recip) {
|
|
29
29
|
return next()
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
// Check sender is in list
|
|
33
|
-
const sender = connection.transaction.mail_from.address
|
|
33
|
+
const sender = connection.transaction.mail_from.address
|
|
34
34
|
if (!utils.in_array(sender, senders)) {
|
|
35
35
|
return next(DENY, `You are not allowed to block mail, ${sender}`)
|
|
36
36
|
}
|
|
@@ -69,7 +69,7 @@ exports.get_config = function (conn) {
|
|
|
69
69
|
if (this.cfg.main.domain_selector === 'mail_from') {
|
|
70
70
|
if (!conn.transaction.mail_from) return this.cfg.main
|
|
71
71
|
dom = conn.transaction.mail_from.host
|
|
72
|
-
address = conn.transaction.mail_from.address
|
|
72
|
+
address = conn.transaction.mail_from.address
|
|
73
73
|
} else {
|
|
74
74
|
if (!conn.transaction.rcpt_to[0]) return this.cfg.main
|
|
75
75
|
dom = conn.transaction.rcpt_to[0].host
|
|
@@ -92,7 +92,7 @@ exports.check_sender = function (next, connection, params) {
|
|
|
92
92
|
const txn = connection?.transaction
|
|
93
93
|
if (!txn) return
|
|
94
94
|
|
|
95
|
-
const email = params[0].address
|
|
95
|
+
const email = params[0].address
|
|
96
96
|
if (!email) {
|
|
97
97
|
txn.results.add(this, { skip: 'mail_from.null', emit: true })
|
|
98
98
|
return next()
|
|
@@ -26,7 +26,7 @@ exports.hook_mail = function (next, connection, params) {
|
|
|
26
26
|
const txn = connection?.transaction
|
|
27
27
|
if (!txn) return
|
|
28
28
|
|
|
29
|
-
const email = params[0].address
|
|
29
|
+
const email = params[0].address
|
|
30
30
|
if (!email) {
|
|
31
31
|
txn.results.add(this, { skip: 'mail_from.null', emit: true })
|
|
32
32
|
return next()
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
|
|
5
5
|
exports.hook_rcpt = (next, connection, params) => {
|
|
6
6
|
if (connection?.transaction) {
|
|
7
|
-
connection.transaction.add_header('X-Envelope-To', params[0].address
|
|
7
|
+
connection.transaction.add_header('X-Envelope-To', params[0].address)
|
|
8
8
|
}
|
|
9
9
|
next()
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
exports.hook_mail = (next, connection, params) => {
|
|
13
13
|
if (connection?.transaction) {
|
|
14
|
-
connection.transaction.add_header('X-Envelope-From', params[0].address
|
|
14
|
+
connection.transaction.add_header('X-Envelope-From', params[0].address)
|
|
15
15
|
}
|
|
16
16
|
next()
|
|
17
17
|
}
|
package/plugins/status.js
CHANGED
|
@@ -171,7 +171,8 @@ exports.hook_init_master = function (next) {
|
|
|
171
171
|
if (msg.event !== 'status.request') return
|
|
172
172
|
|
|
173
173
|
plugin.call_workers(msg, (response) => {
|
|
174
|
-
|
|
174
|
+
const valid = response.filter((el) => el != null)
|
|
175
|
+
msg.result = plugin.merge_worker_responses(msg.params, valid)
|
|
175
176
|
msg.event = 'status.result'
|
|
176
177
|
sender.send(msg)
|
|
177
178
|
})
|
|
@@ -212,13 +213,41 @@ exports.call_master = (cmd, cb) => {
|
|
|
212
213
|
|
|
213
214
|
exports.call_workers = function (cmd, cb) {
|
|
214
215
|
Promise.allSettled(Object.values(server.cluster.workers).map((w) => this.call_worker(w, cmd))).then((r) => {
|
|
215
|
-
cb(
|
|
216
|
-
// r.filter(s => s.status === 'rejected').flatMap(s => s.reason),
|
|
217
|
-
r.filter((s) => s.status === 'fulfilled').flatMap((s) => s.value),
|
|
218
|
-
)
|
|
216
|
+
cb(r.filter((s) => s.status === 'fulfilled').map((s) => s.value))
|
|
219
217
|
})
|
|
220
218
|
}
|
|
221
219
|
|
|
220
|
+
// Merge per-worker responses into a single result matching non-cluster output shape.
|
|
221
|
+
exports.merge_worker_responses = (params, results) => {
|
|
222
|
+
const cmd = params.trim().split(/\s+/).slice(0, 2).join(' ').toUpperCase()
|
|
223
|
+
|
|
224
|
+
switch (cmd) {
|
|
225
|
+
case 'POOL LIST': {
|
|
226
|
+
return Object.assign({}, ...results)
|
|
227
|
+
}
|
|
228
|
+
case 'QUEUE INSPECT': {
|
|
229
|
+
const merged = { delivery_queue: [], temp_fail_queue: [] }
|
|
230
|
+
for (const r of results) {
|
|
231
|
+
if (!r) continue
|
|
232
|
+
if (Array.isArray(r.delivery_queue)) merged.delivery_queue.push(...r.delivery_queue)
|
|
233
|
+
if (Array.isArray(r.temp_fail_queue)) merged.temp_fail_queue.push(...r.temp_fail_queue)
|
|
234
|
+
}
|
|
235
|
+
return merged
|
|
236
|
+
}
|
|
237
|
+
case 'QUEUE STATS': {
|
|
238
|
+
const totals = [0, 0, 0]
|
|
239
|
+
for (const r of results) {
|
|
240
|
+
if (!r) continue
|
|
241
|
+
const parts = String(r).split('/')
|
|
242
|
+
for (let i = 0; i < 3; i++) totals[i] += parseInt(parts[i] ?? 0, 10)
|
|
243
|
+
}
|
|
244
|
+
return totals.join('/')
|
|
245
|
+
}
|
|
246
|
+
default:
|
|
247
|
+
return results
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
222
251
|
// sends command to worker and then wait for response or timeout
|
|
223
252
|
exports.call_worker = (worker, cmd) => {
|
|
224
253
|
return new Promise((resolve) => {
|
package/test/connection.js
CHANGED
|
@@ -5,7 +5,7 @@ const assert = require('node:assert/strict')
|
|
|
5
5
|
|
|
6
6
|
const constants = require('haraka-constants')
|
|
7
7
|
const DSN = require('haraka-dsn')
|
|
8
|
-
const { Address } = require('address
|
|
8
|
+
const { Address } = require('../address')
|
|
9
9
|
|
|
10
10
|
const connection = require('../connection')
|
|
11
11
|
const Server = require('../server')
|
package/test/outbound/index.js
CHANGED
|
@@ -211,7 +211,7 @@ describe('outbound', () => {
|
|
|
211
211
|
it('yields to setImmediate before opening process_delivery pipes', async () => {
|
|
212
212
|
const stream = require('node:stream')
|
|
213
213
|
const Transaction = require('../../transaction')
|
|
214
|
-
const Address = require('address
|
|
214
|
+
const Address = require('../../address').Address
|
|
215
215
|
const outbound = require('../../outbound')
|
|
216
216
|
const plugins = require('../../plugins')
|
|
217
217
|
|
|
@@ -271,7 +271,7 @@ describe('outbound', () => {
|
|
|
271
271
|
|
|
272
272
|
it('adds missing Message-Id/Date and prepends Received before queueing', async () => {
|
|
273
273
|
process.env.HARAKA_TEST_DIR = path.resolve('test')
|
|
274
|
-
const Address = require('address
|
|
274
|
+
const Address = require('../../address').Address
|
|
275
275
|
const outbound = require('../../outbound')
|
|
276
276
|
const plugins = require('../../plugins')
|
|
277
277
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const assert = require('node:assert')
|
|
3
3
|
const { describe, it, beforeEach } = require('node:test')
|
|
4
4
|
|
|
5
|
-
const { Address } = require('address
|
|
5
|
+
const { Address } = require('../../../address')
|
|
6
6
|
const fixtures = require('haraka-test-fixtures')
|
|
7
7
|
const utils = require('haraka-utils')
|
|
8
8
|
|
package/test/plugins/block_me.js
CHANGED
|
@@ -6,7 +6,7 @@ const assert = require('node:assert/strict')
|
|
|
6
6
|
const { describe, it, beforeEach, after } = require('node:test')
|
|
7
7
|
|
|
8
8
|
const fixtures = require('haraka-test-fixtures')
|
|
9
|
-
const { Address } = require('address
|
|
9
|
+
const { Address } = require('@haraka/email-address')
|
|
10
10
|
require('haraka-constants').import(global)
|
|
11
11
|
|
|
12
12
|
// block_me appends to <config>/mail_from.blocklist when a sender is blocked;
|
|
@@ -5,7 +5,7 @@ const assert = require('node:assert/strict')
|
|
|
5
5
|
const { EventEmitter } = require('node:events')
|
|
6
6
|
const path = require('node:path')
|
|
7
7
|
|
|
8
|
-
const { Address } = require('address
|
|
8
|
+
const { Address } = require('../../../address')
|
|
9
9
|
const fixtures = require('haraka-test-fixtures')
|
|
10
10
|
const Notes = require('haraka-notes')
|
|
11
11
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const assert = require('node:assert/strict')
|
|
3
3
|
const { describe, it, beforeEach } = require('node:test')
|
|
4
4
|
|
|
5
|
-
const { Address } = require('address
|
|
5
|
+
const { Address } = require('../../address')
|
|
6
6
|
const fixtures = require('haraka-test-fixtures')
|
|
7
7
|
|
|
8
8
|
const _set_up = () => {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
const assert = require('node:assert/strict')
|
|
3
3
|
const { describe, it, beforeEach } = require('node:test')
|
|
4
4
|
|
|
5
|
-
const { Address } = require('address
|
|
5
|
+
const { Address } = require('../../address')
|
|
6
6
|
const fixtures = require('haraka-test-fixtures')
|
|
7
7
|
require('haraka-constants').import(global)
|
|
8
8
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
-
const assert = require('node:assert
|
|
3
|
+
const assert = require('node:assert')
|
|
4
4
|
const { describe, it, beforeEach } = require('node:test')
|
|
5
5
|
|
|
6
|
-
const { Address } = require('address
|
|
6
|
+
const { Address } = require('../../address')
|
|
7
7
|
const fixtures = require('haraka-test-fixtures')
|
|
8
8
|
|
|
9
9
|
const _set_up = () => {
|
package/test/plugins/status.js
CHANGED
|
@@ -134,4 +134,75 @@ describe('status', () => {
|
|
|
134
134
|
this.plugin.hook_unrecognized_command(() => {}, this.connection, ['STATUS', 'QUEUE PUSH file'])
|
|
135
135
|
})
|
|
136
136
|
})
|
|
137
|
+
|
|
138
|
+
describe('merge_worker_responses', () => {
|
|
139
|
+
beforeEach(_set_up)
|
|
140
|
+
|
|
141
|
+
it('POOL LIST merges objects from all workers', () => {
|
|
142
|
+
const result = JSON.parse(
|
|
143
|
+
JSON.stringify(
|
|
144
|
+
this.plugin.merge_worker_responses('POOL LIST', [
|
|
145
|
+
{ 'host1:25': { inUse: 1, size: 3 } },
|
|
146
|
+
{ 'host2:25': { inUse: 0, size: 2 } },
|
|
147
|
+
{},
|
|
148
|
+
]),
|
|
149
|
+
),
|
|
150
|
+
)
|
|
151
|
+
assert.deepEqual(result, {
|
|
152
|
+
'host1:25': { inUse: 1, size: 3 },
|
|
153
|
+
'host2:25': { inUse: 0, size: 2 },
|
|
154
|
+
})
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('POOL LIST with all empty workers returns empty object', () => {
|
|
158
|
+
const result = JSON.parse(JSON.stringify(this.plugin.merge_worker_responses('POOL LIST', [{}, {}, {}])))
|
|
159
|
+
assert.deepEqual(result, {})
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
it('QUEUE INSPECT merges queues from all workers', () => {
|
|
163
|
+
const result = JSON.parse(
|
|
164
|
+
JSON.stringify(
|
|
165
|
+
this.plugin.merge_worker_responses('QUEUE INSPECT', [
|
|
166
|
+
{ delivery_queue: [{ id: 'a' }], temp_fail_queue: [{ id: 'x', fire_time: 1 }] },
|
|
167
|
+
{ delivery_queue: [{ id: 'b' }], temp_fail_queue: [] },
|
|
168
|
+
{ delivery_queue: [], temp_fail_queue: [{ id: 'y', fire_time: 2 }] },
|
|
169
|
+
]),
|
|
170
|
+
),
|
|
171
|
+
)
|
|
172
|
+
assert.deepEqual(result, {
|
|
173
|
+
delivery_queue: [{ id: 'a' }, { id: 'b' }],
|
|
174
|
+
temp_fail_queue: [
|
|
175
|
+
{ id: 'x', fire_time: 1 },
|
|
176
|
+
{ id: 'y', fire_time: 2 },
|
|
177
|
+
],
|
|
178
|
+
})
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
it('QUEUE INSPECT with all empty queues returns empty lists', () => {
|
|
182
|
+
const result = JSON.parse(
|
|
183
|
+
JSON.stringify(
|
|
184
|
+
this.plugin.merge_worker_responses('QUEUE INSPECT', [
|
|
185
|
+
{ delivery_queue: [], temp_fail_queue: [] },
|
|
186
|
+
{ delivery_queue: [], temp_fail_queue: [] },
|
|
187
|
+
]),
|
|
188
|
+
),
|
|
189
|
+
)
|
|
190
|
+
assert.deepEqual(result, { delivery_queue: [], temp_fail_queue: [] })
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
it('QUEUE STATS sums across workers', () => {
|
|
194
|
+
const result = this.plugin.merge_worker_responses('QUEUE STATS', ['1/2/3', '0/1/0', '2/0/1'])
|
|
195
|
+
assert.equal(result, '3/3/4')
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
it('QUEUE STATS with all zeros', () => {
|
|
199
|
+
const result = this.plugin.merge_worker_responses('QUEUE STATS', ['0/0/0', '0/0/0', '0/0/0'])
|
|
200
|
+
assert.equal(result, '0/0/0')
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
it('unknown command returns results array unchanged', () => {
|
|
204
|
+
const result = this.plugin.merge_worker_responses('POOL UNKNOWN', [{ foo: 1 }, { foo: 2 }])
|
|
205
|
+
assert.equal(result.length, 2)
|
|
206
|
+
})
|
|
207
|
+
})
|
|
137
208
|
})
|
package/test/smtp_client.js
CHANGED
|
@@ -5,7 +5,7 @@ const assert = require('node:assert/strict')
|
|
|
5
5
|
const { PassThrough } = require('node:stream')
|
|
6
6
|
const path = require('node:path')
|
|
7
7
|
|
|
8
|
-
const { Address } = require('address
|
|
8
|
+
const { Address } = require('../address')
|
|
9
9
|
const fixtures = require('haraka-test-fixtures')
|
|
10
10
|
const net_utils = require('haraka-net-utils')
|
|
11
11
|
const message = require('haraka-email-message')
|