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.
Files changed (160) hide show
  1. package/.prettierignore +4 -0
  2. package/CONTRIBUTORS.md +5 -5
  3. package/Changes.md +69 -50
  4. package/Plugins.md +3 -1
  5. package/README.md +1 -1
  6. package/bin/haraka +475 -478
  7. package/config/outbound.ini +3 -0
  8. package/connection.js +1072 -1108
  9. package/docs/Connection.md +29 -30
  10. package/docs/CoreConfig.md +38 -39
  11. package/docs/CustomReturnCodes.md +0 -1
  12. package/docs/HAProxy.md +2 -2
  13. package/docs/Header.md +1 -1
  14. package/docs/Logging.md +29 -5
  15. package/docs/Outbound.md +93 -78
  16. package/docs/Plugins.md +103 -108
  17. package/docs/Transaction.md +49 -51
  18. package/docs/Tutorial.md +127 -143
  19. package/docs/deprecated/access.md +0 -1
  20. package/docs/deprecated/backscatterer.md +2 -3
  21. package/docs/deprecated/connect.rdns_access.md +18 -27
  22. package/docs/deprecated/data.headers.md +0 -1
  23. package/docs/deprecated/data.nomsgid.md +1 -2
  24. package/docs/deprecated/data.noreceived.md +1 -2
  25. package/docs/deprecated/data.rfc5322_header_checks.md +1 -2
  26. package/docs/deprecated/dkim_sign.md +13 -17
  27. package/docs/deprecated/dkim_verify.md +9 -17
  28. package/docs/deprecated/dnsbl.md +36 -38
  29. package/docs/deprecated/dnswl.md +41 -43
  30. package/docs/deprecated/lookup_rdns.strict.md +21 -34
  31. package/docs/deprecated/mail_from.access.md +17 -25
  32. package/docs/deprecated/mail_from.blocklist.md +9 -12
  33. package/docs/deprecated/mail_from.nobounces.md +1 -2
  34. package/docs/deprecated/rcpt_to.access.md +20 -27
  35. package/docs/deprecated/rcpt_to.blocklist.md +10 -13
  36. package/docs/deprecated/rcpt_to.routes.md +0 -1
  37. package/docs/deprecated/rdns.regexp.md +13 -15
  38. package/docs/plugins/aliases.md +89 -89
  39. package/docs/plugins/auth/auth_bridge.md +5 -7
  40. package/docs/plugins/auth/auth_ldap.md +11 -14
  41. package/docs/plugins/auth/auth_proxy.md +10 -12
  42. package/docs/plugins/auth/auth_vpopmaild.md +5 -6
  43. package/docs/plugins/auth/flat_file.md +4 -4
  44. package/docs/plugins/block_me.md +3 -3
  45. package/docs/plugins/data.signatures.md +1 -2
  46. package/docs/plugins/delay_deny.md +3 -4
  47. package/docs/plugins/max_unrecognized_commands.md +4 -4
  48. package/docs/plugins/prevent_credential_leaks.md +6 -6
  49. package/docs/plugins/process_title.md +18 -18
  50. package/docs/plugins/queue/deliver.md +2 -3
  51. package/docs/plugins/queue/discard.md +4 -4
  52. package/docs/plugins/queue/lmtp.md +1 -3
  53. package/docs/plugins/queue/qmail-queue.md +7 -9
  54. package/docs/plugins/queue/quarantine.md +16 -21
  55. package/docs/plugins/queue/rabbitmq.md +8 -11
  56. package/docs/plugins/queue/rabbitmq_amqplib.md +43 -39
  57. package/docs/plugins/queue/smtp_bridge.md +7 -10
  58. package/docs/plugins/queue/smtp_forward.md +42 -34
  59. package/docs/plugins/queue/smtp_proxy.md +30 -29
  60. package/docs/plugins/queue/test.md +1 -3
  61. package/docs/plugins/rcpt_to.in_host_list.md +6 -6
  62. package/docs/plugins/rcpt_to.max_count.md +1 -1
  63. package/docs/plugins/record_envelope_addresses.md +3 -3
  64. package/docs/plugins/reseed_rng.md +6 -6
  65. package/docs/plugins/status.md +9 -8
  66. package/docs/plugins/tarpit.md +7 -11
  67. package/docs/plugins/tls.md +12 -17
  68. package/docs/plugins/toobusy.md +4 -4
  69. package/docs/plugins/xclient.md +3 -3
  70. package/docs/tutorials/Migrating_from_v1_to_v2.md +19 -41
  71. package/docs/tutorials/SettingUpOutbound.md +6 -9
  72. package/endpoint.js +35 -38
  73. package/eslint.config.mjs +22 -19
  74. package/haraka.js +42 -47
  75. package/host_pool.js +75 -79
  76. package/http/html/404.html +45 -49
  77. package/http/html/index.html +39 -28
  78. package/http/package.json +2 -4
  79. package/line_socket.js +27 -28
  80. package/logger.js +182 -201
  81. package/outbound/client_pool.js +34 -27
  82. package/outbound/config.js +64 -59
  83. package/outbound/fsync_writestream.js +24 -25
  84. package/outbound/hmail.js +888 -835
  85. package/outbound/index.js +194 -187
  86. package/outbound/qfile.js +49 -52
  87. package/outbound/queue.js +197 -190
  88. package/outbound/timer_queue.js +41 -43
  89. package/outbound/tls.js +68 -61
  90. package/outbound/todo.js +11 -11
  91. package/package.json +38 -33
  92. package/plugins/.eslintrc.yaml +0 -1
  93. package/plugins/auth/auth_base.js +123 -127
  94. package/plugins/auth/auth_bridge.js +7 -7
  95. package/plugins/auth/auth_proxy.js +121 -126
  96. package/plugins/auth/auth_vpopmaild.js +84 -85
  97. package/plugins/auth/flat_file.js +18 -17
  98. package/plugins/block_me.js +31 -31
  99. package/plugins/data.signatures.js +13 -13
  100. package/plugins/delay_deny.js +65 -61
  101. package/plugins/prevent_credential_leaks.js +23 -23
  102. package/plugins/process_title.js +125 -128
  103. package/plugins/profile.js +5 -5
  104. package/plugins/queue/deliver.js +3 -3
  105. package/plugins/queue/discard.js +13 -14
  106. package/plugins/queue/lmtp.js +16 -17
  107. package/plugins/queue/qmail-queue.js +54 -55
  108. package/plugins/queue/quarantine.js +68 -70
  109. package/plugins/queue/rabbitmq.js +80 -87
  110. package/plugins/queue/rabbitmq_amqplib.js +75 -54
  111. package/plugins/queue/smtp_bridge.js +16 -16
  112. package/plugins/queue/smtp_forward.js +175 -179
  113. package/plugins/queue/smtp_proxy.js +69 -71
  114. package/plugins/queue/test.js +9 -9
  115. package/plugins/rcpt_to.host_list_base.js +30 -34
  116. package/plugins/rcpt_to.in_host_list.js +19 -19
  117. package/plugins/record_envelope_addresses.js +4 -4
  118. package/plugins/reseed_rng.js +4 -4
  119. package/plugins/status.js +90 -97
  120. package/plugins/tarpit.js +25 -14
  121. package/plugins/tls.js +68 -68
  122. package/plugins/toobusy.js +21 -23
  123. package/plugins/xclient.js +51 -53
  124. package/plugins.js +276 -293
  125. package/rfc1869.js +30 -35
  126. package/server.js +308 -299
  127. package/smtp_client.js +244 -228
  128. package/test/.eslintrc.yaml +0 -1
  129. package/test/connection.js +127 -134
  130. package/test/endpoint.js +53 -47
  131. package/test/fixtures/line_socket.js +12 -12
  132. package/test/fixtures/util_hmailitem.js +89 -85
  133. package/test/host_pool.js +90 -92
  134. package/test/installation/plugins/base_plugin.js +2 -2
  135. package/test/installation/plugins/folder_plugin/index.js +2 -3
  136. package/test/installation/plugins/inherits.js +3 -3
  137. package/test/installation/plugins/load_first.js +2 -3
  138. package/test/installation/plugins/plugin.js +1 -3
  139. package/test/installation/plugins/tls.js +2 -4
  140. package/test/logger.js +135 -116
  141. package/test/outbound/hmail.js +49 -35
  142. package/test/outbound/index.js +118 -101
  143. package/test/outbound/qfile.js +51 -53
  144. package/test/outbound_bounce_net_errors.js +84 -69
  145. package/test/outbound_bounce_rfc3464.js +235 -165
  146. package/test/plugins/auth/auth_base.js +420 -279
  147. package/test/plugins/auth/auth_vpopmaild.js +38 -39
  148. package/test/plugins/queue/smtp_forward.js +126 -104
  149. package/test/plugins/rcpt_to.host_list_base.js +85 -67
  150. package/test/plugins/rcpt_to.in_host_list.js +159 -112
  151. package/test/plugins/status.js +71 -64
  152. package/test/plugins/tls.js +37 -34
  153. package/test/plugins.js +97 -92
  154. package/test/rfc1869.js +19 -26
  155. package/test/server.js +293 -272
  156. package/test/smtp_client.js +180 -176
  157. package/test/tls_socket.js +62 -66
  158. package/test/transaction.js +159 -160
  159. package/tls_socket.js +331 -333
  160. 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 = 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)
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
- "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,
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
- "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"],
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 (b, dir = 'plugins/') {
86
+ function listPlugins(b, dir = 'plugins/') {
87
+ const inital_dir = path.join(b ?? base, dir)
88
+ const plugin_dirs = [inital_dir]
87
89
 
88
- const inital_dir = path.join((b ?? base), dir);
89
- const plugin_dirs = [ inital_dir ]
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
- let plugin_list = ``
98
- for (const pd of plugin_dirs) {
99
- plugin_list += `\n${pd.match(/plugins.*$/)[0]}\n`;
96
+ let plugin_list = ``
97
+ for (const pd of plugin_dirs) {
98
+ plugin_list += `\n${pd.match(/plugins.*$/)[0]}\n`
100
99
 
101
- for (const d of fs.readdirSync(pd)) {
102
- if (fs.statSync(path.join(pd, d)).isFile() && ~d.search('.js')) {
103
- plugin_list += `\t${d.replace('.js', '')}\n`;
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
- plugin_list += `\nNPM packages (${b ?? base})\n`
109
- const npm_plugins = []
110
- for (const entry of fs.readdirSync(path.join(b ?? base, 'node_modules'))) {
111
- if (!/^haraka-plugin-/.test(entry)) continue
112
- npm_plugins.push(entry.split('-').slice(2).join('-'))
113
- }
114
- plugin_list += `\t${npm_plugins.join('\n\t')}\n`
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
- return plugin_list;
115
+ return plugin_list
117
116
  }
118
117
 
119
118
  // Warning messsage
120
- function warning (msg) {
121
- console.error(`\x1b[31mwarning\x1b[0m: ${msg}`);
119
+ function warning(msg) {
120
+ console.error(`\x1b[31mwarning\x1b[0m: ${msg}`)
122
121
  }
123
122
 
124
- function fail (msg) {
125
- console.error(`\x1b[31merror\x1b[0m: ${msg}`);
126
- process.exit(-1);
123
+ function fail(msg) {
124
+ console.error(`\x1b[31merror\x1b[0m: ${msg}`)
125
+ process.exit(-1)
127
126
  }
128
127
 
129
- function setupHostname (confPath) {
130
- utils.mkDir(confPath);
128
+ function setupHostname(confPath) {
129
+ utils.mkDir(confPath)
131
130
 
132
- const hostname = `${os.hostname()}${os.EOL}`;
131
+ const hostname = `${os.hostname()}${os.EOL}`
133
132
 
134
- ['me','host_list'].forEach(f => {
135
- const cfPath = path.join(confPath, f);
133
+ ;['me', 'host_list'].forEach((f) => {
134
+ const cfPath = path.join(confPath, f)
136
135
 
137
- try { if (fs.statSync(cfPath).isFile()) return; }
138
- catch (ignore) { }
136
+ try {
137
+ if (fs.statSync(cfPath).isFile()) return
138
+ } catch (ignore) {}
139
139
 
140
- try { fs.writeFileSync(cfPath, hostname); }
141
- catch (err) { warning(`Unable to write to config/${f}: ${err.message}`); }
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 (confPath) {
146
- utils.copyFile(path.join(base, 'config', 'smtp.ini'), path.join(confPath, 'smtp.ini'));
147
- utils.copyFile(path.join(base, 'config', 'log.ini'), path.join(confPath, 'log.ini'));
148
- utils.copyFile(path.join(base, 'config', 'plugins'), path.join(confPath, 'plugins'));
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
- process.env.HARAKA = parsed.configs;
154
- try {
155
- require.paths.push(path.join(process.env.HARAKA, 'node_modules'));
156
- }
157
- catch (e) {
158
- process.env.NODE_PATH = process.env.NODE_PATH ?
159
- (`${process.env.NODE_PATH}:${path.join(process.env.HARAKA, 'node_modules')}`)
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
- "name": "haraka_local",
199
- "description": "An SMTP Server",
200
- "version": "0.0.1",
201
- "dependencies": {},
202
- "repository": "",
203
- "license": "MIT"
204
- }, null, 2);
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
- "// %plugin%",
208
- "",
209
- "// documentation via: haraka -c %config% -h plugins/%plugin%",
210
- "",
211
- "// Put your plugin code here",
212
- "// type: `haraka -h Plugins` for documentation on how to create a plugin",
213
- "",
214
- ].join("\n");
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
- "%plugin%",
218
- "========",
219
- "",
220
- "Describe what your plugin does here.",
221
- "",
222
- "Configuration",
223
- "-------------",
224
- "",
225
- "* `config/some_file` - describe what effect this config file has",
226
- "",
227
- ].join("\n");
228
-
229
-
230
- function getHooks () { // see haraka/Haraka#3306
231
- const pi_path = path.resolve(parsed.configs, 'docs', 'Plugins.md')
232
- return fs.readFileSync(pi_path).toString()
233
- .split('## Available Hooks')[1] // discard everything before this string
234
- .split('### rcpt')[0] // discard everything after this string
235
- .match(/\*\s(\S+)/gm) // grab the first word of lines starting with '* '
236
- .map(a => a.replace(/^\* /, '').replace(/\\/g, '')) // strip list prefix and escapes
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
- console.log(`\x1B[32;40mHaraka.js\x1B[0m — Version: ${ver}`);
245
- }
246
- else if (parsed.list) {
247
- console.log(`\x1B[32;40m*global\x1B[0m\n${ listPlugins() }`);
248
- if (parsed.configs) {
249
- console.log(`\x1B[32;40m*local\x1B[0m\n${ listPlugins(parsed.configs) }`);
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
- else if (parsed.help) {
253
- if (parsed.help === 'true') {
254
- console.log(usage);
255
- return
256
- }
257
-
258
- let md_path;
259
- const md_paths = [
260
- path.join(base, 'docs', `${parsed.help}.md`),
261
- path.join(base, 'docs', 'plugins', `${parsed.help}.md`),
262
- path.join(base, 'docs', 'deprecated', `${parsed.help}.md`),
263
- path.join(base, 'node_modules', `haraka-plugin-${parsed.help}`, 'README.md'),
264
- ];
265
- if (parsed.configs) {
266
- md_paths.unshift(path.join(parsed.configs, 'docs', 'plugins', `${parsed.help}.md`));
267
- md_paths.unshift(path.join(parsed.configs, 'docs', `${parsed.help}.md`));
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
- for (let i=0, j=md_paths.length; i<j; i++) {
270
- const _md_path = md_paths[i];
271
- if (fs.existsSync(_md_path)) {
272
- md_path = [_md_path];
273
- break;
274
- }
275
- }
276
- if (!md_path) {
277
- warning(`No documentation found for: ${parsed.help}`);
278
- process.exit();
279
- }
280
- let pager = 'less';
281
- if (process.env.PAGER) {
282
- const pager_split = process.env.PAGER.split(/ +/);
283
- pager = pager_split.shift();
284
- md_path = pager_split.concat(md_path);
285
- }
286
-
287
- const less = child.spawn( pager, md_path, { stdio: 'inherit' } );
288
- less.on('exit', function () {
289
- process.exit(0);
290
- });
291
- }
292
- else if (parsed.configs && parsed.plugin) {
293
- const js_path = path.join(parsed.configs, 'plugins', `${parsed.plugin}.js`);
294
- utils.createFile(js_path, plugin_src, { plugin: parsed.plugin, config: parsed.configs}, parsed.force);
295
-
296
- const doc_path = path.join(parsed.configs, 'docs', 'plugins', `${parsed.plugin}.md`);
297
- utils.createFile(doc_path, plugin_doc, { plugin: parsed.plugin, config: parsed.configs}, parsed.force);
298
-
299
- console.log(`Plugin ${parsed.plugin} created`);
300
- console.log(`Now edit javascript in: ${js_path}`);
301
- console.log(`Add the plugin to config: ${path.join(parsed.configs, 'config', 'plugins')}`);
302
- console.log(`And edit documentation in: ${doc_path}`);
303
- }
304
- else if (parsed.qlist) {
305
- if (!parsed.configs) fail("qlist option requires config path");
306
- process.env.HARAKA = parsed.configs;
307
- logger = require(path.join(base, 'logger'));
308
- if (!parsed.verbose) logger.log = noop // disable logging
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.qstat) {
318
- if (!parsed.configs) fail("qstat option requires config path");
319
-
320
- process.env.HARAKA = parsed.configs;
321
- logger = require(path.join(base, "logger"));
322
- if (!parsed.verbose) logger.log = noop // disable logging
323
- outbound = require(path.join(base, "outbound"));
324
- outbound.stat_queue(function (err, stats) {
325
- console.log(stats);
326
- process.exit();
327
- });
328
- }
329
- else if (parsed.qunstick) {
330
- if (!parsed.configs) fail('qunstick option requires config path');
331
- const domain = parsed.qunstick.toLowerCase();
332
- process.env.HARAKA = parsed.configs;
333
- logger = require(path.join(base, 'logger'));
334
- if (!parsed.verbose) logger.log = noop // disable logging
335
- const cb = function () { process.exit(); }
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
- else if (parsed.qempty) {
361
- if (!parsed.configs) fail('qempty option requires config path');
362
- fail('qempty is unimplemented');
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
- setupRequire();
393
+ setupRequire()
389
394
 
390
- logger = require(path.join(base, 'logger'));
391
- logger.loglevel = logger.levels.PROTOCOL;
395
+ logger = require(path.join(base, 'logger'))
396
+ logger.loglevel = logger.levels.PROTOCOL
392
397
 
393
- // Attempt to load message early
394
- let msg;
395
- if (parsed.message) {
396
- try {
397
- msg = fs.readFileSync(parsed.message);
398
- }
399
- catch (e) {
400
- logger.crit(e.message);
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
- connection.rcpt_respond = function () {
475
- connection.transaction.parse_body = true;
476
- run_next_hook(arguments[0], 'data', connection);
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
- connection.data_respond = function () {
479
- const args = arguments;
480
- // Add data to stream
481
- if (msg) {
482
- let buf = msg;
483
- let offset;
484
- while ((offset = utils.indexOfLF(buf)) !== -1) {
485
- const line = buf.slice(0, offset+1);
486
- if (buf.length > offset) {
487
- buf = buf.slice(offset+1);
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.data_post_respond = function () {
502
- const args = arguments;
503
- // Dump MIME structure and decoded body text?
504
- function dump_mime_structure (body) {
505
- console.log(`Found MIME part ${body.ct}`);
506
- console.log(body.bodytext);
507
- for (let m=0,l=body.children.length; m < l; m++) {
508
- dump_mime_structure(body.children[m]);
509
- }
510
- }
511
- if (parsed['dump-mime']) {
512
- dump_mime_structure(connection.transaction.body);
513
- }
514
- if (parsed['dump-stream']) {
515
- console.log('STREAM:');
516
- connection.transaction.message_stream.on('end', function () {
517
- run_next_hook(args[0], 'queue', connection);
518
- });
519
- connection.transaction.message_stream.pipe(process.stdout);
520
- }
521
- else {
522
- run_next_hook(args[0], 'queue', connection);
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.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
- }
532
- else if (parsed.configs) {
533
- const haraka_path = path.join(base, 'haraka.js');
534
-
535
- const base_dir = process.argv[3];
536
- const err_msg = `Did you install a Haraka config? (haraka -i ${base_dir })`;
537
- if (!fs.existsSync(base_dir)) {
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
- const smtp_ini_path = path.join(base_dir,'config','smtp.ini');
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
- process.argv[1] = haraka_path;
549
- process.env.HARAKA = parsed.configs;
550
- require(haraka_path);
551
- }
552
- else if (parsed.install) {
553
- const pa = parsed.install;
554
- utils.mkDir(parsed.install);
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
- utils.copyFile(path.join(base, 'docs', 'Plugins.md'), path.join(pa, 'docs', 'Plugins.md'));
559
- utils.createFile(path.join(pa, 'README'), readme, {}, parsed.force);
560
- utils.createFile(path.join(pa, 'package.json'), packageJson, {}, parsed.force);
561
- const bytes = require('crypto').randomBytes(32);
562
- utils.createFile(path.join(pa, 'config', 'internalcmd_key'), bytes.toString('hex'), {}, parsed.force);
563
- setupHostname(path.join(pa, 'config'));
564
- setupBaseConfig(path.join(pa, 'config'));
565
- }
566
- else {
567
- console.log("\033[31;40mError\033[0m: Undefined or erroneous arguments\n");
568
- console.log(usage);
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 (cmd, done) {
572
- config = require('haraka-config');
573
- const key = config.get("internalcmd_key");
574
- const smtp_ini = config.get("smtp.ini");
575
- const listen_addrs = require(path.join(base, "server")).get_listen_addrs(smtp_ini.main);
576
- const hp = /^\[?([^\]]+)\]?:(\d+)$/.exec(listen_addrs[0]);
577
- if (!hp) throw "No listen address in smtp.ini";
578
- // console.log("Connecting to " + listen_addrs[0]);
579
- const sock = net.connect(hp[2], hp[1], function () {
580
- sock.once('data', function (data) {
581
- // this is the greeting. Ignore it...
582
- sock.write(`INTERNALCMD ${key ? (`key:${key} `) : ''}${cmd}\r\n`);
583
- sock.once('data', function (data2) {
584
- console.log(data2.toString().replace(/\r?\n$/, ''));
585
- sock.write('QUIT\r\n');
586
- sock.once('data', function (data3) {
587
- sock.end();
588
- })
589
- });
590
- });
591
- });
592
- sock.on('end', done);
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
  }