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/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,90 +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
|
-
|
|
149
|
-
|
|
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'))
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
function setupRequire
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
(path.join(process.env.HARAKA, 'node_modules'));
|
|
163
|
-
require('module')._initPaths(); // Horrible hack
|
|
164
|
-
}
|
|
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
|
+
}
|
|
165
165
|
}
|
|
166
166
|
|
|
167
|
-
function noop
|
|
167
|
+
function noop() {}
|
|
168
168
|
|
|
169
169
|
const readme = `Haraka
|
|
170
170
|
|
|
@@ -193,402 +193,398 @@ is either the name of a plugin (without the .js extension) or the name of
|
|
|
193
193
|
a core Haraka module, such as 'Connection' or 'Transaction'.
|
|
194
194
|
|
|
195
195
|
To get documentation on writing a plugin type 'haraka -h Plugins'.
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
const packageJson = JSON.stringify(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
+
)
|
|
206
210
|
|
|
207
211
|
const plugin_src = [
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
].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')
|
|
216
220
|
|
|
217
221
|
const plugin_doc = [
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
].join(
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
|
238
244
|
}
|
|
239
245
|
|
|
240
|
-
let config
|
|
241
|
-
let logger
|
|
242
|
-
let outbound
|
|
243
|
-
let plugins
|
|
246
|
+
let config
|
|
247
|
+
let logger
|
|
248
|
+
let outbound
|
|
249
|
+
let plugins
|
|
244
250
|
if (parsed.version) {
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
|
251
279
|
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
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
|
+
)
|
|
269
324
|
}
|
|
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
|
-
|
|
310
|
-
outbound = require(path.join(base, 'outbound'));
|
|
311
|
-
outbound.list_queue(function (err, qlist) {
|
|
312
|
-
for (const todo of qlist) {
|
|
313
|
-
console.log(sprintf("Q: %s rcpt:%d from:%s domain:%s", todo.file, todo.rcpt_to.length, todo.mail_from.toString(), todo.domain));
|
|
314
|
-
}
|
|
315
|
-
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()
|
|
316
365
|
})
|
|
317
|
-
}
|
|
318
|
-
else if (parsed.
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
if (domain == 'true') {
|
|
338
|
-
send_internal_command('flushQueue', cb);
|
|
339
|
-
}
|
|
340
|
-
else {
|
|
341
|
-
send_internal_command(`flushQueue ${domain}`, cb);
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
else if (parsed.graceful) {
|
|
345
|
-
if (!parsed.configs) fail("graceful option requires config path");
|
|
346
|
-
|
|
347
|
-
process.env.HARAKA = parsed.configs;
|
|
348
|
-
logger = require(path.join(base, 'logger'));
|
|
349
|
-
if (!parsed.verbose) logger.log = noop // disable logging
|
|
350
|
-
config = require('haraka-config');
|
|
351
|
-
if (!config.get("smtp.ini").main.nodes) {
|
|
352
|
-
console.log("Graceful restart not possible without `nodes` value in smtp.ini");
|
|
353
|
-
process.exit();
|
|
354
|
-
}
|
|
355
|
-
else {
|
|
356
|
-
send_internal_command('gracefulRestart', () => {
|
|
357
|
-
process.exit();
|
|
358
|
-
});
|
|
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))
|
|
359
386
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
else if (parsed.order) {
|
|
366
|
-
if (!parsed.configs) fail('order option requires config path');
|
|
367
|
-
setupRequire();
|
|
368
|
-
logger = require(path.join(base, 'logger'));
|
|
369
|
-
if (!parsed.verbose) logger.log = noop // disable logging
|
|
370
|
-
plugins = require(path.join(base, 'plugins'));
|
|
371
|
-
plugins.load_plugins();
|
|
372
|
-
console.log('');
|
|
373
|
-
for (const hook of getHooks()) {
|
|
374
|
-
if (!plugins.registered_hooks[hook]) continue;
|
|
375
|
-
console.log(sprintf('%\'--80s', `Hook: ${hook} `));
|
|
376
|
-
console.log(sprintf('%-35s %-35s %-4s %-3s', 'Plugin', 'Method', 'Prio', 'T/O'));
|
|
377
|
-
console.log(sprintf("%'-80s",''));
|
|
378
|
-
for (let p=0; p<plugins.registered_hooks[hook].length; p++) {
|
|
379
|
-
const item = plugins.registered_hooks[hook][p];
|
|
380
|
-
console.log(sprintf('%-35s %-35s %4d %3d', item.plugin, item.method, item.priority, item.timeout));
|
|
381
|
-
}
|
|
382
|
-
console.log('');
|
|
383
|
-
}
|
|
384
|
-
process.exit();
|
|
385
|
-
}
|
|
386
|
-
else if (parsed.test) {
|
|
387
|
-
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')
|
|
388
392
|
|
|
389
|
-
|
|
393
|
+
setupRequire()
|
|
390
394
|
|
|
391
|
-
|
|
392
|
-
|
|
395
|
+
logger = require(path.join(base, 'logger'))
|
|
396
|
+
logger.loglevel = logger.levels.PROTOCOL
|
|
393
397
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
logger.dump_logs(1);
|
|
403
|
-
}
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
plugins = require(path.join(base, "plugins"));
|
|
407
|
-
plugins.server = { notes: {} };
|
|
408
|
-
plugins.load_plugins((parsed.test && parsed.test[0] !== 'all') ? parsed.test : null);
|
|
409
|
-
const Connection = require(path.join(base, "connection"));
|
|
410
|
-
// var Transaction = require(path.join(base, "transaction"));
|
|
411
|
-
const Address = require('address-rfc2821').Address;
|
|
412
|
-
const Notes = require('haraka-notes');
|
|
413
|
-
const constants = require('haraka-constants');
|
|
414
|
-
const client = {
|
|
415
|
-
remote: {
|
|
416
|
-
address: ((parsed.ip) ? parsed.ip : '1.2.3.4'),
|
|
417
|
-
port: 1234,
|
|
418
|
-
},
|
|
419
|
-
destroy () {},
|
|
420
|
-
on (event) {},
|
|
421
|
-
end () {
|
|
422
|
-
process.exit();
|
|
423
|
-
},
|
|
424
|
-
write (buf) {},
|
|
425
|
-
resume () {},
|
|
426
|
-
pause () {},
|
|
427
|
-
}
|
|
428
|
-
const server = {
|
|
429
|
-
address () {
|
|
430
|
-
return { port: 25, family: 'ipv4', address: '127.0.0.1' };
|
|
431
|
-
},
|
|
432
|
-
cfg : require('haraka-config').get('smtp.ini'),
|
|
433
|
-
notes: new Notes(),
|
|
434
|
-
}
|
|
435
|
-
const connection = Connection.createConnection(client, server, server.cfg);
|
|
436
|
-
if (parsed['set-relay']) connection.relaying = true;
|
|
437
|
-
|
|
438
|
-
const run_next_hook = function () {
|
|
439
|
-
const args = Array.prototype.slice.call(arguments);
|
|
440
|
-
const code = args.shift();
|
|
441
|
-
if (!parsed['skip-deny'] && code !== constants.ok && code !== constants.cont) {
|
|
442
|
-
plugins.run_hooks('quit', connection);
|
|
443
|
-
}
|
|
444
|
-
else {
|
|
445
|
-
plugins.run_hooks.apply(this, args);
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
connection.connect_respond = function () {
|
|
450
|
-
let helo = 'test.example.com';
|
|
451
|
-
let mode = 'ehlo';
|
|
452
|
-
if (parsed.ehlo) {
|
|
453
|
-
helo = parsed.ehlo;
|
|
454
|
-
}
|
|
455
|
-
else if (parsed.helo) {
|
|
456
|
-
helo = parsed.helo;
|
|
457
|
-
mode = 'helo';
|
|
458
|
-
}
|
|
459
|
-
connection.hello.host = helo;
|
|
460
|
-
run_next_hook(arguments[0], mode, connection, helo);
|
|
461
|
-
}
|
|
462
|
-
connection.helo_respond = connection.ehlo_respond = function () {
|
|
463
|
-
const args = arguments;
|
|
464
|
-
const mail = new Address(((parsed.envfrom) ? parsed.envfrom : 'test@example.com'));
|
|
465
|
-
connection.init_transaction(function () {
|
|
466
|
-
connection.transaction.mail_from = mail;
|
|
467
|
-
run_next_hook(args[0], 'mail', connection, [mail, null]);
|
|
468
|
-
});
|
|
469
|
-
}
|
|
470
|
-
connection.mail_respond = function () {
|
|
471
|
-
const rcpt = new Address(((parsed.envrcpt) ? parsed.envrcpt : 'test@example.com'));
|
|
472
|
-
this.transaction.rcpt_to.push(rcpt);
|
|
473
|
-
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)
|
|
474
406
|
}
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
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)
|
|
478
448
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
}
|
|
490
|
-
connection.transaction.add_data(line);
|
|
491
|
-
connection.transaction.data_bytes += line.length;
|
|
492
|
-
}
|
|
493
|
-
}
|
|
494
|
-
else {
|
|
495
|
-
// Add empty data to initialize message_stream
|
|
496
|
-
connection.transaction.add_data('');
|
|
497
|
-
}
|
|
498
|
-
connection.data_done(function () {
|
|
499
|
-
run_next_hook(args[0], 'data_post', connection);
|
|
500
|
-
});
|
|
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'
|
|
501
459
|
}
|
|
502
|
-
connection.
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
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)
|
|
524
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('')
|
|
525
497
|
}
|
|
526
|
-
connection.
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
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
|
+
}
|
|
540
511
|
}
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
const smtp_json = path.join(base_dir,'config','smtp.json');
|
|
544
|
-
const smtp_yaml = path.join(base_dir,'config','smtp.yaml');
|
|
545
|
-
if (!fs.existsSync(smtp_ini_path) && !fs.existsSync(smtp_json) && !fs.existsSync(smtp_yaml)) {
|
|
546
|
-
fail( `No smtp.ini at: ${smtp_ini_path}\n${err_msg}` );
|
|
512
|
+
if (parsed['dump-mime']) {
|
|
513
|
+
dump_mime_structure(connection.transaction.body)
|
|
547
514
|
}
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
for (const d of ['plugins', 'docs', 'config']) {
|
|
557
|
-
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)
|
|
558
523
|
}
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
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)
|
|
570
566
|
}
|
|
571
567
|
|
|
572
|
-
function send_internal_command
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
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)
|
|
594
590
|
}
|