Haraka 3.1.3 → 3.1.4
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 +2 -0
- package/CONTRIBUTORS.md +23 -1
- package/Changes.md +38 -0
- package/Plugins.md +81 -64
- package/README.md +1 -1
- package/bin/haraka +7 -5
- package/connection.js +15 -19
- package/docs/Plugins.md +1 -1
- package/docs/plugins/aliases.md +0 -2
- package/docs/plugins/queue/qmail-queue.md +0 -1
- package/logger.js +2 -2
- package/outbound/hmail.js +76 -83
- package/outbound/index.js +36 -34
- package/outbound/queue.js +231 -176
- package/package.json +25 -26
- package/plugins/prevent_credential_leaks.js +2 -2
- package/plugins/process_title.js +1 -1
- package/plugins/queue/smtp_forward.js +1 -1
- package/plugins/status.js +8 -5
- package/plugins/tls.js +1 -1
- package/plugins.js +19 -14
- package/rfc1869.js +10 -10
- package/run_tests +20 -2
- package/server.js +15 -10
- package/smtp_client.js +2 -9
- package/test/config/tls/haraka.local.pem +47 -47
- package/test/connection.js +286 -147
- package/test/fixtures/line_socket.js +1 -0
- package/test/fixtures/util_hmailitem.js +1 -1
- package/test/outbound/bounce_net_errors.js +176 -0
- package/test/outbound/bounce_rfc3464.js +303 -0
- package/test/outbound/hmail.js +140 -104
- package/test/outbound/index.js +61 -101
- package/test/outbound/qfile.js +25 -25
- package/test/outbound/queue.js +233 -0
- package/test/plugins/queue/smtp_forward.js +1 -1
- package/test/plugins/record_envelope_addresses.js +93 -0
- package/test/plugins/tls.js +2 -2
- package/test/plugins/xclient.js +137 -0
- package/test/rfc1869.js +43 -0
- package/test/smtp_client.js +6 -6
- package/test/transaction.js +486 -201
- package/tls_socket.js +3 -3
- package/transaction.js +33 -10
- package/config/rabbitmq.ini +0 -10
- package/config/rabbitmq_amqplib.ini +0 -19
- package/docs/plugins/queue/rabbitmq.md +0 -34
- package/docs/plugins/queue/rabbitmq_amqplib.md +0 -51
- package/plugins/queue/rabbitmq.js +0 -141
- package/plugins/queue/rabbitmq_amqplib.js +0 -96
- package/test/config/tls/ec.pem +0 -23
- package/test/config/tls/mismatched.pem +0 -49
- package/test/outbound_bounce_net_errors.js +0 -157
- package/test/outbound_bounce_rfc3464.js +0 -366
- package/test/tls_socket.js +0 -273
package/test/outbound/hmail.js
CHANGED
|
@@ -1,155 +1,191 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { describe, it, before, beforeEach, afterEach } = require('node:test')
|
|
1
4
|
const assert = require('node:assert')
|
|
5
|
+
const { EventEmitter } = require('node:events')
|
|
2
6
|
const fs = require('node:fs')
|
|
3
7
|
const path = require('node:path')
|
|
4
8
|
|
|
5
|
-
|
|
6
|
-
const outbound = require('../../outbound
|
|
9
|
+
// Load outbound/index FIRST to avoid the circular-dependency boot-order issue.
|
|
10
|
+
const outbound = require('../../outbound')
|
|
11
|
+
const Hmail = outbound.HMailItem
|
|
12
|
+
const client_pool = require('../../outbound/client_pool')
|
|
13
|
+
|
|
14
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
const onEvent = (emitter, event) => new Promise((resolve) => emitter.once(event, resolve))
|
|
17
|
+
|
|
18
|
+
// ── Tests ─────────────────────────────────────────────────────────────────────
|
|
7
19
|
|
|
8
20
|
describe('outbound/hmail', () => {
|
|
9
|
-
|
|
10
|
-
|
|
21
|
+
let hmail
|
|
22
|
+
|
|
23
|
+
beforeEach(() => {
|
|
24
|
+
hmail = new Hmail(
|
|
11
25
|
'1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka',
|
|
12
26
|
'test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka',
|
|
13
27
|
{},
|
|
14
28
|
)
|
|
15
|
-
done()
|
|
16
29
|
})
|
|
17
30
|
|
|
18
|
-
|
|
19
|
-
const
|
|
31
|
+
describe('socket error/timeout handler robustness (#3388)', () => {
|
|
32
|
+
const mx = { using_lmtp: false, port: 25, exchange: 'mx.example.com', bind: null, bind_helo: 'test' }
|
|
33
|
+
let origRelease
|
|
34
|
+
|
|
35
|
+
function makeSocket() {
|
|
36
|
+
const s = new EventEmitter()
|
|
37
|
+
s.name = 'mock'
|
|
38
|
+
s.writable = true
|
|
39
|
+
s.write = () => {}
|
|
40
|
+
s.destroy = () => {}
|
|
41
|
+
return s
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
origRelease = client_pool.release_client
|
|
46
|
+
client_pool.release_client = () => {}
|
|
47
|
+
hmail.todo = { rcpt_to: [] }
|
|
48
|
+
hmail.try_deliver = () => {}
|
|
49
|
+
hmail.logerror = () => {}
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
client_pool.release_client = origRelease
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
it('error then timeout does not throw ERR_UNHANDLED_ERROR', () => {
|
|
57
|
+
const socket = makeSocket()
|
|
58
|
+
hmail.try_deliver_host_on_socket(mx, '1.2.3.4', 25, socket)
|
|
59
|
+
socket.emit('error', new Error('connection refused'))
|
|
60
|
+
assert.doesNotThrow(() => socket.emit('timeout'), 'timeout after error must not crash')
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
it('timeout then error does not throw ERR_UNHANDLED_ERROR', () => {
|
|
64
|
+
const socket = makeSocket()
|
|
65
|
+
hmail.try_deliver_host_on_socket(mx, '1.2.3.4', 25, socket)
|
|
66
|
+
socket.emit('timeout')
|
|
67
|
+
assert.doesNotThrow(
|
|
68
|
+
() => socket.emit('error', new Error('late error')),
|
|
69
|
+
'error after timeout must not crash',
|
|
70
|
+
)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
it('multiple timeouts do not throw ERR_UNHANDLED_ERROR', () => {
|
|
74
|
+
const socket = makeSocket()
|
|
75
|
+
hmail.try_deliver_host_on_socket(mx, '1.2.3.4', 25, socket)
|
|
76
|
+
socket.emit('timeout')
|
|
77
|
+
assert.doesNotThrow(() => socket.emit('timeout'), 'second timeout must not crash')
|
|
78
|
+
})
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
it('sort_mx orders by priority ascending', () => {
|
|
82
|
+
const sorted = hmail.sort_mx([
|
|
20
83
|
{ exchange: 'mx2.example.com', priority: 5 },
|
|
21
84
|
{ exchange: 'mx1.example.com', priority: 6 },
|
|
22
85
|
])
|
|
23
86
|
assert.equal(sorted[0].exchange, 'mx2.example.com')
|
|
24
|
-
done()
|
|
25
87
|
})
|
|
26
|
-
|
|
27
|
-
|
|
88
|
+
|
|
89
|
+
it('sort_mx shuffles equal-priority entries', () => {
|
|
90
|
+
const sorted = hmail.sort_mx([
|
|
28
91
|
{ exchange: 'mx2.example.com', priority: 5 },
|
|
29
92
|
{ exchange: 'mx1.example.com', priority: 6 },
|
|
30
93
|
{ exchange: 'mx3.example.com', priority: 6 },
|
|
31
94
|
])
|
|
32
95
|
assert.equal(sorted[0].exchange, 'mx2.example.com')
|
|
33
|
-
assert.ok(
|
|
34
|
-
done()
|
|
96
|
+
assert.ok(['mx1.example.com', 'mx3.example.com'].includes(sorted[1].exchange))
|
|
35
97
|
})
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
41
|
-
assert.equal(
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
assert.equal(this.hmail.get_force_tls({ exchange: '1.2.3.5' }), true)
|
|
45
|
-
done()
|
|
98
|
+
|
|
99
|
+
it('get_force_tls matches by IP and domain', () => {
|
|
100
|
+
hmail.todo = { domain: 'miss.example.com' }
|
|
101
|
+
hmail.obtls.cfg = { force_tls_hosts: ['1.2.3.4', 'hit.example.com'] }
|
|
102
|
+
assert.equal(hmail.get_force_tls({ exchange: '1.2.3.4' }), true)
|
|
103
|
+
assert.equal(hmail.get_force_tls({ exchange: '1.2.3.5' }), false)
|
|
104
|
+
hmail.todo = { domain: 'hit.example.com' }
|
|
105
|
+
assert.equal(hmail.get_force_tls({ exchange: '1.2.3.5' }), true)
|
|
46
106
|
})
|
|
47
107
|
})
|
|
48
108
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
109
|
+
const TOOLONG_FIXTURE = 'test/queue/1509000000000_1509000000000_0_99999_ToLong_1_haraka'
|
|
110
|
+
|
|
111
|
+
const makeToolongFixture = () => {
|
|
112
|
+
const buf = Buffer.alloc(50)
|
|
113
|
+
buf.writeUInt32BE(9999, 0) // declares 9999 bytes but file has only 46 after the header
|
|
114
|
+
buf.write('{"domain":"example.com"', 4)
|
|
115
|
+
fs.writeFileSync(TOOLONG_FIXTURE, buf)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
describe('outbound/hmail.HMailItem — queue file loading', () => {
|
|
119
|
+
before(makeToolongFixture)
|
|
120
|
+
|
|
121
|
+
it('loads a valid queue file', async () => {
|
|
122
|
+
const h = new Hmail(
|
|
52
123
|
'1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka',
|
|
53
124
|
'test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka',
|
|
54
125
|
{},
|
|
55
126
|
)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
assert.ok(this.hmail)
|
|
59
|
-
done()
|
|
60
|
-
})
|
|
61
|
-
this.hmail.on('error', (err) => {
|
|
62
|
-
console.log(err)
|
|
63
|
-
assert.equal(err, undefined)
|
|
64
|
-
done()
|
|
65
|
-
})
|
|
127
|
+
await onEvent(h, 'ready')
|
|
128
|
+
assert.ok(h)
|
|
66
129
|
})
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
done()
|
|
73
|
-
})
|
|
74
|
-
this.hmail.on('error', (err) => {
|
|
75
|
-
console.log(err)
|
|
76
|
-
assert.equal(err, undefined)
|
|
77
|
-
done()
|
|
78
|
-
})
|
|
130
|
+
|
|
131
|
+
it('loads a TODO with multibyte chars without error', async () => {
|
|
132
|
+
const h = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_1_qfile', 'test/fixtures/todo_qfile.txt', {})
|
|
133
|
+
await onEvent(h, 'ready')
|
|
134
|
+
assert.ok(h)
|
|
79
135
|
})
|
|
80
|
-
|
|
81
|
-
|
|
136
|
+
|
|
137
|
+
it('emits error on too-short declared TODO length', async () => {
|
|
138
|
+
const h = new Hmail(
|
|
82
139
|
'1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka',
|
|
83
140
|
'test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka',
|
|
84
141
|
{},
|
|
85
142
|
)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
done()
|
|
90
|
-
})
|
|
91
|
-
this.hmail.on('error', (err) => {
|
|
92
|
-
console.log(err)
|
|
93
|
-
assert.ok(err)
|
|
94
|
-
done()
|
|
143
|
+
const err = await new Promise((resolve) => {
|
|
144
|
+
h.once('ready', () => resolve(null))
|
|
145
|
+
h.once('error', resolve)
|
|
95
146
|
})
|
|
147
|
+
assert.ok(err, 'expected an error for truncated TODO')
|
|
96
148
|
})
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
)
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
assert.ok(this.hmail)
|
|
106
|
-
done()
|
|
107
|
-
})
|
|
108
|
-
this.hmail.on('error', (err) => {
|
|
109
|
-
console.log(err)
|
|
110
|
-
assert.ok(err)
|
|
111
|
-
done()
|
|
149
|
+
|
|
150
|
+
it('emits error on too-long declared TODO length', async () => {
|
|
151
|
+
// Recreate fixture in case a prior run renamed it to the error queue
|
|
152
|
+
makeToolongFixture()
|
|
153
|
+
const h = new Hmail('1509000000000_1509000000000_0_99999_ToLong_1_haraka', TOOLONG_FIXTURE, {})
|
|
154
|
+
const err = await new Promise((resolve) => {
|
|
155
|
+
h.once('ready', () => resolve(null))
|
|
156
|
+
h.once('error', resolve)
|
|
112
157
|
})
|
|
158
|
+
assert.ok(err, 'expected an error for oversized TODO')
|
|
113
159
|
})
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
this.hmail.on('error', (err) => {
|
|
121
|
-
console.error(err)
|
|
122
|
-
assert.ok(err)
|
|
123
|
-
done()
|
|
160
|
+
|
|
161
|
+
it('skips zero-length file without crash', async () => {
|
|
162
|
+
const h = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_2_zero', 'test/queue/zero-length', {})
|
|
163
|
+
await new Promise((resolve) => {
|
|
164
|
+
h.once('ready', resolve)
|
|
165
|
+
h.once('error', resolve)
|
|
124
166
|
})
|
|
167
|
+
assert.ok(h)
|
|
125
168
|
})
|
|
126
|
-
it('lifecycle, reads and writes a haraka queue file', (done) => {
|
|
127
|
-
this.hmail = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_2_qfile', 'test/fixtures/todo_qfile.txt', {})
|
|
128
169
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
assert.equals(err, undefined)
|
|
132
|
-
done()
|
|
133
|
-
})
|
|
170
|
+
it('lifecycle: reads and writes a queue file', async () => {
|
|
171
|
+
const h = new Hmail('1507509981169_1507509981169_0_61403_e0Y0Ym_2_qfile', 'test/fixtures/todo_qfile.txt', {})
|
|
134
172
|
|
|
135
|
-
|
|
136
|
-
const tmpfile = path.resolve('test', 'test-queue', 'delete-me')
|
|
137
|
-
const ws = new fs.createWriteStream(tmpfile)
|
|
173
|
+
await onEvent(h, 'ready')
|
|
138
174
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
// assert.equals(this.hmail.todo.message_stream.headers.length, 22);
|
|
175
|
+
const tmpfile = path.resolve('test', 'test-queue', 'delete-me')
|
|
176
|
+
await fs.promises.mkdir(path.dirname(tmpfile), { recursive: true })
|
|
177
|
+
const ws = new fs.WriteStream(tmpfile)
|
|
143
178
|
|
|
144
|
-
|
|
179
|
+
await new Promise((resolve, reject) => {
|
|
180
|
+
outbound.build_todo(h.todo, ws, () => {
|
|
181
|
+
const ds = h.data_stream()
|
|
145
182
|
ds.pipe(ws)
|
|
146
|
-
|
|
147
|
-
ws.on('
|
|
148
|
-
// console.log(this.hmail.todo)
|
|
149
|
-
assert.equal(fs.statSync(tmpfile).size, 4204)
|
|
150
|
-
done()
|
|
151
|
-
})
|
|
183
|
+
ws.on('close', resolve)
|
|
184
|
+
ws.on('error', reject)
|
|
152
185
|
})
|
|
153
186
|
})
|
|
187
|
+
|
|
188
|
+
assert.equal(fs.statSync(tmpfile).size, 4204)
|
|
189
|
+
fs.unlinkSync(tmpfile)
|
|
154
190
|
})
|
|
155
191
|
})
|
package/test/outbound/index.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
|
+
const { describe, it, beforeEach, afterEach } = require('node:test')
|
|
3
4
|
const assert = require('node:assert')
|
|
4
|
-
const fs = require('fs')
|
|
5
|
-
const path = require('path')
|
|
5
|
+
const fs = require('node:fs')
|
|
6
|
+
const path = require('node:path')
|
|
6
7
|
|
|
7
8
|
const constants = require('haraka-constants')
|
|
8
9
|
const logger = require('../../logger')
|
|
@@ -22,56 +23,51 @@ describe('outbound', () => {
|
|
|
22
23
|
let contents = lines.join(ending)
|
|
23
24
|
let result = ''
|
|
24
25
|
|
|
25
|
-
// Set data_lines to lines in contents
|
|
26
26
|
let match
|
|
27
27
|
const re = /^([^\n]*\n?)/
|
|
28
28
|
while ((match = re.exec(contents))) {
|
|
29
29
|
let line = match[1]
|
|
30
|
-
line = line.replace(/\r?\n?$/, '\r\n')
|
|
30
|
+
line = line.replace(/\r?\n?$/, '\r\n')
|
|
31
31
|
result += line
|
|
32
|
-
contents = contents.
|
|
33
|
-
if (contents.length === 0)
|
|
34
|
-
break
|
|
35
|
-
}
|
|
32
|
+
contents = contents.substring(match[1].length)
|
|
33
|
+
if (contents.length === 0) break
|
|
36
34
|
}
|
|
37
35
|
|
|
38
36
|
assert.deepEqual(lines.join('\r\n'), result)
|
|
39
37
|
}
|
|
40
38
|
})
|
|
41
39
|
|
|
42
|
-
it('log_methods added', () => {
|
|
40
|
+
it('log_methods added to HMailItem prototype', () => {
|
|
43
41
|
const levels = ['DATA', 'PROTOCOL', 'DEBUG', 'INFO', 'NOTICE', 'WARN', 'ERROR', 'CRIT', 'ALERT', 'EMERG']
|
|
44
|
-
|
|
45
|
-
const HMailItem = require('../../outbound
|
|
46
|
-
|
|
42
|
+
// Load via outbound/index to avoid circular-dep boot-order issue
|
|
43
|
+
const HMailItem = require('../../outbound').HMailItem
|
|
47
44
|
for (const level of levels) {
|
|
48
|
-
assert.ok(HMailItem.prototype[`log${level.toLowerCase()}`], `
|
|
45
|
+
assert.ok(HMailItem.prototype[`log${level.toLowerCase()}`], `log method for ${level}`)
|
|
49
46
|
}
|
|
50
47
|
})
|
|
51
48
|
|
|
52
49
|
it('set_temp_fail_intervals coverage', () => {
|
|
53
50
|
const config = require('../../outbound/config')
|
|
54
|
-
// Test default configuration
|
|
55
51
|
assert.deepEqual(
|
|
56
52
|
config.cfg.temp_fail_intervals,
|
|
57
53
|
[64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072],
|
|
58
54
|
)
|
|
59
|
-
|
|
55
|
+
|
|
60
56
|
config.cfg.temp_fail_intervals = '10s, 1m*2'
|
|
61
57
|
config.set_temp_fail_intervals()
|
|
62
58
|
assert.deepEqual(config.cfg.temp_fail_intervals, [10, 60, 60])
|
|
63
|
-
|
|
59
|
+
|
|
64
60
|
config.cfg.temp_fail_intervals = '30s, 1m, 5m, 9m, 15m*3, 30m*2, 1h*3, 2h*3, 1d'
|
|
65
61
|
config.set_temp_fail_intervals()
|
|
66
62
|
assert.deepEqual(
|
|
67
63
|
config.cfg.temp_fail_intervals,
|
|
68
64
|
[30, 60, 300, 540, 900, 900, 900, 1800, 1800, 3600, 3600, 3600, 7200, 7200, 7200, 86400],
|
|
69
65
|
)
|
|
70
|
-
|
|
66
|
+
|
|
71
67
|
config.cfg.temp_fail_intervals = 'none'
|
|
72
68
|
config.set_temp_fail_intervals()
|
|
73
69
|
assert.deepEqual(config.cfg.temp_fail_intervals, [])
|
|
74
|
-
|
|
70
|
+
|
|
75
71
|
config.cfg.temp_fail_intervals = '60 min'
|
|
76
72
|
config.set_temp_fail_intervals()
|
|
77
73
|
assert.deepEqual(
|
|
@@ -81,29 +77,26 @@ describe('outbound', () => {
|
|
|
81
77
|
})
|
|
82
78
|
|
|
83
79
|
describe('get_tls_options', () => {
|
|
84
|
-
|
|
80
|
+
let outbound, obtls
|
|
81
|
+
|
|
82
|
+
beforeEach(async () => {
|
|
85
83
|
process.env.HARAKA_TEST_DIR = path.resolve('test')
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
outbound = require('../../outbound')
|
|
85
|
+
obtls = require('../../outbound/tls')
|
|
88
86
|
const tls_socket = require('../../tls_socket')
|
|
89
87
|
|
|
90
|
-
// reset config to load from tests directory
|
|
91
88
|
const testDir = path.resolve('test')
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
89
|
+
outbound.config = outbound.config.module_config(testDir)
|
|
90
|
+
obtls.test_config(tls_socket.config.module_config(testDir), outbound.config)
|
|
91
|
+
await new Promise((resolve) => obtls.init(resolve))
|
|
95
92
|
})
|
|
96
93
|
|
|
97
|
-
afterEach((
|
|
94
|
+
afterEach(() => {
|
|
98
95
|
delete process.env.HARAKA_TEST_DIR
|
|
99
|
-
done()
|
|
100
96
|
})
|
|
101
97
|
|
|
102
98
|
it('gets TLS properties from tls.ini.outbound', () => {
|
|
103
|
-
const tls_config =
|
|
104
|
-
exchange: 'mail.example.com',
|
|
105
|
-
})
|
|
106
|
-
|
|
99
|
+
const tls_config = obtls.get_tls_options({ exchange: 'mail.example.com' })
|
|
107
100
|
assert.deepEqual(tls_config, {
|
|
108
101
|
servername: 'mail.example.com',
|
|
109
102
|
key: fs.readFileSync(path.resolve('test', 'config', 'outbound_tls_key.pem')),
|
|
@@ -122,116 +115,83 @@ describe('outbound', () => {
|
|
|
122
115
|
})
|
|
123
116
|
|
|
124
117
|
describe('build_todo', () => {
|
|
125
|
-
|
|
126
|
-
|
|
118
|
+
let outbound
|
|
119
|
+
|
|
120
|
+
beforeEach(() => {
|
|
121
|
+
outbound = require('../../outbound')
|
|
127
122
|
try {
|
|
128
123
|
fs.unlinkSync('test/queue/multibyte')
|
|
129
124
|
fs.unlinkSync('test/queue/plain')
|
|
130
125
|
} catch (ignore) {}
|
|
131
|
-
done()
|
|
132
126
|
})
|
|
133
127
|
|
|
134
|
-
it('saves a file', () => {
|
|
128
|
+
it('saves a plain queue file', () => {
|
|
135
129
|
const todo = JSON.parse(
|
|
136
130
|
'{"queue_time":1507509981169,"domain":"redacteed.com","rcpt_to":[{"original":"<postmaster@redacteed.com>","original_host":"redacteed.com","host":"redacteed.com","user":"postmaster"}],"mail_from":{"original":"<matt@tnpi.net>","original_host":"tnpi.net","host":"tnpi.net","user":"matt"},"notes":{"authentication_results":["spf=pass smtp.mailfrom=tnpi.net"],"spf_mail_result":"Pass","spf_mail_record":"v=spf1 a mx include:mx.theartfarm.com ?include:forwards._spf.tnpi.net include:lists._spf.tnpi.net -all","attachment_count":0,"attachments":[{"ctype":"application/pdf","filename":"FileWithoutAccent Chars.pdf","extension":".pdf","md5":"6c1d5f5c047cff3f6320b1210970bdf6"}],"attachment_ctypes":["application/pdf","multipart/mixed","text/plain","application/pdf"],"attachment_files":["FileWithoutaccent Chars.pdf"],"attachment_archive_files":[]},"uuid":"1D5483B0-3E00-4280-A961-3AFD2017B4FC.1"}',
|
|
137
131
|
)
|
|
138
132
|
const fd = fs.openSync('test/queue/plain', 'w')
|
|
139
|
-
const ws = new fs.createWriteStream('test/queue/plain', {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
})
|
|
143
|
-
ws.on('close', () => {
|
|
144
|
-
// console.log(arguments);
|
|
145
|
-
assert.ok(1)
|
|
146
|
-
})
|
|
147
|
-
ws.on('error', (e) => {
|
|
148
|
-
console.error(e)
|
|
149
|
-
})
|
|
150
|
-
this.outbound.build_todo(todo, ws, () => {
|
|
133
|
+
const ws = new fs.createWriteStream('test/queue/plain', { fd, flags: constants.WRITE_EXCL })
|
|
134
|
+
ws.on('error', (e) => console.error(e))
|
|
135
|
+
outbound.build_todo(todo, ws, () => {
|
|
151
136
|
ws.write(Buffer.from('This is the message body'))
|
|
152
|
-
fs.fsync(fd, () =>
|
|
153
|
-
ws.close()
|
|
154
|
-
})
|
|
137
|
+
fs.fsync(fd, () => ws.close())
|
|
155
138
|
})
|
|
139
|
+
assert.ok(true)
|
|
156
140
|
})
|
|
157
141
|
|
|
158
|
-
it('saves a file with multibyte chars', () => {
|
|
142
|
+
it('saves a queue file with multibyte chars', () => {
|
|
159
143
|
const todo = JSON.parse(
|
|
160
|
-
'{"queue_time":1507509981169,"domain":"redacteed.com","rcpt_to":[{"original":"<postmaster@redacteed.com>","original_host":"redacteed.com","host":"redacteed.com","user":"postmaster"}],"mail_from":{"original":"<matt@tnpi.net>","original_host":"tnpi.net","host":"tnpi.net","user":"matt"},"notes":{"authentication_results":["spf=pass smtp.mailfrom=tnpi.net"],"spf_mail_result":"Pass","spf_mail_record":"v=spf1 a mx include:mx.theartfarm.com ?include:forwards._spf.tnpi.net include:lists._spf.tnpi.net -all","attachment_count":0,"attachments":[{"ctype":"application/pdf","filename":"
|
|
144
|
+
'{"queue_time":1507509981169,"domain":"redacteed.com","rcpt_to":[{"original":"<postmaster@redacteed.com>","original_host":"redacteed.com","host":"redacteed.com","user":"postmaster"}],"mail_from":{"original":"<matt@tnpi.net>","original_host":"tnpi.net","host":"tnpi.net","user":"matt"},"notes":{"authentication_results":["spf=pass smtp.mailfrom=tnpi.net"],"spf_mail_result":"Pass","spf_mail_record":"v=spf1 a mx include:mx.theartfarm.com ?include:forwards._spf.tnpi.net include:lists._spf.tnpi.net -all","attachment_count":0,"attachments":[{"ctype":"application/pdf","filename":"FileW\\u00eeth\\u00c1ccent Chars.pdf","extension":".pdf","md5":"6c1d5f5c047cff3f6320b1210970bdf6"}],"attachment_ctypes":["application/pdf","multipart/mixed","text/plain","application/pdf"],"attachment_files":["FileW\\u00eeth\\u00c1ccent Chars.pdf"],"attachment_archive_files":[]},"uuid":"1D5483B0-3E00-4280-A961-3AFD2017B4FC.1"}',
|
|
161
145
|
)
|
|
162
146
|
const fd = fs.openSync('test/queue/multibyte', 'w')
|
|
163
|
-
const ws = new fs.WriteStream('test/queue/multibyte', {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
})
|
|
167
|
-
ws.on('close', () => {
|
|
168
|
-
assert.ok(1)
|
|
169
|
-
})
|
|
170
|
-
ws.on('error', (e) => {
|
|
171
|
-
console.error(e)
|
|
172
|
-
})
|
|
173
|
-
this.outbound.build_todo(todo, ws, () => {
|
|
147
|
+
const ws = new fs.WriteStream('test/queue/multibyte', { fd, flags: constants.WRITE_EXCL })
|
|
148
|
+
ws.on('error', (e) => console.error(e))
|
|
149
|
+
outbound.build_todo(todo, ws, () => {
|
|
174
150
|
ws.write(Buffer.from('This is the message body'))
|
|
175
|
-
fs.fsync(fd, () =>
|
|
176
|
-
ws.close()
|
|
177
|
-
})
|
|
151
|
+
fs.fsync(fd, () => ws.close())
|
|
178
152
|
})
|
|
153
|
+
assert.ok(true)
|
|
179
154
|
})
|
|
180
155
|
})
|
|
181
156
|
|
|
182
157
|
describe('timer_queue', () => {
|
|
183
|
-
|
|
158
|
+
let outbound, ob_timer_queue
|
|
159
|
+
|
|
160
|
+
beforeEach(() => {
|
|
184
161
|
process.env.HARAKA_TEST_DIR = path.resolve('test')
|
|
185
|
-
|
|
162
|
+
outbound = require('../../outbound')
|
|
186
163
|
const TimerQueue = require('../../outbound/timer_queue')
|
|
187
|
-
|
|
188
|
-
done()
|
|
164
|
+
ob_timer_queue = new TimerQueue(500)
|
|
189
165
|
})
|
|
190
166
|
|
|
191
|
-
afterEach((
|
|
167
|
+
afterEach(() => {
|
|
192
168
|
delete process.env.HARAKA_TEST_DIR
|
|
193
|
-
|
|
194
|
-
done()
|
|
169
|
+
ob_timer_queue.shutdown()
|
|
195
170
|
})
|
|
196
171
|
|
|
197
172
|
it('has initial length of 0', () => {
|
|
198
|
-
assert.equal(
|
|
173
|
+
assert.equal(ob_timer_queue.length(), 0)
|
|
199
174
|
})
|
|
200
175
|
|
|
201
176
|
it('can add items', () => {
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
assert.equal(this.ob_timer_queue.length(), 2)
|
|
177
|
+
ob_timer_queue.add('1', 1000)
|
|
178
|
+
ob_timer_queue.add('2', 2000)
|
|
179
|
+
assert.equal(ob_timer_queue.length(), 2)
|
|
206
180
|
})
|
|
207
181
|
|
|
208
182
|
it('can drain items', () => {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
assert.equal(tq_length, 2)
|
|
215
|
-
|
|
216
|
-
this.ob_timer_queue.drain()
|
|
217
|
-
tq_length = this.ob_timer_queue.length()
|
|
218
|
-
|
|
219
|
-
assert.equal(tq_length, 0)
|
|
183
|
+
ob_timer_queue.add('1', 1000)
|
|
184
|
+
ob_timer_queue.add('2', 2000)
|
|
185
|
+
ob_timer_queue.drain()
|
|
186
|
+
assert.equal(ob_timer_queue.length(), 0)
|
|
220
187
|
})
|
|
221
188
|
|
|
222
189
|
it('can discard items by id', () => {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
assert.equal(tq_length, 2)
|
|
229
|
-
|
|
230
|
-
this.ob_timer_queue.discard('2')
|
|
231
|
-
tq_length = this.ob_timer_queue.length()
|
|
232
|
-
|
|
233
|
-
assert.equal(tq_length, 1)
|
|
234
|
-
assert.equal(this.ob_timer_queue.queue[0].id, '1')
|
|
190
|
+
ob_timer_queue.add('1', 1000)
|
|
191
|
+
ob_timer_queue.add('2', 2000)
|
|
192
|
+
ob_timer_queue.discard('2')
|
|
193
|
+
assert.equal(ob_timer_queue.length(), 1)
|
|
194
|
+
assert.equal(ob_timer_queue.queue[0].id, '1')
|
|
235
195
|
})
|
|
236
196
|
})
|
|
237
197
|
})
|