haraka 0.0.32 → 3.3.0

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 (310) hide show
  1. package/.claude/settings.local.json +28 -0
  2. package/.githooks/pre-commit +41 -0
  3. package/.prettierignore +6 -0
  4. package/.qlty/.gitignore +7 -0
  5. package/.qlty/configs/.shellcheckrc +1 -0
  6. package/.qlty/qlty.toml +15 -0
  7. package/CHANGELOG.md +1872 -62
  8. package/CLAUDE.md +40 -0
  9. package/CONTRIBUTORS.md +34 -0
  10. package/Dockerfile +50 -0
  11. package/GEMINI.md +38 -0
  12. package/LICENSE +2 -1
  13. package/Plugins.md +227 -0
  14. package/README.md +100 -115
  15. package/SECURITY.md +178 -0
  16. package/TODO +22 -0
  17. package/address.js +53 -0
  18. package/bin/haraka +593 -0
  19. package/bin/haraka_grep +32 -0
  20. package/config/aliases +2 -0
  21. package/config/auth_flat_file.ini +7 -0
  22. package/config/auth_vpopmaild.ini +9 -0
  23. package/config/connection.ini +79 -0
  24. package/config/delay_deny.ini +7 -0
  25. package/config/dhparams.pem +8 -0
  26. package/config/host_list +3 -0
  27. package/config/host_list_regex +6 -0
  28. package/config/http.ini +11 -0
  29. package/config/lmtp.ini +7 -0
  30. package/config/log.ini +11 -0
  31. package/config/me +1 -0
  32. package/config/outbound.bounce_message +18 -0
  33. package/config/outbound.bounce_message_html +36 -0
  34. package/config/outbound.bounce_message_image +106 -0
  35. package/config/outbound.ini +24 -0
  36. package/config/plugins +67 -0
  37. package/config/smtp.ini +37 -0
  38. package/config/smtp_bridge.ini +4 -0
  39. package/config/smtp_forward.ini +31 -0
  40. package/config/smtp_proxy.ini +27 -0
  41. package/config/tarpit.timeout +1 -0
  42. package/config/tls.ini +83 -0
  43. package/config/tls_cert.pem +23 -0
  44. package/config/tls_key.pem +28 -0
  45. package/config/watch.ini +12 -0
  46. package/config/xclient.hosts +2 -0
  47. package/connection.js +1863 -0
  48. package/contrib/Haraka.cf +6 -0
  49. package/contrib/Haraka.pm +35 -0
  50. package/contrib/bad_smtp_server.pl +25 -0
  51. package/contrib/bsd-rc.d/haraka +61 -0
  52. package/contrib/debian-init.d/haraka +87 -0
  53. package/contrib/haraka.init +96 -0
  54. package/contrib/haraka.service +23 -0
  55. package/contrib/plugin2npm.sh +81 -0
  56. package/contrib/ubuntu-upstart/haraka.conf +27 -0
  57. package/coverage/coverage-final.json +2 -0
  58. package/coverage/coverage-summary.json +33 -0
  59. package/coverage/tmp/coverage-79131-1779241025146-0.json +1 -0
  60. package/coverage/tmp/coverage-79132-1779240999690-0.json +1 -0
  61. package/coverage/tmp/coverage-79172-1779241000095-0.json +1 -0
  62. package/coverage/tmp/coverage-79210-1779241000156-0.json +1 -0
  63. package/coverage/tmp/coverage-79211-1779241000209-0.json +1 -0
  64. package/coverage/tmp/coverage-79212-1779241000266-0.json +1 -0
  65. package/coverage/tmp/coverage-79213-1779241000441-0.json +1 -0
  66. package/coverage/tmp/coverage-79214-1779241000626-0.json +1 -0
  67. package/coverage/tmp/coverage-79215-1779241000795-0.json +1 -0
  68. package/coverage/tmp/coverage-79216-1779241000965-0.json +1 -0
  69. package/coverage/tmp/coverage-79218-1779241001013-0.json +1 -0
  70. package/coverage/tmp/coverage-79219-1779241001179-0.json +1 -0
  71. package/coverage/tmp/coverage-79220-1779241006249-0.json +1 -0
  72. package/coverage/tmp/coverage-79227-1779241011453-0.json +1 -0
  73. package/coverage/tmp/coverage-79229-1779241011537-0.json +1 -0
  74. package/coverage/tmp/coverage-79230-1779241011647-0.json +1 -0
  75. package/coverage/tmp/coverage-79231-1779241011765-0.json +1 -0
  76. package/coverage/tmp/coverage-79232-1779241011841-0.json +1 -0
  77. package/coverage/tmp/coverage-79233-1779241011909-0.json +1 -0
  78. package/coverage/tmp/coverage-79234-1779241011984-0.json +1 -0
  79. package/coverage/tmp/coverage-79235-1779241012055-0.json +1 -0
  80. package/coverage/tmp/coverage-79236-1779241012230-0.json +1 -0
  81. package/coverage/tmp/coverage-79237-1779241012300-0.json +1 -0
  82. package/coverage/tmp/coverage-79238-1779241012368-0.json +1 -0
  83. package/coverage/tmp/coverage-79239-1779241012438-0.json +1 -0
  84. package/coverage/tmp/coverage-79240-1779241012511-0.json +1 -0
  85. package/coverage/tmp/coverage-79241-1779241012582-0.json +1 -0
  86. package/coverage/tmp/coverage-79242-1779241012652-0.json +1 -0
  87. package/coverage/tmp/coverage-79243-1779241012814-0.json +1 -0
  88. package/coverage/tmp/coverage-79244-1779241012931-0.json +1 -0
  89. package/coverage/tmp/coverage-79245-1779241013007-0.json +1 -0
  90. package/coverage/tmp/coverage-79246-1779241013106-0.json +1 -0
  91. package/coverage/tmp/coverage-79247-1779241013178-0.json +1 -0
  92. package/coverage/tmp/coverage-79248-1779241013244-0.json +1 -0
  93. package/coverage/tmp/coverage-79249-1779241013409-0.json +1 -0
  94. package/coverage/tmp/coverage-79250-1779241013697-0.json +1 -0
  95. package/coverage/tmp/coverage-79251-1779241013847-0.json +1 -0
  96. package/coverage/tmp/coverage-79252-1779241014288-0.json +1 -0
  97. package/coverage/tmp/coverage-79253-1779241014378-0.json +1 -0
  98. package/coverage/tmp/coverage-79254-1779241014428-0.json +1 -0
  99. package/coverage/tmp/coverage-79255-1779241021774-0.json +1 -0
  100. package/coverage/tmp/coverage-80382-1779241021949-0.json +1 -0
  101. package/coverage/tmp/coverage-80383-1779241025019-0.json +1 -0
  102. package/coverage/tmp/coverage-80384-1779241025133-0.json +1 -0
  103. package/docs/Body.md +1 -0
  104. package/docs/Config.md +1 -0
  105. package/docs/Connection.md +153 -0
  106. package/docs/CoreConfig.md +96 -0
  107. package/docs/CustomReturnCodes.md +3 -0
  108. package/docs/HAProxy.md +62 -0
  109. package/docs/Header.md +1 -0
  110. package/docs/Logging.md +129 -0
  111. package/docs/Outbound.md +210 -0
  112. package/docs/Plugins.md +372 -0
  113. package/docs/Results.md +7 -0
  114. package/docs/Transaction.md +135 -0
  115. package/docs/Tutorial.md +183 -0
  116. package/docs/deprecated/access.md +3 -0
  117. package/docs/deprecated/backscatterer.md +9 -0
  118. package/docs/deprecated/connect.rdns_access.md +53 -0
  119. package/docs/deprecated/data.headers.md +3 -0
  120. package/docs/deprecated/data.nomsgid.md +7 -0
  121. package/docs/deprecated/data.noreceived.md +11 -0
  122. package/docs/deprecated/data.rfc5322_header_checks.md +11 -0
  123. package/docs/deprecated/dkim_sign.md +97 -0
  124. package/docs/deprecated/dkim_verify.md +28 -0
  125. package/docs/deprecated/dnsbl.md +80 -0
  126. package/docs/deprecated/dnswl.md +73 -0
  127. package/docs/deprecated/lookup_rdns.strict.md +67 -0
  128. package/docs/deprecated/mail_from.access.md +52 -0
  129. package/docs/deprecated/mail_from.blocklist.md +18 -0
  130. package/docs/deprecated/mail_from.nobounces.md +8 -0
  131. package/docs/deprecated/rcpt_to.access.md +53 -0
  132. package/docs/deprecated/rcpt_to.blocklist.md +18 -0
  133. package/docs/deprecated/rcpt_to.routes.md +3 -0
  134. package/docs/deprecated/rdns.regexp.md +30 -0
  135. package/docs/plugins/aliases.md +3 -0
  136. package/docs/plugins/auth/auth_bridge.md +34 -0
  137. package/docs/plugins/auth/auth_ldap.md +4 -0
  138. package/docs/plugins/auth/auth_proxy.md +36 -0
  139. package/docs/plugins/auth/auth_vpopmaild.md +33 -0
  140. package/docs/plugins/auth/flat_file.md +40 -0
  141. package/docs/plugins/block_me.md +18 -0
  142. package/docs/plugins/data.signatures.md +11 -0
  143. package/docs/plugins/delay_deny.md +23 -0
  144. package/docs/plugins/max_unrecognized_commands.md +6 -0
  145. package/docs/plugins/prevent_credential_leaks.md +22 -0
  146. package/docs/plugins/process_title.md +42 -0
  147. package/docs/plugins/queue/deliver.md +3 -0
  148. package/docs/plugins/queue/discard.md +32 -0
  149. package/docs/plugins/queue/lmtp.md +24 -0
  150. package/docs/plugins/queue/qmail-queue.md +16 -0
  151. package/docs/plugins/queue/quarantine.md +87 -0
  152. package/docs/plugins/queue/smtp_bridge.md +32 -0
  153. package/docs/plugins/queue/smtp_forward.md +127 -0
  154. package/docs/plugins/queue/smtp_proxy.md +68 -0
  155. package/docs/plugins/queue/test.md +7 -0
  156. package/docs/plugins/rcpt_to.in_host_list.md +34 -0
  157. package/docs/plugins/rcpt_to.max_count.md +3 -0
  158. package/docs/plugins/record_envelope_addresses.md +20 -0
  159. package/docs/plugins/relay.md +3 -0
  160. package/docs/plugins/reseed_rng.md +16 -0
  161. package/docs/plugins/status.md +41 -0
  162. package/docs/plugins/tarpit.md +50 -0
  163. package/docs/plugins/tls.md +235 -0
  164. package/docs/plugins/toobusy.md +27 -0
  165. package/docs/plugins/xclient.md +10 -0
  166. package/docs/tutorials/Migrating_from_v1_to_v2.md +96 -0
  167. package/docs/tutorials/SettingUpOutbound.md +62 -0
  168. package/eslint.config.mjs +2 -0
  169. package/haraka.js +74 -0
  170. package/haraka.sh +2 -0
  171. package/http/html/404.html +58 -0
  172. package/http/html/index.html +47 -0
  173. package/http/package.json +21 -0
  174. package/line_socket.js +24 -0
  175. package/logger.js +322 -0
  176. package/outbound/client_pool.js +59 -0
  177. package/outbound/config.js +134 -0
  178. package/outbound/hmail.js +1504 -0
  179. package/outbound/index.js +349 -0
  180. package/outbound/qfile.js +93 -0
  181. package/outbound/queue.js +399 -0
  182. package/outbound/tls.js +85 -0
  183. package/outbound/todo.js +17 -0
  184. package/package.json +91 -29
  185. package/plugins/.eslintrc.yaml +3 -0
  186. package/plugins/auth/auth_base.js +261 -0
  187. package/plugins/auth/auth_bridge.js +20 -0
  188. package/plugins/auth/auth_proxy.js +227 -0
  189. package/plugins/auth/auth_vpopmaild.js +162 -0
  190. package/plugins/auth/flat_file.js +44 -0
  191. package/plugins/block_me.js +88 -0
  192. package/plugins/data.signatures.js +30 -0
  193. package/plugins/delay_deny.js +153 -0
  194. package/plugins/prevent_credential_leaks.js +61 -0
  195. package/plugins/process_title.js +197 -0
  196. package/plugins/profile.js +11 -0
  197. package/plugins/queue/deliver.js +12 -0
  198. package/plugins/queue/discard.js +27 -0
  199. package/plugins/queue/lmtp.js +45 -0
  200. package/plugins/queue/qmail-queue.js +93 -0
  201. package/plugins/queue/quarantine.js +133 -0
  202. package/plugins/queue/smtp_bridge.js +45 -0
  203. package/plugins/queue/smtp_forward.js +371 -0
  204. package/plugins/queue/smtp_proxy.js +142 -0
  205. package/plugins/queue/test.js +15 -0
  206. package/plugins/rcpt_to.host_list_base.js +65 -0
  207. package/plugins/rcpt_to.in_host_list.js +56 -0
  208. package/plugins/record_envelope_addresses.js +17 -0
  209. package/plugins/reseed_rng.js +7 -0
  210. package/plugins/status.js +274 -0
  211. package/plugins/tarpit.js +45 -0
  212. package/plugins/tls.js +164 -0
  213. package/plugins/toobusy.js +47 -0
  214. package/plugins/xclient.js +124 -0
  215. package/plugins.js +604 -0
  216. package/queue/1772642154987_1775581346001_4_82235_TGwgfd_2_mattbook-m3.home.simerson.net +0 -0
  217. package/run_tests +11 -0
  218. package/server.js +827 -0
  219. package/smtp_client.js +504 -0
  220. package/test/.eslintrc.yaml +11 -0
  221. package/test/config/auth_flat_file.ini +5 -0
  222. package/test/config/block_me.recipient +1 -0
  223. package/test/config/block_me.senders +1 -0
  224. package/test/config/dhparams.pem +8 -0
  225. package/test/config/host_list +2 -0
  226. package/test/config/outbound_tls_cert.pem +1 -0
  227. package/test/config/outbound_tls_key.pem +1 -0
  228. package/test/config/plugins +7 -0
  229. package/test/config/smtp.ini +11 -0
  230. package/test/config/smtp_forward.ini +30 -0
  231. package/test/config/tls/example.com/_.example.com.key +28 -0
  232. package/test/config/tls/example.com/example.com.crt +25 -0
  233. package/test/config/tls/haraka.local.pem +51 -0
  234. package/test/config/tls.ini +45 -0
  235. package/test/config/tls_cert.pem +21 -0
  236. package/test/config/tls_key.pem +28 -0
  237. package/test/connection.js +817 -0
  238. package/test/fixtures/haproxy_allowed/config/connection.ini +3 -0
  239. package/test/fixtures/haproxy_disabled/config/connection.ini +3 -0
  240. package/test/fixtures/haproxy_untrusted/config/connection.ini +3 -0
  241. package/test/fixtures/line_socket.js +21 -0
  242. package/test/fixtures/todo_qfile.txt +0 -0
  243. package/test/fixtures/util_hmailitem.js +156 -0
  244. package/test/installation/config/test-plugin-flat +1 -0
  245. package/test/installation/config/test-plugin.ini +10 -0
  246. package/test/installation/config/tls.ini +1 -0
  247. package/test/installation/node_modules/load_first/index.js +5 -0
  248. package/test/installation/node_modules/load_first/package.json +11 -0
  249. package/test/installation/node_modules/test-plugin/config/test-plugin-flat +1 -0
  250. package/test/installation/node_modules/test-plugin/config/test-plugin.ini +9 -0
  251. package/test/installation/node_modules/test-plugin/package.json +5 -0
  252. package/test/installation/node_modules/test-plugin/test-plugin.js +5 -0
  253. package/test/installation/plugins/base_plugin.js +3 -0
  254. package/test/installation/plugins/folder_plugin/index.js +3 -0
  255. package/test/installation/plugins/folder_plugin/package.json +11 -0
  256. package/test/installation/plugins/inherits.js +7 -0
  257. package/test/installation/plugins/load_first.js +3 -0
  258. package/test/installation/plugins/plugin.js +1 -0
  259. package/test/installation/plugins/tls.js +3 -0
  260. package/test/logger.js +217 -0
  261. package/test/loud/config/dhparams.pem +0 -0
  262. package/test/loud/config/tls/goobered.pem +45 -0
  263. package/test/loud/config/tls.ini +43 -0
  264. package/test/mail_specimen/base64-root-part.txt +23 -0
  265. package/test/mail_specimen/varied-fold-lengths-preserve-data.txt +283 -0
  266. package/test/outbound/bounce_net_errors.js +133 -0
  267. package/test/outbound/bounce_rfc3464.js +226 -0
  268. package/test/outbound/hmail.js +210 -0
  269. package/test/outbound/index.js +385 -0
  270. package/test/outbound/qfile.js +124 -0
  271. package/test/outbound/queue.js +325 -0
  272. package/test/plugins/auth/auth_base.js +620 -0
  273. package/test/plugins/auth/auth_bridge.js +80 -0
  274. package/test/plugins/auth/auth_vpopmaild.js +81 -0
  275. package/test/plugins/auth/flat_file.js +123 -0
  276. package/test/plugins/block_me.js +141 -0
  277. package/test/plugins/data.signatures.js +111 -0
  278. package/test/plugins/delay_deny.js +262 -0
  279. package/test/plugins/prevent_credential_leaks.js +174 -0
  280. package/test/plugins/process_title.js +141 -0
  281. package/test/plugins/queue/deliver.js +98 -0
  282. package/test/plugins/queue/discard.js +78 -0
  283. package/test/plugins/queue/lmtp.js +137 -0
  284. package/test/plugins/queue/qmail-queue.js +98 -0
  285. package/test/plugins/queue/quarantine.js +80 -0
  286. package/test/plugins/queue/smtp_bridge.js +152 -0
  287. package/test/plugins/queue/smtp_forward.js +1023 -0
  288. package/test/plugins/queue/smtp_proxy.js +138 -0
  289. package/test/plugins/rcpt_to.host_list_base.js +102 -0
  290. package/test/plugins/rcpt_to.in_host_list.js +186 -0
  291. package/test/plugins/record_envelope_addresses.js +66 -0
  292. package/test/plugins/reseed_rng.js +34 -0
  293. package/test/plugins/status.js +207 -0
  294. package/test/plugins/tarpit.js +90 -0
  295. package/test/plugins/tls.js +86 -0
  296. package/test/plugins/toobusy.js +21 -0
  297. package/test/plugins/xclient.js +119 -0
  298. package/test/plugins.js +230 -0
  299. package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  300. package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  301. package/test/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  302. package/test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  303. package/test/queue/zero-length +0 -0
  304. package/test/server.js +1012 -0
  305. package/test/smtp_client.js +1303 -0
  306. package/test/tls_socket.js +321 -0
  307. package/test/transaction.js +554 -0
  308. package/tls_socket.js +771 -0
  309. package/transaction.js +267 -0
  310. package/lib/index.js +0 -371
