Haraka 2.8.28 → 3.0.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 (135) hide show
  1. package/.eslintrc.yaml +2 -10
  2. package/Changes.md +84 -2
  3. package/Dockerfile +1 -1
  4. package/Plugins.md +9 -4
  5. package/README.md +2 -6
  6. package/bin/haraka +5 -4
  7. package/config/outbound.ini +0 -7
  8. package/config/plugins +1 -1
  9. package/config/smtp.ini +1 -1
  10. package/config/smtp_forward.ini +2 -8
  11. package/config/smtp_proxy.ini +0 -6
  12. package/connection.js +178 -204
  13. package/coverage/lcov.info +13863 -0
  14. package/coverage/tmp/coverage-42958-1658373250585-0.json +1 -0
  15. package/coverage/tmp/coverage-42961-1658373250529-0.json +1 -0
  16. package/dkim.js +66 -73
  17. package/docs/Body.md +1 -22
  18. package/docs/CoreConfig.md +2 -2
  19. package/docs/Header.md +1 -47
  20. package/docs/Outbound.md +8 -36
  21. package/endpoint.js +1 -1
  22. package/haraka.js +1 -1
  23. package/host_pool.js +8 -12
  24. package/logger.js +25 -32
  25. package/outbound/client_pool.js +11 -153
  26. package/outbound/config.js +5 -11
  27. package/outbound/hmail.js +109 -143
  28. package/outbound/index.js +13 -25
  29. package/outbound/mx_lookup.js +10 -7
  30. package/outbound/queue.js +8 -12
  31. package/outbound/timer_queue.js +2 -4
  32. package/outbound/tls.js +17 -18
  33. package/outbound/todo.js +1 -0
  34. package/package.json +57 -55
  35. package/plugins/auth/auth_base.js +39 -63
  36. package/plugins/auth/auth_bridge.js +3 -4
  37. package/plugins/auth/auth_proxy.js +16 -16
  38. package/plugins/auth/auth_vpopmaild.js +30 -37
  39. package/plugins/auth/flat_file.js +9 -13
  40. package/plugins/avg.js +9 -11
  41. package/plugins/backscatterer.js +1 -1
  42. package/plugins/block_me.js +2 -6
  43. package/plugins/bounce.js +106 -124
  44. package/plugins/clamd.js +59 -63
  45. package/plugins/data.signatures.js +6 -6
  46. package/plugins/data.uribl.js +1 -415
  47. package/plugins/delay_deny.js +19 -20
  48. package/plugins/dkim_sign.js +56 -62
  49. package/plugins/dkim_verify.js +9 -8
  50. package/plugins/dns_list_base.js +43 -42
  51. package/plugins/dnsbl.js +41 -46
  52. package/plugins/dnswl.js +23 -26
  53. package/plugins/early_talker.js +24 -28
  54. package/plugins/esets.js +8 -11
  55. package/plugins/greylist.js +161 -190
  56. package/plugins/helo.checks.js +175 -197
  57. package/plugins/mail_from.is_resolvable.js +38 -38
  58. package/plugins/messagesniffer.js +33 -40
  59. package/plugins/prevent_credential_leaks.js +7 -5
  60. package/plugins/process_title.js +16 -17
  61. package/plugins/queue/deliver.js +2 -2
  62. package/plugins/queue/lmtp.js +5 -6
  63. package/plugins/queue/qmail-queue.js +11 -13
  64. package/plugins/queue/quarantine.js +25 -34
  65. package/plugins/queue/rabbitmq.js +3 -2
  66. package/plugins/queue/rabbitmq_amqplib.js +9 -9
  67. package/plugins/queue/smtp_bridge.js +5 -4
  68. package/plugins/queue/smtp_forward.js +81 -89
  69. package/plugins/queue/smtp_proxy.js +21 -22
  70. package/plugins/queue/test.js +2 -1
  71. package/plugins/rcpt_to.host_list_base.js +20 -30
  72. package/plugins/rcpt_to.in_host_list.js +12 -14
  73. package/plugins/rcpt_to.max_count.js +7 -5
  74. package/plugins/record_envelope_addresses.js +4 -6
  75. package/plugins/relay.js +64 -74
  76. package/plugins/reseed_rng.js +1 -2
  77. package/plugins/spamassassin.js +56 -68
  78. package/plugins/status.js +2 -3
  79. package/plugins/tarpit.js +8 -11
  80. package/plugins/tls.js +14 -17
  81. package/plugins/toobusy.js +6 -8
  82. package/plugins/xclient.js +14 -25
  83. package/plugins.js +24 -29
  84. package/rfc1869.js +2 -2
  85. package/server.js +3 -13
  86. package/smtp_client.js +138 -215
  87. package/tests/config/smtp_forward.ini +0 -6
  88. package/tests/fixtures/line_socket.js +1 -1
  89. package/tests/fixtures/util_hmailitem.js +5 -7
  90. package/tests/fixtures/vm_harness.js +2 -2
  91. package/tests/host_pool.js +13 -14
  92. package/tests/installation/plugins/inherits.js +1 -2
  93. package/tests/logger.js +2 -2
  94. package/tests/plugins/bounce.js +6 -8
  95. package/tests/plugins/dkim_signer.js +7 -7
  96. package/tests/plugins/dns_list_base.js +7 -7
  97. package/tests/plugins/helo.checks.js +1 -1
  98. package/tests/plugins/mail_from.is_resolvable.js +10 -54
  99. package/tests/plugins/queue/smtp_forward.js +11 -11
  100. package/tests/plugins/rcpt_to.host_list_base.js +1 -1
  101. package/tests/plugins/rcpt_to.in_host_list.js +1 -1
  102. package/tests/plugins/spamassassin.js +1 -1
  103. package/tests/queue/multibyte +0 -0
  104. package/tests/queue/plain +0 -0
  105. package/tests/rfc1869.js +4 -1
  106. package/tests/server.js +15 -9
  107. package/tests/smtp_client/auth.js +4 -14
  108. package/tests/smtp_client/basic.js +5 -15
  109. package/tests/smtp_client.js +7 -3
  110. package/tests/transaction.js +72 -19
  111. package/tls_socket.js +75 -85
  112. package/transaction.js +7 -9
  113. package/attachment_stream.js +0 -118
  114. package/bin/spf +0 -48
  115. package/chunkemitter.js +0 -75
  116. package/config/data.uribl.excludes +0 -202
  117. package/config/data.uribl.ini +0 -37
  118. package/config/spf.ini +0 -1
  119. package/docs/plugins/attachment.md +0 -92
  120. package/docs/plugins/data.uribl.md +0 -120
  121. package/docs/plugins/spf.md +0 -142
  122. package/mailbody.js +0 -502
  123. package/mailheader.js +0 -304
  124. package/messagestream.js +0 -441
  125. package/plugins/aliases.js +0 -120
  126. package/plugins/attachment.js +0 -503
  127. package/plugins/connect.p0f.js +0 -5
  128. package/plugins/spf.js +0 -327
  129. package/spf.js +0 -689
  130. package/tests/mailbody.js +0 -348
  131. package/tests/mailheader.js +0 -138
  132. package/tests/messagestream.js +0 -34
  133. package/tests/plugins/aliases.js +0 -376
  134. package/tests/plugins/spf.js +0 -251
  135. package/tests/spf.js +0 -96
