Haraka 3.0.3 → 3.0.4

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 (238) hide show
  1. package/.eslintrc.yaml +5 -9
  2. package/.prettierrc.yml +1 -0
  3. package/CONTRIBUTORS.md +11 -0
  4. package/Changes.md +1365 -1214
  5. package/Plugins.md +117 -105
  6. package/README.md +4 -13
  7. package/bin/haraka +197 -298
  8. package/config/auth_flat_file.ini +1 -0
  9. package/config/dhparams.pem +8 -0
  10. package/config/mail_from.is_resolvable.ini +4 -2
  11. package/config/me +1 -0
  12. package/config/outbound.ini +0 -2
  13. package/config/plugins +36 -35
  14. package/config/smtp.ini +0 -1
  15. package/config/smtp.json +17 -0
  16. package/config/tls_cert.pem +23 -0
  17. package/config/tls_key.pem +28 -0
  18. package/connection.js +46 -73
  19. package/contrib/bsd-rc.d/haraka +3 -1
  20. package/contrib/plugin2npm.sh +6 -36
  21. package/docs/CoreConfig.md +2 -2
  22. package/docs/Logging.md +7 -21
  23. package/docs/Outbound.md +104 -201
  24. package/docs/Plugins.md +2 -2
  25. package/docs/Transaction.md +59 -82
  26. package/docs/plugins/queue/smtp_proxy.md +5 -10
  27. package/docs/plugins/tls.md +29 -9
  28. package/endpoint.js +16 -13
  29. package/haraka.js +10 -14
  30. package/host_pool.js +5 -5
  31. package/line_socket.js +3 -4
  32. package/logger.js +44 -28
  33. package/outbound/client_pool.js +27 -23
  34. package/outbound/config.js +4 -6
  35. package/outbound/fsync_writestream.js +1 -1
  36. package/outbound/hmail.js +178 -218
  37. package/outbound/index.js +86 -99
  38. package/outbound/qfile.js +1 -1
  39. package/outbound/queue.js +51 -44
  40. package/outbound/timer_queue.js +3 -2
  41. package/outbound/tls.js +19 -7
  42. package/package.json +59 -48
  43. package/plugins/.eslintrc.yaml +0 -6
  44. package/plugins/auth/auth_base.js +4 -2
  45. package/plugins/auth/auth_proxy.js +14 -12
  46. package/plugins/auth/auth_vpopmaild.js +1 -1
  47. package/plugins/block_me.js +1 -1
  48. package/plugins/data.signatures.js +2 -4
  49. package/plugins/early_talker.js +2 -1
  50. package/plugins/mail_from.is_resolvable.js +65 -135
  51. package/plugins/queue/deliver.js +4 -5
  52. package/plugins/queue/lmtp.js +11 -14
  53. package/plugins/queue/qmail-queue.js +2 -2
  54. package/plugins/queue/quarantine.js +2 -2
  55. package/plugins/queue/rabbitmq.js +16 -17
  56. package/plugins/queue/smtp_forward.js +3 -3
  57. package/plugins/queue/smtp_proxy.js +10 -1
  58. package/plugins/queue/test.js +2 -2
  59. package/plugins/rcpt_to.host_list_base.js +5 -5
  60. package/plugins/rcpt_to.in_host_list.js +2 -2
  61. package/plugins/relay.js +6 -7
  62. package/plugins/reseed_rng.js +1 -1
  63. package/plugins/status.js +37 -33
  64. package/plugins/tls.js +2 -2
  65. package/plugins/xclient.js +3 -2
  66. package/plugins.js +50 -54
  67. package/run_tests +3 -30
  68. package/server.js +190 -190
  69. package/smtp_client.js +30 -23
  70. package/{tests → test}/config/plugins +0 -2
  71. package/{tests → test}/config/smtp.ini +1 -1
  72. package/test/config/tls/example.com/_.example.com.key +28 -0
  73. package/test/config/tls/example.com/example.com.crt +25 -0
  74. package/test/connection.js +302 -0
  75. package/test/endpoint.js +94 -0
  76. package/{tests → test}/fixtures/line_socket.js +1 -1
  77. package/{tests → test}/fixtures/util_hmailitem.js +19 -25
  78. package/{tests → test}/host_pool.js +42 -57
  79. package/test/logger.js +258 -0
  80. package/test/outbound/hmail.js +141 -0
  81. package/test/outbound/index.js +220 -0
  82. package/test/outbound/qfile.js +126 -0
  83. package/test/outbound_bounce_net_errors.js +142 -0
  84. package/{tests → test}/outbound_bounce_rfc3464.js +110 -122
  85. package/test/plugins/auth/auth_base.js +484 -0
  86. package/test/plugins/auth/auth_vpopmaild.js +83 -0
  87. package/test/plugins/early_talker.js +104 -0
  88. package/test/plugins/mail_from.is_resolvable.js +35 -0
  89. package/test/plugins/queue/smtp_forward.js +206 -0
  90. package/test/plugins/rcpt_to.host_list_base.js +122 -0
  91. package/test/plugins/rcpt_to.in_host_list.js +193 -0
  92. package/test/plugins/relay.js +303 -0
  93. package/test/plugins/status.js +130 -0
  94. package/test/plugins/tls.js +70 -0
  95. package/test/plugins.js +228 -0
  96. package/test/rfc1869.js +73 -0
  97. package/test/server.js +491 -0
  98. package/test/smtp_client.js +299 -0
  99. package/test/tls_socket.js +273 -0
  100. package/test/transaction.js +270 -0
  101. package/tls_socket.js +202 -252
  102. package/transaction.js +8 -23
  103. package/CONTRIBUTING.md +0 -1
  104. package/bin/dkimverify +0 -40
  105. package/config/access.domains +0 -13
  106. package/config/attachment.ctype.regex +0 -2
  107. package/config/attachment.filename.regex +0 -1
  108. package/config/avg.ini +0 -5
  109. package/config/bounce.ini +0 -15
  110. package/config/data.headers.ini +0 -61
  111. package/config/dkim/dkim_key_gen.sh +0 -78
  112. package/config/dkim_sign.ini +0 -4
  113. package/config/dkim_verify.ini +0 -7
  114. package/config/dnsbl.ini +0 -23
  115. package/config/greylist.ini +0 -43
  116. package/config/helo.checks.ini +0 -52
  117. package/config/messagesniffer.ini +0 -18
  118. package/config/spamassassin.ini +0 -56
  119. package/dkim.js +0 -614
  120. package/docs/plugins/avg.md +0 -35
  121. package/docs/plugins/bounce.md +0 -69
  122. package/docs/plugins/clamd.md +0 -147
  123. package/docs/plugins/esets.md +0 -8
  124. package/docs/plugins/greylist.md +0 -90
  125. package/docs/plugins/helo.checks.md +0 -135
  126. package/docs/plugins/messagesniffer.md +0 -163
  127. package/docs/plugins/spamassassin.md +0 -180
  128. package/outbound/mx_lookup.js +0 -70
  129. package/plugins/auth/auth_ldap.js +0 -3
  130. package/plugins/avg.js +0 -162
  131. package/plugins/backscatterer.js +0 -25
  132. package/plugins/bounce.js +0 -381
  133. package/plugins/clamd.js +0 -382
  134. package/plugins/data.uribl.js +0 -4
  135. package/plugins/dkim_sign.js +0 -395
  136. package/plugins/dkim_verify.js +0 -62
  137. package/plugins/dns_list_base.js +0 -221
  138. package/plugins/dnsbl.js +0 -146
  139. package/plugins/dnswl.js +0 -58
  140. package/plugins/esets.js +0 -71
  141. package/plugins/graph.js +0 -5
  142. package/plugins/greylist.js +0 -645
  143. package/plugins/helo.checks.js +0 -533
  144. package/plugins/messagesniffer.js +0 -381
  145. package/plugins/rcpt_to.ldap.js +0 -3
  146. package/plugins/rcpt_to.max_count.js +0 -24
  147. package/plugins/spamassassin.js +0 -384
  148. package/tests/config/dkim/example.com/dns +0 -29
  149. package/tests/config/dkim/example.com/private +0 -6
  150. package/tests/config/dkim/example.com/public +0 -4
  151. package/tests/config/dkim/example.com/selector +0 -1
  152. package/tests/config/dkim.private.key +0 -6
  153. package/tests/config/dkim_sign.ini +0 -4
  154. package/tests/config/helo.checks.ini +0 -52
  155. package/tests/connection.js +0 -327
  156. package/tests/endpoint.js +0 -128
  157. package/tests/fixtures/vm_harness.js +0 -59
  158. package/tests/logger.js +0 -327
  159. package/tests/outbound/hmail.js +0 -112
  160. package/tests/outbound/index.js +0 -324
  161. package/tests/outbound/qfile.js +0 -67
  162. package/tests/outbound_bounce_net_errors.js +0 -173
  163. package/tests/plugins/auth/auth_base.js +0 -463
  164. package/tests/plugins/auth/auth_vpopmaild.js +0 -91
  165. package/tests/plugins/bounce.js +0 -307
  166. package/tests/plugins/clamd.js +0 -224
  167. package/tests/plugins/deprecated/relay_acl.js +0 -140
  168. package/tests/plugins/deprecated/relay_all.js +0 -59
  169. package/tests/plugins/dkim_sign.js +0 -315
  170. package/tests/plugins/dkim_signer.js +0 -108
  171. package/tests/plugins/dns_list_base.js +0 -259
  172. package/tests/plugins/dnsbl.js +0 -101
  173. package/tests/plugins/early_talker.js +0 -115
  174. package/tests/plugins/greylist.js +0 -58
  175. package/tests/plugins/helo.checks.js +0 -525
  176. package/tests/plugins/mail_from.is_resolvable.js +0 -116
  177. package/tests/plugins/queue/smtp_forward.js +0 -221
  178. package/tests/plugins/rcpt_to.host_list_base.js +0 -132
  179. package/tests/plugins/rcpt_to.in_host_list.js +0 -218
  180. package/tests/plugins/relay.js +0 -339
  181. package/tests/plugins/spamassassin.js +0 -171
  182. package/tests/plugins/status.js +0 -138
  183. package/tests/plugins/tls.js +0 -84
  184. package/tests/plugins.js +0 -247
  185. package/tests/rfc1869.js +0 -61
  186. package/tests/server.js +0 -510
  187. package/tests/smtp_client/auth.js +0 -105
  188. package/tests/smtp_client/basic.js +0 -101
  189. package/tests/smtp_client.js +0 -80
  190. package/tests/tls_socket.js +0 -333
  191. package/tests/transaction.js +0 -284
  192. /package/docs/{plugins → deprecated}/dkim_sign.md +0 -0
  193. /package/docs/{plugins → deprecated}/dkim_verify.md +0 -0
  194. /package/docs/{plugins → deprecated}/dnsbl.md +0 -0
  195. /package/docs/{plugins → deprecated}/dnswl.md +0 -0
  196. /package/{tests → test}/.eslintrc.yaml +0 -0
  197. /package/{tests → test}/config/auth_flat_file.ini +0 -0
  198. /package/{tests → test}/config/dhparams.pem +0 -0
  199. /package/{tests → test}/config/host_list +0 -0
  200. /package/{tests → test}/config/outbound_tls_cert.pem +0 -0
  201. /package/{tests → test}/config/outbound_tls_key.pem +0 -0
  202. /package/{tests → test}/config/smtp_forward.ini +0 -0
  203. /package/{tests → test}/config/tls/ec.pem +0 -0
  204. /package/{tests → test}/config/tls/haraka.local.pem +0 -0
  205. /package/{tests → test}/config/tls/mismatched.pem +0 -0
  206. /package/{tests → test}/config/tls.ini +0 -0
  207. /package/{tests → test}/config/tls_cert.pem +0 -0
  208. /package/{tests → test}/config/tls_key.pem +0 -0
  209. /package/{tests → test}/fixtures/todo_qfile.txt +0 -0
  210. /package/{tests → test}/installation/config/test-plugin-flat +0 -0
  211. /package/{tests → test}/installation/config/test-plugin.ini +0 -0
  212. /package/{tests → test}/installation/config/tls.ini +0 -0
  213. /package/{tests → test}/installation/node_modules/load_first/index.js +0 -0
  214. /package/{tests → test}/installation/node_modules/load_first/package.json +0 -0
  215. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin-flat +0 -0
  216. /package/{tests → test}/installation/node_modules/test-plugin/config/test-plugin.ini +0 -0
  217. /package/{tests → test}/installation/node_modules/test-plugin/package.json +0 -0
  218. /package/{tests → test}/installation/node_modules/test-plugin/test-plugin.js +0 -0
  219. /package/{tests → test}/installation/plugins/base_plugin.js +0 -0
  220. /package/{tests → test}/installation/plugins/folder_plugin/index.js +0 -0
  221. /package/{tests → test}/installation/plugins/folder_plugin/package.json +0 -0
  222. /package/{tests → test}/installation/plugins/inherits.js +0 -0
  223. /package/{tests → test}/installation/plugins/load_first.js +0 -0
  224. /package/{tests → test}/installation/plugins/plugin.js +0 -0
  225. /package/{tests → test}/installation/plugins/tls.js +0 -0
  226. /package/{tests → test}/loud/config/dhparams.pem +0 -0
  227. /package/{tests → test}/loud/config/tls/goobered.pem +0 -0
  228. /package/{tests → test}/loud/config/tls.ini +0 -0
  229. /package/{tests → test}/mail_specimen/base64-root-part.txt +0 -0
  230. /package/{tests → test}/mail_specimen/varied-fold-lengths-preserve-data.txt +0 -0
  231. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  232. /package/{tests → test}/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  233. /package/{tests → test}/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  234. /package/{tests → test}/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  235. /package/{tests → test}/queue/multibyte +0 -0
  236. /package/{tests → test}/queue/plain +0 -0
  237. /package/{tests → test}/queue/zero-length +0 -0
  238. /package/{tests → test}/test-queue/delete-me +0 -0
