Haraka 3.1.3 → 3.1.5

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 (65) hide show
  1. package/.prettierignore +2 -0
  2. package/CONTRIBUTORS.md +23 -1
  3. package/Changes.md +52 -0
  4. package/Plugins.md +81 -64
  5. package/README.md +1 -1
  6. package/bin/haraka +7 -5
  7. package/connection.js +15 -19
  8. package/docs/Plugins.md +1 -1
  9. package/docs/plugins/aliases.md +0 -2
  10. package/docs/plugins/queue/qmail-queue.md +0 -1
  11. package/logger.js +2 -2
  12. package/outbound/hmail.js +76 -83
  13. package/outbound/index.js +36 -34
  14. package/outbound/queue.js +231 -176
  15. package/package.json +26 -29
  16. package/plugins/prevent_credential_leaks.js +2 -2
  17. package/plugins/process_title.js +1 -1
  18. package/plugins/queue/smtp_forward.js +5 -5
  19. package/plugins/status.js +8 -5
  20. package/plugins/tls.js +1 -1
  21. package/plugins.js +19 -14
  22. package/rfc1869.js +10 -10
  23. package/run_tests +8 -2
  24. package/server.js +15 -10
  25. package/smtp_client.js +10 -15
  26. package/test/config/tls/haraka.local.pem +47 -47
  27. package/test/connection.js +286 -147
  28. package/test/endpoint.js +5 -4
  29. package/test/fixtures/line_socket.js +1 -0
  30. package/test/fixtures/util_hmailitem.js +1 -1
  31. package/test/host_pool.js +57 -31
  32. package/test/logger.js +75 -135
  33. package/test/outbound/bounce_net_errors.js +132 -0
  34. package/test/outbound/bounce_rfc3464.js +226 -0
  35. package/test/outbound/hmail.js +140 -104
  36. package/test/outbound/index.js +61 -101
  37. package/test/outbound/qfile.js +25 -25
  38. package/test/outbound/queue.js +233 -0
  39. package/test/plugins/auth/auth_base.js +39 -44
  40. package/test/plugins/auth/auth_vpopmaild.js +8 -9
  41. package/test/plugins/queue/smtp_forward.js +953 -183
  42. package/test/plugins/rcpt_to.host_list_base.js +58 -93
  43. package/test/plugins/rcpt_to.in_host_list.js +126 -175
  44. package/test/plugins/record_envelope_addresses.js +93 -0
  45. package/test/plugins/status.js +10 -10
  46. package/test/plugins/tls.js +11 -21
  47. package/test/plugins/xclient.js +102 -0
  48. package/test/plugins.js +10 -13
  49. package/test/rfc1869.js +71 -48
  50. package/test/server.js +281 -436
  51. package/test/smtp_client.js +1194 -220
  52. package/test/tls_socket.js +74 -243
  53. package/test/transaction.js +486 -201
  54. package/tls_socket.js +19 -23
  55. package/transaction.js +33 -10
  56. package/config/rabbitmq.ini +0 -10
  57. package/config/rabbitmq_amqplib.ini +0 -19
  58. package/docs/plugins/queue/rabbitmq.md +0 -34
  59. package/docs/plugins/queue/rabbitmq_amqplib.md +0 -51
  60. package/plugins/queue/rabbitmq.js +0 -141
  61. package/plugins/queue/rabbitmq_amqplib.js +0 -96
  62. package/test/config/tls/ec.pem +0 -23
  63. package/test/config/tls/mismatched.pem +0 -49
  64. package/test/outbound_bounce_net_errors.js +0 -157
  65. package/test/outbound_bounce_rfc3464.js +0 -366
@@ -1,18 +1,17 @@
1
1
  'use strict'
2
- const assert = require('node:assert')
2
+ const assert = require('node:assert/strict')
3
+ const { describe, it, beforeEach } = require('node:test')
3
4
 
4
5
  const { Address } = require('address-rfc2821')
5
6
  const fixtures = require('haraka-test-fixtures')
