haraka 0.0.33 → 3.3.1

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 (254) hide show
  1. package/.githooks/pre-commit +41 -0
  2. package/.prettierignore +7 -0
  3. package/.qlty/.gitignore +7 -0
  4. package/.qlty/configs/.shellcheckrc +1 -0
  5. package/.qlty/qlty.toml +15 -0
  6. package/CHANGELOG.md +1898 -0
  7. package/CONTRIBUTORS.md +34 -0
  8. package/Dockerfile +50 -0
  9. package/LICENSE +22 -0
  10. package/Plugins.md +227 -0
  11. package/README.md +119 -4
  12. package/SECURITY.md +178 -0
  13. package/TODO +22 -0
  14. package/bin/haraka +593 -0
  15. package/bin/haraka_grep +32 -0
  16. package/config/aliases +2 -0
  17. package/config/auth_flat_file.ini +7 -0
  18. package/config/auth_vpopmaild.ini +9 -0
  19. package/config/connection.ini +79 -0
  20. package/config/delay_deny.ini +7 -0
  21. package/config/host_list +3 -0
  22. package/config/host_list_regex +6 -0
  23. package/config/http.ini +11 -0
  24. package/config/lmtp.ini +7 -0
  25. package/config/log.ini +11 -0
  26. package/config/outbound.bounce_message +18 -0
  27. package/config/outbound.bounce_message_html +36 -0
  28. package/config/outbound.bounce_message_image +106 -0
  29. package/config/outbound.ini +24 -0
  30. package/config/plugins +67 -0
  31. package/config/smtp.ini +37 -0
  32. package/config/smtp_bridge.ini +4 -0
  33. package/config/smtp_forward.ini +31 -0
  34. package/config/smtp_proxy.ini +27 -0
  35. package/config/tarpit.timeout +1 -0
  36. package/config/tls.ini +83 -0
  37. package/config/watch.ini +12 -0
  38. package/config/xclient.hosts +2 -0
  39. package/connection.js +1865 -0
  40. package/contrib/Haraka.cf +6 -0
  41. package/contrib/Haraka.pm +35 -0
  42. package/contrib/bad_smtp_server.pl +25 -0
  43. package/contrib/bsd-rc.d/haraka +63 -0
  44. package/contrib/debian-init.d/haraka +87 -0
  45. package/contrib/haraka.init +96 -0
  46. package/contrib/haraka.service +23 -0
  47. package/contrib/plugin2npm.sh +81 -0
  48. package/contrib/ubuntu-upstart/haraka.conf +27 -0
  49. package/docs/Body.md +1 -0
  50. package/docs/Config.md +1 -0
  51. package/docs/Connection.md +153 -0
  52. package/docs/CoreConfig.md +96 -0
  53. package/docs/CustomReturnCodes.md +3 -0
  54. package/docs/HAProxy.md +62 -0
  55. package/docs/Header.md +1 -0
  56. package/docs/Logging.md +129 -0
  57. package/docs/Outbound.md +210 -0
  58. package/docs/Plugins.md +372 -0
  59. package/docs/Results.md +7 -0
  60. package/docs/Transaction.md +135 -0
  61. package/docs/Tutorial.md +183 -0
  62. package/docs/deprecated/access.md +3 -0
  63. package/docs/deprecated/backscatterer.md +9 -0
  64. package/docs/deprecated/connect.rdns_access.md +53 -0
  65. package/docs/deprecated/data.headers.md +3 -0
  66. package/docs/deprecated/data.nomsgid.md +7 -0
  67. package/docs/deprecated/data.noreceived.md +11 -0
  68. package/docs/deprecated/data.rfc5322_header_checks.md +11 -0
  69. package/docs/deprecated/dkim_sign.md +97 -0
  70. package/docs/deprecated/dkim_verify.md +28 -0
  71. package/docs/deprecated/dnsbl.md +80 -0
  72. package/docs/deprecated/dnswl.md +73 -0
  73. package/docs/deprecated/lookup_rdns.strict.md +67 -0
  74. package/docs/deprecated/mail_from.access.md +52 -0
  75. package/docs/deprecated/mail_from.blocklist.md +18 -0
  76. package/docs/deprecated/mail_from.nobounces.md +8 -0
  77. package/docs/deprecated/rcpt_to.access.md +53 -0
  78. package/docs/deprecated/rcpt_to.blocklist.md +18 -0
  79. package/docs/deprecated/rcpt_to.routes.md +3 -0
  80. package/docs/deprecated/rdns.regexp.md +30 -0
  81. package/docs/plugins/aliases.md +3 -0
  82. package/docs/plugins/auth/auth_bridge.md +34 -0
  83. package/docs/plugins/auth/auth_ldap.md +4 -0
  84. package/docs/plugins/auth/auth_proxy.md +36 -0
  85. package/docs/plugins/auth/auth_vpopmaild.md +33 -0
  86. package/docs/plugins/auth/flat_file.md +40 -0
  87. package/docs/plugins/block_me.md +18 -0
  88. package/docs/plugins/data.signatures.md +11 -0
  89. package/docs/plugins/delay_deny.md +23 -0
  90. package/docs/plugins/max_unrecognized_commands.md +6 -0
  91. package/docs/plugins/prevent_credential_leaks.md +22 -0
  92. package/docs/plugins/process_title.md +42 -0
  93. package/docs/plugins/queue/deliver.md +3 -0
  94. package/docs/plugins/queue/discard.md +32 -0
  95. package/docs/plugins/queue/lmtp.md +24 -0
  96. package/docs/plugins/queue/qmail-queue.md +16 -0
  97. package/docs/plugins/queue/quarantine.md +87 -0
  98. package/docs/plugins/queue/smtp_bridge.md +32 -0
  99. package/docs/plugins/queue/smtp_forward.md +127 -0
  100. package/docs/plugins/queue/smtp_proxy.md +68 -0
  101. package/docs/plugins/queue/test.md +7 -0
  102. package/docs/plugins/rcpt_to.in_host_list.md +34 -0
  103. package/docs/plugins/rcpt_to.max_count.md +3 -0
  104. package/docs/plugins/record_envelope_addresses.md +20 -0
  105. package/docs/plugins/relay.md +3 -0
  106. package/docs/plugins/reseed_rng.md +16 -0
  107. package/docs/plugins/status.md +41 -0
  108. package/docs/plugins/tarpit.md +50 -0
  109. package/docs/plugins/tls.md +235 -0
  110. package/docs/plugins/toobusy.md +27 -0
  111. package/docs/plugins/xclient.md +10 -0
  112. package/docs/tutorials/Migrating_from_v1_to_v2.md +96 -0
  113. package/docs/tutorials/SettingUpOutbound.md +62 -0
  114. package/eslint.config.mjs +2 -0
  115. package/haraka.js +74 -0
  116. package/haraka.sh +2 -0
  117. package/http/html/404.html +58 -0
  118. package/http/html/index.html +47 -0
  119. package/http/package.json +21 -0
  120. package/line_socket.js +24 -0
  121. package/logger.js +322 -0
  122. package/outbound/client_pool.js +59 -0
  123. package/outbound/config.js +134 -0
  124. package/outbound/hmail.js +1504 -0
  125. package/outbound/index.js +349 -0
  126. package/outbound/qfile.js +93 -0
  127. package/outbound/queue.js +399 -0
  128. package/outbound/tls.js +85 -0
  129. package/outbound/todo.js +17 -0
  130. package/package.json +100 -4
  131. package/plugins/.eslintrc.yaml +3 -0
  132. package/plugins/auth/auth_base.js +261 -0
  133. package/plugins/auth/auth_bridge.js +20 -0
  134. package/plugins/auth/auth_proxy.js +227 -0
  135. package/plugins/auth/auth_vpopmaild.js +162 -0
  136. package/plugins/auth/flat_file.js +44 -0
  137. package/plugins/block_me.js +88 -0
  138. package/plugins/data.signatures.js +30 -0
  139. package/plugins/delay_deny.js +153 -0
  140. package/plugins/prevent_credential_leaks.js +61 -0
  141. package/plugins/process_title.js +197 -0
  142. package/plugins/profile.js +11 -0
  143. package/plugins/queue/deliver.js +12 -0
  144. package/plugins/queue/discard.js +27 -0
  145. package/plugins/queue/lmtp.js +45 -0
  146. package/plugins/queue/qmail-queue.js +93 -0
  147. package/plugins/queue/quarantine.js +133 -0
  148. package/plugins/queue/smtp_bridge.js +45 -0
  149. package/plugins/queue/smtp_forward.js +371 -0
  150. package/plugins/queue/smtp_proxy.js +142 -0
  151. package/plugins/queue/test.js +15 -0
  152. package/plugins/rcpt_to.host_list_base.js +65 -0
  153. package/plugins/rcpt_to.in_host_list.js +56 -0
  154. package/plugins/record_envelope_addresses.js +17 -0
  155. package/plugins/reseed_rng.js +7 -0
  156. package/plugins/status.js +274 -0
  157. package/plugins/tarpit.js +45 -0
  158. package/plugins/tls.js +164 -0
  159. package/plugins/toobusy.js +47 -0
  160. package/plugins/xclient.js +124 -0
  161. package/plugins.js +605 -0
  162. package/run_tests +11 -0
  163. package/server.js +827 -0
  164. package/smtp_client.js +504 -0
  165. package/test/.eslintrc.yaml +11 -0
  166. package/test/config/auth_flat_file.ini +5 -0
  167. package/test/config/block_me.recipient +1 -0
  168. package/test/config/block_me.senders +1 -0
  169. package/test/config/dhparams.pem +8 -0
  170. package/test/config/host_list +2 -0
  171. package/test/config/outbound_tls_cert.pem +1 -0
  172. package/test/config/outbound_tls_key.pem +1 -0
  173. package/test/config/plugins +7 -0
  174. package/test/config/smtp.ini +11 -0
  175. package/test/config/smtp_forward.ini +30 -0
  176. package/test/config/tls/example.com/_.example.com.key +28 -0
  177. package/test/config/tls/example.com/example.com.crt +25 -0
  178. package/test/config/tls/haraka.local.pem +51 -0
  179. package/test/config/tls.ini +45 -0
  180. package/test/config/tls_cert.pem +21 -0
  181. package/test/config/tls_key.pem +28 -0
  182. package/test/connection.js +820 -0
  183. package/test/fixtures/haproxy_allowed/config/connection.ini +3 -0
  184. package/test/fixtures/haproxy_disabled/config/connection.ini +3 -0
  185. package/test/fixtures/haproxy_untrusted/config/connection.ini +3 -0
  186. package/test/fixtures/line_socket.js +21 -0
  187. package/test/fixtures/todo_qfile.txt +0 -0
  188. package/test/fixtures/util_hmailitem.js +156 -0
  189. package/test/installation/config/test-plugin-flat +1 -0
  190. package/test/installation/config/test-plugin.ini +10 -0
  191. package/test/installation/config/tls.ini +1 -0
  192. package/test/installation/node_modules/load_first/index.js +5 -0
  193. package/test/installation/node_modules/load_first/package.json +11 -0
  194. package/test/installation/node_modules/test-plugin/config/test-plugin-flat +1 -0
  195. package/test/installation/node_modules/test-plugin/config/test-plugin.ini +9 -0
  196. package/test/installation/node_modules/test-plugin/package.json +5 -0
  197. package/test/installation/node_modules/test-plugin/test-plugin.js +5 -0
  198. package/test/installation/plugins/base_plugin.js +3 -0
  199. package/test/installation/plugins/folder_plugin/index.js +3 -0
  200. package/test/installation/plugins/folder_plugin/package.json +11 -0
  201. package/test/installation/plugins/inherits.js +7 -0
  202. package/test/installation/plugins/load_first.js +3 -0
  203. package/test/installation/plugins/plugin.js +1 -0
  204. package/test/installation/plugins/tls.js +3 -0
  205. package/test/logger.js +217 -0
  206. package/test/loud/config/dhparams.pem +0 -0
  207. package/test/loud/config/tls/goobered.pem +45 -0
  208. package/test/loud/config/tls.ini +43 -0
  209. package/test/mail_specimen/base64-root-part.txt +23 -0
  210. package/test/mail_specimen/varied-fold-lengths-preserve-data.txt +283 -0
  211. package/test/outbound/bounce_net_errors.js +133 -0
  212. package/test/outbound/bounce_rfc3464.js +226 -0
  213. package/test/outbound/hmail.js +210 -0
  214. package/test/outbound/index.js +385 -0
  215. package/test/outbound/qfile.js +124 -0
  216. package/test/outbound/queue.js +325 -0
  217. package/test/plugins/auth/auth_base.js +620 -0
  218. package/test/plugins/auth/auth_bridge.js +80 -0
  219. package/test/plugins/auth/auth_vpopmaild.js +81 -0
  220. package/test/plugins/auth/flat_file.js +123 -0
  221. package/test/plugins/block_me.js +141 -0
  222. package/test/plugins/data.signatures.js +111 -0
  223. package/test/plugins/delay_deny.js +262 -0
  224. package/test/plugins/prevent_credential_leaks.js +174 -0
  225. package/test/plugins/process_title.js +141 -0
  226. package/test/plugins/queue/deliver.js +98 -0
  227. package/test/plugins/queue/discard.js +78 -0
  228. package/test/plugins/queue/lmtp.js +137 -0
  229. package/test/plugins/queue/qmail-queue.js +98 -0
  230. package/test/plugins/queue/quarantine.js +80 -0
  231. package/test/plugins/queue/smtp_bridge.js +152 -0
  232. package/test/plugins/queue/smtp_forward.js +1023 -0
  233. package/test/plugins/queue/smtp_proxy.js +138 -0
  234. package/test/plugins/rcpt_to.host_list_base.js +102 -0
  235. package/test/plugins/rcpt_to.in_host_list.js +186 -0
  236. package/test/plugins/record_envelope_addresses.js +66 -0
  237. package/test/plugins/reseed_rng.js +34 -0
  238. package/test/plugins/status.js +207 -0
  239. package/test/plugins/tarpit.js +90 -0
  240. package/test/plugins/tls.js +86 -0
  241. package/test/plugins/toobusy.js +198 -0
  242. package/test/plugins/xclient.js +119 -0
  243. package/test/plugins.js +230 -0
  244. package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_fixed +0 -0
  245. package/test/queue/1507509981169_1507509981169_0_61403_e0Y0Ym_1_haraka +0 -0
  246. package/test/queue/1508269674999_1508269674999_0_34002_socVUF_1_haraka +0 -0
  247. package/test/queue/1508455115683_1508455115683_0_90253_9Q4o4V_1_haraka +0 -0
  248. package/test/queue/zero-length +0 -0
  249. package/test/server.js +1012 -0
  250. package/test/smtp_client.js +1303 -0
  251. package/test/tls_socket.js +321 -0
  252. package/test/transaction.js +554 -0
  253. package/tls_socket.js +771 -0
  254. package/transaction.js +267 -0