package/bin/haraka CHANGED
@@ -1,19 +1,19 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // this script takes inspiration from:
4
- // https://github.com/visionmedia/express/blob/master/bin/express
5
4
  // https://github.com/tnantoka/LooseLeaf/blob/master/bin/looseleaf
6
5
 
7
- const fs = require('fs');
8
- const net = require('net');
9
- const nopt = require('nopt');
10
- const path = require('path');
11
- const os = require('os');
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');
12
11
 
12
+ const nopt = require('nopt');
13
13
  const utils = require('haraka-utils');
14
14
  const sprintf = require('sprintf-js').sprintf;
15
15
  const base = path.join(__dirname, '..');
16
- const ver = JSON.parse(fs.readFileSync(`${base}/package.json`, 'utf8')).version;
16
+ const ver = utils.getVersion(base)
17
17
  const knownOpts = {
18
18
  "version": Boolean,
19
19
  "verbose": Boolean,
@@ -54,67 +54,66 @@ const shortHands = {
54
54
  }
55
55
  const parsed = nopt(knownOpts, shortHands, process.argv, 2);
56
56
 
57
- const usage = [
58
- "\033[32;40mHaraka.js\033[0m A Node.js Email Server project",
59
- "Usage: haraka [options] [path]",
60
- "Options:",
61
- "\t-v, --version \t\tOutputs version number",
62
- "\t-h, --help \t\tOutputs this help message",
63
- "\t-h NAME \t\tShows help for NAME",
64
- "\t-c, --configs \t\tPath to your config directory",
65
- "\t-i, --install \t\tCopies the default configs to a specified dir",
66
- "\t-l, --list \t\tList the plugins bundled with Haraka",
67
- "\t-p, --plugin \t\tGenerate a new plugin with the given name",
68
- "\t-f, --force \t\tForce overwriting of old files",
69
- "\t--qlist \t\tList the outbound queue",
70
- "\t--qstat \t\tGet statistics on the outbound queue",
71
- "\t--qunstick \t\tUnstick (force delivery) for a given domain",
72
- "\t-o, --order \t\tShow all registered plugins and their run order",
73
- "\t-t PLUGIN \t\tPlugin test mode",
74
- "\t--------------- PLUGIN TEST MODE OPTIONS (all optional) --------------",
75
- "\t--ip IP \t\tIP address to use",
76
- "\t--helo HELO \t\tHELO to use",
77
- "\t--ehlo EHLO \t\tEHLO to use",
78
- "\t--envfrom FROM\t\tMAIL FROM to use",
79
- "\t--envfrom TO \t\tRCPT TO(s) to use",
80
- "\t--message FILE\t\tMessage file to use",
81
- "\t--dump-mime \t\tDump the MIME structure and body text",
82
- "\t--dump-stream \t\tDump the MessageStream to stdout",
83
- "\t--skip-deny \t\tContinue running hooks after DENY/DENYSOFT",
84
- "\t--set-relay \t\tSet connection.relaying",
85
- ].join('\n');
86
-
87
-
88
- function listPlugins (b, dir) {
89
-
90
- if (!dir) { dir = "plugins/"; }
91
-
92
- let plist = `${dir}\n`;
93
- const subdirs = [];
94
- const gl = path.join((b ? b : base), dir);
95
- const pd = fs.readdirSync(gl);
96
-
97
- pd.forEach(function (p) {
98
- const stat = fs.statSync(`${gl}/${p}`);
99
- if (stat.isFile() && ~p.search('.js')) {
100
- plist += `\t${p.replace('.js', '')}\n`;
101
- }
102
- else if (stat.isDirectory()) {
103
- subdirs.push(`${dir + p}/`);
57
+ const usage = `\x1B[32;40mHaraka.js\x1B[0m — A Node.js Email Server project
58
+ Usage: haraka [options] [path]
59
+ Options:
60
+ -v, --version \t\tOutputs version number
61
+ -h, --help \t\tOutputs this help message
62
+ -h NAME \t\tShows help for NAME
63
+ -c, --configs \t\tPath to your config directory
64
+ -i, --install \t\tCopies the default configs to a specified dir
65
+ -l, --list \t\tList the plugins bundled with Haraka
66
+ -p, --plugin \t\tGenerate a new plugin with the given name
67
+ -f, --force \t\tForce overwriting of old files
68
+ --qlist \t\tList the outbound queue
69
+ --qstat \t\tGet statistics on the outbound queue
70
+ --qunstick \t\tUnstick (force delivery) for a given domain
71
+ -o, --order \t\tShow all registered plugins and their run order
72
+ -t PLUGIN \t\tPlugin test mode
73
+ --------------- PLUGIN TEST MODE OPTIONS (all optional) --------------
74
+ --ip IP \t\tIP address to use
75
+ --helo HELO \t\tHELO to use
76
+ --ehlo EHLO \t\tEHLO to use
77
+ --envfrom FROM\t\tMAIL FROM to use
78
+ --envfrom TO \t\tRCPT TO(s) to use
79
+ --message FILE\t\tMessage file to use
80
+ --dump-mime \t\tDump the MIME structure and body text
81
+ --dump-stream \t\tDump the MessageStream to stdout
82
+ --skip-deny \t\tContinue running hooks after DENY/DENYSOFT
83
+ --set-relay \t\tSet connection.relaying
84
+ `;
85
+
86
+ function listPlugins (b, dir = 'plugins/') {
87
+
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));
104
94
  }
105
- });
95
+ }
106
96
 
