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.
- package/.prettierignore +2 -0
- package/CONTRIBUTORS.md +23 -1
- package/Changes.md +52 -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 +26 -29
- package/plugins/prevent_credential_leaks.js +2 -2
- package/plugins/process_title.js +1 -1
- package/plugins/queue/smtp_forward.js +5 -5
- 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 +8 -2
- package/server.js +15 -10
- package/smtp_client.js +10 -15
- package/test/config/tls/haraka.local.pem +47 -47
- package/test/connection.js +286 -147
- package/test/endpoint.js +5 -4
- package/test/fixtures/line_socket.js +1 -0
- package/test/fixtures/util_hmailitem.js +1 -1
- package/test/host_pool.js +57 -31
- package/test/logger.js +75 -135
- package/test/outbound/bounce_net_errors.js +132 -0
- package/test/outbound/bounce_rfc3464.js +226 -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/auth/auth_base.js +39 -44
- package/test/plugins/auth/auth_vpopmaild.js +8 -9
- package/test/plugins/queue/smtp_forward.js +953 -183
- package/test/plugins/rcpt_to.host_list_base.js +58 -93
- package/test/plugins/rcpt_to.in_host_list.js +126 -175
- package/test/plugins/record_envelope_addresses.js +93 -0
- package/test/plugins/status.js +10 -10
- package/test/plugins/tls.js +11 -21
- package/test/plugins/xclient.js +102 -0
- package/test/plugins.js +10 -13
- package/test/rfc1869.js +71 -48
- package/test/server.js +281 -436
- package/test/smtp_client.js +1194 -220
- package/test/tls_socket.js +74 -243
- package/test/transaction.js +486 -201
- package/tls_socket.js +19 -23
- 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
|
@@ -1,157 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
// Testing bounce email contents related to errors occuring during SMTP dialog
|
|
4
|
-
|
|
5
|
-
// About running the tests:
|
|
6
|
-
// - Making a folder for queuing files
|
|
7
|
-
// - Creating a HMailItem instance using fixtures/util_hmailitem
|
|
8
|
-
// - Talk some STMP in the playbook
|
|
9
|
-
// - Test the outcome by replacing trigger functions with our testing code (outbound.send_email, HMailItem.temp_fail, ...)
|
|
10
|
-
|
|
11
|
-
const assert = require('node:assert')
|
|
12
|
-
const dns = require('node:dns')
|
|
13
|
-
const fs = require('node:fs')
|
|
14
|
-
const path = require('node:path')
|
|
15
|
-
|
|
16
|
-
const constants = require('haraka-constants')
|
|
17
|
-
const util_hmailitem = require('./fixtures/util_hmailitem')
|
|
18
|
-
const TODOItem = require('../outbound/todo')
|
|
19
|
-
const HMailItem = require('../outbound/hmail')
|
|
20
|
-
const outbound = require('../outbound')
|
|
21
|
-
|
|
22
|
-
const outbound_context = {
|
|
23
|
-
TODOItem,
|
|
24
|
-
exports: outbound,
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const queue_dir = path.resolve(__dirname, 'test-queue')
|
|
28
|
-
|
|
29
|
-
describe('outbound_bounce_net_errors', () => {
|
|
30
|
-
beforeEach((done) => {
|
|
31
|
-
fs.exists(queue_dir, (exists) => {
|
|
32
|
-
if (exists) {
|
|
33
|
-
done()
|
|
34
|
-
} else {
|
|
35
|
-
fs.mkdir(queue_dir, done)
|
|
36
|
-
}
|
|
37
|
-
})
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
afterEach((done) => {
|
|
41
|
-
fs.exists(queue_dir, (exists) => {
|
|
42
|
-
if (exists) {
|
|
43
|
-
for (const file of fs.readdirSync(queue_dir)) {
|
|
44
|
-
const curPath = path.resolve(queue_dir, file)
|
|
45
|
-
if (fs.lstatSync(curPath).isDirectory()) {
|
|
46
|
-
console.error(`did not expect an sub folder here ("${curPath}")! cancel`)
|
|
47
|
-
}
|
|
48
|
-
fs.unlinkSync(curPath)
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
done()
|
|
52
|
-
})
|
|
53
|
-
})
|
|
54
|
-
|
|
55
|
-
it('test get-mx-deny triggers bounce(...)', (done) => {
|
|
56
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
57
|
-
const orig_bounce = HMailItem.prototype.bounce
|
|
58
|
-
HMailItem.prototype.bounce = function (err, opts) {
|
|
59
|
-
assert.ok(true, 'get_mx=DENY: bounce function called')
|
|
60
|
-
/* dsn_code: 550,
|
|
61
|
-
dsn_status: '5.1.2',
|
|
62
|
-
dsn_action: 'failed' */
|
|
63
|
-
assert.equal('5.1.2', this.todo.rcpt_to[0].dsn_status, 'get_mx=DENY dsn status = 5.1.2')
|
|
64
|
-
done()
|
|
65
|
-
}
|
|
66
|
-
mock_hmail.domain = mock_hmail.todo.domain
|
|
67
|
-
HMailItem.prototype.get_mx_respond.apply(mock_hmail, [constants.deny, {}])
|
|
68
|
-
HMailItem.prototype.bounce = orig_bounce
|
|
69
|
-
})
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
it('test get-mx-denysoft triggers temp_fail(...)', (done) => {
|
|
73
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
74
|
-
const orig_temp_fail = HMailItem.prototype.temp_fail
|
|
75
|
-
HMailItem.prototype.temp_fail = function (err, opts) {
|
|
76
|
-
assert.ok(true, 'get_mx-DENYSOFT: temp_fail function called')
|
|
77
|
-
/*dsn_code: 450,
|
|
78
|
-
dsn_status: '4.1.2',
|
|
79
|
-
dsn_action: 'delayed' */
|
|
80
|
-
assert.equal('4.1.2', this.todo.rcpt_to[0].dsn_status, 'get_mx=DENYSOFT dsn status = 4.1.2')
|
|
81
|
-
done()
|
|
82
|
-
}
|
|
83
|
-
mock_hmail.domain = mock_hmail.todo.domain
|
|
84
|
-
HMailItem.prototype.get_mx_respond.apply(mock_hmail, [constants.denysoft, {}])
|
|
85
|
-
HMailItem.prototype.temp_fail = orig_temp_fail
|
|
86
|
-
})
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
it('test found_mx({code:dns.NXDOMAIN}) triggers bounce(...)', (done) => {
|
|
90
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
91
|
-
const orig_bounce = HMailItem.prototype.bounce
|
|
92
|
-
HMailItem.prototype.bounce = function (err, opts) {
|
|
93
|
-
assert.ok(true, 'get_mx_error({code: dns.NXDOMAIN}): bounce function called')
|
|
94
|
-
assert.equal(
|
|
95
|
-
'5.1.2',
|
|
96
|
-
this.todo.rcpt_to[0].dsn_status,
|
|
97
|
-
'get_mx_error({code: dns.NXDOMAIN}: dsn status = 5.1.2',
|
|
98
|
-
)
|
|
99
|
-
done()
|
|
100
|
-
}
|
|
101
|
-
HMailItem.prototype.get_mx_error.apply(mock_hmail, [{ code: dns.NXDOMAIN }])
|
|
102
|
-
HMailItem.prototype.bounce = orig_bounce
|
|
103
|
-
})
|
|
104
|
-
})
|
|
105
|
-
|
|
106
|
-
it("test get_mx_error({code:'SOME-OTHER-ERR'}) triggers temp_fail(...)", (done) => {
|
|
107
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
108
|
-
const orig_temp_fail = HMailItem.prototype.temp_fail
|
|
109
|
-
HMailItem.prototype.temp_fail = function (err, opts) {
|
|
110
|
-
assert.ok(true, 'get_mx_error({code: "SOME-OTHER-ERR"}): temp_fail function called')
|
|
111
|
-
assert.equal(
|
|
112
|
-
'4.1.0',
|
|
113
|
-
this.todo.rcpt_to[0].dsn_status,
|
|
114
|
-
'get_mx_error({code: "SOME-OTHER-ERR"}: dsn status = 4.1.0',
|
|
115
|
-
)
|
|
116
|
-
done()
|
|
117
|
-
}
|
|
118
|
-
HMailItem.prototype.get_mx_error.apply(mock_hmail, [{ code: 'SOME-OTHER-ERR' }, {}])
|
|
119
|
-
HMailItem.prototype.temp_fail = orig_temp_fail
|
|
120
|
-
})
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it("test found_mx(null, [{priority:0,exchange:''}]) triggers bounce(...)", (done) => {
|
|
124
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
125
|
-
const orig_bounce = HMailItem.prototype.bounce
|
|
126
|
-
HMailItem.prototype.bounce = function (err, opts) {
|
|
127
|
-
assert.ok(true, 'found_mx(null, [{priority:0,exchange:""}]): bounce function called')
|
|
128
|
-
assert.equal(
|
|
129
|
-
'5.1.2',
|
|
130
|
-
this.todo.rcpt_to[0].dsn_status,
|
|
131
|
-
'found_mx(null, [{priority:0,exchange:""}]): dsn status = 5.1.2',
|
|
132
|
-
)
|
|
133
|
-
done()
|
|
134
|
-
}
|
|
135
|
-
HMailItem.prototype.found_mx.apply(mock_hmail, [[{ priority: 0, exchange: '' }]])
|
|
136
|
-
HMailItem.prototype.bounce = orig_bounce
|
|
137
|
-
})
|
|
138
|
-
})
|
|
139
|
-
|
|
140
|
-
it('test try_deliver while hmail.mxlist=[] triggers bounce(...)', (done) => {
|
|
141
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
142
|
-
mock_hmail.mxlist = []
|
|
143
|
-
const orig_temp_fail = HMailItem.prototype.temp_fail
|
|
144
|
-
HMailItem.prototype.temp_fail = function (err, opts) {
|
|
145
|
-
assert.ok(true, 'try_deliver while hmail.mxlist=[]: temp_fail function called')
|
|
146
|
-
assert.equal(
|
|
147
|
-
'5.1.2',
|
|
148
|
-
this.todo.rcpt_to[0].dsn_status,
|
|
149
|
-
'try_deliver while hmail.mxlist=[]: dsn status = 5.1.2',
|
|
150
|
-
)
|
|
151
|
-
done()
|
|
152
|
-
}
|
|
153
|
-
HMailItem.prototype.try_deliver.apply(mock_hmail, [])
|
|
154
|
-
HMailItem.prototype.temp_fail = orig_temp_fail
|
|
155
|
-
})
|
|
156
|
-
})
|
|
157
|
-
})
|
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
|
-
// Testing bounce email contents related to errors occuring during SMTP dialog
|
|
4
|
-
|
|
5
|
-
// About running the tests:
|
|
6
|
-
// - Making a folder for queuing files
|
|
7
|
-
// - Creating a HMailItem instance using fixtures/util_hmailitem
|
|
8
|
-
// - Talk some SMTP in the playbook
|
|
9
|
-
// - Test the outcome by replacing trigger functions with our testing code (outbound.send_email, HMailItem.temp_fail, ...)
|
|
10
|
-
// At one point, the mocked remote SMTP says "5XX" or "4XX" and we test that
|
|
11
|
-
// * outbound.send_email is called with a RFC3464 bounce message
|
|
12
|
-
// * or, in case of 4XX: that temp_fail is called and dsn vars are available)
|
|
13
|
-
|
|
14
|
-
const assert = require('node:assert')
|
|
15
|
-
const fs = require('node:fs')
|
|
16
|
-
const path = require('node:path')
|
|
17
|
-
|
|
18
|
-
const util_hmailitem = require('./fixtures/util_hmailitem')
|
|
19
|
-
const TODOItem = require('../outbound/todo')
|
|
20
|
-
const HMailItem = require('../outbound/hmail')
|
|
21
|
-
const obc = require('../outbound/config')
|
|
22
|
-
const outbound = require('../outbound')
|
|
23
|
-
const mock_sock = require('./fixtures/line_socket')
|
|
24
|
-
|
|
25
|
-
obc.cfg.pool_concurrency_max = 0
|
|
26
|
-
|
|
27
|
-
const outbound_context = {
|
|
28
|
-
TODOItem,
|
|
29
|
-
exports: outbound,
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const queue_dir = path.resolve(__dirname, 'test-queue')
|
|
33
|
-
|
|
34
|
-
describe('outbound_bounce_rfc3464', () => {
|
|
35
|
-
beforeEach((done) => {
|
|
36
|
-
fs.exists(queue_dir, (exists) => {
|
|
37
|
-
if (exists) return done()
|
|
38
|
-
|
|
39
|
-
fs.mkdir(queue_dir, (err) => {
|
|
40
|
-
if (err) return done(err)
|
|
41
|
-
done()
|
|
42
|
-
})
|
|
43
|
-
})
|
|
44
|
-
})
|
|
45
|
-
|
|
46
|
-
afterEach((done) => {
|
|
47
|
-
fs.exists(queue_dir, (exists) => {
|
|
48
|
-
if (!exists) return done()
|
|
49
|
-
|
|
50
|
-
const files = fs.readdirSync(queue_dir)
|
|
51
|
-
files.forEach((file, index) => {
|
|
52
|
-
const curPath = path.resolve(queue_dir, file)
|
|
53
|
-
if (fs.lstatSync(curPath).isDirectory()) {
|
|
54
|
-
// recurse
|
|
55
|
-
return done(new Error(`did not expect an sub folder here ("${curPath}")! cancel`))
|
|
56
|
-
}
|
|
57
|
-
})
|
|
58
|
-
files.forEach((file, index) => {
|
|
59
|
-
const curPath = path.resolve(queue_dir, file)
|
|
60
|
-
fs.unlinkSync(curPath)
|
|
61
|
-
})
|
|
62
|
-
done()
|
|
63
|
-
})
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
it('test MAIL FROM responded with 500 5.0.0 triggers send_email() containing bounce msg with codes and message', (done) => {
|
|
67
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
68
|
-
const mock_socket = mock_sock.connect('testhost', 'testport')
|
|
69
|
-
mock_socket.writable = true
|
|
70
|
-
|
|
71
|
-
const orig_send_email = outbound_context.exports.send_email
|
|
72
|
-
|
|
73
|
-
outbound_context.exports.send_email = (from, to, contents, cb, opts) => {
|
|
74
|
-
assert.ok(true, 'outbound.send_email called')
|
|
75
|
-
assert.ok(contents.match(/^Content-type: message\/delivery-status/m), 'its a bounce report')
|
|
76
|
-
assert.ok(
|
|
77
|
-
contents.match(/^Final-Recipient: rfc822;recipient@domain/m),
|
|
78
|
-
'bounce report contains final recipient',
|
|
79
|
-
)
|
|
80
|
-
assert.ok(contents.match(/^Action: failed/m), 'DATA-5XX: bounce report contains action field')
|
|
81
|
-
assert.ok(
|
|
82
|
-
contents.match(/^Status: 5\.0\.0/m),
|
|
83
|
-
'bounce report contains status field with ext. smtp code',
|
|
84
|
-
)
|
|
85
|
-
assert.ok(
|
|
86
|
-
contents.match(/Absolutely not acceptable\. Basic Test Only\./),
|
|
87
|
-
'original upstream message available',
|
|
88
|
-
)
|
|
89
|
-
outbound_context.exports.send_email = orig_send_email
|
|
90
|
-
done()
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// The playbook
|
|
94
|
-
// from remote: This line is to be sent (from an mocked remote SMTP) to haraka outbound. This is done in this test.
|
|
95
|
-
// from haraka: Expected answer from haraka-outbound to the mocked remote SMTP.
|
|
96
|
-
// 'test' can hold a function(line) returning true for success, or a string tested for equality
|
|
97
|
-
const testPlaybook = [
|
|
98
|
-
// Haraka connects, we say first
|
|
99
|
-
{ from: 'remote', line: '220 testing-smtp' },
|
|
100
|
-
|
|
101
|
-
{
|
|
102
|
-
from: 'haraka',
|
|
103
|
-
test: (line) => line.match(/^EHLO /),
|
|
104
|
-
description: 'Haraka should say EHLO',
|
|
105
|
-
},
|
|
106
|
-
{ from: 'remote', line: '220-testing-smtp' },
|
|
107
|
-
{ from: 'remote', line: '220 8BITMIME' },
|
|
108
|
-
|
|
109
|
-
{ from: 'haraka', test: 'MAIL FROM:<sender@domain>' },
|
|
110
|
-
{
|
|
111
|
-
from: 'remote',
|
|
112
|
-
line: '500 5.0.0 Absolutely not acceptable. Basic Test Only.',
|
|
113
|
-
},
|
|
114
|
-
|
|
115
|
-
{ from: 'haraka', test: 'QUIT', end_test: true }, // this will trigger calling the callback
|
|
116
|
-
]
|
|
117
|
-
|
|
118
|
-
util_hmailitem.playTestSmtpConversation(mock_hmail, mock_socket, done, testPlaybook, () => {})
|
|
119
|
-
})
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('test that early response of 3XX triggers temp_fail', (done) => {
|
|
123
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
124
|
-
const mock_socket = mock_sock.connect('testhost', 'testport')
|
|
125
|
-
mock_socket.writable = true
|
|
126
|
-
|
|
127
|
-
const orig_temp_fail = HMailItem.prototype.temp_fail
|
|
128
|
-
HMailItem.prototype.temp_fail = function (err, opts) {
|
|
129
|
-
assert.ok(true, 'early-3XX: outbound.temp_fail called')
|
|
130
|
-
assert.equal('3.0.0', this.todo.rcpt_to[0].dsn_status, 'early-3XX: dsn status = 3.0.0')
|
|
131
|
-
assert.equal('delayed', this.todo.rcpt_to[0].dsn_action, 'early-3XX: dsn action = delayed')
|
|
132
|
-
assert.ok(
|
|
133
|
-
this.todo.rcpt_to[0].dsn_smtp_response.match(/No time for you right now/),
|
|
134
|
-
'early-3XX: original upstream message available',
|
|
135
|
-
)
|
|
136
|
-
HMailItem.prototype.temp_fail = orig_temp_fail
|
|
137
|
-
done()
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const testPlaybook = [
|
|
141
|
-
{ from: 'remote', line: '220 testing-smtp' },
|
|
142
|
-
|
|
143
|
-
{
|
|
144
|
-
from: 'haraka',
|
|
145
|
-
test: (line) => line.match(/^EHLO /),
|
|
146
|
-
description: 'Haraka should say EHLO',
|
|
147
|
-
},
|
|
148
|
-
{ from: 'remote', line: '220-testing-smtp' },
|
|
149
|
-
{ from: 'remote', line: '220 8BITMIME' },
|
|
150
|
-
|
|
151
|
-
{ from: 'haraka', test: 'MAIL FROM:<sender@domain>' },
|
|
152
|
-
{
|
|
153
|
-
from: 'remote',
|
|
154
|
-
line: '300 3.0.0 No time for you right now',
|
|
155
|
-
},
|
|
156
|
-
|
|
157
|
-
{ from: 'haraka', test: 'QUIT', end_test: true }, // this will trigger calling the callback
|
|
158
|
-
]
|
|
159
|
-
|
|
160
|
-
util_hmailitem.playTestSmtpConversation(mock_hmail, mock_socket, done, testPlaybook, () => {})
|
|
161
|
-
})
|
|
162
|
-
})
|
|
163
|
-
|
|
164
|
-
it('test that response of 4XX for RCPT-TO triggers temp_fail', (done) => {
|
|
165
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
166
|
-
const mock_socket = mock_sock.connect('testhost', 'testport')
|
|
167
|
-
mock_socket.writable = true
|
|
168
|
-
|
|
169
|
-
const orig_temp_fail = HMailItem.prototype.temp_fail
|
|
170
|
-
HMailItem.prototype.temp_fail = function (err, opts) {
|
|
171
|
-
assert.ok(true, 'RCPT-TO-4XX: outbound.temp_fail called')
|
|
172
|
-
assert.equal('4.0.0', this.todo.rcpt_to[0].dsn_status, 'RCPT-TO-4XX: dsn status = 4.0.0')
|
|
173
|
-
assert.equal('delayed', this.todo.rcpt_to[0].dsn_action, 'RCPT-TO-4XX: dsn action = delayed')
|
|
174
|
-
assert.ok(
|
|
175
|
-
this.todo.rcpt_to[0].dsn_smtp_response.match(/Currently not available\. Try again later\./),
|
|
176
|
-
'RCPT-TO-4XX: original upstream message available',
|
|
177
|
-
)
|
|
178
|
-
HMailItem.prototype.temp_fail = orig_temp_fail
|
|
179
|
-
done()
|
|
180
|
-
}
|
|
181
|
-
const testPlaybook = [
|
|
182
|
-
{ from: 'remote', line: '220 testing-smtp' },
|
|
183
|
-
|
|
184
|
-
{
|
|
185
|
-
from: 'haraka',
|
|
186
|
-
test: (line) => line.match(/^EHLO /),
|
|
187
|
-
description: 'Haraka should say EHLO',
|
|
188
|
-
},
|
|
189
|
-
{ from: 'remote', line: '220-testing-smtp' },
|
|
190
|
-
{ from: 'remote', line: '220 8BITMIME' },
|
|
191
|
-
|
|
192
|
-
{ from: 'haraka', test: 'MAIL FROM:<sender@domain>' },
|
|
193
|
-
{ from: 'remote', line: '250 2.1.0 Ok' },
|
|
194
|
-
|
|
195
|
-
{ from: 'haraka', test: 'RCPT TO:<recipient@domain>' },
|
|
196
|
-
{
|
|
197
|
-
from: 'remote',
|
|
198
|
-
line: '400 4.0.0 Currently not available. Try again later.',
|
|
199
|
-
},
|
|
200
|
-
|
|
201
|
-
{ from: 'haraka', test: 'QUIT', end_test: true }, // this will trigger calling the callback
|
|
202
|
-
]
|
|
203
|
-
|
|
204
|
-
util_hmailitem.playTestSmtpConversation(mock_hmail, mock_socket, done, testPlaybook, () => {})
|
|
205
|
-
})
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
it('test that response of 4XX for DATA triggers temp_fail', (done) => {
|
|
209
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
210
|
-
const mock_socket = mock_sock.connect('testhost', 'testport')
|
|
211
|
-
mock_socket.writable = true
|
|
212
|
-
|
|
213
|
-
const orig_temp_fail = HMailItem.prototype.temp_fail
|
|
214
|
-
HMailItem.prototype.temp_fail = function (err, opts) {
|
|
215
|
-
assert.ok(true, 'DATA-4XX: outbound.temp_fail called')
|
|
216
|
-
assert.equal('4.6.0', this.todo.rcpt_to[0].dsn_status, 'DATA-4XX: dsn status = 4.6.0')
|
|
217
|
-
assert.equal('delayed', this.todo.rcpt_to[0].dsn_action, 'DATA-4XX: dsn action = delayed')
|
|
218
|
-
assert.ok(
|
|
219
|
-
this.todo.rcpt_to[0].dsn_smtp_response.match(/Currently I do not like ascii art cats\./),
|
|
220
|
-
'DATA-4XX: original upstream message available',
|
|
221
|
-
)
|
|
222
|
-
HMailItem.prototype.temp_fail = orig_temp_fail
|
|
223
|
-
done()
|
|
224
|
-
}
|
|
225
|
-
const testPlaybook = [
|
|
226
|
-
{ from: 'remote', line: '220 testing-smtp' },
|
|
227
|
-
|
|
228
|
-
{
|
|
229
|
-
from: 'haraka',
|
|
230
|
-
test: (line) => line.match(/^EHLO /),
|
|
231
|
-
description: 'Haraka should say EHLO',
|
|
232
|
-
},
|
|
233
|
-
{ from: 'remote', line: '220-testing-smtp' },
|
|
234
|
-
{ from: 'remote', line: '220 8BITMIME' },
|
|
235
|
-
|
|
236
|
-
{ from: 'haraka', test: 'MAIL FROM:<sender@domain>' },
|
|
237
|
-
{ from: 'remote', line: '250 2.1.0 Ok' },
|
|
238
|
-
|
|
239
|
-
{ from: 'haraka', test: 'RCPT TO:<recipient@domain>' },
|
|
240
|
-
{ from: 'remote', line: '250 2.1.5 Ok' },
|
|
241
|
-
|
|
242
|
-
{ from: 'haraka', test: 'DATA' },
|
|
243
|
-
// haraka will send us more lines
|
|
244
|
-
{
|
|
245
|
-
from: 'remote',
|
|
246
|
-
line: '450 4.6.0 Currently I do not like ascii art cats.',
|
|
247
|
-
},
|
|
248
|
-
|
|
249
|
-
{ from: 'haraka', test: 'QUIT', end_test: true }, // this will trigger calling the callback
|
|
250
|
-
]
|
|
251
|
-
|
|
252
|
-
util_hmailitem.playTestSmtpConversation(mock_hmail, mock_socket, done, testPlaybook, () => {})
|
|
253
|
-
})
|
|
254
|
-
})
|
|
255
|
-
|
|
256
|
-
it('test that response of 5XX for RCPT-TO triggers send_email() containing bounce msg with codes and message', (done) => {
|
|
257
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
258
|
-
const mock_socket = mock_sock.connect('testhost', 'testport')
|
|
259
|
-
mock_socket.writable = true
|
|
260
|
-
|
|
261
|
-
const orig_send_email = outbound_context.exports.send_email
|
|
262
|
-
outbound_context.exports.send_email = (from, to, contents, cb, opts) => {
|
|
263
|
-
assert.ok(true, 'RCPT-TO-5XX: outbound.send_email called')
|
|
264
|
-
assert.ok(
|
|
265
|
-
contents.match(/^Content-type: message\/delivery-status/m),
|
|
266
|
-
'RCPT-TO-5XX: its a bounce report',
|
|
267
|
-
)
|
|
268
|
-
assert.ok(
|
|
269
|
-
contents.match(/^Final-Recipient: rfc822;recipient@domain/m),
|
|
270
|
-
'RCPT-TO-5XX: bounce report contains final recipient',
|
|
271
|
-
)
|
|
272
|
-
assert.ok(contents.match(/^Action: failed/m), 'DATA-5XX: bounce report contains action field')
|
|
273
|
-
assert.ok(
|
|
274
|
-
contents.match(/^Status: 5\.1\.1/m),
|
|
275
|
-
'RCPT-TO-5XX: bounce report contains status field with our ext. smtp code',
|
|
276
|
-
)
|
|
277
|
-
assert.ok(
|
|
278
|
-
contents.match(/Not available and will not come back/),
|
|
279
|
-
'RCPT-TO-5XX: original upstream message available',
|
|
280
|
-
)
|
|
281
|
-
outbound_context.exports.send_email = orig_send_email
|
|
282
|
-
done()
|
|
283
|
-
}
|
|
284
|
-
const testPlaybook = [
|
|
285
|
-
{ from: 'remote', line: '220 testing-smtp' },
|
|
286
|
-
|
|
287
|
-
{
|
|
288
|
-
from: 'haraka',
|
|
289
|
-
test: (line) => line.match(/^EHLO /),
|
|
290
|
-
description: 'Haraka should say EHLO',
|
|
291
|
-
},
|
|
292
|
-
{ from: 'remote', line: '220-testing-smtp' },
|
|
293
|
-
{ from: 'remote', line: '220 8BITMIME' },
|
|
294
|
-
|
|
295
|
-
{ from: 'haraka', test: 'MAIL FROM:<sender@domain>' },
|
|
296
|
-
{ from: 'remote', line: '250 2.1.0 Ok' },
|
|
297
|
-
|
|
298
|
-
{ from: 'haraka', test: 'RCPT TO:<recipient@domain>' },
|
|
299
|
-
{
|
|
300
|
-
from: 'remote',
|
|
301
|
-
line: '550 5.1.1 Not available and will not come back',
|
|
302
|
-
},
|
|
303
|
-
|
|
304
|
-
{ from: 'haraka', test: 'QUIT', end_test: true }, // this will trigger calling the callback
|
|
305
|
-
]
|
|
306
|
-
|
|
307
|
-
util_hmailitem.playTestSmtpConversation(mock_hmail, mock_socket, done, testPlaybook, () => {})
|
|
308
|
-
})
|
|
309
|
-
})
|
|
310
|
-
|
|
311
|
-
it('test that response of 5XX for DATA triggers send_email() containing bounce msg with codes and message', (done) => {
|
|
312
|
-
util_hmailitem.newMockHMailItem(outbound_context, done, {}, (mock_hmail) => {
|
|
313
|
-
const mock_socket = mock_sock.connect('testhost', 'testport')
|
|
314
|
-
mock_socket.writable = true
|
|
315
|
-
|
|
316
|
-
const orig_send_email = outbound_context.exports.send_email
|
|
317
|
-
outbound_context.exports.send_email = (from, to, contents, cb, opts) => {
|
|
318
|
-
assert.ok(true, 'DATA-5XX: outbound.send_email called')
|
|
319
|
-
assert.ok(contents.match(/^Content-type: message\/delivery-status/m), 'DATA-5XX: its a bounce report')
|
|
320
|
-
assert.ok(
|
|
321
|
-
contents.match(/^Final-Recipient: rfc822;recipient@domain/m),
|
|
322
|
-
'DATA-5XX: bounce report contains final recipient',
|
|
323
|
-
)
|
|
324
|
-
assert.ok(contents.match(/^Action: failed/m), 'DATA-5XX: bounce report contains action field')
|
|
325
|
-
assert.ok(
|
|
326
|
-
contents.match(/^Status: 5\.6\.0/m),
|
|
327
|
-
'DATA-5XX: bounce report contains status field with our ext. smtp code',
|
|
328
|
-
)
|
|
329
|
-
assert.ok(
|
|
330
|
-
contents.match(/I never did and will like ascii art cats/),
|
|
331
|
-
'DATA-5XX: original upstream message available',
|
|
332
|
-
)
|
|
333
|
-
outbound_context.exports.send_email = orig_send_email
|
|
334
|
-
done()
|
|
335
|
-
}
|
|
336
|
-
const testPlaybook = [
|
|
337
|
-
{ from: 'remote', line: '220 testing-smtp' },
|
|
338
|
-
|
|
339
|
-
{
|
|
340
|
-
from: 'haraka',
|
|
341
|
-
test: (line) => line.match(/^EHLO /),
|
|
342
|
-
description: 'Haraka should say EHLO',
|
|
343
|
-
},
|
|
344
|
-
{ from: 'remote', line: '220-testing-smtp' },
|
|
345
|
-
{ from: 'remote', line: '220 8BITMIME' },
|
|
346
|
-
|
|
347
|
-
{ from: 'haraka', test: 'MAIL FROM:<sender@domain>' },
|
|
348
|
-
{ from: 'remote', line: '250 2.1.0 Ok' },
|
|
349
|
-
|
|
350
|
-
{ from: 'haraka', test: 'RCPT TO:<recipient@domain>' },
|
|
351
|
-
{ from: 'remote', line: '250 2.1.5 Ok' },
|
|
352
|
-
|
|
353
|
-
{ from: 'haraka', test: 'DATA' },
|
|
354
|
-
// haraka will send us more lines
|
|
355
|
-
{
|
|
356
|
-
from: 'remote',
|
|
357
|
-
line: '550 5.6.0 I never did and will like ascii art cats.',
|
|
358
|
-
},
|
|
359
|
-
|
|
360
|
-
{ from: 'haraka', test: 'QUIT', end_test: true }, // this will trigger calling the callback
|
|
361
|
-
]
|
|
362
|
-
|
|
363
|
-
util_hmailitem.playTestSmtpConversation(mock_hmail, mock_socket, done, testPlaybook, () => {})
|
|
364
|
-
})
|
|
365
|
-
})
|
|
366
|
-
})
|