@@ -0,0 +1,78 @@
1
+ 'use strict'
2
+
3
+ const assert = require('node:assert')
4
+ const { describe, it, beforeEach, before } = require('node:test')
5
+
6
+ const { makeConnection, makePlugin } = require('haraka-test-fixtures')
7
+
8
+ before(() => {
9
+ require('haraka-constants').import(global)
10
+ })
11
+
12
+ describe('queue/discard', () => {
13
+ describe('discard hook', () => {
14
+ let plugin, conn
15
+
16
+ beforeEach(() => {
17
+ plugin = makePlugin('queue/discard')
18
+ conn = makeConnection({ withTxn: true })
19
+ delete process.env.YES_REALLY_DO_DISCARD
20
+ })
21
+
22
+ it('calls next() when queue.wants is set to another plugin', (t, done) => {
23
+ conn.transaction.notes.set('queue.wants', 'smtp_forward')
24
+ plugin.discard((rc) => {
25
+ assert.equal(rc, undefined)
26
+ done()
27
+ }, conn)
28
+ })
29
+
30
+ it('calls next(OK) when connection.notes.discard is set', (t, done) => {
31
+ conn.notes.discard = true
32
+ plugin.discard((rc) => {
33
+ assert.equal(rc, OK)
34
+ done()
35
+ }, conn)
36
+ })
37
+
38
+ it('calls next(OK) when txn.notes.discard is set', (t, done) => {
39
+ conn.transaction.notes.discard = true
40
+ plugin.discard((rc) => {
41
+ assert.equal(rc, OK)
42
+ done()
43
+ }, conn)
44
+ })
45
+
46
+ it('calls next(OK) when queue.wants is discard', (t, done) => {
47
+ conn.transaction.notes.set('queue.wants', 'discard')
48
+ plugin.discard((rc) => {
49
+ assert.equal(rc, OK)
50
+ done()
51
+ }, conn)
52
+ })
53
+
54
+ it('calls next(OK) when YES_REALLY_DO_DISCARD env var is set', (t, done) => {
55
+ process.env.YES_REALLY_DO_DISCARD = '1'
56
+ plugin.discard((rc) => {
57
+ assert.equal(rc, OK)
58
+ done()
59
+ }, conn)
60
+ })
61
+
62
+ it('calls next() (pass-through) when no discard conditions are met', (t, done) => {
63
+ plugin.discard((rc) => {
64
+ assert.equal(rc, undefined)
65
+ done()
66
+ }, conn)
67
+ })
68
+
69
+ it('queue.wants=discard takes priority over other queue wants', (t, done) => {
70
+ // Once queue.wants is set, it's compared against 'discard'; since it equals 'discard', discard runs
71
+ conn.transaction.notes.set('queue.wants', 'discard')
72
+ plugin.discard((rc) => {
73
+ assert.equal(rc, OK)
74
+ done()
75
+ }, conn)
76
+ })
77
+ })
78
+ })
@@ -0,0 +1,137 @@
1
+ 'use strict'
2
+
3
+ const assert = require('node:assert')
4
+ const { describe, it, beforeEach, before } = require('node:test')
5
+
6
+ const { makeConnection, makePlugin } = require('haraka-test-fixtures')
7
+
8
+ before(() => {
9
+ require('haraka-constants').import(global)
10
+ })
11
+
12
+ describe('queue/lmtp', () => {
13
+ describe('hook_get_mx', () => {
14
+ let plugin
15
+
16
+ beforeEach(() => {
17
+ plugin = makePlugin('queue/lmtp', { register: false })
18
+ plugin.load_lmtp_ini()
19
+ })
20
+
21
+ it('calls next() when hmail does not have using_lmtp note', (t, done) => {
22
+ const hmail = { todo: { notes: {} } }
23
+ plugin.hook_get_mx(
24
+ (rc, mx) => {
25
+ assert.equal(rc, undefined)
26
+ assert.equal(mx, undefined)
27
+ done()
28
+ },
29
+ hmail,
30
+ 'example.com',
31
+ )
32
+ })
33
+
34
+ it('returns OK with default host and port when using_lmtp is set', (t, done) => {
35
+ const hmail = { todo: { notes: { using_lmtp: true } } }
36
+ plugin.cfg = { main: {} }
37
+ plugin.hook_get_mx(
38
+ (rc, mx) => {
39
+ assert.equal(rc, OK)
40
+ assert.equal(mx.using_lmtp, true)
41
+ assert.equal(mx.exchange, '127.0.0.1')
42
+ assert.equal(mx.port, 24)
43
+ assert.equal(mx.priority, 0)
44
+ done()
45
+ },
46
+ hmail,
47
+ 'example.com',
48
+ )
49
+ })
50
+
51
+ it('uses domain-specific section when available', (t, done) => {
52
+ const hmail = { todo: { notes: { using_lmtp: true } } }
53
+ plugin.cfg = {
54
+ main: { host: '127.0.0.1' },
55
+ 'example.com': { host: 'lmtp.example.com', port: 2400 },
56
+ }
57
+ plugin.hook_get_mx(
58
+ (rc, mx) => {
59
+ assert.equal(rc, OK)
60
+ assert.equal(mx.exchange, 'lmtp.example.com')
61
+ assert.equal(mx.port, 2400)
62
+ done()
63
+ },
64
+ hmail,
65
+ 'example.com',
66
+ )
67
+ })
68
+
69
+ it('falls back to main section when no domain-specific section', (t, done) => {
70
+ const hmail = { todo: { notes: { using_lmtp: true } } }
71
+ plugin.cfg = { main: { host: 'lmtp.default.com', port: 24 } }
72
+ plugin.hook_get_mx(
73
+ (rc, mx) => {
74
+ assert.equal(rc, OK)
75
+ assert.equal(mx.exchange, 'lmtp.default.com')
76
+ done()
77
+ },
78
+ hmail,
79
+ 'other.com',
80
+ )
81
+ })
82
+
83
+ it('includes path in mx when configured', (t, done) => {
84
+ const hmail = { todo: { notes: { using_lmtp: true } } }
85
+ plugin.cfg = { main: { host: '127.0.0.1', path: '/var/run/lmtp.sock' } }
86
+ plugin.hook_get_mx(
87
+ (rc, mx) => {
88
+ assert.equal(rc, OK)
89
+ assert.equal(mx.path, '/var/run/lmtp.sock')
90
+ done()
91
+ },
92
+ hmail,
93
+ 'example.com',
94
+ )
95
+ })
96
+
97
+ it('does not include path in mx when not configured', (t, done) => {
98
+ const hmail = { todo: { notes: { using_lmtp: true } } }
99
+ plugin.cfg = { main: {} }
100
+ plugin.hook_get_mx(
101
+ (rc, mx) => {
102
+ assert.equal(rc, OK)
103
+ assert.equal(mx.path, undefined)
104
+ done()
105
+ },
106
+ hmail,
107
+ 'example.com',
108
+ )
109
+ })
110
+ })
111
+
112
+ describe('hook_queue', () => {
113
+ let plugin, conn
114
+
115
+ beforeEach(() => {
116
+ plugin = makePlugin('queue/lmtp', { register: false })
117
+ plugin.load_lmtp_ini()
118
+ conn = makeConnection({ withTxn: true })
119
+ })
120
+
121
+ it('calls next() when there is no transaction', (t, done) => {
122
+ const connNoTxn = makeConnection()
123
+ plugin.hook_queue((rc) => {
124
+ assert.equal(rc, undefined)
125
+ done()
126
+ }, connNoTxn)
127
+ })
128
+
129
+ it('calls next() when queue.wants is set to another plugin', (t, done) => {
130
+ conn.transaction.notes.set('queue.wants', 'smtp_forward')
131
+ plugin.hook_queue((rc) => {
132
+ assert.equal(rc, undefined)
133
+ done()
134
+ }, conn)
135
+ })
136
+ })
137
+ })
@@ -0,0 +1,98 @@
1
+ 'use strict'
2
+
3
+ const assert = require('node:assert')
4
+ const { describe, it, beforeEach, before } = require('node:test')
5
+
6
+ const { makeConnection, makePlugin } = require('haraka-test-fixtures')
7
+
8
+ before(() => {
9
+ require('haraka-constants').import(global)
10
+ })
11
+
12
+ describe('queue/qmail-queue', () => {
13
+ describe('register', () => {
14
+ it('throws when qmail-queue binary is not found', () => {
15
+ const plugin = makePlugin('queue/qmail-queue', { register: false })
16
+ assert.throws(() => plugin.register(), /Cannot find qmail-queue binary/)
17
+ })
18
+ })
19
+
20
+ describe('load_qmail_queue_ini', () => {
21
+ it('loads config with enable_outbound boolean', () => {
22
+ const plugin = makePlugin('queue/qmail-queue', { register: false })
23
+ plugin.load_qmail_queue_ini()
24
+ assert.ok(typeof plugin.cfg.main.enable_outbound === 'boolean')
25
+ })
26
+ })
27
+
28
+ describe('hook_queue', () => {
29
+ let plugin, conn
30
+
31
+ beforeEach(() => {
32
+ plugin = makePlugin('queue/qmail-queue', { register: false })
33
+ plugin.load_qmail_queue_ini()
34
+ plugin.queue_exec = '/bin/echo' // use a real binary that exists
35
+ conn = makeConnection({ withTxn: true })
36
+ })
37
+
38
+ it('calls next() when there is no transaction', (t, done) => {
39
+ const connNoTxn = makeConnection()
40
+ plugin.hook_queue((rc) => {
41
+ assert.equal(rc, undefined)
42
+ done()
43
+ }, connNoTxn)
44
+ })
45
+
46
+ it('calls next() when queue.wants is set to another plugin', (t, done) => {
47
+ conn.transaction.notes.set('queue.wants', 'smtp_forward')
48
+ plugin.hook_queue((rc) => {
49
+ assert.equal(rc, undefined)
50
+ done()
51
+ }, conn)
52
+ })
53
+
54
+ it('calls next() when queue.wants is set to discard', (t, done) => {
55
+ conn.transaction.notes.set('queue.wants', 'discard')
56
+ plugin.hook_queue((rc) => {
57
+ assert.equal(rc, undefined)
58
+ done()
59
+ }, conn)
60
+ })
61
+ })
62
+
63
+ describe('build_envelope', () => {
64
+ let plugin
65
+ beforeEach(() => {
66
+ plugin = makePlugin('queue/qmail-queue', { register: false })
67
+ })
68
+
69
+ it('formats F<sender>\\0(T<rcpt>\\0)*\\0 with no padding', () => {
70
+ const buf = plugin.build_envelope({
71
+ mail_from: { address: 'snd@example.com' },
72
+ rcpt_to: [{ address: 'a@example.com' }, { address: 'b@example.com' }],
73
+ })
74
+ assert.deepEqual(buf, Buffer.from('Fsnd@example.com\x00Ta@example.com\x00Tb@example.com\x00\x00', 'utf8'))
75
+ })
76
+
77
+ it('is not truncated for large recipient sets', () => {
78
+ const rcpt_to = []
79
+ for (let i = 0; i < 500; i++) {
80
+ rcpt_to.push({ address: `recipient-with-a-fairly-long-localpart-${i}@example.com` })
81
+ }
82
+ const buf = plugin.build_envelope({ mail_from: { address: 'snd@example.com' }, rcpt_to })
83
+ assert.equal((buf.toString('utf8').match(/T/g) || []).length, 500)
84
+ assert.ok(buf.length > 4096, 'exceeds the old fixed 4096-byte cap')
85
+ assert.ok(buf.toString('utf8').includes('recipient-with-a-fairly-long-localpart-499@example.com'))
86
+ assert.equal(buf[buf.length - 1], 0)
87
+ })
88
+
89
+ it('encodes non-ASCII (SMTPUTF8) addresses correctly', () => {
90
+ const buf = plugin.build_envelope({
91
+ mail_from: { address: 'sénder@exämple.com' },
92
+ rcpt_to: [{ address: 'reçìpient@exämple.com' }],
93
+ })
94
+ assert.ok(buf.includes(Buffer.from('sénder@exämple.com', 'utf8')))
95
+ assert.ok(buf.includes(Buffer.from('reçìpient@exämple.com', 'utf8')))
96
+ })
97
+ })
98
+ })
@@ -0,0 +1,80 @@
1
+ 'use strict'
2
+
3
+ const assert = require('node:assert')
4
+ const { describe, it, beforeEach } = require('node:test')
5
+
6
+ const { makeConnection, makePlugin } = require('haraka-test-fixtures')
7
+
8
+ describe('queue/quarantine', () => {
9
+ let plugin
10
+
11
+ beforeEach(() => {
12
+ plugin = makePlugin('queue/quarantine', { register: false })
13
+ plugin.load_quarantine_ini()
14
+ })
15
+
16
+ describe('zeroPad', () => {
17
+ it('pads a single digit number to 2 digits', () => {
18
+ assert.equal(plugin.zeroPad(5, 2), '05')
19
+ })
20
+
21
+ it('pads a single digit to 4 digits', () => {
22
+ assert.equal(plugin.zeroPad(7, 4), '0007')
23
+ })
24
+
25
+ it('does not pad when already at target length', () => {
26
+ assert.equal(plugin.zeroPad(12, 2), '12')
27
+ })
28
+
29
+ it('does not pad when number exceeds target length', () => {
30
+ assert.equal(plugin.zeroPad(2025, 2), '2025')
31
+ })
32
+
33
+ it('handles 0 correctly', () => {
34
+ assert.equal(plugin.zeroPad(0, 2), '00')
35
+ })
36
+ })
37
+
38
+ describe('get_base_dir', () => {
39
+ it('returns default quarantine path when not configured', () => {
40
+ plugin.cfg = { main: {} }
41
+ assert.equal(plugin.get_base_dir(), '/var/spool/haraka/quarantine')
42
+ })
43
+
44
+ it('returns configured quarantine_path when set', () => {
45
+ plugin.cfg = { main: { quarantine_path: '/tmp/my-quarantine' } }
46
+ assert.equal(plugin.get_base_dir(), '/tmp/my-quarantine')
47
+ })
48
+ })
49
+
50
+ describe('quarantine hook', () => {
51
+ let conn
52
+
53
+ beforeEach(() => {
54
+ conn = makeConnection({ withTxn: true })
55
+ })
56
+
57
+ it('calls next() when no quarantine conditions are met', (t, done) => {
58
+ plugin.quarantine((rc) => {
59
+ assert.equal(rc, undefined)
60
+ done()
61
+ }, conn)
62
+ })
63
+
64
+ it('calls next() when connection.notes.quarantine is falsy', (t, done) => {
65
+ conn.notes.quarantine = false
66
+ plugin.quarantine((rc) => {
67
+ assert.equal(rc, undefined)
68
+ done()
69
+ }, conn)
70
+ })
71
+
72
+ it('calls next() when queue.wants is set to something other than quarantine', (t, done) => {
73
+ conn.transaction.notes.set('queue.wants', 'smtp_forward')
74
+ plugin.quarantine((rc) => {
75
+ assert.equal(rc, undefined)
76
+ done()
77
+ }, conn)
78
+ })
79
+ })
80
+ })
@@ -0,0 +1,152 @@
1
+ 'use strict'
2
+
3
+ const assert = require('node:assert')
4
+ const { describe, it, beforeEach, before } = require('node:test')
5
+
6
+ const { makeConnection, makePlugin } = require('haraka-test-fixtures')
7
+
8
+ before(() => {
9
+ require('haraka-constants').import(global)
10
+ })
11
+
12
+ describe('queue/smtp_bridge', () => {
13
+ describe('hook_data_post', () => {
14
+ let plugin, conn
15
+
16
+ beforeEach(() => {
17
+ plugin = makePlugin('queue/smtp_bridge')
18
+ plugin.load_flat_ini()
19
+ conn = makeConnection({ withTxn: true })
20
+ })
21
+
22
+ it('calls next() when no transaction', (t, done) => {
23
+ const connNoTxn = makeConnection()
24
+ plugin.hook_data_post((rc) => {
25
+ assert.equal(rc, undefined)
26
+ done()
27
+ }, connNoTxn)
28
+ })
29
+
30
+ it('copies auth_user from connection notes to transaction notes', (t, done) => {
31
+ conn.notes.auth_user = 'alice'
32
+ conn.notes.auth_passwd = 'secret'
33
+ plugin.hook_data_post((rc) => {
34
+ assert.equal(rc, undefined)
35
+ assert.equal(conn.transaction.notes.auth_user, 'alice')
36
+ done()
37
+ }, conn)
38
+ })
39
+
40
+ it('copies auth_passwd from connection notes to transaction notes', (t, done) => {
41
+ conn.notes.auth_user = 'bob'
42
+ conn.notes.auth_passwd = 'mypassword'
43
+ plugin.hook_data_post(() => {
44
+ assert.equal(conn.transaction.notes.auth_passwd, 'mypassword')
45
+ done()
46
+ }, conn)
47
+ })
48
+
49
+ it('copies undefined auth values when not set on connection', (t, done) => {
50
+ plugin.hook_data_post(() => {
51
+ assert.equal(conn.transaction.notes.auth_user, undefined)
52
+ assert.equal(conn.transaction.notes.auth_passwd, undefined)
53
+ done()
54
+ }, conn)
55
+ })
56
+ })
57
+
58
+ describe('hook_get_mx', () => {
59
+ let plugin
60
+
61
+ beforeEach(() => {
62
+ plugin = makePlugin('queue/smtp_bridge')
63
+ })
64
+
65
+ it('returns OK with default priority 10 and configured host', (t, done) => {
66
+ plugin.cfg.main = { host: 'relay.example.com' }
67
+ const hmail = { todo: { notes: {} } }
68
+ plugin.hook_get_mx(
69
+ (rc, mx) => {
70
+ assert.equal(rc, OK)
71
+ assert.equal(mx.priority, 10)
72
+ assert.equal(mx.exchange, 'relay.example.com')
73
+ done()
74
+ },
75
+ hmail,
76
+ 'example.com',
77
+ )
78
+ })
79
+
80
+ it('uses configured priority when set', (t, done) => {
81
+ plugin.cfg.main = { host: 'relay.example.com', priority: 20 }
82
+ const hmail = { todo: { notes: {} } }
83
+ plugin.hook_get_mx(
84
+ (rc, mx) => {
85
+ assert.equal(rc, OK)
86
+ assert.equal(mx.priority, 20)
87
+ done()
88
+ },
89
+ hmail,
90
+ 'example.com',
91
+ )
92
+ })
93
+
94
+ it('passes auth_type from config', (t, done) => {
95
+ plugin.cfg.main = { host: 'relay.example.com', auth_type: 'PLAIN' }
96
+ const hmail = { todo: { notes: {} } }
97
+ plugin.hook_get_mx(
98
+ (rc, mx) => {
99
+ assert.equal(rc, OK)
100
+ assert.equal(mx.auth_type, 'PLAIN')
101
+ done()
102
+ },
103
+ hmail,
104
+ 'example.com',
105
+ )
106
+ })
107
+
108
+ it('passes port from config', (t, done) => {
109
+ plugin.cfg.main = { host: 'relay.example.com', port: '587' }
110
+ const hmail = { todo: { notes: {} } }
111
+ plugin.hook_get_mx(
112
+ (rc, mx) => {
113
+ assert.equal(rc, OK)
114
+ assert.equal(mx.port, '587')
115
+ done()
116
+ },
117
+ hmail,
118
+ 'example.com',
119
+ )
120
+ })
121
+
122
+ it('passes auth_user and auth_pass from hmail notes', (t, done) => {
123
+ plugin.cfg.main = { host: 'relay.example.com' }
124
+ const hmail = { todo: { notes: { auth_user: 'alice', auth_passwd: 'secret' } } }
125
+ plugin.hook_get_mx(
126
+ (rc, mx) => {
127
+ assert.equal(rc, OK)
128
+ assert.equal(mx.auth_user, 'alice')
129
+ assert.equal(mx.auth_pass, 'secret')
130
+ done()
131
+ },
132
+ hmail,
133
+ 'example.com',
134
+ )
135
+ })
136
+
137
+ it('sets port null and auth_type null when not configured', (t, done) => {
138
+ plugin.cfg.main = { host: 'relay.example.com' }
139
+ const hmail = { todo: { notes: {} } }
140
+ plugin.hook_get_mx(
141
+ (rc, mx) => {
142
+ assert.equal(rc, OK)
143
+ assert.equal(mx.port, null)
144
+ assert.equal(mx.auth_type, null)
145
+ done()
146
+ },
147
+ hmail,
148
+ 'example.com',
149
+ )
150
+ })
151
+ })
152
+ })