Haraka 3.1.0 → 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 +69 -50
- package/Plugins.md +3 -1
- package/README.md +1 -1
- package/bin/haraka +475 -478
- 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 +34 -27
- 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 +38 -33
- 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/bin/haraka
CHANGED
|
@@ -3,56 +3,56 @@
|
|
|
3
3
|
// this script takes inspiration from:
|
|
4
4
|
// https://github.com/tnantoka/LooseLeaf/blob/master/bin/looseleaf
|
|
5
5
|
|
|
6
|
-
const child = require('node:child_process')
|
|
7
|
-
const fs
|
|
8
|
-
const net
|
|
9
|
-
const path = require('node:path')
|
|
10
|
-
const os
|
|
11
|
-
|
|
12
|
-
const nopt = require('nopt')
|
|
13
|
-
const utils
|
|
14
|
-
const sprintf = require('sprintf-js').sprintf
|
|
15
|
-
const base = path.join(__dirname, '..')
|
|
16
|
-
const ver
|
|
6
|
+
const child = require('node:child_process')
|
|
7
|
+
const fs = require('node:fs')
|
|
8
|
+
const net = require('node:net')
|
|
9
|
+
const path = require('node:path')
|
|
10
|
+
const os = require('node:os')
|
|
11
|
+
|
|
12
|
+
const nopt = require('nopt')
|
|
13
|
+
const utils = require('haraka-utils')
|
|
14
|
+
const sprintf = require('sprintf-js').sprintf
|
|
15
|
+
const base = path.join(__dirname, '..')
|
|
16
|
+
const ver = utils.getVersion(base)
|
|
17
17
|
const knownOpts = {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
18
|
+
version: Boolean,
|
|
19
|
+
verbose: Boolean,
|
|
20
|
+
help: [String, null],
|
|
21
|
+
configs: path,
|
|
22
|
+
install: path,
|
|
23
|
+
list: Boolean,
|
|
24
|
+
plugin: Array,
|
|
25
|
+
force: Boolean,
|
|
26
|
+
qlist: Boolean,
|
|
27
|
+
qstat: Boolean,
|
|
28
|
+
qempty: Boolean,
|
|
29
|
+
qunstick: [String, null],
|
|
30
|
+
graceful: Boolean,
|
|
31
|
+
order: Boolean,
|
|
32
|
+
test: [String, Array],
|
|
33
|
+
ip: String,
|
|
34
|
+
helo: String,
|
|
35
|
+
ehlo: String,
|
|
36
|
+
envfrom: String,
|
|
37
|
+
envrcpt: [String, Array],
|
|
38
|
+
message: path,
|
|
39
|
+
'dump-mime': Boolean,
|
|
40
|
+
'dump-stream': Boolean,
|
|
41
|
+
'skip-deny': Boolean,
|
|
42
|
+
'set-relay': Boolean,
|
|
43
43
|
}
|
|
44
44
|
const shortHands = {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
v: ['--version'],
|
|
46
|
+
h: ['--help'],
|
|
47
|
+
c: ['--configs'],
|
|
48
|
+
i: ['--install'],
|
|
49
|
+
l: ['--list'],
|
|
50
|
+
p: ['--plugin'],
|
|
51
|
+
f: ['--force'],
|
|
52
|
+
o: ['--order'],
|
|
53
|
+
t: ['--test'],
|
|
54
54
|
}
|
|
55
|
-
const parsed = nopt(knownOpts, shortHands, process.argv, 2)
|
|
55
|
+
const parsed = nopt(knownOpts, shortHands, process.argv, 2)
|
|
56
56
|
|
|
57
57
|
const usage = `\x1B[32;40mHaraka.js\x1B[0m — A Node.js Email Server project
|
|
58
58
|
Usage: haraka [options] [path]
|
|
@@ -81,89 +81,90 @@ Options:
|
|
|
81
81
|
--dump-stream \t\tDump the MessageStream to stdout
|
|
82
82
|
--skip-deny \t\tContinue running hooks after DENY/DENYSOFT
|
|
83
83
|
--set-relay \t\tSet connection.relaying
|
|
84
|
-
|
|
84
|
+
`
|
|
85
85
|
|
|
86
|
-
function listPlugins
|
|
86
|
+
function listPlugins(b, dir = 'plugins/') {
|
|
87
|
+
const inital_dir = path.join(b ?? base, dir)
|
|
88
|
+
const plugin_dirs = [inital_dir]
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
for (const d of fs.readdirSync(inital_dir)) {
|
|
92
|
-
if (fs.statSync(path.join(inital_dir, d)).isDirectory()) {
|
|
93
|
-
plugin_dirs.push(path.join(inital_dir, d));
|
|
94
|
-
}
|
|
90
|
+
for (const d of fs.readdirSync(inital_dir)) {
|
|
91
|
+
if (fs.statSync(path.join(inital_dir, d)).isDirectory()) {
|
|
92
|
+
plugin_dirs.push(path.join(inital_dir, d))
|
|
95
93
|
}
|
|
94
|
+
}
|
|
96
95
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
96
|
+
let plugin_list = ``
|
|
97
|
+
for (const pd of plugin_dirs) {
|
|
98
|
+
plugin_list += `\n${pd.match(/plugins.*$/)[0]}\n`
|
|
100
99
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
100
|
+
for (const d of fs.readdirSync(pd)) {
|
|
101
|
+
if (fs.statSync(path.join(pd, d)).isFile() && ~d.search('.js')) {
|
|
102
|
+
plugin_list += `\t${d.replace('.js', '')}\n`
|
|
103
|
+
}
|
|
106
104
|
}
|
|
105
|
+
}
|
|
107
106
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
107
|
+
plugin_list += `\nNPM packages (${b ?? base})\n`
|
|
108
|
+
const npm_plugins = []
|
|
109
|
+
for (const entry of fs.readdirSync(path.join(b ?? base, 'node_modules'))) {
|
|
110
|
+
if (!/^haraka-plugin-/.test(entry)) continue
|
|
111
|
+
npm_plugins.push(entry.split('-').slice(2).join('-'))
|
|
112
|
+
}
|
|
113
|
+
plugin_list += `\t${npm_plugins.join('\n\t')}\n`
|
|
115
114
|
|
|
116
|
-
|
|
115
|
+
return plugin_list
|
|
117
116
|
}
|
|
118
117
|
|
|
119
118
|
// Warning messsage
|
|
120
|
-
function warning
|
|
121
|
-
|
|
119
|
+
function warning(msg) {
|
|
120
|
+
console.error(`\x1b[31mwarning\x1b[0m: ${msg}`)
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
function fail
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
function fail(msg) {
|
|
124
|
+
console.error(`\x1b[31merror\x1b[0m: ${msg}`)
|
|
125
|
+
process.exit(-1)
|
|
127
126
|
}
|
|
128
127
|
|
|
129
|
-
function setupHostname
|
|
130
|
-
|
|
128
|
+
function setupHostname(confPath) {
|
|
129
|
+
utils.mkDir(confPath)
|
|
131
130
|
|
|
132
|
-
|
|
131
|
+
const hostname = `${os.hostname()}${os.EOL}`
|
|
133
132
|
|
|
134
|
-
|
|
135
|
-
|
|
133
|
+
;['me', 'host_list'].forEach((f) => {
|
|
134
|
+
const cfPath = path.join(confPath, f)
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
try {
|
|
137
|
+
if (fs.statSync(cfPath).isFile()) return
|
|
138
|
+
} catch (ignore) {}
|
|
139
139
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
})
|
|
140
|
+
try {
|
|
141
|
+
fs.writeFileSync(cfPath, hostname)
|
|
142
|
+
} catch (err) {
|
|
143
|
+
warning(`Unable to write to config/${f}: ${err.message}`)
|
|
144
|
+
}
|
|
145
|
+
})
|
|
143
146
|
}
|
|
144
147
|
|
|
145
|
-
function setupBaseConfig
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
148
|
+
function setupBaseConfig(confPath) {
|
|
149
|
+
utils.copyFile(path.join(base, 'config', 'connection.ini'), path.join(confPath, 'connection.ini'))
|
|
150
|
+
utils.copyFile(path.join(base, 'config', 'smtp.ini'), path.join(confPath, 'smtp.ini'))
|
|
151
|
+
utils.copyFile(path.join(base, 'config', 'log.ini'), path.join(confPath, 'log.ini'))
|
|
152
|
+
utils.copyFile(path.join(base, 'config', 'plugins'), path.join(confPath, 'plugins'))
|
|
149
153
|
}
|
|
150
154
|
|
|
151
|
-
function setupRequire
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
(path.join(process.env.HARAKA, 'node_modules'));
|
|
162
|
-
require('module')._initPaths(); // Horrible hack
|
|
163
|
-
}
|
|
155
|
+
function setupRequire() {
|
|
156
|
+
process.env.HARAKA = parsed.configs
|
|
157
|
+
try {
|
|
158
|
+
require.paths.push(path.join(process.env.HARAKA, 'node_modules'))
|
|
159
|
+
} catch (e) {
|
|
160
|
+
process.env.NODE_PATH = process.env.NODE_PATH
|
|
161
|
+
? `${process.env.NODE_PATH}:${path.join(process.env.HARAKA, 'node_modules')}`
|
|
162
|
+
: path.join(process.env.HARAKA, 'node_modules')
|
|
163
|
+
require('module')._initPaths() // Horrible hack
|
|
164
|
+
}
|
|
164
165
|
}
|
|
165
166
|
|
|
166
|
-
function noop
|
|
167
|
+
function noop() {}
|
|
167
168
|
|
|
168
169
|
const readme = `Haraka
|
|
169
170
|
|
|
@@ -192,402 +193,398 @@ is either the name of a plugin (without the .js extension) or the name of
|
|
|
192
193
|
a core Haraka module, such as 'Connection' or 'Transaction'.
|
|
193
194
|
|
|
194
195
|
To get documentation on writing a plugin type 'haraka -h Plugins'.
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
const packageJson = JSON.stringify(
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
196
|
+
`
|
|
197
|
+
|
|
198
|
+
const packageJson = JSON.stringify(
|
|
199
|
+
{
|
|
200
|
+
name: 'haraka_local',
|
|
201
|
+
description: 'An SMTP Server',
|
|
202
|
+
version: '0.0.1',
|
|
203
|
+
dependencies: {},
|
|
204
|
+
repository: '',
|
|
205
|
+
license: 'MIT',
|
|
206
|
+
},
|
|
207
|
+
null,
|
|
208
|
+
2,
|
|
209
|
+
)
|
|
205
210
|
|
|
206
211
|
const plugin_src = [
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
].join(
|
|
212
|
+
'// %plugin%',
|
|
213
|
+
'',
|
|
214
|
+
'// documentation via: haraka -c %config% -h plugins/%plugin%',
|
|
215
|
+
'',
|
|
216
|
+
'// Put your plugin code here',
|
|
217
|
+
'// type: `haraka -h Plugins` for documentation on how to create a plugin',
|
|
218
|
+
'',
|
|
219
|
+
].join('\n')
|
|
215
220
|
|
|
216
221
|
const plugin_doc = [
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
].join(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
222
|
+
'%plugin%',
|
|
223
|
+
'========',
|
|
224
|
+
'',
|
|
225
|
+
'Describe what your plugin does here.',
|
|
226
|
+
'',
|
|
227
|
+
'Configuration',
|
|
228
|
+
'-------------',
|
|
229
|
+
'',
|
|
230
|
+
'* `config/some_file` - describe what effect this config file has',
|
|
231
|
+
'',
|
|
232
|
+
].join('\n')
|
|
233
|
+
|
|
234
|
+
function getHooks() {
|
|
235
|
+
// see haraka/Haraka#3306
|
|
236
|
+
const pi_path = path.resolve(parsed.configs, 'docs', 'Plugins.md')
|
|
237
|
+
return fs
|
|
238
|
+
.readFileSync(pi_path)
|
|
239
|
+
.toString()
|
|
240
|
+
.split('## Available Hooks')[1] // discard everything before this string
|
|
241
|
+
.split('### rcpt')[0] // discard everything after this string
|
|
242
|
+
.match(/[*\-]\s(\S+)/gm) // grab the first word of lines starting with '* ' or '- '
|
|
243
|
+
.map((a) => a.replace(/^\* /, '').replace(/\\/g, '')) // strip list prefix and escapes
|
|
237
244
|
}
|
|
238
245
|
|
|
239
|
-
let config
|
|
240
|
-
let logger
|
|
241
|
-
let outbound
|
|
242
|
-
let plugins
|
|
246
|
+
let config
|
|
247
|
+
let logger
|
|
248
|
+
let outbound
|
|
249
|
+
let plugins
|
|
243
250
|
if (parsed.version) {
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
251
|
+
console.log(`\x1B[32;40mHaraka.js\x1B[0m — Version: ${ver}`)
|
|
252
|
+
} else if (parsed.list) {
|
|
253
|
+
console.log(`\x1B[32;40m*global\x1B[0m\n${listPlugins()}`)
|
|
254
|
+
if (parsed.configs) {
|
|
255
|
+
console.log(`\x1B[32;40m*local\x1B[0m\n${listPlugins(parsed.configs)}`)
|
|
256
|
+
}
|
|
257
|
+
} else if (parsed.help) {
|
|
258
|
+
if (parsed.help === 'true') {
|
|
259
|
+
console.log(usage)
|
|
260
|
+
return
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let md_path
|
|
264
|
+
const md_paths = [
|
|
265
|
+
path.join(base, 'docs', `${parsed.help}.md`),
|
|
266
|
+
path.join(base, 'docs', 'plugins', `${parsed.help}.md`),
|
|
267
|
+
path.join(base, 'docs', 'deprecated', `${parsed.help}.md`),
|
|
268
|
+
path.join(base, 'node_modules', `haraka-plugin-${parsed.help}`, 'README.md'),
|
|
269
|
+
]
|
|
270
|
+
if (parsed.configs) {
|
|
271
|
+
md_paths.unshift(path.join(parsed.configs, 'docs', 'plugins', `${parsed.help}.md`))
|
|
272
|
+
md_paths.unshift(path.join(parsed.configs, 'docs', `${parsed.help}.md`))
|
|
273
|
+
}
|
|
274
|
+
for (let i = 0, j = md_paths.length; i < j; i++) {
|
|
275
|
+
const _md_path = md_paths[i]
|
|
276
|
+
if (fs.existsSync(_md_path)) {
|
|
277
|
+
md_path = [_md_path]
|
|
278
|
+
break
|
|
250
279
|
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
280
|
+
}
|
|
281
|
+
if (!md_path) {
|
|
282
|
+
warning(`No documentation found for: ${parsed.help}`)
|
|
283
|
+
process.exit()
|
|
284
|
+
}
|
|
285
|
+
let pager = 'less'
|
|
286
|
+
if (process.env.PAGER) {
|
|
287
|
+
const pager_split = process.env.PAGER.split(/ +/)
|
|
288
|
+
pager = pager_split.shift()
|
|
289
|
+
md_path = pager_split.concat(md_path)
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const less = child.spawn(pager, md_path, { stdio: 'inherit' })
|
|
293
|
+
less.on('exit', function () {
|
|
294
|
+
process.exit(0)
|
|
295
|
+
})
|
|
296
|
+
} else if (parsed.configs && parsed.plugin) {
|
|
297
|
+
const js_path = path.join(parsed.configs, 'plugins', `${parsed.plugin}.js`)
|
|
298
|
+
utils.createFile(js_path, plugin_src, { plugin: parsed.plugin, config: parsed.configs }, parsed.force)
|
|
299
|
+
|
|
300
|
+
const doc_path = path.join(parsed.configs, 'docs', 'plugins', `${parsed.plugin}.md`)
|
|
301
|
+
utils.createFile(doc_path, plugin_doc, { plugin: parsed.plugin, config: parsed.configs }, parsed.force)
|
|
302
|
+
|
|
303
|
+
console.log(`Plugin ${parsed.plugin} created`)
|
|
304
|
+
console.log(`Now edit javascript in: ${js_path}`)
|
|
305
|
+
console.log(`Add the plugin to config: ${path.join(parsed.configs, 'config', 'plugins')}`)
|
|
306
|
+
console.log(`And edit documentation in: ${doc_path}`)
|
|
307
|
+
} else if (parsed.qlist) {
|
|
308
|
+
if (!parsed.configs) fail('qlist option requires config path')
|
|
309
|
+
process.env.HARAKA = parsed.configs
|
|
310
|
+
logger = require(path.join(base, 'logger'))
|
|
311
|
+
if (!parsed.verbose) logger.log = noop // disable logging
|
|
312
|
+
outbound = require(path.join(base, 'outbound'))
|
|
313
|
+
outbound.list_queue(function (err, qlist) {
|
|
314
|
+
for (const todo of qlist) {
|
|
315
|
+
console.log(
|
|
316
|
+
sprintf(
|
|
317
|
+
'Q: %s rcpt:%d from:%s domain:%s',
|
|
318
|
+
todo.file,
|
|
319
|
+
todo.rcpt_to.length,
|
|
320
|
+
todo.mail_from.toString(),
|
|
321
|
+
todo.domain,
|
|
322
|
+
),
|
|
323
|
+
)
|
|
268
324
|
}
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
outbound = require(path.join(base, 'outbound'));
|
|
310
|
-
outbound.list_queue(function (err, qlist) {
|
|
311
|
-
for (const todo of qlist) {
|
|
312
|
-
console.log(sprintf("Q: %s rcpt:%d from:%s domain:%s", todo.file, todo.rcpt_to.length, todo.mail_from.toString(), todo.domain));
|
|
313
|
-
}
|
|
314
|
-
process.exit();
|
|
325
|
+
process.exit()
|
|
326
|
+
})
|
|
327
|
+
} else if (parsed.qstat) {
|
|
328
|
+
if (!parsed.configs) fail('qstat option requires config path')
|
|
329
|
+
|
|
330
|
+
process.env.HARAKA = parsed.configs
|
|
331
|
+
logger = require(path.join(base, 'logger'))
|
|
332
|
+
if (!parsed.verbose) logger.log = noop // disable logging
|
|
333
|
+
outbound = require(path.join(base, 'outbound'))
|
|
334
|
+
outbound.stat_queue(function (err, stats) {
|
|
335
|
+
console.log(stats)
|
|
336
|
+
process.exit()
|
|
337
|
+
})
|
|
338
|
+
} else if (parsed.qunstick) {
|
|
339
|
+
if (!parsed.configs) fail('qunstick option requires config path')
|
|
340
|
+
const domain = parsed.qunstick.toLowerCase()
|
|
341
|
+
process.env.HARAKA = parsed.configs
|
|
342
|
+
logger = require(path.join(base, 'logger'))
|
|
343
|
+
if (!parsed.verbose) logger.log = noop // disable logging
|
|
344
|
+
const cb = function () {
|
|
345
|
+
process.exit()
|
|
346
|
+
}
|
|
347
|
+
if (domain == 'true') {
|
|
348
|
+
send_internal_command('flushQueue', cb)
|
|
349
|
+
} else {
|
|
350
|
+
send_internal_command(`flushQueue ${domain}`, cb)
|
|
351
|
+
}
|
|
352
|
+
} else if (parsed.graceful) {
|
|
353
|
+
if (!parsed.configs) fail('graceful option requires config path')
|
|
354
|
+
|
|
355
|
+
process.env.HARAKA = parsed.configs
|
|
356
|
+
logger = require(path.join(base, 'logger'))
|
|
357
|
+
if (!parsed.verbose) logger.log = noop // disable logging
|
|
358
|
+
config = require('haraka-config')
|
|
359
|
+
if (!config.get('smtp.ini').main.nodes) {
|
|
360
|
+
console.log('Graceful restart not possible without `nodes` value in smtp.ini')
|
|
361
|
+
process.exit()
|
|
362
|
+
} else {
|
|
363
|
+
send_internal_command('gracefulRestart', () => {
|
|
364
|
+
process.exit()
|
|
315
365
|
})
|
|
316
|
-
}
|
|
317
|
-
else if (parsed.
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
if (domain == 'true') {
|
|
337
|
-
send_internal_command('flushQueue', cb);
|
|
338
|
-
}
|
|
339
|
-
else {
|
|
340
|
-
send_internal_command(`flushQueue ${domain}`, cb);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
else if (parsed.graceful) {
|
|
344
|
-
if (!parsed.configs) fail("graceful option requires config path");
|
|
345
|
-
|
|
346
|
-
process.env.HARAKA = parsed.configs;
|
|
347
|
-
logger = require(path.join(base, 'logger'));
|
|
348
|
-
if (!parsed.verbose) logger.log = noop // disable logging
|
|
349
|
-
config = require('haraka-config');
|
|
350
|
-
if (!config.get("smtp.ini").main.nodes) {
|
|
351
|
-
console.log("Graceful restart not possible without `nodes` value in smtp.ini");
|
|
352
|
-
process.exit();
|
|
353
|
-
}
|
|
354
|
-
else {
|
|
355
|
-
send_internal_command('gracefulRestart', () => {
|
|
356
|
-
process.exit();
|
|
357
|
-
});
|
|
366
|
+
}
|
|
367
|
+
} else if (parsed.qempty) {
|
|
368
|
+
if (!parsed.configs) fail('qempty option requires config path')
|
|
369
|
+
fail('qempty is unimplemented')
|
|
370
|
+
} else if (parsed.order) {
|
|
371
|
+
if (!parsed.configs) fail('order option requires config path')
|
|
372
|
+
setupRequire()
|
|
373
|
+
logger = require(path.join(base, 'logger'))
|
|
374
|
+
if (!parsed.verbose) logger.log = noop // disable logging
|
|
375
|
+
plugins = require(path.join(base, 'plugins'))
|
|
376
|
+
plugins.load_plugins()
|
|
377
|
+
console.log('')
|
|
378
|
+
for (const hook of getHooks()) {
|
|
379
|
+
if (!plugins.registered_hooks[hook]) continue
|
|
380
|
+
console.log(sprintf("%'--80s", `Hook: ${hook} `))
|
|
381
|
+
console.log(sprintf('%-35s %-35s %-4s %-3s', 'Plugin', 'Method', 'Prio', 'T/O'))
|
|
382
|
+
console.log(sprintf("%'-80s", ''))
|
|
383
|
+
for (let p = 0; p < plugins.registered_hooks[hook].length; p++) {
|
|
384
|
+
const item = plugins.registered_hooks[hook][p]
|
|
385
|
+
console.log(sprintf('%-35s %-35s %4d %3d', item.plugin, item.method, item.priority, item.timeout))
|
|
358
386
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
else if (parsed.order) {
|
|
365
|
-
if (!parsed.configs) fail('order option requires config path');
|
|
366
|
-
setupRequire();
|
|
367
|
-
logger = require(path.join(base, 'logger'));
|
|
368
|
-
if (!parsed.verbose) logger.log = noop // disable logging
|
|
369
|
-
plugins = require(path.join(base, 'plugins'));
|
|
370
|
-
plugins.load_plugins();
|
|
371
|
-
console.log('');
|
|
372
|
-
for (const hook of getHooks()) {
|
|
373
|
-
if (!plugins.registered_hooks[hook]) continue;
|
|
374
|
-
console.log(sprintf('%\'--80s', `Hook: ${hook} `));
|
|
375
|
-
console.log(sprintf('%-35s %-35s %-4s %-3s', 'Plugin', 'Method', 'Prio', 'T/O'));
|
|
376
|
-
console.log(sprintf("%'-80s",''));
|
|
377
|
-
for (let p=0; p<plugins.registered_hooks[hook].length; p++) {
|
|
378
|
-
const item = plugins.registered_hooks[hook][p];
|
|
379
|
-
console.log(sprintf('%-35s %-35s %4d %3d', item.plugin, item.method, item.priority, item.timeout));
|
|
380
|
-
}
|
|
381
|
-
console.log('');
|
|
382
|
-
}
|
|
383
|
-
process.exit();
|
|
384
|
-
}
|
|
385
|
-
else if (parsed.test) {
|
|
386
|
-
if (!parsed.configs) fail("test option requires config path");
|
|
387
|
+
console.log('')
|
|
388
|
+
}
|
|
389
|
+
process.exit()
|
|
390
|
+
} else if (parsed.test) {
|
|
391
|
+
if (!parsed.configs) fail('test option requires config path')
|
|
387
392
|
|
|
388
|
-
|
|
393
|
+
setupRequire()
|
|
389
394
|
|
|
390
|
-
|
|
391
|
-
|
|
395
|
+
logger = require(path.join(base, 'logger'))
|
|
396
|
+
logger.loglevel = logger.levels.PROTOCOL
|
|
392
397
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
logger.dump_logs(1);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
plugins = require(path.join(base, "plugins"));
|
|
406
|
-
plugins.server = { notes: {} };
|
|
407
|
-
plugins.load_plugins((parsed.test && parsed.test[0] !== 'all') ? parsed.test : null);
|
|
408
|
-
const Connection = require(path.join(base, "connection"));
|
|
409
|
-
// var Transaction = require(path.join(base, "transaction"));
|
|
410
|
-
const Address = require('address-rfc2821').Address;
|
|
411
|
-
const Notes = require('haraka-notes');
|
|
412
|
-
const constants = require('haraka-constants');
|
|
413
|
-
const client = {
|
|
414
|
-
remote: {
|
|
415
|
-
address: ((parsed.ip) ? parsed.ip : '1.2.3.4'),
|
|
416
|
-
port: 1234,
|
|
417
|
-
},
|
|
418
|
-
destroy () {},
|
|
419
|
-
on (event) {},
|
|
420
|
-
end () {
|
|
421
|
-
process.exit();
|
|
422
|
-
},
|
|
423
|
-
write (buf) {},
|
|
424
|
-
resume () {},
|
|
425
|
-
pause () {},
|
|
426
|
-
}
|
|
427
|
-
const server = {
|
|
428
|
-
address () {
|
|
429
|
-
return { port: 25, family: 'ipv4', address: '127.0.0.1' };
|
|
430
|
-
},
|
|
431
|
-
cfg : require('haraka-config').get('smtp.ini'),
|
|
432
|
-
notes: new Notes(),
|
|
433
|
-
}
|
|
434
|
-
const connection = Connection.createConnection(client, server, server.cfg);
|
|
435
|
-
if (parsed['set-relay']) connection.relaying = true;
|
|
436
|
-
|
|
437
|
-
const run_next_hook = function () {
|
|
438
|
-
const args = Array.prototype.slice.call(arguments);
|
|
439
|
-
const code = args.shift();
|
|
440
|
-
if (!parsed['skip-deny'] && code !== constants.ok && code !== constants.cont) {
|
|
441
|
-
plugins.run_hooks('quit', connection);
|
|
442
|
-
}
|
|
443
|
-
else {
|
|
444
|
-
plugins.run_hooks.apply(this, args);
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
connection.connect_respond = function () {
|
|
449
|
-
let helo = 'test.example.com';
|
|
450
|
-
let mode = 'ehlo';
|
|
451
|
-
if (parsed.ehlo) {
|
|
452
|
-
helo = parsed.ehlo;
|
|
453
|
-
}
|
|
454
|
-
else if (parsed.helo) {
|
|
455
|
-
helo = parsed.helo;
|
|
456
|
-
mode = 'helo';
|
|
457
|
-
}
|
|
458
|
-
connection.hello.host = helo;
|
|
459
|
-
run_next_hook(arguments[0], mode, connection, helo);
|
|
460
|
-
}
|
|
461
|
-
connection.helo_respond = connection.ehlo_respond = function () {
|
|
462
|
-
const args = arguments;
|
|
463
|
-
const mail = new Address(((parsed.envfrom) ? parsed.envfrom : 'test@example.com'));
|
|
464
|
-
connection.init_transaction(function () {
|
|
465
|
-
connection.transaction.mail_from = mail;
|
|
466
|
-
run_next_hook(args[0], 'mail', connection, [mail, null]);
|
|
467
|
-
});
|
|
468
|
-
}
|
|
469
|
-
connection.mail_respond = function () {
|
|
470
|
-
const rcpt = new Address(((parsed.envrcpt) ? parsed.envrcpt : 'test@example.com'));
|
|
471
|
-
this.transaction.rcpt_to.push(rcpt);
|
|
472
|
-
run_next_hook(arguments[0], 'rcpt', connection, [rcpt, null]);
|
|
398
|
+
// Attempt to load message early
|
|
399
|
+
let msg
|
|
400
|
+
if (parsed.message) {
|
|
401
|
+
try {
|
|
402
|
+
msg = fs.readFileSync(parsed.message)
|
|
403
|
+
} catch (e) {
|
|
404
|
+
logger.crit(e.message)
|
|
405
|
+
logger.dump_logs(1)
|
|
473
406
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
plugins = require(path.join(base, 'plugins'))
|
|
410
|
+
plugins.server = { notes: {} }
|
|
411
|
+
plugins.load_plugins(parsed.test && parsed.test[0] !== 'all' ? parsed.test : null)
|
|
412
|
+
const Connection = require(path.join(base, 'connection'))
|
|
413
|
+
// var Transaction = require(path.join(base, "transaction"));
|
|
414
|
+
const Address = require('address-rfc2821').Address
|
|
415
|
+
const Notes = require('haraka-notes')
|
|
416
|
+
const constants = require('haraka-constants')
|
|
417
|
+
const client = {
|
|
418
|
+
remote: {
|
|
419
|
+
address: parsed.ip ? parsed.ip : '1.2.3.4',
|
|
420
|
+
port: 1234,
|
|
421
|
+
},
|
|
422
|
+
destroy() {},
|
|
423
|
+
on(event) {},
|
|
424
|
+
end() {
|
|
425
|
+
process.exit()
|
|
426
|
+
},
|
|
427
|
+
write(buf) {},
|
|
428
|
+
resume() {},
|
|
429
|
+
pause() {},
|
|
430
|
+
}
|
|
431
|
+
const server = {
|
|
432
|
+
address() {
|
|
433
|
+
return { port: 25, family: 'ipv4', address: '127.0.0.1' }
|
|
434
|
+
},
|
|
435
|
+
cfg: require('haraka-config').get('smtp.ini'),
|
|
436
|
+
notes: new Notes(),
|
|
437
|
+
}
|
|
438
|
+
const connection = Connection.createConnection(client, server, server.cfg)
|
|
439
|
+
if (parsed['set-relay']) connection.relaying = true
|
|
440
|
+
|
|
441
|
+
const run_next_hook = function () {
|
|
442
|
+
const args = Array.prototype.slice.call(arguments)
|
|
443
|
+
const code = args.shift()
|
|
444
|
+
if (!parsed['skip-deny'] && code !== constants.ok && code !== constants.cont) {
|
|
445
|
+
plugins.run_hooks('quit', connection)
|
|
446
|
+
} else {
|
|
447
|
+
plugins.run_hooks.apply(this, args)
|
|
477
448
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
}
|
|
489
|
-
connection.transaction.add_data(line);
|
|
490
|
-
connection.transaction.data_bytes += line.length;
|
|
491
|
-
}
|
|
492
|
-
}
|
|
493
|
-
else {
|
|
494
|
-
// Add empty data to initialize message_stream
|
|
495
|
-
connection.transaction.add_data('');
|
|
496
|
-
}
|
|
497
|
-
connection.data_done(function () {
|
|
498
|
-
run_next_hook(args[0], 'data_post', connection);
|
|
499
|
-
});
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
connection.connect_respond = function () {
|
|
452
|
+
let helo = 'test.example.com'
|
|
453
|
+
let mode = 'ehlo'
|
|
454
|
+
if (parsed.ehlo) {
|
|
455
|
+
helo = parsed.ehlo
|
|
456
|
+
} else if (parsed.helo) {
|
|
457
|
+
helo = parsed.helo
|
|
458
|
+
mode = 'helo'
|
|
500
459
|
}
|
|
501
|
-
connection.
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
460
|
+
connection.hello.host = helo
|
|
461
|
+
run_next_hook(arguments[0], mode, connection, helo)
|
|
462
|
+
}
|
|
463
|
+
connection.helo_respond = connection.ehlo_respond = function () {
|
|
464
|
+
const args = arguments
|
|
465
|
+
const mail = new Address(parsed.envfrom ? parsed.envfrom : 'test@example.com')
|
|
466
|
+
connection.init_transaction(function () {
|
|
467
|
+
connection.transaction.mail_from = mail
|
|
468
|
+
run_next_hook(args[0], 'mail', connection, [mail, null])
|
|
469
|
+
})
|
|
470
|
+
}
|
|
471
|
+
connection.mail_respond = function () {
|
|
472
|
+
const rcpt = new Address(parsed.envrcpt ? parsed.envrcpt : 'test@example.com')
|
|
473
|
+
this.transaction.rcpt_to.push(rcpt)
|
|
474
|
+
run_next_hook(arguments[0], 'rcpt', connection, [rcpt, null])
|
|
475
|
+
}
|
|
476
|
+
connection.rcpt_respond = function () {
|
|
477
|
+
connection.transaction.parse_body = true
|
|
478
|
+
run_next_hook(arguments[0], 'data', connection)
|
|
479
|
+
}
|
|
480
|
+
connection.data_respond = function () {
|
|
481
|
+
const args = arguments
|
|
482
|
+
// Add data to stream
|
|
483
|
+
if (msg) {
|
|
484
|
+
let buf = msg
|
|
485
|
+
let offset
|
|
486
|
+
while ((offset = utils.indexOfLF(buf)) !== -1) {
|
|
487
|
+
const line = buf.slice(0, offset + 1)
|
|
488
|
+
if (buf.length > offset) {
|
|
489
|
+
buf = buf.slice(offset + 1)
|
|
523
490
|
}
|
|
491
|
+
connection.transaction.add_data(line)
|
|
492
|
+
connection.transaction.data_bytes += line.length
|
|
493
|
+
}
|
|
494
|
+
} else {
|
|
495
|
+
// Add empty data to initialize message_stream
|
|
496
|
+
connection.transaction.add_data('')
|
|
524
497
|
}
|
|
525
|
-
connection.
|
|
526
|
-
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
fail( `No such directory: ${base_dir}\n${err_msg}` );
|
|
498
|
+
connection.data_done(function () {
|
|
499
|
+
run_next_hook(args[0], 'data_post', connection)
|
|
500
|
+
})
|
|
501
|
+
}
|
|
502
|
+
connection.data_post_respond = function () {
|
|
503
|
+
const args = arguments
|
|
504
|
+
// Dump MIME structure and decoded body text?
|
|
505
|
+
function dump_mime_structure(body) {
|
|
506
|
+
console.log(`Found MIME part ${body.ct}`)
|
|
507
|
+
console.log(body.bodytext)
|
|
508
|
+
for (let m = 0, l = body.children.length; m < l; m++) {
|
|
509
|
+
dump_mime_structure(body.children[m])
|
|
510
|
+
}
|
|
539
511
|
}
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
const smtp_json = path.join(base_dir,'config','smtp.json');
|
|
543
|
-
const smtp_yaml = path.join(base_dir,'config','smtp.yaml');
|
|
544
|
-
if (!fs.existsSync(smtp_ini_path) && !fs.existsSync(smtp_json) && !fs.existsSync(smtp_yaml)) {
|
|
545
|
-
fail( `No smtp.ini at: ${smtp_ini_path}\n${err_msg}` );
|
|
512
|
+
if (parsed['dump-mime']) {
|
|
513
|
+
dump_mime_structure(connection.transaction.body)
|
|
546
514
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
for (const d of ['plugins', 'docs', 'config']) {
|
|
556
|
-
utils.mkDir(path.join(pa, d));
|
|
515
|
+
if (parsed['dump-stream']) {
|
|
516
|
+
console.log('STREAM:')
|
|
517
|
+
connection.transaction.message_stream.on('end', function () {
|
|
518
|
+
run_next_hook(args[0], 'queue', connection)
|
|
519
|
+
})
|
|
520
|
+
connection.transaction.message_stream.pipe(process.stdout)
|
|
521
|
+
} else {
|
|
522
|
+
run_next_hook(args[0], 'queue', connection)
|
|
557
523
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
524
|
+
}
|
|
525
|
+
connection.queue_respond = function () {
|
|
526
|
+
run_next_hook(arguments[0], 'queue_ok', connection)
|
|
527
|
+
}
|
|
528
|
+
connection.queue_ok_respond = function () {
|
|
529
|
+
run_next_hook(arguments[0], 'quit', connection)
|
|
530
|
+
}
|
|
531
|
+
} else if (parsed.configs) {
|
|
532
|
+
const haraka_path = path.join(base, 'haraka.js')
|
|
533
|
+
|
|
534
|
+
const base_dir = process.argv[3]
|
|
535
|
+
const err_msg = `Did you install a Haraka config? (haraka -i ${base_dir})`
|
|
536
|
+
if (!fs.existsSync(base_dir)) {
|
|
537
|
+
fail(`No such directory: ${base_dir}\n${err_msg}`)
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
const smtp_ini_path = path.join(base_dir, 'config', 'smtp.ini')
|
|
541
|
+
const smtp_json = path.join(base_dir, 'config', 'smtp.json')
|
|
542
|
+
const smtp_yaml = path.join(base_dir, 'config', 'smtp.yaml')
|
|
543
|
+
if (!fs.existsSync(smtp_ini_path) && !fs.existsSync(smtp_json) && !fs.existsSync(smtp_yaml)) {
|
|
544
|
+
fail(`No smtp.ini at: ${smtp_ini_path}\n${err_msg}`)
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
process.argv[1] = haraka_path
|
|
548
|
+
process.env.HARAKA = parsed.configs
|
|
549
|
+
require(haraka_path)
|
|
550
|
+
} else if (parsed.install) {
|
|
551
|
+
const pa = parsed.install
|
|
552
|
+
utils.mkDir(parsed.install)
|
|
553
|
+
for (const d of ['plugins', 'docs', 'config']) {
|
|
554
|
+
utils.mkDir(path.join(pa, d))
|
|
555
|
+
}
|
|
556
|
+
utils.copyFile(path.join(base, 'docs', 'Plugins.md'), path.join(pa, 'docs', 'Plugins.md'))
|
|
557
|
+
utils.createFile(path.join(pa, 'README'), readme, {}, parsed.force)
|
|
558
|
+
utils.createFile(path.join(pa, 'package.json'), packageJson, {}, parsed.force)
|
|
559
|
+
const bytes = require('crypto').randomBytes(32)
|
|
560
|
+
utils.createFile(path.join(pa, 'config', 'internalcmd_key'), bytes.toString('hex'), {}, parsed.force)
|
|
561
|
+
setupHostname(path.join(pa, 'config'))
|
|
562
|
+
setupBaseConfig(path.join(pa, 'config'))
|
|
563
|
+
} else {
|
|
564
|
+
console.log('\033[31;40mError\033[0m: Undefined or erroneous arguments\n')
|
|
565
|
+
console.log(usage)
|
|
569
566
|
}
|
|
570
567
|
|
|
571
|
-
function send_internal_command
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
568
|
+
function send_internal_command(cmd, done) {
|
|
569
|
+
config = require('haraka-config')
|
|
570
|
+
const key = config.get('internalcmd_key')
|
|
571
|
+
const smtp_ini = config.get('smtp.ini')
|
|
572
|
+
const listen_addrs = require(path.join(base, 'server')).get_listen_addrs(smtp_ini.main)
|
|
573
|
+
const hp = /^\[?([^\]]+)\]?:(\d+)$/.exec(listen_addrs[0])
|
|
574
|
+
if (!hp) throw 'No listen address in smtp.ini'
|
|
575
|
+
// console.log("Connecting to " + listen_addrs[0]);
|
|
576
|
+
const sock = net.connect(hp[2], hp[1], function () {
|
|
577
|
+
sock.once('data', function (data) {
|
|
578
|
+
// this is the greeting. Ignore it...
|
|
579
|
+
sock.write(`INTERNALCMD ${key ? `key:${key} ` : ''}${cmd}\r\n`)
|
|
580
|
+
sock.once('data', function (data2) {
|
|
581
|
+
console.log(data2.toString().replace(/\r?\n$/, ''))
|
|
582
|
+
sock.write('QUIT\r\n')
|
|
583
|
+
sock.once('data', function (data3) {
|
|
584
|
+
sock.end()
|
|
585
|
+
})
|
|
586
|
+
})
|
|
587
|
+
})
|
|
588
|
+
})
|
|
589
|
+
sock.on('end', done)
|
|
593
590
|
}
|