Haraka 2.8.28 → 3.0.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 (134) hide show
  1. package/.eslintrc.yaml +2 -10
  2. package/Changes.md +68 -2
  3. package/Dockerfile +1 -1
  4. package/Plugins.md +7 -4
  5. package/README.md +2 -6
  6. package/config/outbound.ini +0 -7
  7. package/config/plugins +1 -1
  8. package/config/smtp.ini +1 -1
  9. package/config/smtp_forward.ini +2 -8
  10. package/config/smtp_proxy.ini +0 -6
  11. package/connection.js +178 -204
  12. package/coverage/lcov.info +13863 -0
  13. package/coverage/tmp/coverage-42958-1658373250585-0.json +1 -0
  14. package/coverage/tmp/coverage-42961-1658373250529-0.json +1 -0
  15. package/dkim.js +65 -73
  16. package/docs/Body.md +1 -22
  17. package/docs/CoreConfig.md +2 -2
  18. package/docs/Header.md +1 -47
  19. package/docs/Outbound.md +8 -36
  20. package/endpoint.js +1 -1
  21. package/haraka.js +1 -1
  22. package/host_pool.js +8 -12
  23. package/logger.js +25 -32
  24. package/outbound/client_pool.js +11 -153
  25. package/outbound/config.js +5 -11
  26. package/outbound/hmail.js +109 -143
  27. package/outbound/index.js +13 -25
  28. package/outbound/mx_lookup.js +10 -7
  29. package/outbound/queue.js +8 -12
  30. package/outbound/timer_queue.js +2 -4
  31. package/outbound/tls.js +17 -18
  32. package/outbound/todo.js +1 -0
  33. package/package.json +42 -40
  34. package/plugins/auth/auth_base.js +39 -63
  35. package/plugins/auth/auth_bridge.js +3 -4
  36. package/plugins/auth/auth_proxy.js +16 -16
  37. package/plugins/auth/auth_vpopmaild.js +30 -37
  38. package/plugins/auth/flat_file.js +9 -13
  39. package/plugins/avg.js +9 -11
  40. package/plugins/backscatterer.js +1 -1
  41. package/plugins/block_me.js +2 -6
  42. package/plugins/bounce.js +106 -124
  43. package/plugins/clamd.js +59 -63
  44. package/plugins/data.signatures.js +6 -6
  45. package/plugins/data.uribl.js +1 -415
  46. package/plugins/delay_deny.js +19 -20
  47. package/plugins/dkim_sign.js +56 -62
  48. package/plugins/dkim_verify.js +9 -8
  49. package/plugins/dns_list_base.js +43 -42
  50. package/plugins/dnsbl.js +41 -46
  51. package/plugins/dnswl.js +23 -26
  52. package/plugins/early_talker.js +24 -28
  53. package/plugins/esets.js +8 -11
  54. package/plugins/greylist.js +161 -190
  55. package/plugins/helo.checks.js +175 -197
  56. package/plugins/mail_from.is_resolvable.js +38 -38
  57. package/plugins/messagesniffer.js +33 -40
  58. package/plugins/prevent_credential_leaks.js +7 -5
  59. package/plugins/process_title.js +16 -17
  60. package/plugins/queue/deliver.js +2 -2
  61. package/plugins/queue/lmtp.js +5 -6
  62. package/plugins/queue/qmail-queue.js +11 -13
  63. package/plugins/queue/quarantine.js +25 -34
  64. package/plugins/queue/rabbitmq.js +3 -2
  65. package/plugins/queue/rabbitmq_amqplib.js +9 -9
  66. package/plugins/queue/smtp_bridge.js +5 -4
  67. package/plugins/queue/smtp_forward.js +81 -89
  68. package/plugins/queue/smtp_proxy.js +21 -22
  69. package/plugins/queue/test.js +2 -1
  70. package/plugins/rcpt_to.host_list_base.js +20 -30
  71. package/plugins/rcpt_to.in_host_list.js +12 -14
  72. package/plugins/rcpt_to.max_count.js +7 -5
  73. package/plugins/record_envelope_addresses.js +4 -6
  74. package/plugins/relay.js +64 -74
  75. package/plugins/reseed_rng.js +1 -2
  76. package/plugins/spamassassin.js +56 -68
  77. package/plugins/status.js +2 -3
  78. package/plugins/tarpit.js +8 -11
  79. package/plugins/tls.js +14 -17
  80. package/plugins/toobusy.js +6 -8
  81. package/plugins/xclient.js +14 -25
  82. package/plugins.js +24 -29
  83. package/rfc1869.js +2 -2
  84. package/server.js +3 -13
  85. package/smtp_client.js +138 -215
  86. package/tests/config/smtp_forward.ini +0 -6
  87. package/tests/fixtures/line_socket.js +1 -1
  88. package/tests/fixtures/util_hmailitem.js +5 -7
  89. package/tests/fixtures/vm_harness.js +2 -2
  90. package/tests/host_pool.js +13 -14
  91. package/tests/installation/plugins/inherits.js +1 -2
  92. package/tests/logger.js +2 -2
  93. package/tests/plugins/bounce.js +6 -8
  94. package/tests/plugins/dkim_signer.js +7 -7
  95. package/tests/plugins/dns_list_base.js +7 -7
  96. package/tests/plugins/helo.checks.js +1 -1
  97. package/tests/plugins/mail_from.is_resolvable.js +10 -54
  98. package/tests/plugins/queue/smtp_forward.js +11 -11
  99. package/tests/plugins/rcpt_to.host_list_base.js +1 -1
  100. package/tests/plugins/rcpt_to.in_host_list.js +1 -1
  101. package/tests/plugins/spamassassin.js +1 -1
  102. package/tests/queue/multibyte +0 -0
  103. package/tests/queue/plain +0 -0
  104. package/tests/rfc1869.js +4 -1
  105. package/tests/server.js +15 -9
  106. package/tests/smtp_client/auth.js +4 -14
  107. package/tests/smtp_client/basic.js +5 -15
  108. package/tests/smtp_client.js +7 -3
  109. package/tests/transaction.js +72 -19
  110. package/tls_socket.js +75 -85
  111. package/transaction.js +7 -9
  112. package/attachment_stream.js +0 -118
  113. package/bin/spf +0 -48
  114. package/chunkemitter.js +0 -75
  115. package/config/data.uribl.excludes +0 -202
  116. package/config/data.uribl.ini +0 -37
  117. package/config/spf.ini +0 -1
  118. package/docs/plugins/attachment.md +0 -92
  119. package/docs/plugins/data.uribl.md +0 -120
  120. package/docs/plugins/spf.md +0 -142
  121. package/mailbody.js +0 -502
  122. package/mailheader.js +0 -304
  123. package/messagestream.js +0 -441
  124. package/plugins/aliases.js +0 -120
  125. package/plugins/attachment.js +0 -503
  126. package/plugins/connect.p0f.js +0 -5
  127. package/plugins/spf.js +0 -327
  128. package/spf.js +0 -689
  129. package/tests/mailbody.js +0 -348
  130. package/tests/mailheader.js +0 -138
  131. package/tests/messagestream.js +0 -34
  132. package/tests/plugins/aliases.js +0 -376
  133. package/tests/plugins/spf.js +0 -251
  134. 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
- }