6
7
 
7
- const _set_up = (done) => {
8
+ const _set_up = () => {
8
9
  this.plugin = new fixtures.plugin('rcpt_to.host_list_base')
9
10
  this.plugin.cfg = {}
10
11
  this.plugin.host_list = {}
11
12
 
12
13
  this.connection = fixtures.connection.createConnection()
13
14
  this.connection.init_transaction()
14
-
15
- done()
16
15
  }
17
16
 
18
17
  describe('rcpt_to.host_list_base', () => {
@@ -32,109 +31,75 @@ describe('rcpt_to.host_list_base', () => {
32
31
  describe('in_host_regex', () => {
33
32
  beforeEach(_set_up)
34
33
 
35
- it('undef', () => {
36
- const r = this.plugin.in_host_regex('test.com')
37
- assert.equal(false, r)
38
- })
39
-
40
- it('miss', () => {
41
- this.plugin.host_list_regex = ['miss.com']
42
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
43
- const r = this.plugin.in_host_regex('test.com')
44
- assert.equal(false, r)
45
- })
34
+ const setRegex = (patterns) => {
35
+ this.plugin.host_list_regex = patterns
36
+ this.plugin.hl_re = new RegExp(`^(?:${patterns.join('|')})$`, 'i')
37
+ }
46
38
 
47
- it('exact hit', () => {
48
- this.plugin.host_list_regex = ['test.com']
49
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
50
- const r = this.plugin.in_host_regex('test.com')
51
- assert.equal(true, r)
39
+ it('returns false when hl_re is not set', () => {
40
+ assert.equal(this.plugin.in_host_regex('test.com'), false)
52
41
  })
53
42
 
54
- it('re hit', () => {
55
- this.plugin.host_list_regex = ['.*est.com']
56
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
57
- const r = this.plugin.in_host_regex('test.com')
58
- assert.equal(true, r)
59
- })
43
+ const cases = [
44
+ { desc: 'miss', patterns: ['miss.com'], domain: 'test.com', expected: false },
45
+ { desc: 'exact hit', patterns: ['test.com'], domain: 'test.com', expected: true },
46
+ { desc: 're hit', patterns: ['.*est.com'], domain: 'test.com', expected: true },
47
+ ]
48
+ for (const { desc, patterns, domain, expected } of cases) {
49
+ it(desc, () => {
50
+ setRegex(patterns)
51
+ assert.equal(this.plugin.in_host_regex(domain), expected)
52
+ })
53
+ }
60
54
  })
61
55
 
62
56
  describe('hook_mail', () => {
63
57
  beforeEach(_set_up)
64
58
 
65
- it('null sender', (done) => {
66
- this.connection.relaying = true
67
- this.plugin.hook_mail(
68
- (rc, msg) => {
69
- assert.equal(undefined, rc)
70
- assert.equal(undefined, msg)
71
- done()
72
- },
73
- this.connection,
74
- [new Address('<>')],
75
- )
76
- })
59
+ const setRegex = (patterns) => {
60
+ this.plugin.host_list_regex = patterns
61
+ this.plugin.hl_re = new RegExp(`^(?:${patterns.join('|')})$`, 'i')
62
+ }
77
63
 
78
- it('miss', (done) => {
79
- this.plugin.host_list = { 'miss.com': true }
80
- this.plugin.hook_mail(
81
- (rc, msg) => {
82
- assert.equal(undefined, rc)
83
- assert.equal(undefined, msg)
84
- const res = this.connection.transaction.results.get('rcpt_to.host_list_base')
85
- assert.notEqual(-1, res.msg.indexOf('mail_from!local'))
86
- done()
87
- },
88
- this.connection,
89
- [new Address('<user@example.com>')],
90
- )
91
- })
64
+ const callMailHook = (addr) =>
65
+ new Promise((resolve) => {
66
+ this.plugin.hook_mail((rc, msg) => resolve({ rc, msg }), this.connection, [new Address(addr)])
67
+ })
92
68
 
93
- it('hit', (done) => {
94
- this.plugin.host_list = { 'example.com': true }
95
- this.plugin.hook_mail(
96
- (rc, msg) => {
97
- assert.equal(undefined, rc)
98
- assert.equal(undefined, msg)
99
- const res = this.connection.transaction.results.get('rcpt_to.host_list_base')
100
- assert.notEqual(-1, res.pass.indexOf('mail_from'))
101
- done()
102
- },
103
- this.connection,
104
- [new Address('<user@example.com>')],
105
- )
69
+ it('null sender always passes when relaying', async () => {
70
+ this.connection.relaying = true
71
+ const { rc, msg } = await callMailHook('<>')
72
+ assert.equal(rc, undefined)
73
+ assert.equal(msg, undefined)
106
74
  })
107
75
 
108
- it('hit, regex, exact', (done) => {
109
- this.plugin.host_list_regex = ['example.com']
110
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
111
- this.plugin.hook_mail(
112
- (rc, msg) => {
113
- assert.equal(undefined, rc)
114
- assert.equal(undefined, msg)
115
- const res = this.connection.transaction.results.get('rcpt_to.host_list_base')
116
- assert.notEqual(-1, res.pass.indexOf('mail_from'))
117
- done()
118
- },
119
- this.connection,
120
- [new Address('<user@example.com>')],
121
- )
76
+ it('miss: records mail_from!local in results', async () => {
77
+ this.plugin.host_list = { 'miss.com': true }
78
+ const { rc, msg } = await callMailHook('<user@example.com>')
79
+ assert.equal(rc, undefined)
80
+ assert.equal(msg, undefined)
81
+ const res = this.connection.transaction.results.get('rcpt_to.host_list_base')
82
+ assert.ok(res.msg.includes('mail_from!local'))
122
83
  })
123
84
 
124
- it('hit, regex, pattern', (done) => {
125
- this.plugin.host_list_regex = ['.*mple.com']
126
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
127
- this.plugin.hook_mail(
128
- (rc, msg) => {
129
- assert.equal(undefined, rc)
130
- assert.equal(undefined, msg)
131
- const res = this.connection.transaction.results.get('rcpt_to.host_list_base')
132
- assert.notEqual(-1, res.pass.indexOf('mail_from'))
133
- done()
85
+ for (const [desc, setup] of [
86
+ [
87
+ 'hit',
88
+ () => {
89
+ this.plugin.host_list = { 'example.com': true }
134
90
  },
135
- this.connection,
136
- [new Address('<user@example.com>')],
137
- )
138
- })
91
+ ],
92
+ ['hit, regex, exact', () => setRegex(['example.com'])],
93
+ ['hit, regex, pattern', () => setRegex(['.*mple.com'])],
94
+ ]) {
95
+ it(desc, async () => {
96
+ setup()
97
+ const { rc, msg } = await callMailHook('<user@example.com>')
98
+ assert.equal(rc, undefined)
99
+ assert.equal(msg, undefined)
100
+ const res = this.connection.transaction.results.get('rcpt_to.host_list_base')
101
+ assert.ok(res.pass.includes('mail_from'))
102
+ })
103
+ }
139
104
  })
140
105
  })
@@ -1,10 +1,12 @@
1
1
  'use strict'
2
- const assert = require('node:assert')
2
+ const assert = require('node:assert/strict')
3
+ const { describe, it, beforeEach } = require('node:test')
3
4
 
4
5
  const { Address } = require('address-rfc2821')
5
6
  const fixtures = require('haraka-test-fixtures')
7
+ require('haraka-constants').import(global)
6
8
 
7
- const _set_up = (done) => {
9
+ const _set_up = () => {
8
10
  this.plugin = new fixtures.plugin('rcpt_to.in_host_list')
9
11
  this.plugin.inherits('rcpt_to.host_list_base')
10
12
  this.plugin.cfg = {}
@@ -15,8 +17,6 @@ const _set_up = (done) => {
15
17
  results: new fixtures.results(this.connection),
16
18
  notes: {},
17
19
  }
18
-
19
- done()
20
20
  }
21
21
 
22
22
  describe('in_host_list', () => {
@@ -34,207 +34,158 @@ describe('in_host_list', () => {
34
34
  describe('in_host_regex', () => {
35
35
  beforeEach(_set_up)
36
36
 
37
- it('undef', () => {
38
- assert.equal(this.plugin.in_host_regex('test.com'), false)
39
- })
37
+ const setRegex = (patterns) => {
38
+ this.plugin.host_list_regex = patterns
39
+ this.plugin.hl_re = new RegExp(`^(?:${patterns.join('|')})$`, 'i')
40
+ }
40
41
 
41
- it('miss', () => {
42
- this.plugin.host_list_regex = ['miss.com']
43
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
42
+ it('returns false when hl_re is not set', () => {
44
43
  assert.equal(this.plugin.in_host_regex('test.com'), false)
45
44
  })
46
45
 
47
- it('exact hit', () => {
48
- this.plugin.host_list_regex = ['test.com']
49
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
50
- assert.equal(this.plugin.in_host_regex('test.com'), true)
51
- })
52
-
53
- it('re hit', () => {
54
- this.plugin.host_list_regex = ['.*est.com']
55
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
56
- assert.equal(this.plugin.in_host_regex('test.com'), true)
57
- })
46
+ const cases = [
47
+ { desc: 'miss', patterns: ['miss.com'], domain: 'test.com', expected: false },
48
+ { desc: 'exact hit', patterns: ['test.com'], domain: 'test.com', expected: true },
49
+ { desc: 're hit', patterns: ['.*est.com'], domain: 'test.com', expected: true },
50
+ ]
51
+ for (const { desc, patterns, domain, expected } of cases) {
52
+ it(desc, () => {
53
+ setRegex(patterns)
54
+ assert.equal(this.plugin.in_host_regex(domain), expected)
55
+ })
56
+ }
58
57
  })
59
58
 
60
59
  describe('hook_mail', () => {
61
60
  beforeEach(_set_up)
62
61
 
63
- it('null sender', (done) => {
64
- this.connection.relaying = true
65
- this.plugin.hook_mail(
66
- (rc, msg) => {
67
- assert.equal(undefined, rc)
68
- assert.equal(undefined, msg)
69
- done()
70
- },
71
- this.connection,
72
- [new Address('<>')],
73
- )
74
- })
62
+ const setRegex = (patterns) => {
63
+ this.plugin.host_list_regex = patterns
64
+ this.plugin.hl_re = new RegExp(`^(?:${patterns.join('|')})$`, 'i')
65
+ }
75
66
 
76
- it('miss', (done) => {
77
- this.plugin.host_list = { 'miss.com': true }
78
- this.plugin.hook_mail(
79
- (rc, msg) => {
80
- assert.equal(undefined, rc)
81
- assert.equal(undefined, msg)
82
- const res = this.connection.transaction.results.get('rcpt_to.in_host_list')
83
- assert.notEqual(-1, res.msg.indexOf('mail_from!local'))
84
- done()
85
- },
86
- this.connection,
87
- [new Address('<user@example.com>')],
88
- )
89
- })
67
+ const callMailHook = (addr) =>
68
+ new Promise((resolve) => {
69
+ this.plugin.hook_mail((rc, msg) => resolve({ rc, msg }), this.connection, [new Address(addr)])
70
+ })
90
71
 
91
- it('hit', (done) => {
92
- this.plugin.host_list = { 'example.com': true }
93
- this.plugin.hook_mail(
94
- (rc, msg) => {
95
- assert.equal(undefined, rc)
96
- assert.equal(undefined, msg)
97
- const res = this.connection.transaction.results.get('rcpt_to.in_host_list')
98
- assert.notEqual(-1, res.pass.indexOf('mail_from'))
99
- done()
100
- },
101
- this.connection,
102
- [new Address('<user@example.com>')],
103
- )
104
- })
105
-
106
- it('hit, regex, exact', (done) => {
107
- this.plugin.host_list_regex = ['example.com']
108
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
109
- this.plugin.hook_mail(
110
- (rc, msg) => {
111
- assert.equal(undefined, rc)
112
- assert.equal(undefined, msg)
113
- const res = this.connection.transaction.results.get('rcpt_to.in_host_list')
114
- assert.notEqual(-1, res.pass.indexOf('mail_from'))
115
- done()
116
- },
117
- this.connection,
118
- [new Address('<user@example.com>')],
119
- )
72
+ it('null sender always passes when relaying', async () => {
73
+ this.connection.relaying = true
74
+ const { rc, msg } = await callMailHook('<>')
75
+ assert.equal(rc, undefined)
76
+ assert.equal(msg, undefined)
120
77
  })
121
78
 
122
- it('hit, regex, pattern', (done) => {
123
- this.plugin.host_list_regex = ['.*mple.com']
124
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
125
- this.plugin.hook_mail(
126
- (rc, msg) => {
127
- assert.equal(undefined, rc)
128
- assert.equal(undefined, msg)
129
- const res = this.connection.transaction.results.get('rcpt_to.in_host_list')
130
- // console.log(res);
131
- assert.notEqual(-1, res.pass.indexOf('mail_from'))
132
- done()
79
+ it('miss: records mail_from!local in results', async () => {
80
+ this.plugin.host_list = { 'miss.com': true }
81
+ const { rc, msg } = await callMailHook('<user@example.com>')
82
+ assert.equal(rc, undefined)
83
+ assert.equal(msg, undefined)
84
+ const res = this.connection.transaction.results.get('rcpt_to.in_host_list')
85
+ assert.ok(res.msg.includes('mail_from!local'))
86
+ })
87
+
88
+ for (const [desc, setup] of [
89
+ [
90
+ 'hit',
91
+ () => {
92
+ this.plugin.host_list = { 'example.com': true }
133
93
  },
134
- this.connection,
135
- [new Address('<user@example.com>')],
136
- )
137
- })
94
+ ],
95
+ ['hit, regex, exact', () => setRegex(['example.com'])],
96
+ ['hit, regex, pattern', () => setRegex(['.*mple.com'])],
97
+ ]) {
98
+ it(desc, async () => {
99
+ setup()
100
+ const { rc, msg } = await callMailHook('<user@example.com>')
101
+ assert.equal(rc, undefined)
102
+ assert.equal(msg, undefined)
103
+ const res = this.connection.transaction.results.get('rcpt_to.in_host_list')
104
+ assert.ok(res.pass.includes('mail_from'))
105
+ })
106
+ }
138
107
  })
139
108
 
140
109
  describe('hook_rcpt', () => {
141
110
  beforeEach(_set_up)
142
111
 
143
- it('missing txn', (done) => {
144
- // sometimes txn goes away, make sure it's handled
145
- delete this.connection.transaction
146
- this.plugin.hook_rcpt(
147
- (rc, msg) => {
148
- assert.equal(undefined, rc)
149
- assert.equal(undefined, msg)
150
- },
151
- this.connection,
152
- [new Address('test@test.com')],
153
- )
154
- assert.ok(true)
155
- done()
156
- })
112
+ const setRegex = (patterns) => {
113
+ this.plugin.host_list_regex = patterns
114
+ this.plugin.hl_re = new RegExp(`^(?:${patterns.join('|')})$`, 'i')
115
+ }
157
116
 
158
- it('hit list', (done) => {
159
- this.plugin.host_list = { 'test.com': true }
160
- this.plugin.hook_rcpt(
161
- (rc, msg) => {
162
- assert.equal(OK, rc)
163
- assert.equal(undefined, msg)
164
- done()
165
- },
166
- this.connection,
167
- [new Address('test@test.com')],
168
- )
169
- })
117
+ const callRcptHook = (addr) =>
118
+ new Promise((resolve) => {
119
+ this.plugin.hook_rcpt((rc, msg) => resolve({ rc, msg }), this.connection, [new Address(addr)])
120
+ })
170
121
 
171
- it('miss list', (done) => {
172
- this.plugin.host_list = { 'miss.com': true }
173
- this.plugin.hook_rcpt(
174
- (rc, msg) => {
175
- assert.equal(undefined, rc)
176
- assert.equal(undefined, msg)
177
- done()
178
- },
179
- this.connection,
180
- [new Address('test@test.com')],
181
- )
182
- })
183
-
184
- it('hit regex, exact', (done) => {
185
- this.plugin.host_list_regex = ['test.com']
186
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
122
+ it('missing txn: does not call next (returns early)', () => {
123
+ // When transaction is missing the plugin returns without calling next()
124
+ delete this.connection.transaction
125
+ let nextCalled = false
187
126
  this.plugin.hook_rcpt(
188
- (rc, msg) => {
189
- assert.equal(OK, rc)
190
- assert.equal(undefined, msg)
191
- done()
127
+ () => {
128
+ nextCalled = true
192
129
  },
193
130
  this.connection,
194
131
  [new Address('test@test.com')],
195
132
  )
133
+ assert.equal(nextCalled, false)
196
134
  })
197
135
 
198
- it('hit regex, pattern', (done) => {
199
- this.plugin.host_list_regex = ['.est.com']
200
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
201
- this.plugin.hook_rcpt(
202
- (rc, msg) => {
203
- assert.equal(OK, rc)
204
- assert.equal(undefined, msg)
205
- done()
136
+ const cases = [
137
+ {
138
+ desc: 'hit list',
139
+ setup: () => {
140
+ this.plugin.host_list = { 'test.com': true }
206
141
  },
207
- this.connection,
208
- [new Address('test@test.com')],
209
- )
210
- })
211
-
212
- it('miss regex, pattern', (done) => {
213
- this.plugin.host_list_regex = ['a.est.com']
214
- this.plugin.hl_re = new RegExp(`^(?:${this.plugin.host_list_regex.join('|')})$`, 'i')
215
- this.plugin.hook_rcpt(
216
- (rc, msg) => {
217
- assert.equal(undefined, rc)
218
- assert.equal(undefined, msg)
219
- done()
142
+ addr: 'test@test.com',
143
+ expectedRC: OK,
144
+ },
145
+ {
146
+ desc: 'miss list',
147
+ setup: () => {
148
+ this.plugin.host_list = { 'miss.com': true }
220
149
  },
221
- this.connection,
222
- [new Address('test@test.com')],
223
- )
224
- })
225
-
226
- it('rcpt miss, relaying to local sender', (done) => {
227
- this.connection.relaying = true
228
- this.connection.transaction.notes = { local_sender: true }
229
- this.plugin.hook_rcpt(
230
- (rc, msg) => {
231
- assert.equal(OK, rc)
232
- assert.equal(undefined, msg)
233
- done()
150
+ addr: 'test@test.com',
151
+ expectedRC: undefined,
152
+ },
153
+ {
154
+ desc: 'hit regex, exact',
155
+ setup: () => setRegex(['test.com']),
156
+ addr: 'test@test.com',
157
+ expectedRC: OK,
158
+ },
159
+ {
160
+ desc: 'hit regex, pattern',
161
+ setup: () => setRegex(['.est.com']),
162
+ addr: 'test@test.com',
163
+ expectedRC: OK,
164
+ },
165
+ {
166
+ desc: 'miss regex, pattern',
167
+ setup: () => setRegex(['a.est.com']),
168
+ addr: 'test@test.com',
169
+ expectedRC: undefined,
170
+ },
171
+ {
172
+ desc: 'rcpt miss, relaying to local sender',
173
+ setup: () => {
174
+ this.connection.relaying = true
175
+ this.connection.transaction.notes = { local_sender: true }
234
176
  },
235
- this.connection,
236
- [new Address('test@test.com')],
237
- )
238
- })
177
+ addr: 'test@test.com',
178
+ expectedRC: OK,
179
+ },
180
+ ]
181
+
182
+ for (const { desc, setup, addr, expectedRC } of cases) {
183
+ it(desc, async () => {
184
+ setup()
185
+ const { rc, msg } = await callRcptHook(addr)
186
+ assert.equal(rc, expectedRC)
187
+ assert.equal(msg, undefined)
188
+ })
189
+ }
239
190
  })
240
191
  })
@@ -0,0 +1,93 @@
1
+ 'use strict'
2
+
3
+ const assert = require('node:assert/strict')
4
+ const { describe, it, beforeEach } = require('node:test')
5
+
6
+ const { Address } = require('address-rfc2821')
7
+ const fixtures = require('haraka-test-fixtures')
8
+
9
+ const _set_up = () => {
10
+ this.plugin = new fixtures.plugin('record_envelope_addresses')
11
+ this.connection = fixtures.connection.createConnection()
12
+ this.connection.init_transaction()
13
+ }
14
+
15
+ describe('record_envelope_addresses', () => {
16
+ beforeEach(_set_up)
17
+
18
+ describe('hook_mail', () => {
19
+ it('adds X-Envelope-From header from MAIL FROM address', (t, done) => {
20
+ const addr = new Address('<sender@example.com>')
21
+ this.plugin.hook_mail(
22
+ () => {
23
+ const vals = this.connection.transaction.header.get_all('X-Envelope-From')
24
+ assert.equal(vals.length, 1, 'header was added')
25
+ assert.equal(vals[0], 'sender@example.com')
26
+ done()
27
+ },
28
+ this.connection,
29
+ [addr],
30
+ )
31
+ })
32
+
33
+ it('does not throw when connection has no transaction', (t, done) => {
34
+ this.connection.transaction = null
35
+ const addr = new Address('<sender@example.com>')
36
+ this.plugin.hook_mail(
37
+ () => {
38
+ assert.ok(true, 'next was called without error')
39
+ done()
40
+ },
41
+ this.connection,
42
+ [addr],
43
+ )
44
+ })
45
+ })
46
+
47
+ describe('hook_rcpt', () => {
48
+ it('adds X-Envelope-To header from RCPT TO address', (t, done) => {
49
+ const addr = new Address('<rcpt@example.com>')
50
+ this.plugin.hook_rcpt(
51
+ () => {
52
+ const vals = this.connection.transaction.header.get_all('X-Envelope-To')
53
+ assert.equal(vals.length, 1, 'header was added')
54
+ assert.equal(vals[0], 'rcpt@example.com')
55
+ done()
56
+ },
57
+ this.connection,
58
+ [addr],
59
+ )
60
+ })
61
+
62
+ it('adds X-Envelope-To header for each recipient', (t, done) => {
63
+ const addr1 = new Address('<one@example.com>')
64
+ const addr2 = new Address('<two@example.com>')
65
+ let calls = 0
66
+ const next = () => {
67
+ calls++
68
+ if (calls === 2) {
69
+ const vals = this.connection.transaction.header.get_all('X-Envelope-To')
70
+ assert.equal(vals.length, 2, 'two headers added')
71
+ assert.equal(vals[0], 'one@example.com')
72
+ assert.equal(vals[1], 'two@example.com')
73
+ done()
74
+ }
75
+ }
76
+ this.plugin.hook_rcpt(next, this.connection, [addr1])
77
+ this.plugin.hook_rcpt(next, this.connection, [addr2])
78
+ })
79
+
80
+ it('does not throw when connection has no transaction', (t, done) => {
81
+ this.connection.transaction = null
82
+ const addr = new Address('<rcpt@example.com>')
83
+ this.plugin.hook_rcpt(
84
+ () => {
85
+ assert.ok(true, 'next was called without error')
86
+ done()
87
+ },
88
+ this.connection,
89
+ [addr],
90
+ )
91
+ })
92
+ })
93
+ })