@@ -1,503 +0,0 @@
1
- 'use strict';
2
- /*eslint no-shadow: ["error", { "allow": ["file", "depth", "code", "signal"] }]*/
3
- // attachment
4
-
5
- const fs = require('fs');
6
- const { spawn } = require('child_process');
7
- const path = require('path');
8
- const crypto = require('crypto');
9
- const utils = require('haraka-utils');
10
-
11
- let tmp;
12
- let bsdtar_path;
13
- let archives_disabled = false;
14
- const default_archive_extns = [
15
- '.zip', '.tar', '.tgz', '.taz', '.z', '.gz', '.rar', '.7z'
16
- ];
17
-
18
- exports.register = function () {
19
- try {
20
- tmp = require('tmp');
21
- tmp.setGracefulCleanup();
22
- }
23
- catch (e) {
24
- archives_disabled = true;
25
- this.logwarn(`This plugin requires the 'tmp' module to extract filenames from archive files`);
26
- }
27
- this.load_attachment_ini();
28
- this.register_hook('data_post', 'wait_for_attachment_hooks');
29
- this.register_hook('data_post', 'check_attachments');
30
- }
31
-
32
- exports.load_attachment_ini = function () {
33
- const plugin = this;
34
-
35
- plugin.cfg = plugin.config.get('attachment.ini', () => {
36
- plugin.load_attachment_ini();
37
- });
38
-
39
- plugin.cfg.timeout = (plugin.cfg.main.timeout || 30) * 1000;
40
- plugin.archive_max_depth = plugin.cfg.main.archive_max_depth || 5;
41
- plugin.archive_exts = options_to_array(plugin.cfg.main.archive_extensions) ||
42
- default_archive_extns;
43
- }
44
-
45
- exports.find_bsdtar_path = cb => {
46
- let found = false;
47
- let i = 0;
48
- ['/bin', '/usr/bin', '/usr/local/bin'].forEach((dir) => {
49
- if (found) return;
50
- i++;
51
- fs.stat(`${dir}/bsdtar`, (err, stats) => {
52
- i--;
53
- if (found) return;
54
- if (err) {
55
- if (i===0) cb(new Error('bsdtar not found'));
56
- return;
57
- }
58
- found = true;
59
- cb(null, dir);
60
- });
61
- if (i===0) cb(new Error('bsdtar not found'));
62
- });
63
- }
64
-
65
- exports.hook_init_master = exports.hook_init_child = function (next) {
66
- const plugin = this;
67
- plugin.find_bsdtar_path((err, dir) => {
68
- if (err) {
69
- archives_disabled = true;
70
- plugin.logwarn(`This plugin requires the 'bsdtar' binary to extract filenames from archive files`);
71
- }
72
- else {
73
- plugin.logdebug(`found bsdtar in ${dir}`);
74
- bsdtar_path = `${dir}/bsdtar`;
75
- }
76
- return next();
77
- });
78
- }
79
-
80
- function options_to_array (options) {
81
- if (!options) return false;
82
- const arr = options.toLowerCase().replace(/\s+/,' ').split(/[;, ]/);
83
- let len = arr.length;
84
- while (len--) {
85
- // Remove any empty elements
86
- if (arr[len] === "" || arr[len] === null) {
87
- arr.splice(len, 1);
88
- }
89
- else {
90
- arr[len] = arr[len].trim();
91
- }
92
- }
93
- return (arr.length ? arr : false);
94
- }
95
-
96
- exports.unarchive_recursive = function (connection, f, archive_file_name, cb) {
97
- if (archives_disabled) {
98
- connection.logdebug(this, 'archive support disabled');
99
- return cb();
100
- }
101
-
102
- const plugin = this;
103
- const files = [];
104
- const tmpfiles = [];
105
- const depth_exceeded = false;
106
- let count = 0;
107
- let done_cb = false;
108
- let timer;
109
-
110
- function do_cb (err, files2) {
111
- if (timer) clearTimeout(timer);
112
- if (done_cb) return;
113
-
114
- done_cb = true;
115
- deleteTempFiles();
116
- return cb(err, files2);
117
- }
118
-
119
- function deleteTempFiles () {
120
- tmpfiles.forEach(t => {
121
- fs.close(t[0], () => {
122
- connection.logdebug(plugin, `closed fd: ${t[0]}`);
123
- fs.unlink(t[1], () => {
124
- connection.logdebug(plugin, `deleted tempfile: ${t[1]}`);
125
- });
126
- });
127
- });
128
- }
129
-
130
- function listFiles (in_file, prefix, depth) {
131
- if (!depth) depth = 0;
132
- if (depth >= plugin.archive_max_depth || depth_exceeded) {
133
- if (count === 0) {
134
- return do_cb(new Error('maximum archive depth exceeded'));
135
- }
136
- return;
137
- }
138
- count++;
139
- const bsdtar = spawn(bsdtar_path, [ '-tf', in_file ], {
140
- 'cwd': '/tmp',
141
- 'env': { 'LANG': 'C' },
142
- });
143
-
144
- // Start timer
145
- let t1_timeout = false;
146
- const t1_timer = setTimeout(() => {
147
- t1_timeout = true;
148
- bsdtar.kill();
149
- return do_cb(new Error('bsdtar timed out'));
150
- }, plugin.cfg.timeout);
151
-
152
- let lines = '';
153
- bsdtar.stdout.on('data', (data) => {
154
- lines += data;
155
- });
156
-
157
- let stderr = '';
158
- bsdtar.stderr.on('data', (data) => {
159
- stderr += data;
160
- });
161
-
162
- bsdtar.on('exit', (code, signal) => {
163
- count--;
164
- if (t1_timeout) return;
165
- clearTimeout(t1_timer);
166
- if (code && code > 0) {
167
- // Error was returned
168
- return do_cb(new Error(`bsdtar returned error code: ${code} error=${stderr.replace(/\r?\n/,' ')}`));
169
- }
170
- if (signal) {
171
- // Process terminated due to signal
172
- return do_cb(new Error(`bsdtar terminated by signal: ${signal}`));
173
- }
174
- // Process filenames
175
- const fl = lines.split(/\r?\n/);
176
- for (let i=0; i<fl.length; i++) {
177
- const file = fl[i];
178
- // Skip any blank lines
179
- if (!file) continue;
180
- connection.logdebug(plugin, `file: ${file} depth=${depth}`);
181
- files.push((prefix ? `${prefix}/` : '') + file);
182
- const extn = path.extname(file.toLowerCase());
183
- if (!plugin.archive_exts.includes(extn) &&
184
- !plugin.archive_exts.includes(extn.substring(1))) {
185
- // Not an archive file extension
186
- continue;
187
- }
188
- connection.logdebug(plugin, `need to extract file: ${file}`);
189
- count++;
190
- depth++;
191
- ((file, depth) => {
192
- tmp.file((err, tmpfile, fd) => {
193
- count--;
194
- if (err) return do_cb(err.message);
195
- connection.logdebug(plugin, `created tmp file: ${tmpfile} (fd=${fd}) for file ${prefix ? `${prefix}/` : ''} ${file}`);
196
- tmpfiles.push([fd, tmpfile]);
197
- // Extract this file from the archive
198
- count++;
199
- const cmd = spawn(bsdtar_path,
200
- [ '-Oxf', in_file, `--include=${file}` ],
201
- {
202
- 'cwd': '/tmp',
203
- 'env': {
204
- 'LANG': 'C'
205
- },
206
- }
207
- );
208
- // Start timer
209
- let t2_timeout = false;
210
- const t2_timer = setTimeout(() => {
211
- t2_timeout = true;
212
- return do_cb(new Error(`bsdtar timed out extracting file ${file}`));
213
- }, plugin.cfg.timeout);
214
-
215
- // Create WriteStream for this file
216
- const tws = fs.createWriteStream(tmpfile, { fd });
217
- err = "";
218
-
219
- cmd.stderr.on('data', (data) => {
220
- err += data;
221
- });
222
-
223
- cmd.on('exit', (code, signal) => {
224
- count--;
225
- if (t2_timeout) return;
226
- clearTimeout(t2_timer);
227
- if (code && code > 0) {
228
- // Error was returned
229
- return do_cb(new Error(`bsdtar returned error code: ${code} error=${err.replace(/\r?\n/,' ')}`));
230
- }
231
- if (signal) {
232
- // Process terminated due to signal
233
- return do_cb(new Error(`bsdtar terminated by signal: ${signal}`));
234
- }
235
- // Recurse
236
- return listFiles(tmpfile, (prefix ? `${prefix}/` : '') + file, depth);
237
- });
238
- cmd.stdout.pipe(tws);
239
- });
240
- })(file, depth);
241
- }
242
- connection.loginfo(plugin, `finish: count=${count} depth=${depth}`);
243
- if (count === 0) {
244
- return do_cb(null, files);
245
- }
246
- });
247
- }
248
-
249
- timer = setTimeout(() => {
250
- return do_cb(new Error('timeout unpacking attachments'));
251
- }, plugin.cfg.timeout);
252
-
253
- listFiles(f, archive_file_name);
254
- }
255
-
256
- exports.start_attachment = function (connection, ctype, filename, body, stream) {
257
- const plugin = this;
258
- const txn = connection.transaction;
259
-
260
- function next () {
261
- if (txn.notes.attachment_count === 0 && txn.notes.attachment_next) {
262
- return txn.notes.attachment_next();
263
- }
264
- return;
265
- }
266
-
267
- // Parse Content-Type
268
- let ct;
269
- if ((ct = ctype.match(/^([^/]+\/[^;\r\n ]+)/)) && ct[1]) {
270
- connection.logdebug(plugin, `found content type: ${ct[1]}`);
271
- txn.notes.attachment_ctypes.push(ct[1]);
272
- }
273
-
274
- // Parse filename
275
- let ext;
276
- let fileext = '.unknown';
277
- if (filename) {
278
- if ((ext = filename.match(/(\.[^. ]+)$/)) && ext[1]) {
279
- fileext = ext[1].toLowerCase();
280
- }
281
- txn.notes.attachment_files.push(filename);
282
- }
283
-
284
- // Calculate and report the md5 of each attachment
285
- const md5 = crypto.createHash('md5');
286
- let digest;
287
- let bytes = 0;
288
-
289
- stream.on('data', (data) => {
290
- md5.update(data);
291
- bytes += data.length;
292
- });
293
-
294
- stream.once('end', () => {
295
- stream.pause();
296
-
297
- digest = md5.digest('hex');
298
- connection.loginfo(plugin, `file="${filename}" ctype="${ctype}" md5=${digest} bytes=${bytes}`);
299
- txn.notes.attachments.push({
300
- ctype: ((ct && ct[1]) ? ct[1].toLowerCase() : 'unknown/unknown'),
301
- filename: (filename ? filename : ''),
302
- extension: (ext && ext[1] ? ext[1].toLowerCase() : ''),
303
- md5: ((digest) ? digest : ''),
304
- });
305
- });
306
-
307
- if (!filename) return;
308
- connection.logdebug(plugin, `found attachment file: ${filename}`);
309
- // See if filename extension matches archive extension list
310
- // We check with the dot prefixed and without
311
- if (archives_disabled || (!plugin.archive_exts.includes(fileext) &&
312
- !plugin.archive_exts.includes(fileext.substring(1)))) {
313
- return;
314
- }
315
- connection.logdebug(plugin, `found ${fileext} on archive list`);
316
- txn.notes.attachment_count++;
317
- stream.connection = connection;
318
- stream.pause();
319
- tmp.file((err, fn, fd) => {
320
- function cleanup () {
321
- fs.close(fd, () => {
322
- connection.logdebug(plugin, `closed fd: ${fd}`);
323
- fs.unlink(fn, () => {
324
- connection.logdebug(plugin, `unlinked: ${fn}`);
325
- });
326
- });
327
- stream.resume();
328
- }
329
- if (err) {
330
- txn.notes.attachment_result = [ DENYSOFT, err.message ];
331
- connection.logerror(plugin, `Error writing tempfile: ${err.message}`);
332
- txn.notes.attachment_count--;
333
- cleanup();
334
- stream.resume();
335
- return next();
336
- }
337
- connection.logdebug(plugin, `Got tmpfile: attachment="${filename}" tmpfile="${fn}" fd={fd}`);
338
-
339
- const ws = fs.createWriteStream(fn);
340
- stream.pipe(ws);
341
- stream.resume();
342
-
343
- ws.on('error', (error) => {
344
- txn.notes.attachment_count--;
345
- txn.notes.attachment_result = [ DENYSOFT, error.message ];
346
- connection.logerror(plugin, `stream error: ${error.message}`);
347
- cleanup();
348
- return next();
349
- });
350
-
351
- ws.on('close', () => {
352
- connection.logdebug(plugin, 'end of stream reached');
353
- connection.pause();
354
- plugin.unarchive_recursive(connection, fn, filename, (error, files) => {
355
- txn.notes.attachment_count--;
356
- cleanup();
357
- if (err) {
358
- connection.logerror(plugin, error.message);
359
- if (err.message === 'maximum archive depth exceeded') {
360
- txn.notes.attachment_result = [ DENY, 'Message contains nested archives exceeding the maximum depth' ];
361
- }
362
- else if (/Encrypted file is unsupported/i.test(error.message)) {
363
- if (!plugin.cfg.main.allow_encrypted_archives) {
364
- txn.notes.attachment_result = [ DENY, 'Message contains encrypted archive' ];
365
- }
366
- }
367
- else if (/Mac metadata is too large/i.test(error.message)) {
368
- // Skip this error
369
- }
370
- else {
371
- if (!connection.relaying) {
372
- txn.notes.attachment_result = [ DENYSOFT, 'Error unpacking archive' ];
373
- }
374
- }
375
- }
376
- else {
377
- txn.notes.attachment_archive_files = txn.notes.attachment_archive_files.concat(files);
378
- }
379
- connection.resume();
380
- return next();
381
- });
382
- });
383
- });
384
- }
385
-
386
-
387
- exports.hook_data = function (next, connection) {
388
- const plugin = this;
389
- const txn = connection.transaction;
390
- txn.parse_body = 1;
391
- txn.notes.attachment_count = 0;
392
- txn.notes.attachments = [];
393
- txn.notes.attachment_ctypes = [];
394
- txn.notes.attachment_files = [];
395
- txn.notes.attachment_archive_files = [];
396
- txn.attachment_hooks((ctype, filename, body, stream) => {
397
- plugin.start_attachment(connection, ctype, filename, body, stream);
398
- });
399
- return next();
400
- }
401
-
402
- exports.check_attachments = function (next, connection) {
403
- const txn = connection.transaction;
404
- const ctype_config = this.config.get('attachment.ctype.regex','list');
405
- const file_config = this.config.get('attachment.filename.regex','list');
406
- const archive_config = this.config.get('attachment.archive.filename.regex','list');
407
-
408
- // Add in any wildcard configuration
409
- const ctype_wc = this.config.get('attachment.ctype.wc', 'list');
410
- for (let i=0; i<ctype_wc.length; i++) {
411
- ctype_config.push(utils.wildcard_to_regexp(ctype_wc[i]));
412
- }
413
- const file_wc = this.config.get('attachment.filename.wc', 'list');
414
- for (let i=0; i<file_wc.length; i++) {
415
- file_config.push(utils.wildcard_to_regexp(file_wc[i]));
416
- }
417
- const archive_wc = this.config.get('attachment.archive.filename.wc', 'list');
418
- for (let i=0; i<archive_wc.length; i++) {
419
- archive_config.push(utils.wildcard_to_regexp(archive_wc[i]));
420
- }
421
-
422
- // Check for any stored errors from the attachment hooks
423
- if (txn.notes.attachment_result) {
424
- const result = txn.notes.attachment_result;
425
- return next(result[0], result[1]);
426
- }
427
-
428
- const ctypes = txn.notes.attachment_ctypes;
429
-
430
- // Add in any content type from message body
431
- const body = txn.body;
432
- let body_ct;
433
- if (body && (body_ct = /^([^/]+\/[^;\r\n ]+)/.exec(body.header.get('content-type')))) {
434
- connection.logdebug(this, `found content type: ${body_ct[1]}`);
435
- ctypes.push(body_ct[1]);
436
- }
437
- // MIME parts
438
- if (body && body.children) {
439
- for (let c=0; c<body.children.length; c++) {
440
- let child_ct;
441
- if (body.children[c] && (child_ct = /^([^/]+\/[^;\r\n ]+)/.exec(body.children[c].header.get('content-type')))) {
442
- connection.logdebug(this, `found content type: ${child_ct[1]}`);
443
- ctypes.push(child_ct[1]);
444
- }
445
- }
446
- }
447
-
448
- const ctypes_result = this.check_items_against_regexps(ctypes, ctype_config);
449
- if (ctypes_result) {
450
- connection.loginfo(this, `match ctype="${ctypes_result[0]}" regexp=/${ctypes_result[1]}/`);
451
- return next(DENY, `Message contains unacceptable content type (${ctypes_result[0]})`);
452
- }
453
-
454
- const files = txn.notes.attachment_files;
455
- const files_result = this.check_items_against_regexps(files, file_config);
456
- if (files_result) {
457
- connection.loginfo(this, `match file="${files_result[0]}" regexp=/${files_result[1]}/`);
458
- return next(DENY, `Message contains unacceptable attachment (${files_result[0]})`);
459
- }
460
-
461
- const archive_files = txn.notes.attachment_archive_files;
462
- const archives_result = this.check_items_against_regexps(archive_files, archive_config);
463
- if (archives_result) {
464
- connection.loginfo(this, `match file="${archives_result[0]}" regexp=/${archives_result[1]}/`);
465
- return next(DENY, `Message contains unacceptable attachment (${archives_result[0]})`);
466
- }
467
-
468
- return next();
469
- }
470
-
471
- exports.check_items_against_regexps = function (items, regexps) {
472
- if ((regexps && Array.isArray(regexps) && regexps.length > 0) &&
473
- (items && Array.isArray(items) && items.length > 0)) {
474
- for (let r=0; r < regexps.length; r++) {
475
- let reg;
476
- try {
477
- reg = new RegExp(regexps[r], 'i');
478
- }
479
- catch (e) {
480
- this.logerror(`skipping invalid regexp: /${regexps[r]}/ (${e})`);
481
- }
482
- if (reg) {
483
- for (let i=0; i < items.length; i++) {
484
- if (reg.test(items[i])) {
485
- return [ items[i], regexps[r] ];
486
- }
487
- }
488
- }
489
- }
490
- }
491
- return false;
492
- }
493
-
494
- exports.wait_for_attachment_hooks = (next, connection) => {
495
- const txn = connection.transaction;
496
- if (txn.notes.attachment_count > 0) {
497
- // this.loginfo("We still have attachment hooks running");
498
- txn.notes.attachment_next = next;
499
- }
500
- else {
501
- next();
502
- }
503
- }
@@ -1,5 +0,0 @@
1
- 'use strict';
2
-
3
- exports.register = function () {
4
- this.logerror('This plugin has moved. See https://github.com/haraka/haraka-plugin-p0f');
5
- }