@@ -0,0 +1,372 @@
1
+ # Plugins
2
+
3
+ Most aspects of receiving an email in Haraka are controlled by plugins. Mail cannot even be received unless at least a 'rcpt' and 'queue' plugin are enabled.
4
+
5
+ Recipient (_rcpt_) plugins determine if a particular recipient is allowed to be relayed or received for. A _queue_ plugin queues the message somewhere - normally to disk or to an another SMTP server.
6
+
7
+ ## Plugin Lists
8
+
9
+ Get a list of installed plugins by running `haraka -l`. To include locally installed plugins, add the `-c /path/to/config` option.
10
+
11
+ The [top-level Plugins.md](../Plugins.md) is the registry of known plugins — both core and community.
12
+
13
+ Display the help text for a plugin by running:
14
+
15
+ `haraka -h <name> -c /path/to/config`
16
+
17
+ # Writing Haraka Plugins
18
+
19
+ ## Overview
20
+
21
+ ## Anatomy of a Plugin
22
+
23
+ Plugins in Haraka are JS files in the `plugins` directory (legacy) and npm modules in the node_modules directory. See "Plugins as Modules" below.
24
+
25
+ Plugins can be installed in the Haraka global directory (default: /$os/$specific/lib/node_modules/Haraka) or in the Haraka install directory (whatever you chose when you typed `haraka -i`. Example: `haraka -i /etc/haraka`
26
+
27
+ To enable a plugin, add its name to `config/plugins`. For npm packaged plugins, the name does not include the `haraka-plugin` prefix.
28
+
29
+ ### Register
30
+
31
+ Register is the only plugin function that is syncronous and receives no arguments. Its primary purpose is enabling your plugin to register SMTP hooks. It is also used for syncronous initialization tasks such as [loading a config file](https://github.com/haraka/haraka-config). For heavier initialization tasks such as establishing database connections, look to `init_master` and `init_child` instead.
32
+
33
+ ### Register a Hook
34
+
35
+ There are two ways for plugins to register hooks. Both examples register a function on the _rcpt_ hook:
36
+
37
+ 1. The `register_hook` function in register():
38
+
39
+ ```js
40
+ exports.register = function () {
41
+ this.register_hook('rcpt', 'my_rcpt_validate')
42
+ }
43
+
44
+ exports.my_rcpt_validate = function (next, connection, params) {
45
+ // do processing
46
+ next()
47
+ }
48
+ ```
49
+
50
+ 2. The hook\_[$name] syntax:
51
+
52
+ ```js
53
+ exports.hook_rcpt = function (next, connection, params) {
54
+ // do processing
55
+ next()
56
+ }
57
+ ```
58
+
59
+ The register_hook function within `register()` offers a few advantages:
60
+
61
+ 1. register a hook multiple times (see below)
62
+ 2. a unique function name in stack traces
63
+ 3. [a better function name](https://google.com/search?q=programming%20good%20function%20names)
64
+ 4. hooks can be registered conditionally (ie, based on a config setting)
65
+
66
+ ### Register a Hook Multiple Times
67
+
68
+ To register the same hook more than once, call `register_hook()` multiple times with the same hook name:
69
+
70
+ ```js
71
+ exports.register = function () {
72
+ this.register_hook('queue', 'try_queue_my_way')
73
+ this.register_hook('queue', 'try_queue_highway')
74
+ }
75
+ ```
76
+
77
+ When `try_queue_my_way()` calls `next()`, the next function registered on hook _queue_ will be called, in this case, `try_queue_highway()`.
78
+
79
+ #### Determine hook name
80
+
81
+ When a single function runs on multiple hooks, the function can check the
82
+ _hook_ property of the _connection_ or _hmail_ argument to determine which hook it is running on:
83
+
84
+ ```js
85
+ exports.register = function () {
86
+ this.register_hook('rcpt', 'my_rcpt')
87
+ this.register_hook('rcpt_ok', 'my_rcpt')
88
+ }
89
+
90
+ exports.my_rcpt = function (next, connection, params) {
91
+ const hook_name = connection.hook // rcpt or rcpt_ok
92
+ // email address is in params[0]
93
+ // do processing
94
+ }
95
+ ```
96
+
97
+ ### Next()
98
+
99
+ After registering a hook, functions are called with that hooks arguments (see **Available Hooks** below. The first argument is a callback function, conventionally named `next`. When the function is completed, it calls `next()` and the connection continues. Failing to call `next()` will result in the connection hanging until that plugin's timer expires.
100
+
101
+ `next([code, msg])` accepts two optional parameters:
102
+
103
+ 1. `code` is one of the listed return codes.
104
+ 2. `msg` is a string to send to the client in case of a failure. Use an array to send a multi-line message. `msg` should NOT contain the code number - that is handled by Haraka.
105
+
106
+ #### Next() Return Codes
107
+
108
+ These constants are in your plugin when it is loaded, you do not
109
+ need to define them:
110
+
111
+ - CONT
112
+
113
+ Continue and let other plugins handle this particular hook. This is the
114
+ default. These are identical: `next()` and `next(CONT)`;
115
+
116
+ - DENY - Reject with a 5xx error.
117
+
118
+ - DENYSOFT - Reject with a 4xx error.
119
+
120
+ - DENYDISCONNECT - Reject with a 5xx error and immediately disconnect.
121
+
122
+ - DENYSOFTDISCONNECT - Reject with a 4xx error and immediately disconnect.
123
+
124
+ - DISCONNECT - Immediately disconnect
125
+
126
+ - OK
127
+
128
+ Required by `rcpt` plugins to accept a recipient and `queue` plugins when the queue was successful.
129
+
130
+ After a plugin calls `next(OK)`, no further plugins on that hook will run.
131
+
132
+ Exceptions to next(OK):
133
+
134
+ - connect_init and disconnect hooks are **always called**.
135
+
136
+ - On the deny hook, `next(OK)` overrides the default CONT.
137
+
138
+ - HOOK_NEXT
139
+
140
+ HOOK_NEXT is only available on the `unrecognized_command` hook. It instructs Haraka to run a different plugin hook. The `msg` argument must be set to the name of the hook to be run. Ex: `next(HOOK_NEXT, 'rcpt_ok');`
141
+
142
+ ## Available Hooks
143
+
144
+ These are the hook and their parameters (next excluded):
145
+
146
+ - init_master - called when the main (master) process is started
147
+ - init_child - in cluster, called when a child process is started
148
+ - init_http - called when Haraka is started.
149
+ - init_wss - called after init_http
150
+ - connect_init - used to init data structures, called for *every* connection
151
+ - lookup_rdns - called to look up the rDNS - return the rDNS via `next(OK, rdns)`
152
+ - connect - called after we got rDNS
153
+ - capabilities - called to get the ESMTP capabilities (such as STARTTLS)
154
+ - unrecognized_command - called when the remote end sends a command we don't recognise
155
+ - disconnect - called upon disconnect
156
+ - helo (hostname)
157
+ - ehlo (hostname)
158
+ - quit
159
+ - vrfy
160
+ - noop
161
+ - rset
162
+ - mail ([from, esmtp\_params])
163
+ - rcpt ([to, esmtp\_params])
164
+ - rcpt_ok (to)
165
+ - data - called at the DATA command
166
+ - data_post - called at the end-of-data marker
167
+ - max_data_exceeded - called when the message exceeds connection.max.bytes
168
+ - queue - called to queue the mail
169
+ - queue_outbound - called to queue the mail when connection.relaying is set
170
+ - queue_ok - called when a mail has been queued successfully
171
+ - reset_transaction - called before the transaction is reset (via RSET, or MAIL)
172
+ - deny - called when a plugin returns DENY, DENYSOFT or DENYDISCONNECT
173
+ - get_mx (hmail, domain) - called by outbound to resolve the MX record
174
+ - deferred (hmail, params) - called when an outbound message is deferred
175
+ - bounce (hmail, err) - called when an outbound message bounces
176
+ - delivered (hmail, [host, ip, response, delay, port, mode, ok_recips, secured, authenticated]) - called when outbound mail is delivered
177
+ - send_email (hmail) - called when outbound is about to be sent
178
+ - pre_send_trans_email (fake_connection) - called just before an email is queued to disk with a faked connection object
179
+ - log (logger, log_item) - called for every log message; log plugins (e.g. haraka-plugin-syslog) use this hook to ship logs elsewhere
180
+
181
+ ### rcpt
182
+
183
+ The _rcpt_ hook is slightly special.
184
+
185
+ When **connection.relaying == false** (the default, to avoid being an open relay), a rcpt plugin MUST return `next(OK)` or the sender will receive the error message "I cannot deliver for that user". The default _rcpt_ plugin is **rcpt_to.in_host_list**, which lists the domains for which to accept email.
186
+
187
+ After a _rcpt_ plugin calls `next(OK)`, the _rcpt_ok_ hook is run.
188
+
189
+ If a plugin prior to the _rcpt_ hook sets **connection.relaying = true**, then it is not necessary for a rcpt plugin to call `next(OK)`.
190
+
191
+ ### connect_init
192
+
193
+ The `connect_init` hook is unique in that all return codes are ignored. This is so that plugins that need to do initialization for every connection can be assured they will run. Return values are ignored.
194
+
195
+ ### hook_init_http (next, Server)
196
+
197
+ If http listeners are are enabled in http.ini and the express module loaded, the express library will be located at Server.http.express. More importantly, the express [app / instance](http://expressjs.com/4x/api.html#app) will be located at Server.http.app. Plugins can register routes on the app just as they would with any [Express.js](http://expressjs.com/) app.
198
+
199
+ ### hook_init_wss (next, Server)
200
+
201
+ If express loaded, an attempt is made to load [ws](https://www.npmjs.com/package/ws), the websocket server. If it succeeds, the wss server will be located at Server.http.wss. Because of how websockets work, only one websocket plugin will work at a time. One plugin using wss is [watch](https://github.com/haraka/Haraka/tree/master/plugins/watch).
202
+
203
+ ### pre_send_trans_email (next, fake_connection)
204
+
205
+ The `fake` connection here is a holder for a new transaction object. It only has the log methods and a `transaction` property
206
+ so don't expect it to behave like a a real connection object. This hook is designed so you can add headers and modify mails
207
+ sent via `outbound.send_email()`, see the dkim plugin for an example.
208
+
209
+ ## Hook Order
210
+
211
+ The ordering of hooks is determined by the SMTP protocol. Knowledge of [RFC 5321](http://tools.ietf.org/html/rfc5321) is beneficial.
212
+
213
+ ##### Typical Inbound Connection
214
+
215
+ - hook_connect_init
216
+ - hook_lookup_rdns
217
+ - hook_connect
218
+ - hook_helo **OR** hook_ehlo
219
+ - hook_helo
220
+ - hook_ehlo (when ESMTP is desired, allows extensions such as STARTTLS, AUTH, SIZE etc.)
221
+ - hook_capabilities
222
+ - *hook_unrecognized_command* is run for each ESMTP extension the client requests (e.g. STARTTLS, AUTH etc.)
223
+ - hook_mail
224
+ - hook_rcpt (once per-recipient)
225
+ - hook_rcpt_ok (for every recipient that hook_rcpt returned `next(OK)` for)
226
+ - hook_data
227
+ - _[attachment hooks]_
228
+ - hook_data_post
229
+ - hook_queue **OR** hook_queue_outbound
230
+ - hook_queue_ok (called if hook_queue or hook_queue_outbound returns `next(OK)`)
231
+ - hook_quit **OR** hook_rset **OR** hook_helo **OR** hook_ehlo (after a message has been sent or rejected, the client can disconnect or start a new transaction with RSET, EHLO or HELO)
232
+ - hook_reset_transaction
233
+ - hook_disconnect
234
+
235
+ ##### Typical Outbound mail
236
+
237
+ By 'outbound' we mean messages using Haraka's built-in queue and delivery
238
+ mechanism. The Outbound queue is used when `connection.relaying = true` is set during the transaction and `hook_queue_outbound` is called to queue the message.
239
+
240
+ The Outbound hook ordering mirrors the Inbound hook order above until after `hook_queue_outbound`, which is followed by:
241
+
242
+ - hook_send_email
243
+ - hook_get_mx
244
+ - at least one of:
245
+ - hook_delivered (once per delivery domain with at least one successful recipient)
246
+ - hook_deferred (once per delivery domain where at least one recipient or connection was deferred)
247
+ - hook_bounce (once per delivery domain where the recipient(s) or message was rejected by the destination)
248
+
249
+ ## Plugin Run Order
250
+
251
+ Plugins are run on each hook in the order that they are specified in `config/plugins`. When a plugin returns anything other than `next()` on a hook, all subsequent plugins due to run on that hook are skipped (exceptions: connect_init, disconnect).
252
+
253
+ This is important as some plugins might rely on `results` or `notes` that have been set by plugins that need to run before them. This should be noted in the plugins documentation. Make sure to read it.
254
+
255
+ If you are writing a complex plugin, you may have to split it into multiple plugins to run in a specific order e.g. you want hook_deny to run last after all other plugins and hook_lookup_rdns to run first, then you can explicitly register your hooks and provide a `priority` value which is an integer between -100 (highest priority) to 100 (lowest priority) which defaults to 0 (zero) if not supplied. You can apply a priority to your hook in the following way:
256
+
257
+ ```js
258
+ exports.register = function () {
259
+ this.register_hook('connect', 'do_connect_stuff', -100)
260
+ }
261
+ ```
262
+
263
+ This would ensure that your `do_connect_stuff` function will run before any other
264
+ plugins registered on the `connect` hook, regardless of the order it was
265
+ specified in `config/plugins`.
266
+
267
+ Check the order that the plugins will run on each hook by running:
268
+
269
+ `haraka -o -c /path/to/config`
270
+
271
+ ## Skipping Plugins
272
+
273
+ Plugins can be skipped at runtime by pushing the name of the plugin into the `skip_plugins` array in `transaction.notes`. This array is reset for every transaction and once a plugin is added to the list, it will not run any hooks in that plugin for the remainder of the transaction. For example, one could create a whitelist plugin that skipped `spamassassin` if the sender was in a whitelist.
274
+
275
+ ## Logging
276
+
277
+ Plugins inherit all the logging methods of `logger.js`, which are:
278
+
279
+ - logprotocol
280
+ - logdebug
281
+ - loginfo
282
+ - lognotice
283
+ - logwarn
284
+ - logerror
285
+ - logcrit
286
+ - logalert
287
+ - logemerg
288
+
289
+ If plugins throw an exception when in a hook, the exception will be caught and generate a logcrit level error. However, exceptions will not be caught as gracefully when plugins are running async code. Use error codes for that, log the error, and run your next() function appropriately.
290
+
291
+ ## Sharing State
292
+
293
+ There are several cases where you might need to share information between plugins. This is done using `notes` - there are three types available:
294
+
295
+ - server.notes
296
+
297
+ Available in all plugins. This is created at PID start-up and is shared amongst all plugins on the same PID and listener. Typical uses for notes at this level would be to share database connections between multiple plugins or connection pools etc.
298
+
299
+ - connection.notes
300
+
301
+ Available on any hook that passes 'connection' as a function parameter. This is shared amongst all plugins for a single connection and is destroyed after the client disconnects. Typical uses for notes at this level would be to store information about the connected client e.g. rDNS names, HELO/EHLO, white/black list status etc.
302
+
303
+ - connection.transaction.notes
304
+
305
+ Available on any hook that passes 'connection' as a function parameter between hook_mail and hook_data_post.
306
+ This is shared amongst all plugins for this transaction (e.g. MAIL FROM through until a message is received or the connection is reset). Typical uses for notes at this level would be to store information on things like greylisting which uses client, sender and recipient information etc.
307
+
308
+ - hmail.todo.notes
309
+
310
+ Available on any outbound hook that passes `hmail` as a function parameter. This is the same object as 'connection.transaction.notes', so anything you store in the transaction notes is automatically available in the
311
+ outbound functions here.
312
+
313
+ All of these notes are JS objects - use them as simple key/value store e.g.
314
+
315
+ connection.transaction.notes.test = 'testing';
316
+
317
+ ## Plugins as Modules
318
+
319
+ Plugins as NPM modules are named with the `haraka-plugin` prefix. Therefore, a plugin that frobnobricates might be called `haraka-plugin-frobnobricate` and published to NPM with that name. The prefix is not required in the
320
+ `config/plugins` file.
321
+
322
+ Plugins loaded as NPM modules behave slightly different than plugins loaded as plain JS files.
323
+
324
+ Plain JS plugins have a custom `require()` which allows loading core Haraka modules via specifying `require('./name')` (note the `./` prefix). Although the core modules aren't in the same folder, the custom `require` intercepts
325
+ this and look for core modules. Note that if there is a module in your plugins folder of the same name that will not take preference, so avoid using names similar to core modules.
326
+
327
+ Plugins loaded as modules do not have the special `require()`. To load a core Haraka module you must use `this.haraka_require('name')`. This should also be preferred for plain JS plugins, as the `./` hack is likely to be removed in the future.
328
+
329
+ Plugins loaded as modules are not compiled in the Haraka plugin sandbox, which blocks access to certain globals and provides a global `server` object. To access the `server` object, use `connection.server` instead.
330
+
331
+ Module plugins support default config in their local `config` directory. See the "Default Config and Overrides" section in [haraka-config](https://github.com/haraka/haraka-config#default-config-and-overrides).
332
+
333
+ ### Inheriting from another plugin
334
+
335
+ A plugin can inherit methods from another plugin by calling `plugin.inherits(name)` from its `register()`. The parent's exported methods become available on `this` (without overwriting any methods the child has already defined), and the parent's `register()` runs in the child's context. `rcpt_to.host_list_base` is a typical parent used by multiple `rcpt_to.*` plugins.
336
+
337
+ ```js
338
+ exports.register = function () {
339
+ this.inherits('rcpt_to.host_list_base')
340
+ this.register_hook('rcpt', 'my_rcpt')
341
+ }
342
+ ```
343
+
344
+ ### Deprecated plugin names
345
+
346
+ Some plugin names have been folded into newer packages — for example `connect.fcrdns` is now `fcrdns`, `dnsbl` is now `dns-list`, and `rate_limit` / `max_unrecognized_commands` are now `limit`. Haraka logs a notice and loads the replacement automatically; the full mapping lives in `plugins.js → plugins.deprecated`.
347
+
348
+ ## Shutdown
349
+
350
+ On graceful reload, Haraka will call a plugin's `shutdown` method.
351
+
352
+ This is so you can clear any timers or intervals, or shut down any connections to remote servers. See [Issue 2024](https://github.com/haraka/Haraka/issues/2024).
353
+
354
+ e.g.
355
+
356
+ ```js
357
+ exports.shutdown = function () {
358
+ clearInterval(this._interval)
359
+ }
360
+ ```
361
+
362
+ If you don't implement this in your plugin and have a connection open or a timer running then Haraka will take 30 seconds to shut down and have to forcibly kill your process.
363
+
364
+ Note: This only applies when running with a `nodes=...` value in smtp.ini.
365
+
366
+ ## See also, [Results](Results.md)
367
+
368
+ ## Further Reading
369
+
370
+ Read about the [Connection](Connection.md) object.
371
+
372
+ Outbound hooks are [also documented](Outbound.md).
@@ -0,0 +1,7 @@
1
+ # Results
2
+
3
+ Add, log, retrieve, and share the results of plugin tests.
4
+
5
+ ## Synopsis
6
+
7
+ Moved to [https://github.com/haraka/haraka-results](https://github.com/haraka/haraka-results)
@@ -0,0 +1,135 @@
1
+ # Transaction Object
2
+
3
+ An SMTP transaction begins at `MAIL FROM` and ends at `RSET` or end-of-data (the "final dot"). A connection can carry multiple transactions; the current one is available as `connection.transaction`.
4
+
5
+ ## Properties
6
+
7
+ ### transaction.uuid
8
+
9
+ A unique UUID for this transaction, of the form `<connection.uuid>.N`, where `N` increments per transaction on the connection.
10
+
11
+ ### transaction.mail_from
12
+
13
+ The `MAIL FROM` argument as an [`Address`][address] object.
14
+
15
+ ### transaction.rcpt_to
16
+
17
+ An array of [`Address`][address] objects, one per accepted `RCPT TO`.
18
+
19
+ ### transaction.header
20
+
21
+ The parsed message header. See [haraka-email-message → Header](https://github.com/haraka/email-message#header).
22
+
23
+ ### transaction.body
24
+
25
+ The parsed message body, available only when `parse_body` is `true`. See [haraka-email-message → Body](https://github.com/haraka/email-message#body).
26
+
27
+ ### transaction.message_stream
28
+
29
+ A Node.js `Readable` stream for the message (headers + body). Pipe it into any `Writable` — a socket, file, stdout, or your own stream:
30
+
31
+ ```js
32
+ transaction.message_stream.pipe(writable, options)
33
+ ```
34
+
35
+ `options` may override:
36
+
37
+ | Option | Default | Description |
38
+ | -------------- | -------- | --- |
39
+ | `line_endings` | `"\r\n"` | newline sequence |
40
+ | `dot_stuffed` | `true` | emit SMTP dot-stuffed output |
41
+ | `ending_dot` | `false` | terminate with `.\r\n` (SMTP end-of-data) |
42
+ | `end` | `true` | call `.end()` on the writable when finished |
43
+ | `buffer_size` | `65535` | internal buffer size |
44
+ | `clamd_style` | `false` | ClamAV CLAMSCAN-INSTREAM framing |
45
+
46
+ ```js
47
+ transaction.message_stream.pipe(socket, { ending_dot: true })
48
+ ```
49
+
50
+ ### transaction.data_bytes
51
+
52
+ Number of bytes received during `DATA`.
53
+
54
+ ### transaction.parse_body
55
+
56
+ `false` by default. Set to `true` (in `hook_data` or earlier) to enable MIME body parsing, after which `transaction.body` becomes available. `attachment_hooks()`, `set_banner()`, and `add_body_filter()` set this automatically.
57
+
58
+ ### transaction.discard_data
59
+
60
+ Set to `true` to drop the raw message as it arrives instead of buffering it in `message_stream`. The parsed body and attachments are still available when `parse_body` is `true`. Useful for plugins that only need attachments or text without retaining the whole message.
61
+
62
+ ### transaction.notes
63
+
64
+ A `haraka-notes` instance scoped to this transaction. Use it to pass state between hooks; for structured per-test output prefer `transaction.results`. See [haraka-notes](https://github.com/haraka/haraka-notes).
65
+
66
+ `transaction.notes.skip_plugins` is honoured by the plugin runner — push plugin names into it to bypass them for the remainder of the transaction.
67
+
68
+ ### transaction.results
69
+
70
+ Structured store for plugin results. See [haraka-results](https://github.com/haraka/haraka-results).
71
+
72
+ ### transaction.rcpt_count
73
+
74
+ Per-disposition counters (`accept`, `tempfail`, `reject`) tracking recipients in this transaction.
75
+
76
+ ### transaction.mime_part_count
77
+
78
+ Number of MIME parts seen so far (when `parse_body` is enabled).
79
+
80
+ ### transaction.encoding
81
+
82
+ Character encoding used to convert incoming bytes to strings. Defaults to `'utf8'`.
83
+
84
+ ## Methods
85
+
86
+ ### transaction.add_header(key, value)
87
+
88
+ Append a header to the message.
89
+
90
+ ### transaction.add_leading_header(key, value)
91
+
92
+ Prepend a header to the message. Most plugins want `add_header()`; use this only when ordering matters (e.g. `Received:` chains).
93
+
94
+ ### transaction.remove_header(key)
95
+
96
+ Remove all headers with `key`.
97
+
98
+ ### transaction.add_data(line)
99
+
100
+ Append a raw line to the message. The input must already be in SMTP wire format (CRLF newlines, dot-stuffed). Not the right tool for adding banners or transforming body parts — see `set_banner()` and `add_body_filter()`.
101
+
102
+ ### transaction.attachment_hooks(start)
103
+
104
+ Register a callback fired for each attachment. `start` is called with `(content_type, filename, body, stream)`; `stream` is a Node.js `Readable`. Setting `stream.connection = connection` applies backpressure to the SMTP connection so attachments can be processed before the message ends.
105
+
106
+ ```js
107
+ exports.hook_data = (next, connection) => {
108
+ connection.transaction.attachment_hooks((ct, fn, body, stream) => {
109
+ start_att(connection, ct, fn, body, stream)
110
+ })
111
+ next()
112
+ }
113
+
114
+ function start_att(connection, ct, fn, body, stream) {
115
+ connection.loginfo(`attachment: ${ct} ${fn}`)
116
+ stream.connection = connection // enable backpressure
117
+ stream.pause()
118
+
119
+ require('node:tmp').file((err, path, fd) => {
120
+ const ws = require('node:fs').createWriteStream(path)
121
+ stream.pipe(ws)
122
+ stream.resume()
123
+ })
124
+ }
125
+ ```
126
+
127
+ ### transaction.set_banner(text, html)
128
+
129
+ Append a banner to the end of the message. If `html` is omitted, each newline in `text` is replaced with `<br/>\n` when inserted into HTML parts.
130
+
131
+ ### transaction.add_body_filter(ct_match, filter)
132
+
133
+ Register a filter applied to body parts. `ct_match` is either a regex matched against the content-type line, or a string matched as a prefix (e.g. `/^text\/html/` or `'text/plain'`). `filter` receives `(content_type, encoding, buffer)` and must return a `Buffer` with the replacement body (in the same encoding).
134
+
135
+ [address]: https://github.com/haraka/email-address