Haraka 3.1.1 → 3.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierignore +4 -0
- package/CONTRIBUTORS.md +5 -5
- package/Changes.md +62 -50
- package/Plugins.md +3 -1
- package/README.md +1 -1
- package/bin/haraka +475 -479
- package/config/outbound.ini +3 -0
- package/connection.js +1072 -1108
- package/docs/Connection.md +29 -30
- package/docs/CoreConfig.md +38 -39
- package/docs/CustomReturnCodes.md +0 -1
- package/docs/HAProxy.md +2 -2
- package/docs/Header.md +1 -1
- package/docs/Logging.md +29 -5
- package/docs/Outbound.md +93 -78
- package/docs/Plugins.md +103 -108
- package/docs/Transaction.md +49 -51
- package/docs/Tutorial.md +127 -143
- package/docs/deprecated/access.md +0 -1
- package/docs/deprecated/backscatterer.md +2 -3
- package/docs/deprecated/connect.rdns_access.md +18 -27
- package/docs/deprecated/data.headers.md +0 -1
- package/docs/deprecated/data.nomsgid.md +1 -2
- package/docs/deprecated/data.noreceived.md +1 -2
- package/docs/deprecated/data.rfc5322_header_checks.md +1 -2
- package/docs/deprecated/dkim_sign.md +13 -17
- package/docs/deprecated/dkim_verify.md +9 -17
- package/docs/deprecated/dnsbl.md +36 -38
- package/docs/deprecated/dnswl.md +41 -43
- package/docs/deprecated/lookup_rdns.strict.md +21 -34
- package/docs/deprecated/mail_from.access.md +17 -25
- package/docs/deprecated/mail_from.blocklist.md +9 -12
- package/docs/deprecated/mail_from.nobounces.md +1 -2
- package/docs/deprecated/rcpt_to.access.md +20 -27
- package/docs/deprecated/rcpt_to.blocklist.md +10 -13
- package/docs/deprecated/rcpt_to.routes.md +0 -1
- package/docs/deprecated/rdns.regexp.md +13 -15
- package/docs/plugins/aliases.md +89 -89
- package/docs/plugins/auth/auth_bridge.md +5 -7
- package/docs/plugins/auth/auth_ldap.md +11 -14
- package/docs/plugins/auth/auth_proxy.md +10 -12
- package/docs/plugins/auth/auth_vpopmaild.md +5 -6
- package/docs/plugins/auth/flat_file.md +4 -4
- package/docs/plugins/block_me.md +3 -3
- package/docs/plugins/data.signatures.md +1 -2
- package/docs/plugins/delay_deny.md +3 -4
- package/docs/plugins/max_unrecognized_commands.md +4 -4
- package/docs/plugins/prevent_credential_leaks.md +6 -6
- package/docs/plugins/process_title.md +18 -18
- package/docs/plugins/queue/deliver.md +2 -3
- package/docs/plugins/queue/discard.md +4 -4
- package/docs/plugins/queue/lmtp.md +1 -3
- package/docs/plugins/queue/qmail-queue.md +7 -9
- package/docs/plugins/queue/quarantine.md +16 -21
- package/docs/plugins/queue/rabbitmq.md +8 -11
- package/docs/plugins/queue/rabbitmq_amqplib.md +43 -39
- package/docs/plugins/queue/smtp_bridge.md +7 -10
- package/docs/plugins/queue/smtp_forward.md +42 -34
- package/docs/plugins/queue/smtp_proxy.md +30 -29
- package/docs/plugins/queue/test.md +1 -3
- package/docs/plugins/rcpt_to.in_host_list.md +6 -6
- package/docs/plugins/rcpt_to.max_count.md +1 -1
- package/docs/plugins/record_envelope_addresses.md +3 -3
- package/docs/plugins/reseed_rng.md +6 -6
- package/docs/plugins/status.md +9 -8
- package/docs/plugins/tarpit.md +7 -11
- package/docs/plugins/tls.md +12 -17
- package/docs/plugins/toobusy.md +4 -4
- package/docs/plugins/xclient.md +3 -3
- package/docs/tutorials/Migrating_from_v1_to_v2.md +19 -41
- package/docs/tutorials/SettingUpOutbound.md +6 -9
- package/endpoint.js +35 -38
- package/eslint.config.mjs +22 -19
- package/haraka.js +42 -47
- package/host_pool.js +75 -79
- package/http/html/404.html +45 -49
- package/http/html/index.html +39 -28
- package/http/package.json +2 -4
- package/line_socket.js +27 -28
- package/logger.js +182 -201
- package/outbound/client_pool.js +33 -33
- package/outbound/config.js +64 -59
- package/outbound/fsync_writestream.js +24 -25
- package/outbound/hmail.js +888 -835
- package/outbound/index.js +194 -187
- package/outbound/qfile.js +49 -52
- package/outbound/queue.js +197 -190
- package/outbound/timer_queue.js +41 -43
- package/outbound/tls.js +68 -61
- package/outbound/todo.js +11 -11
- package/package.json +32 -32
- package/plugins/.eslintrc.yaml +0 -1
- package/plugins/auth/auth_base.js +123 -127
- package/plugins/auth/auth_bridge.js +7 -7
- package/plugins/auth/auth_proxy.js +121 -126
- package/plugins/auth/auth_vpopmaild.js +84 -85
- package/plugins/auth/flat_file.js +18 -17
- package/plugins/block_me.js +31 -31
- package/plugins/data.signatures.js +13 -13
- package/plugins/delay_deny.js +65 -61
- package/plugins/prevent_credential_leaks.js +23 -23
- package/plugins/process_title.js +125 -128
- package/plugins/profile.js +5 -5
- package/plugins/queue/deliver.js +3 -3
- package/plugins/queue/discard.js +13 -14
- package/plugins/queue/lmtp.js +16 -17
- package/plugins/queue/qmail-queue.js +54 -55
- package/plugins/queue/quarantine.js +68 -70
- package/plugins/queue/rabbitmq.js +80 -87
- package/plugins/queue/rabbitmq_amqplib.js +75 -54
- package/plugins/queue/smtp_bridge.js +16 -16
- package/plugins/queue/smtp_forward.js +175 -179
- package/plugins/queue/smtp_proxy.js +69 -71
- package/plugins/queue/test.js +9 -9
- package/plugins/rcpt_to.host_list_base.js +30 -34
- package/plugins/rcpt_to.in_host_list.js +19 -19
- package/plugins/record_envelope_addresses.js +4 -4
- package/plugins/reseed_rng.js +4 -4
- package/plugins/status.js +90 -97
- package/plugins/tarpit.js +25 -14
- package/plugins/tls.js +68 -68
- package/plugins/toobusy.js +21 -23
- package/plugins/xclient.js +51 -53
- package/plugins.js +276 -293
- package/rfc1869.js +30 -35
- package/server.js +308 -299
- package/smtp_client.js +244 -228
- package/test/.eslintrc.yaml +0 -1
- package/test/connection.js +127 -134
- package/test/endpoint.js +53 -47
- package/test/fixtures/line_socket.js +12 -12
- package/test/fixtures/util_hmailitem.js +89 -85
- package/test/host_pool.js +90 -92
- package/test/installation/plugins/base_plugin.js +2 -2
- package/test/installation/plugins/folder_plugin/index.js +2 -3
- package/test/installation/plugins/inherits.js +3 -3
- package/test/installation/plugins/load_first.js +2 -3
- package/test/installation/plugins/plugin.js +1 -3
- package/test/installation/plugins/tls.js +2 -4
- package/test/logger.js +135 -116
- package/test/outbound/hmail.js +49 -35
- package/test/outbound/index.js +118 -101
- package/test/outbound/qfile.js +51 -53
- package/test/outbound_bounce_net_errors.js +84 -69
- package/test/outbound_bounce_rfc3464.js +235 -165
- package/test/plugins/auth/auth_base.js +420 -279
- package/test/plugins/auth/auth_vpopmaild.js +38 -39
- package/test/plugins/queue/smtp_forward.js +126 -104
- package/test/plugins/rcpt_to.host_list_base.js +85 -67
- package/test/plugins/rcpt_to.in_host_list.js +159 -112
- package/test/plugins/status.js +71 -64
- package/test/plugins/tls.js +37 -34
- package/test/plugins.js +97 -92
- package/test/rfc1869.js +19 -26
- package/test/server.js +293 -272
- package/test/smtp_client.js +180 -176
- package/test/tls_socket.js +62 -66
- package/test/transaction.js +159 -160
- package/tls_socket.js +331 -333
- package/transaction.js +129 -137
package/outbound/queue.js
CHANGED
|
@@ -1,121 +1,119 @@
|
|
|
1
|
-
'use strict'
|
|
1
|
+
'use strict'
|
|
2
2
|
|
|
3
|
-
const child_process = require('node:child_process')
|
|
4
|
-
const fs
|
|
5
|
-
const path
|
|
3
|
+
const child_process = require('node:child_process')
|
|
4
|
+
const fs = require('node:fs')
|
|
5
|
+
const path = require('node:path')
|
|
6
6
|
|
|
7
|
-
const async
|
|
8
|
-
const { Address } = require('address-rfc2821')
|
|
9
|
-
const config
|
|
7
|
+
const async = require('async')
|
|
8
|
+
const { Address } = require('address-rfc2821')
|
|
9
|
+
const config = require('haraka-config')
|
|
10
10
|
|
|
11
|
-
const logger
|
|
12
|
-
const TimerQueue
|
|
13
|
-
const HMailItem
|
|
14
|
-
const obc
|
|
15
|
-
const _qfile
|
|
16
|
-
const obtls
|
|
11
|
+
const logger = require('../logger')
|
|
12
|
+
const TimerQueue = require('./timer_queue')
|
|
13
|
+
const HMailItem = require('./hmail')
|
|
14
|
+
const obc = require('./config')
|
|
15
|
+
const _qfile = require('./qfile')
|
|
16
|
+
const obtls = require('./tls')
|
|
17
17
|
|
|
18
|
-
exports.name = 'outbound/queue'
|
|
18
|
+
exports.name = 'outbound/queue'
|
|
19
19
|
|
|
20
|
-
let queue_dir
|
|
20
|
+
let queue_dir
|
|
21
21
|
if (config.get('queue_dir')) {
|
|
22
|
-
queue_dir = path.resolve(config.get('queue_dir'))
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
else {
|
|
28
|
-
queue_dir = path.resolve('test', 'test-queue');
|
|
22
|
+
queue_dir = path.resolve(config.get('queue_dir'))
|
|
23
|
+
} else if (process.env.HARAKA) {
|
|
24
|
+
queue_dir = path.resolve(process.env.HARAKA, 'queue')
|
|
25
|
+
} else {
|
|
26
|
+
queue_dir = path.resolve('test', 'test-queue')
|
|
29
27
|
}
|
|
30
28
|
|
|
31
|
-
exports.queue_dir = queue_dir
|
|
29
|
+
exports.queue_dir = queue_dir
|
|
32
30
|
|
|
33
31
|
const load_queue = async.queue((file, cb) => {
|
|
34
|
-
const hmail = new HMailItem(file, path.join(queue_dir, file))
|
|
35
|
-
exports._add_hmail(hmail)
|
|
36
|
-
hmail.once('ready', cb)
|
|
37
|
-
}, obc.cfg.concurrency_max)
|
|
38
|
-
|
|
39
|
-
let in_progress = 0
|
|
40
|
-
const delivery_queue = exports.delivery_queue = async.queue((hmail, cb) => {
|
|
41
|
-
in_progress
|
|
32
|
+
const hmail = new HMailItem(file, path.join(queue_dir, file))
|
|
33
|
+
exports._add_hmail(hmail)
|
|
34
|
+
hmail.once('ready', cb)
|
|
35
|
+
}, obc.cfg.concurrency_max)
|
|
36
|
+
|
|
37
|
+
let in_progress = 0
|
|
38
|
+
const delivery_queue = (exports.delivery_queue = async.queue((hmail, cb) => {
|
|
39
|
+
in_progress++
|
|
42
40
|
hmail.next_cb = () => {
|
|
43
|
-
in_progress
|
|
44
|
-
cb()
|
|
45
|
-
}
|
|
46
|
-
if (obtls.cfg) return hmail.send()
|
|
41
|
+
in_progress--
|
|
42
|
+
cb()
|
|
43
|
+
}
|
|
44
|
+
if (obtls.cfg) return hmail.send()
|
|
47
45
|
obtls.init(() => {
|
|
48
|
-
hmail.send()
|
|
49
|
-
})
|
|
50
|
-
}, obc.cfg.concurrency_max)
|
|
46
|
+
hmail.send()
|
|
47
|
+
})
|
|
48
|
+
}, obc.cfg.concurrency_max))
|
|
51
49
|
|
|
52
|
-
const temp_fail_queue = exports.temp_fail_queue = new TimerQueue()
|
|
50
|
+
const temp_fail_queue = (exports.temp_fail_queue = new TimerQueue())
|
|
53
51
|
|
|
54
|
-
let queue_count = 0
|
|
52
|
+
let queue_count = 0
|
|
55
53
|
|
|
56
|
-
exports.get_stats = () => `${in_progress}/${exports.delivery_queue.length()}/${exports.temp_fail_queue.length()}
|
|
54
|
+
exports.get_stats = () => `${in_progress}/${exports.delivery_queue.length()}/${exports.temp_fail_queue.length()}`
|
|
57
55
|
|
|
58
|
-
exports.list_queue = cb => {
|
|
59
|
-
exports._load_cur_queue(null, exports._list_file, cb)
|
|
56
|
+
exports.list_queue = (cb) => {
|
|
57
|
+
exports._load_cur_queue(null, exports._list_file, cb)
|
|
60
58
|
}
|
|
61
59
|
|
|
62
60
|
exports._stat_file = (file, cb) => {
|
|
63
|
-
queue_count
|
|
64
|
-
setImmediate(cb)
|
|
61
|
+
queue_count++
|
|
62
|
+
setImmediate(cb)
|
|
65
63
|
}
|
|
66
64
|
|
|
67
|
-
exports.stat_queue = cb => {
|
|
68
|
-
const self = exports
|
|
69
|
-
exports._load_cur_queue(null, exports._stat_file, err => {
|
|
70
|
-
if (err) return cb(err)
|
|
71
|
-
return cb(null, self.stats())
|
|
72
|
-
})
|
|
65
|
+
exports.stat_queue = (cb) => {
|
|
66
|
+
const self = exports
|
|
67
|
+
exports._load_cur_queue(null, exports._stat_file, (err) => {
|
|
68
|
+
if (err) return cb(err)
|
|
69
|
+
return cb(null, self.stats())
|
|
70
|
+
})
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
exports.load_queue = pid => {
|
|
73
|
+
exports.load_queue = (pid) => {
|
|
76
74
|
// Initialise and load queue
|
|
77
75
|
// This function is called first when not running under cluster,
|
|
78
|
-
exports.ensure_queue_dir()
|
|
79
|
-
exports.delete_dot_files()
|
|
76
|
+
exports.ensure_queue_dir()
|
|
77
|
+
exports.delete_dot_files()
|
|
80
78
|
|
|
81
79
|
exports._load_cur_queue(pid, exports._add_file, () => {
|
|
82
|
-
logger.info(exports, `[pid: ${pid}] ${delivery_queue.length()} files in my delivery queue`)
|
|
83
|
-
logger.info(exports, `[pid: ${pid}] ${load_queue.length()} files in my load queue`)
|
|
84
|
-
logger.info(exports, `[pid: ${pid}] ${temp_fail_queue.length()} files in my temp fail queue`)
|
|
85
|
-
})
|
|
80
|
+
logger.info(exports, `[pid: ${pid}] ${delivery_queue.length()} files in my delivery queue`)
|
|
81
|
+
logger.info(exports, `[pid: ${pid}] ${load_queue.length()} files in my load queue`)
|
|
82
|
+
logger.info(exports, `[pid: ${pid}] ${temp_fail_queue.length()} files in my temp fail queue`)
|
|
83
|
+
})
|
|
86
84
|
}
|
|
87
85
|
|
|
88
86
|
exports._load_cur_queue = (pid, iteratee, cb) => {
|
|
89
|
-
logger.info(exports,
|
|
87
|
+
logger.info(exports, 'Loading outbound queue from ', queue_dir)
|
|
90
88
|
fs.readdir(queue_dir, (err, files) => {
|
|
91
89
|
if (err) {
|
|
92
|
-
return logger.error(exports, `Failed to load queue directory (${queue_dir}): ${err}`)
|
|
90
|
+
return logger.error(exports, `Failed to load queue directory (${queue_dir}): ${err}`)
|
|
93
91
|
}
|
|
94
92
|
|
|
95
|
-
this.cur_time = new Date()
|
|
93
|
+
this.cur_time = new Date() // set once so we're not calling it a lot
|
|
96
94
|
|
|
97
|
-
this.load_queue_files(pid, files, iteratee, cb)
|
|
98
|
-
})
|
|
95
|
+
this.load_queue_files(pid, files, iteratee, cb)
|
|
96
|
+
})
|
|
99
97
|
}
|
|
100
98
|
|
|
101
|
-
exports.read_parts = file => {
|
|
99
|
+
exports.read_parts = (file) => {
|
|
102
100
|
if (file.indexOf(_qfile.platformDOT) === 0) {
|
|
103
|
-
logger.warn(exports, `'Skipping' dot-file in queue folder: ${file}`)
|
|
104
|
-
return false
|
|
101
|
+
logger.warn(exports, `'Skipping' dot-file in queue folder: ${file}`)
|
|
102
|
+
return false
|
|
105
103
|
}
|
|
106
104
|
|
|
107
105
|
if (file.startsWith('error.')) {
|
|
108
|
-
logger.warn(exports, `'Skipping' error file in queue folder: ${file}`)
|
|
109
|
-
return false
|
|
106
|
+
logger.warn(exports, `'Skipping' error file in queue folder: ${file}`)
|
|
107
|
+
return false
|
|
110
108
|
}
|
|
111
109
|
|
|
112
|
-
const parts = _qfile.parts(file)
|
|
110
|
+
const parts = _qfile.parts(file)
|
|
113
111
|
if (!parts) {
|
|
114
|
-
logger.error(exports, `Unrecognized file in queue folder: ${file}`)
|
|
115
|
-
return false
|
|
112
|
+
logger.error(exports, `Unrecognized file in queue folder: ${file}`)
|
|
113
|
+
return false
|
|
116
114
|
}
|
|
117
115
|
|
|
118
|
-
return parts
|
|
116
|
+
return parts
|
|
119
117
|
}
|
|
120
118
|
|
|
121
119
|
exports.rename_to_actual_pid = (file, parts, cb) => {
|
|
@@ -125,166 +123,176 @@ exports.rename_to_actual_pid = (file, parts, cb) => {
|
|
|
125
123
|
uid: parts.uid,
|
|
126
124
|
next_attempt: parts.next_attempt,
|
|
127
125
|
attempts: parts.attempts,
|
|
128
|
-
})
|
|
126
|
+
})
|
|
129
127
|
|
|
130
|
-
fs.rename(path.join(queue_dir, file), path.join(queue_dir, new_filename), err => {
|
|
128
|
+
fs.rename(path.join(queue_dir, file), path.join(queue_dir, new_filename), (err) => {
|
|
131
129
|
if (err) {
|
|
132
|
-
return cb(`Unable to rename queue file: ${file} to ${new_filename} : ${err}`)
|
|
130
|
+
return cb(`Unable to rename queue file: ${file} to ${new_filename} : ${err}`)
|
|
133
131
|
}
|
|
134
132
|
|
|
135
|
-
cb(null, new_filename)
|
|
136
|
-
})
|
|
133
|
+
cb(null, new_filename)
|
|
134
|
+
})
|
|
137
135
|
}
|
|
138
136
|
|
|
139
137
|
exports._add_file = (file, cb) => {
|
|
140
|
-
const self = exports
|
|
141
|
-
const parts = _qfile.parts(file)
|
|
138
|
+
const self = exports
|
|
139
|
+
const parts = _qfile.parts(file)
|
|
142
140
|
|
|
143
141
|
if (parts.next_attempt <= self.cur_time) {
|
|
144
|
-
logger.debug(exports, `File ${file} needs processing now`)
|
|
145
|
-
load_queue.push(file)
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
142
|
+
logger.debug(exports, `File ${file} needs processing now`)
|
|
143
|
+
load_queue.push(file)
|
|
144
|
+
} else {
|
|
145
|
+
logger.debug(exports, `File ${file} needs processing later: ${parts.next_attempt - self.cur_time}ms`)
|
|
146
|
+
temp_fail_queue.add(file, parts.next_attempt - self.cur_time, () => {
|
|
147
|
+
load_queue.push(file)
|
|
148
|
+
})
|
|
150
149
|
}
|
|
151
150
|
|
|
152
|
-
cb()
|
|
151
|
+
cb()
|
|
153
152
|
}
|
|
154
153
|
|
|
155
154
|
exports.load_queue_files = (pid, input_files, iteratee, callback = function () {}) => {
|
|
156
|
-
const self = exports
|
|
157
|
-
const searchPid = parseInt(pid)
|
|
155
|
+
const self = exports
|
|
156
|
+
const searchPid = parseInt(pid)
|
|
158
157
|
|
|
159
|
-
let stat_renamed = 0
|
|
160
|
-
let stat_loaded = 0
|
|
158
|
+
let stat_renamed = 0
|
|
159
|
+
let stat_loaded = 0
|
|
161
160
|
|
|
162
161
|
if (searchPid) {
|
|
163
|
-
logger.info(exports, `Grabbing queue files for pid: ${pid}`)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
logger.info(exports, "Loading the queue...");
|
|
162
|
+
logger.info(exports, `Grabbing queue files for pid: ${pid}`)
|
|
163
|
+
} else {
|
|
164
|
+
logger.info(exports, 'Loading the queue...')
|
|
167
165
|
}
|
|
168
166
|
|
|
169
|
-
async.map(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
167
|
+
async.map(
|
|
168
|
+
input_files,
|
|
169
|
+
(file, cb) => {
|
|
170
|
+
const parts = self.read_parts(file)
|
|
171
|
+
if (!parts) return cb()
|
|
172
|
+
|
|
173
|
+
if (searchPid) {
|
|
174
|
+
if (parts.pid !== searchPid) return cb()
|
|
175
|
+
|
|
176
|
+
self.rename_to_actual_pid(file, parts, (error, renamed_file) => {
|
|
177
|
+
if (error) {
|
|
178
|
+
logger.error(exports, `${error}`)
|
|
179
|
+
return cb()
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
stat_renamed++
|
|
183
|
+
stat_loaded++
|
|
184
|
+
cb(null, renamed_file)
|
|
185
|
+
})
|
|
186
|
+
} else {
|
|
187
|
+
stat_loaded++
|
|
188
|
+
cb(null, file)
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
(err, results) => {
|
|
192
|
+
if (err) logger.err(exports, `[pid: ${pid}] ${err}`)
|
|
193
|
+
if (searchPid) logger.info(exports, `[pid: ${pid}] ${stat_renamed} files old PID queue fixed up`)
|
|
194
|
+
logger.debug(exports, `[pid: ${pid}] ${stat_loaded} files loaded`)
|
|
195
|
+
|
|
196
|
+
async.map(
|
|
197
|
+
results.filter((i) => i),
|
|
198
|
+
iteratee,
|
|
199
|
+
callback,
|
|
200
|
+
)
|
|
201
|
+
},
|
|
202
|
+
)
|
|
199
203
|
}
|
|
200
204
|
|
|
201
205
|
exports.stats = () => {
|
|
202
206
|
return {
|
|
203
207
|
queue_dir,
|
|
204
208
|
queue_count,
|
|
205
|
-
}
|
|
209
|
+
}
|
|
206
210
|
}
|
|
207
211
|
|
|
208
212
|
exports._list_file = (file, cb) => {
|
|
209
|
-
const tl_reader = fs.createReadStream(path.join(queue_dir, file), {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
})
|
|
213
|
-
tl_reader.
|
|
213
|
+
const tl_reader = fs.createReadStream(path.join(queue_dir, file), {
|
|
214
|
+
start: 0,
|
|
215
|
+
end: 3,
|
|
216
|
+
})
|
|
217
|
+
tl_reader.on('error', (err) => {
|
|
218
|
+
console.error(`Error reading queue file: ${file}:`, err)
|
|
219
|
+
})
|
|
220
|
+
tl_reader.once('data', (buf) => {
|
|
214
221
|
// I'm making the assumption here we won't ever read less than 4 bytes
|
|
215
222
|
// as no filesystem on the planet should be that dumb...
|
|
216
|
-
tl_reader.destroy()
|
|
217
|
-
const todo_len = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]
|
|
218
|
-
const td_reader = fs.createReadStream(path.join(queue_dir, file), {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
tl_reader.destroy()
|
|
224
|
+
const todo_len = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]
|
|
225
|
+
const td_reader = fs.createReadStream(path.join(queue_dir, file), {
|
|
226
|
+
encoding: 'utf8',
|
|
227
|
+
start: 4,
|
|
228
|
+
end: todo_len + 3,
|
|
229
|
+
})
|
|
230
|
+
let todo = ''
|
|
231
|
+
td_reader.on('data', (str) => {
|
|
232
|
+
todo += str
|
|
222
233
|
if (Buffer.byteLength(todo) === todo_len) {
|
|
223
234
|
// we read everything
|
|
224
|
-
const todo_struct = JSON.parse(todo)
|
|
225
|
-
todo_struct.rcpt_to = todo_struct.rcpt_to.map(a => new Address
|
|
226
|
-
todo_struct.mail_from = new Address(todo_struct.mail_from)
|
|
227
|
-
todo_struct.file = file
|
|
228
|
-
todo_struct.full_path = path.join(queue_dir, file)
|
|
229
|
-
const parts = _qfile.parts(file)
|
|
230
|
-
todo_struct.pid =
|
|
231
|
-
cb(null, todo_struct)
|
|
235
|
+
const todo_struct = JSON.parse(todo)
|
|
236
|
+
todo_struct.rcpt_to = todo_struct.rcpt_to.map((a) => new Address(a))
|
|
237
|
+
todo_struct.mail_from = new Address(todo_struct.mail_from)
|
|
238
|
+
todo_struct.file = file
|
|
239
|
+
todo_struct.full_path = path.join(queue_dir, file)
|
|
240
|
+
const parts = _qfile.parts(file)
|
|
241
|
+
todo_struct.pid = parts?.pid || null
|
|
242
|
+
cb(null, todo_struct)
|
|
232
243
|
}
|
|
233
|
-
})
|
|
244
|
+
})
|
|
234
245
|
td_reader.on('end', () => {
|
|
235
246
|
if (Buffer.byteLength(todo) !== todo_len) {
|
|
236
|
-
console.error("Didn't find right amount of data in todo for file:", file)
|
|
237
|
-
return cb()
|
|
247
|
+
console.error("Didn't find right amount of data in todo for file:", file)
|
|
248
|
+
return cb()
|
|
238
249
|
}
|
|
239
|
-
})
|
|
240
|
-
})
|
|
250
|
+
})
|
|
251
|
+
})
|
|
241
252
|
}
|
|
242
253
|
|
|
243
254
|
exports.flush_queue = (domain, pid) => {
|
|
244
255
|
if (domain) {
|
|
245
256
|
exports.list_queue((err, qlist) => {
|
|
246
|
-
if (err) return logger.error(exports, `Failed to load queue: ${err}`)
|
|
257
|
+
if (err) return logger.error(exports, `Failed to load queue: ${err}`)
|
|
247
258
|
for (const todo of qlist) {
|
|
248
|
-
if (todo.domain.toLowerCase() != domain.toLowerCase()) return
|
|
249
|
-
if (pid && todo.pid != pid) return
|
|
259
|
+
if (todo.domain.toLowerCase() != domain.toLowerCase()) return
|
|
260
|
+
if (pid && todo.pid != pid) return
|
|
250
261
|
// console.log("requeue: ", todo);
|
|
251
|
-
delivery_queue.push(new HMailItem(todo.file, todo.full_path))
|
|
262
|
+
delivery_queue.push(new HMailItem(todo.file, todo.full_path))
|
|
252
263
|
}
|
|
253
264
|
})
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
temp_fail_queue.drain();
|
|
265
|
+
} else {
|
|
266
|
+
temp_fail_queue.drain()
|
|
257
267
|
}
|
|
258
268
|
}
|
|
259
269
|
|
|
260
|
-
exports.load_pid_queue = pid => {
|
|
261
|
-
logger.info(exports, `Loading queue for pid: ${pid}`)
|
|
262
|
-
exports.load_queue(pid)
|
|
270
|
+
exports.load_pid_queue = (pid) => {
|
|
271
|
+
logger.info(exports, `Loading queue for pid: ${pid}`)
|
|
272
|
+
exports.load_queue(pid)
|
|
263
273
|
}
|
|
264
274
|
|
|
265
275
|
exports.ensure_queue_dir = () => {
|
|
266
276
|
// this code is only run at start-up.
|
|
267
|
-
if (fs.existsSync(queue_dir)) return
|
|
277
|
+
if (fs.existsSync(queue_dir)) return
|
|
268
278
|
|
|
269
|
-
logger.debug(exports, `Creating queue directory ${queue_dir}`)
|
|
279
|
+
logger.debug(exports, `Creating queue directory ${queue_dir}`)
|
|
270
280
|
try {
|
|
271
|
-
fs.mkdirSync(queue_dir, 493)
|
|
272
|
-
const cfg = config.get('smtp.ini')
|
|
281
|
+
fs.mkdirSync(queue_dir, 493) // 493 == 0755
|
|
282
|
+
const cfg = config.get('smtp.ini')
|
|
273
283
|
let uid
|
|
274
284
|
let gid
|
|
275
|
-
if (cfg.user) uid = child_process.execSync(`id -u ${cfg.user}`).toString().trim()
|
|
276
|
-
if (cfg.group) gid = child_process.execSync(`id -g ${cfg.group}`).toString().trim()
|
|
285
|
+
if (cfg.user) uid = child_process.execSync(`id -u ${cfg.user}`).toString().trim()
|
|
286
|
+
if (cfg.group) gid = child_process.execSync(`id -g ${cfg.group}`).toString().trim()
|
|
277
287
|
if (uid && gid) {
|
|
278
288
|
fs.chown(queue_dir, uid, gid)
|
|
279
|
-
}
|
|
280
|
-
else if (uid) {
|
|
289
|
+
} else if (uid) {
|
|
281
290
|
fs.chown(queue_dir, uid)
|
|
282
291
|
}
|
|
283
|
-
}
|
|
284
|
-
catch (err) {
|
|
292
|
+
} catch (err) {
|
|
285
293
|
if (err.code !== 'EEXIST') {
|
|
286
|
-
logger.error(exports, `Error creating queue directory: ${err}`)
|
|
287
|
-
throw err
|
|
294
|
+
logger.error(exports, `Error creating queue directory: ${err}`)
|
|
295
|
+
throw err
|
|
288
296
|
}
|
|
289
297
|
}
|
|
290
298
|
}
|
|
@@ -292,43 +300,42 @@ exports.ensure_queue_dir = () => {
|
|
|
292
300
|
exports.delete_dot_files = () => {
|
|
293
301
|
for (const file of fs.readdirSync(queue_dir)) {
|
|
294
302
|
if (file.indexOf(_qfile.platformDOT) === 0) {
|
|
295
|
-
logger.warn(exports, `Removing left over dot-file: ${file}`)
|
|
296
|
-
return fs.unlinkSync(path.join(queue_dir, file))
|
|
303
|
+
logger.warn(exports, `Removing left over dot-file: ${file}`)
|
|
304
|
+
return fs.unlinkSync(path.join(queue_dir, file))
|
|
297
305
|
}
|
|
298
306
|
}
|
|
299
307
|
}
|
|
300
308
|
|
|
301
|
-
exports._add_hmail = hmail => {
|
|
309
|
+
exports._add_hmail = (hmail) => {
|
|
302
310
|
if (hmail.next_process < exports.cur_time) {
|
|
303
|
-
delivery_queue.push(hmail)
|
|
304
|
-
}
|
|
305
|
-
else {
|
|
311
|
+
delivery_queue.push(hmail)
|
|
312
|
+
} else {
|
|
306
313
|
temp_fail_queue.add(hmail.filename, hmail.next_process - exports.cur_time, () => {
|
|
307
|
-
delivery_queue.push(hmail)
|
|
308
|
-
})
|
|
314
|
+
delivery_queue.push(hmail)
|
|
315
|
+
})
|
|
309
316
|
}
|
|
310
317
|
}
|
|
311
318
|
|
|
312
|
-
exports.scan_queue_pids = cb => {
|
|
313
|
-
const self = exports
|
|
319
|
+
exports.scan_queue_pids = (cb) => {
|
|
320
|
+
const self = exports
|
|
314
321
|
|
|
315
322
|
// Under cluster, this is called first by the master
|
|
316
|
-
self.ensure_queue_dir()
|
|
317
|
-
self.delete_dot_files()
|
|
323
|
+
self.ensure_queue_dir()
|
|
324
|
+
self.delete_dot_files()
|
|
318
325
|
|
|
319
326
|
fs.readdir(queue_dir, (err, files) => {
|
|
320
327
|
if (err) {
|
|
321
|
-
logger.error(exports, `Failed to load queue directory (${queue_dir}): ${err}`)
|
|
322
|
-
return cb(err)
|
|
328
|
+
logger.error(exports, `Failed to load queue directory (${queue_dir}): ${err}`)
|
|
329
|
+
return cb(err)
|
|
323
330
|
}
|
|
324
331
|
|
|
325
|
-
const pids = {}
|
|
332
|
+
const pids = {}
|
|
326
333
|
|
|
327
334
|
for (const file of files) {
|
|
328
|
-
const parts = self.read_parts(file)
|
|
329
|
-
if (parts) pids[parts.pid] = true
|
|
335
|
+
const parts = self.read_parts(file)
|
|
336
|
+
if (parts) pids[parts.pid] = true
|
|
330
337
|
}
|
|
331
338
|
|
|
332
|
-
return cb(null, Object.keys(pids))
|
|
333
|
-
})
|
|
339
|
+
return cb(null, Object.keys(pids))
|
|
340
|
+
})
|
|
334
341
|
}
|
package/outbound/timer_queue.js
CHANGED
|
@@ -1,88 +1,86 @@
|
|
|
1
|
-
'use strict'
|
|
1
|
+
'use strict'
|
|
2
2
|
|
|
3
|
-
const logger = require('../logger')
|
|
3
|
+
const logger = require('../logger')
|
|
4
4
|
|
|
5
5
|
class TQTimer {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
this.
|
|
9
|
-
this.
|
|
10
|
-
this.cb = cb;
|
|
6
|
+
constructor(id, fire_time, cb) {
|
|
7
|
+
this.id = id
|
|
8
|
+
this.fire_time = fire_time
|
|
9
|
+
this.cb = cb
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
cancel
|
|
14
|
-
this.cb = null
|
|
12
|
+
cancel() {
|
|
13
|
+
this.cb = null
|
|
15
14
|
}
|
|
16
|
-
|
|
17
15
|
}
|
|
18
16
|
|
|
19
17
|
class TimerQueue {
|
|
20
|
-
|
|
21
|
-
constructor (interval = 1000) {
|
|
18
|
+
constructor(interval = 1000) {
|
|
22
19
|
this.name = 'outbound/timer_queue'
|
|
23
|
-
this.queue = []
|
|
24
|
-
this.interval_timer = setInterval(() => {
|
|
20
|
+
this.queue = []
|
|
21
|
+
this.interval_timer = setInterval(() => {
|
|
22
|
+
this.fire()
|
|
23
|
+
}, interval)
|
|
25
24
|
this.interval_timer.unref() // allow server to exit
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
add
|
|
29
|
-
const fire_time = Date.now() + ms
|
|
27
|
+
add(id, ms, cb) {
|
|
28
|
+
const fire_time = Date.now() + ms
|
|
30
29
|
|
|
31
|
-
const timer = new TQTimer(id, fire_time, cb)
|
|
30
|
+
const timer = new TQTimer(id, fire_time, cb)
|
|
32
31
|
|
|
33
|
-
if (
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
return timer;
|
|
32
|
+
if (this.queue.length === 0 || fire_time >= this.queue[this.queue.length - 1].fire_time) {
|
|
33
|
+
this.queue.push(timer)
|
|
34
|
+
return timer
|
|
37
35
|
}
|
|
38
36
|
|
|
39
|
-
for (let i=0; i < this.queue.length; i++) {
|
|
37
|
+
for (let i = 0; i < this.queue.length; i++) {
|
|
40
38
|
if (this.queue[i].fire_time > fire_time) {
|
|
41
|
-
this.queue.splice(i, 0, timer)
|
|
42
|
-
return timer
|
|
39
|
+
this.queue.splice(i, 0, timer)
|
|
40
|
+
return timer
|
|
43
41
|
}
|
|
44
42
|
}
|
|
45
43
|
|
|
46
|
-
throw
|
|
44
|
+
throw 'Should never get here'
|
|
47
45
|
}
|
|
48
46
|
|
|
49
|
-
discard
|
|
47
|
+
discard(id) {
|
|
50
48
|
for (let i = 0; i < this.queue.length; i++) {
|
|
51
49
|
if (this.queue[i].id === id) {
|
|
52
|
-
this.queue[i].cancel()
|
|
53
|
-
return this.queue.splice(i, 1)
|
|
50
|
+
this.queue[i].cancel()
|
|
51
|
+
return this.queue.splice(i, 1)
|
|
54
52
|
}
|
|
55
53
|
}
|
|
56
54
|
|
|
57
|
-
throw `${id} not found
|
|
55
|
+
throw `${id} not found`
|
|
58
56
|
}
|
|
59
57
|
|
|
60
|
-
fire
|
|
61
|
-
if (this.queue.length === 0) return
|
|
58
|
+
fire() {
|
|
59
|
+
if (this.queue.length === 0) return
|
|
62
60
|
|
|
63
|
-
const now = Date.now()
|
|
61
|
+
const now = Date.now()
|
|
64
62
|
|
|
65
63
|
while (this.queue.length && this.queue[0].fire_time <= now) {
|
|
66
|
-
const to_run = this.queue.shift()
|
|
67
|
-
if (to_run.cb) to_run.cb()
|
|
64
|
+
const to_run = this.queue.shift()
|
|
65
|
+
if (to_run.cb) to_run.cb()
|
|
68
66
|
}
|
|
69
67
|
}
|
|
70
68
|
|
|
71
|
-
length
|
|
72
|
-
return this.queue.length
|
|
69
|
+
length() {
|
|
70
|
+
return this.queue.length
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
drain
|
|
76
|
-
logger.debug(this, `Draining ${this.queue.length} items from the queue`)
|
|
73
|
+
drain() {
|
|
74
|
+
logger.debug(this, `Draining ${this.queue.length} items from the queue`)
|
|
77
75
|
while (this.queue.length) {
|
|
78
|
-
const to_run = this.queue.shift()
|
|
79
|
-
if (to_run.cb) to_run.cb()
|
|
76
|
+
const to_run = this.queue.shift()
|
|
77
|
+
if (to_run.cb) to_run.cb()
|
|
80
78
|
}
|
|
81
79
|
}
|
|
82
80
|
|
|
83
|
-
shutdown
|
|
84
|
-
clearInterval(this.interval_timer)
|
|
81
|
+
shutdown() {
|
|
82
|
+
clearInterval(this.interval_timer)
|
|
85
83
|
}
|
|
86
84
|
}
|
|
87
85
|
|
|
88
|
-
module.exports = TimerQueue
|
|
86
|
+
module.exports = TimerQueue
|