107
- subdirs.forEach(function (s) {
108
- plist += `\n${listPlugins(b, s)}`;
109
- });
97
+ let plugin_list = ``
98
+ for (const pd of plugin_dirs) {
99
+ plugin_list += `\n${pd.match(/plugins.*$/)[0]}\n`;
110
100
 
111
- return plist;
112
- }
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
+ }
106
+ }
113
107
 
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`
114
115
 
115
- // Show message when create
116
- function create (dirPath) {
117
- console.log(`\x1b[32mcreate\x1b[0m: ${dirPath}`);
116
+ return plugin_list;
118
117
  }
119
118
 
120
119
  // Warning messsage
@@ -127,128 +126,73 @@ function fail (msg) {
127
126
  process.exit(-1);
128
127
  }
129
128
 
130
- // Make directory if NOT exist
131
- function mkDir (dstPath) {
132
- try {
133
- if (fs.statSync(dstPath).isDirectory()) return;
134
- }
135
- catch (ignore) {}
136
-
137
- try {
138
- fs.mkdirSync(dstPath, { recursive: true });
139
- create(dstPath);
140
- }
141
- catch (e) {
142
- // File exists
143
- console.error(e);
144
- if (e.errno == 17) {
145
- warning(e.message);
146
- }
147
- else {
148
- throw e;
149
- }
150
- }
151
- }
152
-
153
- // Copy directory
154
- function copyDir (srcPath, dstPath) {
155
-
156
- mkDir(dstPath);
157
- const files = fs.readdirSync(srcPath);
129
+ function setupHostname (confPath) {
130
+ utils.mkDir(confPath);
158
131
 
159
- for (let i=0; i < files.length; i++) {
132
+ const hostname = `${os.hostname()}${os.EOL}`;
160
133
 
161
- // Ignore ".*"
162
- if (/^\./.test(files[i])) continue;
134
+ ['me','host_list'].forEach(f => {
135
+ const cfPath = path.join(confPath, f);
163
136
 
164
- const srcFile = path.join(srcPath, files[i]);
165
- const dstFile = path.join(dstPath, files[i]);
137
+ try { if (fs.statSync(cfPath).isFile()) return; }
138
+ catch (ignore) { }
166
139
 
167
- const srcStat = fs.statSync(srcFile);
140
+ try { fs.writeFileSync(cfPath, hostname); }
141
+ catch (err) { warning(`Unable to write to config/${f}: ${err.message}`); }
142
+ })
143
+ }
168
144
 
169
- // Recursive call If direcotory
170
- if (srcStat.isDirectory()) {
171
- copyDir(srcFile, dstFile);
172
- }
173
- // Copy to dstPath if file
174
- else if (srcStat.isFile()) {
175
- // NOT overwrite file
176
- copyFile(srcFile, dstFile);
177
- }
178
- }
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'));
179
149
  }
180
150
 
181
- function copyFile (srcFile, dstFile) {
151
+ function setupRequire () {
182
152
 
153
+ process.env.HARAKA = parsed.configs;
183
154
  try {
184
- if (fs.statSync(dstFile).isFile()) {
185
- warning(`EEXIST, File exists '${dstFile}'`);
186
- return;
187
- }
188
- throw `EEXIST but not a file: '${dstFile}'`;
155
+ require.paths.push(path.join(process.env.HARAKA, 'node_modules'));
189
156
  }
190
157
  catch (e) {
191
- // File NOT exists
192
- if (e.code == 'ENOENT') {
193
- mkDir(path.dirname(dstFile));
194
- fs.writeFileSync(dstFile, fs.readFileSync(srcFile));
195
- create(dstFile)
196
- }
197
- else {
198
- console.log(`copy ${srcFile} to ${dstFile}`);
199
- throw e;
200
- }
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
201
163
  }
202
164
  }
203
165
 
204
- function setupHostname (confPath) {
205
- mkDir(confPath);
166
+ function noop () {}
206
167
 
207
- const hostname = `${os.hostname()}${os.EOL}`;
168
+ const readme = `Haraka
208
169
 
209
- ['me','host_list'].forEach(f => {
210
- const cfPath = path.join(confPath, f);
170
+ Congratulations on creating a new installation of Haraka.
211
171
 
212
- try { if (fs.statSync(cfPath).isFile()) return; }
213
- catch (ignore) { }
172
+ This directory contains two key directories for how Haraka will function:
214
173
 
215
- try { fs.writeFileSync(cfPath, hostname); }
216
- catch (err) { warning(`Unable to write to config/${f}: ${err.message}`); }
217
- })
218
- }
174
+ - config
219
175
 
220
- function setupBaseConfig (confPath) {
221
- copyFile(path.join(base, 'config', 'smtp.ini'), path.join(confPath, 'smtp.ini'));
222
- copyFile(path.join(base, 'config', 'log.ini'), path.join(confPath, 'log.ini'));
223
- copyFile(path.join(base, 'config', 'plugins'), path.join(confPath, 'plugins'));
224
- copyDir(path.join(base, 'config', 'dkim'), path.join(confPath, 'dkim'));
225
- }
176
+ This directory contains configuration files for Haraka. The
177
+ directory contains the default configuration. You probably want
178
+ to modify some files in here, particularly 'smtp.ini'.
226
179
 
227
- const readme = [
228
- "Haraka",
229
- "",
230
- "Congratulations on creating a new installation of Haraka.",
231
- "",
232
- "This directory contains two key directories for how Haraka will function:",
233
- "",
234
- " - config",
235
- " This directory contains configuration files for Haraka. The",
236
- " directory contains the default configuration. You probably want",
237
- " to modify some files in here, particularly `smtp.ini`.",
238
- " - plugins",
239
- " This directory contains custom plugins which you write to run in",
240
- " Haraka. The plugins which ship with Haraka are still available",
241
- " to use.",
242
- " - docs/plugins",
243
- " This directory contains documentation for your plugins.",
244
- "",
245
- "Documentation for Haraka is available via `haraka -h <name> where the name",
246
- "is either the name of a plugin (without the .js extension) or the name of",
247
- "a core Haraka module, such as `Connection` or `Transaction`.",
248
- "",
249
- "To get documentation on writing a plugin type `haraka -h Plugins`.",
250
- "",
251
- ].join("\n");
180
+ - plugins
181
+
182
+ This directory contains custom plugins which you write to run in
183
+ Haraka. The plugins which ship with Haraka are still available
184
+ to use.
185
+
186
+ - docs/plugins
187
+
188
+ This directory contains documentation for your plugins.
189
+
190
+ Documentation for Haraka is available via 'haraka -h <name>' where the name
191
+ is either the name of a plugin (without the .js extension) or the name of
192
+ a core Haraka module, such as 'Connection' or 'Transaction'.
193
+
194
+ To get documentation on writing a plugin type 'haraka -h Plugins'.
195
+ `;
252
196
 
253
197
  const packageJson = JSON.stringify({
254
198
  "name": "haraka_local",
@@ -282,19 +226,13 @@ const plugin_doc = [
282
226
  "",
283
227
  ].join("\n");
284
228
 
285
- function createFile (filePath, data, info) {
286
- try {
287
- if (fs.existsSync(filePath) && !parsed.force) {
288
- throw `${filePath} already exists`;
289
- }
290
- mkDir(path.dirname(filePath));
291
- const fd = fs.openSync(filePath, 'w');
292
- const output = data.replace(/%(\w+)%/g, function (i, m1) { return info[m1] });
293
- fs.writeSync(fd, output, null);
294
- }
295
- catch (e) {
296
- warning(`Unable to create file: ${e}`);
297
- }
229
+
230
+ function getHooks () { // see haraka/Haraka#3306
231
+ fs.readFileSync('docs/Plugins.md').toString()
232
+ .split('## Available Hooks')[1] // discard everything before this string
233
+ .split('### rcpt')[0] // discard everything after this string
234
+ .match(/\*\s(\S+)/gm) // grab the first word of lines starting with '* '
235
+ .map(a => a.replace(/^\* /, '').replace(/\\/g, '')) // strip list prefix and escapes
298
236
  }
299
237
 
300
238
  let config;
@@ -313,52 +251,49 @@ else if (parsed.list) {
313
251
  else if (parsed.help) {
314
252
  if (parsed.help === 'true') {
315
253
  console.log(usage);
254
+ return
316
255
  }
317
- else {
318
- let md_path;
319
- const md_paths = [
320
- path.join(base, 'docs', `${parsed.help}.md`),
321
- path.join(base, 'docs', 'plugins', `${parsed.help}.md`),
322
- path.join(base, 'docs', 'deprecated', `${parsed.help}.md`),
323
- path.join(base, 'node_modules', `haraka-plugin-${parsed.help}`, 'README.md'),
324
- ];
325
- if (parsed.configs) {
326
- md_paths.unshift(path.join(parsed.configs, 'docs', 'plugins', `${parsed.help}.md`));
327
- md_paths.unshift(path.join(parsed.configs, 'docs', `${parsed.help}.md`));
328
- }
329
- for (let i=0, j=md_paths.length; i<j; i++) {
330
- const _md_path = md_paths[i];
331
- if (fs.existsSync(_md_path)) {
332
- md_path = [_md_path];
333
- break;
334
- }
335
- }
336
- if (!md_path) {
337
- warning(`No documentation found for: ${parsed.help}`);
338
- process.exit();
339
- }
340
- let pager = 'less';
341
- const spawn = require('child_process').spawn;
342
- if (process.env.PAGER) {
343
- const pager_split = process.env.PAGER.split(/ +/);
344
- pager = pager_split.shift();
345
- md_path = pager_split.concat(md_path);
346
- }
347
256
 
348
- const less = spawn( pager, md_path, { stdio: 'inherit' } );
349
- less.on('exit', function () {
350
- process.exit(0);
351
- });
257
+ let md_path;
258
+ const md_paths = [
259
+ path.join(base, 'docs', `${parsed.help}.md`),
260
+ path.join(base, 'docs', 'plugins', `${parsed.help}.md`),
261
+ path.join(base, 'docs', 'deprecated', `${parsed.help}.md`),
262
+ path.join(base, 'node_modules', `haraka-plugin-${parsed.help}`, 'README.md'),
263
+ ];
264
+ if (parsed.configs) {
265
+ md_paths.unshift(path.join(parsed.configs, 'docs', 'plugins', `${parsed.help}.md`));
266
+ md_paths.unshift(path.join(parsed.configs, 'docs', `${parsed.help}.md`));
267
+ }
268
+ for (let i=0, j=md_paths.length; i<j; i++) {
269
+ const _md_path = md_paths[i];
270
+ if (fs.existsSync(_md_path)) {
271
+ md_path = [_md_path];
272
+ break;
273
+ }
352
274
  }
275
+ if (!md_path) {
276
+ warning(`No documentation found for: ${parsed.help}`);
277
+ process.exit();
278
+ }
279
+ let pager = 'less';
280
+ if (process.env.PAGER) {
281
+ const pager_split = process.env.PAGER.split(/ +/);
282
+ pager = pager_split.shift();
283
+ md_path = pager_split.concat(md_path);
284
+ }
285
+
286
+ const less = child.spawn( pager, md_path, { stdio: 'inherit' } );
287
+ less.on('exit', function () {
288
+ process.exit(0);
289
+ });
353
290
  }
354
291
  else if (parsed.configs && parsed.plugin) {
355
292
  const js_path = path.join(parsed.configs, 'plugins', `${parsed.plugin}.js`);
356
- createFile(js_path,
357
- plugin_src, { plugin: parsed.plugin, config: parsed.configs });
293
+ utils.createFile(js_path, plugin_src, { plugin: parsed.plugin, config: parsed.configs}, parsed.force);
358
294
 
359
295
  const doc_path = path.join(parsed.configs, 'docs', 'plugins', `${parsed.plugin}.md`);
360
- createFile(doc_path,
361
- plugin_doc, { plugin: parsed.plugin, config: parsed.configs });
296
+ utils.createFile(doc_path, plugin_doc, { plugin: parsed.plugin, config: parsed.configs}, parsed.force);
362
297
 
363
298
  console.log(`Plugin ${parsed.plugin} created`);
364
299
  console.log(`Now edit javascript in: ${js_path}`);
@@ -366,29 +301,24 @@ else if (parsed.configs && parsed.plugin) {
366
301
  console.log(`And edit documentation in: ${doc_path}`);
367
302
  }
368
303
  else if (parsed.qlist) {
369
- if (!parsed.configs) {
370
- fail("qlist option requires config path");
371
- }
304
+ if (!parsed.configs) fail("qlist option requires config path");
372
305
  process.env.HARAKA = parsed.configs;
373
- logger = require(path.join(base, "logger"));
374
- if (!parsed.verbose)
375
- logger.log = function () {} // disable logging for this
376
- outbound = require(path.join(base, "outbound"));
306
+ logger = require(path.join(base, 'logger'));
307
+ if (!parsed.verbose) logger.log = noop // disable logging
308
+ outbound = require(path.join(base, 'outbound'));
377
309
  outbound.list_queue(function (err, qlist) {
378
- qlist.forEach(function (todo) {
310
+ for (const todo of qlist) {
379
311
  console.log(sprintf("Q: %s rcpt:%d from:%s domain:%s", todo.file, todo.rcpt_to.length, todo.mail_from.toString(), todo.domain));
380
- });
312
+ }
381
313
  process.exit();
382
- });
314
+ })
383
315
  }
384
316
  else if (parsed.qstat) {
385
- if (!parsed.configs) {
386
- fail("qstat option requires config path");
387
- }
317
+ if (!parsed.configs) fail("qstat option requires config path");
318
+
388
319
  process.env.HARAKA = parsed.configs;
389
320
  logger = require(path.join(base, "logger"));
390
- if (!parsed.verbose)
391
- logger.log = function () {} // disable logging for this
321
+ if (!parsed.verbose) logger.log = noop // disable logging
392
322
  outbound = require(path.join(base, "outbound"));
393
323
  outbound.stat_queue(function (err, stats) {
394
324
  console.log(stats);
@@ -396,17 +326,12 @@ else if (parsed.qstat) {
396
326
  });
397
327
  }
398
328
  else if (parsed.qunstick) {
399
- if (!parsed.configs) {
400
- fail("qunstick option requires config path");
401
- }
329
+ if (!parsed.configs) fail('qunstick option requires config path');
402
330
  const domain = parsed.qunstick.toLowerCase();
403
331
  process.env.HARAKA = parsed.configs;
404
- logger = require(path.join(base, "logger"));
405
- if (!parsed.verbose)
406
- logger.log = function () {} // disable logging for this
407
- const cb = function () {
408
- process.exit();
409
- };
332
+ logger = require(path.join(base, 'logger'));
333
+ if (!parsed.verbose) logger.log = noop // disable logging
334
+ const cb = function () { process.exit(); }
410
335
  if (domain == 'true') {
411
336
  send_internal_command('flushQueue', cb);
412
337
  }
@@ -415,54 +340,36 @@ else if (parsed.qunstick) {
415
340
  }
416
341
  }
417
342
  else if (parsed.graceful) {
418
- if (!parsed.configs) {
419
- fail("graceful option requires config path");
420
- }
343
+ if (!parsed.configs) fail("graceful option requires config path");
344
+
421
345
  process.env.HARAKA = parsed.configs;
422
- logger = require(path.join(base, "logger"));
423
- if (!parsed.verbose)
424
- logger.log = function () {} // disable logging for this
346
+ logger = require(path.join(base, 'logger'));
347
+ if (!parsed.verbose) logger.log = noop // disable logging
425
348
  config = require('haraka-config');
426
349
  if (!config.get("smtp.ini").main.nodes) {
427
350
  console.log("Graceful restart not possible without `nodes` value in smtp.ini");
428
351
  process.exit();
429
352
  }
430
353
  else {
431
- send_internal_command('gracefulRestart', function () {
354
+ send_internal_command('gracefulRestart', () => {
432
355
  process.exit();
433
356
  });
434
357
  }
435
358
  }
436
359
  else if (parsed.qempty) {
437
- if (!parsed.configs) {
438
- fail("qempty option requires config path");
439
- }
440
- fail("qempty is unimplemented");
360
+ if (!parsed.configs) fail('qempty option requires config path');
361
+ fail('qempty is unimplemented');
441
362
  }
442
363
  else if (parsed.order) {
443
- if (!parsed.configs) {
444
- fail("order option requires config path");
445
- }
446
- process.env.HARAKA = parsed.configs;
447
- try {
448
- require.paths.push(path.join(process.env.HARAKA, 'node_modules'));
449
- }
450
- catch (e) {
451
- process.env.NODE_PATH = process.env.NODE_PATH ?
452
- (`${process.env.NODE_PATH}:${path.join(process.env.HARAKA, 'node_modules')}`)
453
- :
454
- (path.join(process.env.HARAKA, 'node_modules'));
455
- require('module')._initPaths(); // Horrible hack
456
- }
457
- logger = require(path.join(base, "logger"));
458
- if (!parsed.verbose)
459
- logger.log = function () {} // disable logging for this
460
- plugins = require(path.join(base, "plugins"));
364
+ if (!parsed.configs) fail('order option requires config path');
365
+ setupRequire();
366
+ logger = require(path.join(base, 'logger'));
367
+ if (!parsed.verbose) logger.log = noop // disable logging
368
+ plugins = require(path.join(base, 'plugins'));
461
369
  plugins.load_plugins();
462
370
  console.log('');
463
- const hooks = Object.keys(plugins.registered_hooks);
464
- for (let h = 0; h < hooks.length; h++) {
465
- const hook = hooks[h];
371
+ for (const hook of getHooks()) {
372
+ if (!plugins.registered_hooks[hook]) continue;
466
373
  console.log(sprintf('%\'--80s', `Hook: ${hook} `));
467
374
  console.log(sprintf('%-35s %-35s %-4s %-3s', 'Plugin', 'Method', 'Prio', 'T/O'));
468
375
  console.log(sprintf("%'-80s",''));
@@ -475,21 +382,11 @@ else if (parsed.order) {
475
382
  process.exit();
476
383
  }
477
384
  else if (parsed.test) {
478
- if (!parsed.configs) {
479
- fail("test option requires config path");
480
- }
481
- process.env.HARAKA = parsed.configs;
482
- try {
483
- require.paths.push(path.join(process.env.HARAKA, 'node_modules'));
484
- }
485
- catch (e) {
486
- process.env.NODE_PATH = process.env.NODE_PATH ?
487
- (`${process.env.NODE_PATH}:${path.join(process.env.HARAKA, 'node_modules')}`)
488
- :
489
- (path.join(process.env.HARAKA, 'node_modules'));
490
- require('module')._initPaths(); // Horrible hack
491
- }
492
- logger = require(path.join(base, "logger"));
385
+ if (!parsed.configs) fail("test option requires config path");
386
+
387
+ setupRequire();
388
+
389
+ logger = require(path.join(base, 'logger'));
493
390
  logger.loglevel = logger.levels.PROTOCOL;
494
391
 
495
392
  // Attempt to load message early
@@ -499,7 +396,7 @@ else if (parsed.test) {
499
396
  msg = fs.readFileSync(parsed.message);
500
397
  }
501
398
  catch (e) {
502
- logger.logcrit(e.message);
399
+ logger.crit(e.message);
503
400
  logger.dump_logs(1);
504
401
  }
505
402
  }
@@ -513,10 +410,12 @@ else if (parsed.test) {
513
410
  const Notes = require('haraka-notes');
514
411
  const constants = require('haraka-constants');
515
412
  const client = {
516
- remoteAddress: ((parsed.ip) ? parsed.ip : '1.2.3.4'),
517
- remotePort: 1234,
413
+ remote: {
414
+ address: ((parsed.ip) ? parsed.ip : '1.2.3.4'),
415
+ port: 1234,
416
+ },
518
417
  destroy () {},
519
- on (event, done) {},
418
+ on (event) {},
520
419
  end () {
521
420
  process.exit();
522
421
  },
@@ -651,14 +550,14 @@ else if (parsed.configs) {
651
550
  }
652
551
  else if (parsed.install) {
653
552
  const pa = parsed.install;
654
- mkDir(parsed.install);
655
- mkDir(path.join(pa, 'plugins'));
656
- mkDir(path.join(pa, 'docs'));
657
- mkDir(path.join(pa, 'config'));
658
- createFile(path.join(pa, 'README'), readme);
659
- createFile(path.join(pa, 'package.json'), packageJson);
553
+ utils.mkDir(parsed.install);
554
+ for (const d of ['plugins', 'docs', 'config']) {
555
+ utils.mkDir(path.join(pa, d));
556
+ }
557
+ utils.createFile(path.join(pa, 'README'), readme, {}, parsed.force);
558
+ utils.createFile(path.join(pa, 'package.json'), packageJson, {}, parsed.force);
660
559
  const bytes = require('crypto').randomBytes(32);
661
- createFile(path.join(pa, 'config', 'internalcmd_key'), bytes.toString('hex'));
560
+ utils.createFile(path.join(pa, 'config', 'internalcmd_key'), bytes.toString('hex'), {}, parsed.force);
662
561
  setupHostname(path.join(pa, 'config'));
663
562
  setupBaseConfig(path.join(pa, 'config'));
664
563
  }
@@ -1,3 +1,4 @@
1
+
1
2
  [core]
2
3
  methods=CRAM-MD5
3
4
  ; constrain_sender=true
@@ -0,0 +1,8 @@
1
+ -----BEGIN DH PARAMETERS-----
2
+ MIIBDAKCAQEApFk8xNOjLnepwoVTwGBOtmR25KqRjdF3A/DPb+enmE6Kmwtr5KCP
3
+ EwdaSyjLVFMqE9DQzMdfl5mpU+1HghTBRmiUIWnpUWhkXJsGVYvMN/ny6aVO8zuO
4
+ K+7RtqqPo0Cop6ayMmMYg+cFKTEdP6B3LDZgrTRUy1jLVq2RsZajOn+pQMQjCgjK
5
+ bF44ctsUgc3RRL5bO/bsyh+N+KC0LpwcsnvNxX8lwVry9+4uPwH1j/PORAt1AxvT
6
+ AEW4PET1C44IoocclLKXA4jaGxLW8Bhhf875KnH8ACzi24Wn0doIbPzm5KY2nSEw
7
+ e5tD4DLWs6jk66lRKJ/WtEKMwxgum8SFDwIBAgICAOE=
8
+ -----END DH PARAMETERS-----