haraka-plugin-karma 1.0.13 → 2.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.
package/index.js CHANGED
@@ -1,612 +1,565 @@
1
- 'use strict';
1
+ 'use strict'
2
2
  // karma - reward good and penalize bad mail senders
3
3
 
4
- const constants = require('haraka-constants');
5
- const utils = require('haraka-utils');
4
+ const constants = require('haraka-constants')
5
+ const utils = require('haraka-utils')
6
6
 
7
7
  const phase_prefixes = utils.to_object([
8
8
  'connect','helo','mail_from','rcpt_to','data'
9
- ]);
9
+ ])
10
10
 
11
11
  exports.register = function () {
12
- const plugin = this;
13
12
 
14
- plugin.inherits('haraka-plugin-redis');
13
+ this.inherits('haraka-plugin-redis')
15
14
 
16
15
  // set up defaults
17
- plugin.deny_hooks = utils.to_object(
18
- ['unrecognized_command','helo','data','data_post','queue']
19
- );
20
- plugin.deny_exclude_hooks = utils.to_object('rcpt_to, queue');
21
- plugin.deny_exclude_plugins = utils.to_object([
16
+ this.deny_hooks = utils.to_object(
17
+ ['unrecognized_command','helo','data','data_post','queue','queue_outbound']
18
+ )
19
+ this.deny_exclude_hooks = utils.to_object('rcpt_to queue queue_outbound')
20
+ this.deny_exclude_plugins = utils.to_object([
22
21
  'access', 'helo.checks', 'data.headers', 'spamassassin',
23
22
  'mail_from.is_resolvable', 'clamd', 'tls'
24
- ]);
23
+ ])
25
24
 
26
- plugin.load_karma_ini();
25
+ this.load_karma_ini()
27
26
 
28
- plugin.register_hook('init_master', 'init_redis_plugin');
29
- plugin.register_hook('init_child', 'init_redis_plugin');
27
+ this.register_hook('init_master', 'init_redis_plugin')
28
+ this.register_hook('init_child', 'init_redis_plugin')
30
29
 
31
- plugin.register_hook('connect_init', 'results_init');
32
- plugin.register_hook('connect_init', 'ip_history_from_redis');
30
+ this.register_hook('connect_init', 'results_init')
31
+ this.register_hook('connect_init', 'ip_history_from_redis')
33
32
  }
34
33
 
35
34
  exports.load_karma_ini = function () {
36
- const plugin = this;
35
+ const plugin = this
37
36
 
38
37
  plugin.cfg = plugin.config.get('karma.ini', {
39
38
  booleans: [
40
39
  '+asn.enable',
41
40
  ],
42
41
  }, function () {
43
- plugin.load_karma_ini();
44
- });
42
+ plugin.load_karma_ini()
43
+ })
45
44
 
46
- plugin.merge_redis_ini();
45
+ plugin.merge_redis_ini()
47
46
 
48
- const cfg = plugin.cfg;
47
+ const cfg = plugin.cfg
49
48
  if (cfg.deny && cfg.deny.hooks) {
50
- plugin.deny_hooks = utils.to_object(cfg.deny.hooks);
49
+ plugin.deny_hooks = utils.to_object(cfg.deny.hooks)
51
50
  }
52
51
 
53
- const e = cfg.deny_excludes;
52
+ const e = cfg.deny_excludes
54
53
  if (e && e.hooks) {
55
- plugin.deny_exclude_hooks = utils.to_object(e.hooks);
54
+ plugin.deny_exclude_hooks = utils.to_object(e.hooks)
56
55
  }
57
56
 
58
57
  if (e && e.plugins) {
59
- plugin.deny_exclude_plugins = utils.to_object(e.plugins);
58
+ plugin.deny_exclude_plugins = utils.to_object(e.plugins)
60
59
  }
61
60
 
62
61
  if (cfg.result_awards) {
63
- plugin.preparse_result_awards();
62
+ plugin.preparse_result_awards()
64
63
  }
65
64
 
66
- if (!cfg.redis) cfg.redis = {};
65
+ if (!cfg.redis) cfg.redis = {}
67
66
  if (!cfg.redis.host && cfg.redis.server_ip) {
68
- cfg.redis.host = cfg.redis.server_ip; // backwards compat
67
+ cfg.redis.host = cfg.redis.server_ip // backwards compat
69
68
  }
70
69
  if (!cfg.redis.port && cfg.redis.server_port) {
71
- cfg.redis.port = cfg.redis.server_port; // backwards compat
70
+ cfg.redis.port = cfg.redis.server_port // backwards compat
72
71
  }
73
72
  }
74
73
 
75
- exports.results_init = function (next, connection) {
76
- const plugin = this;
74
+ exports.results_init = async function (next, connection) {
77
75
 
78
- if (plugin.should_we_skip(connection)) {
79
- connection.logdebug(plugin, 'skipping');
80
- return next();
76
+ if (this.should_we_skip(connection)) {
77
+ connection.logdebug(this, 'skipping')
78
+ return next()
81
79
  }
82
80
 
83
81
  if (connection.results.get('karma')) {
84
- connection.logerror(plugin, 'this should never happen');
85
- return next(); // init once per connection
82
+ connection.logerror(this, 'this should never happen')
83
+ return next() // init once per connection
86
84
  }
87
85
 
88
- if (plugin.cfg.awards) {
86
+ if (this.cfg.awards) {
89
87
  // todo is a list of connection/transaction awards to 'watch' for.
90
88
  // When discovered, apply the awards value
91
- const todo = {};
92
- for (const key in plugin.cfg.awards) {
93
- const award = plugin.cfg.awards[key].toString();
94
- todo[key] = award;
89
+ const todo = {}
90
+ for (const key in this.cfg.awards) {
91
+ const award = this.cfg.awards[key].toString()
92
+ todo[key] = award
95
93
  }
96
- connection.results.add(plugin, { score:0, todo: todo });
94
+ connection.results.add(this, { score:0, todo })
97
95
  }
98
96
  else {
99
- connection.results.add(plugin, { score:0 });
97
+ connection.results.add(this, { score:0 })
100
98
  }
101
99
 
102
100
  if (!connection.server.notes.redis) {
103
- connection.logerror(plugin, 'karma requires the redis plugin');
104
- return next();
101
+ connection.logerror(this, 'karma requires the redis plugin')
102
+ return next()
105
103
  }
106
104
 
107
- if (!plugin.result_awards) return next(); // not configured
105
+ if (!this.result_awards) return next() // not configured
108
106
 
109
107
  // subscribe to result_store publish messages
110
- plugin.redis_subscribe(connection, () => {
111
- connection.notes.redis.on('pmessage', (pattern, channel, message) => {
112
- plugin.check_result(connection, message);
113
- });
114
- next();
108
+ await this.redis_subscribe(connection, (channel, message) => {
109
+ this.check_result(connection, message)
115
110
  })
111
+
112
+ next()
116
113
  }
117
114
 
118
115
  exports.preparse_result_awards = function () {
119
- const plugin = this;
120
- if (!plugin.result_awards) plugin.result_awards = {};
116
+ if (!this.result_awards) this.result_awards = {}
121
117
 
118
+ const cra = this.cfg.result_awards
122
119
  // arrange results for rapid traversal by check_result() :
123
120
  // ex: karma.result_awards.clamd.fail = { .... }
124
- Object.keys(plugin.cfg.result_awards).forEach(anum => {
125
- // plugin, property, operator, value, award, reason, resolution
126
- const parts = plugin.cfg.result_awards[anum].split(/(?:\s*\|\s*)/);
127
- const pi_name = parts[0];
128
- const property = parts[1];
129
- if (!plugin.result_awards[pi_name]) {
130
- plugin.result_awards[pi_name] = {};
131
- }
132
- if (!plugin.result_awards[pi_name][property]) {
133
- plugin.result_awards[pi_name][property] = [];
134
- }
135
- plugin.result_awards[pi_name][property].push({
136
- id : anum,
137
- operator : parts[2],
138
- value : parts[3],
139
- award : parts[4],
140
- reason : parts[5],
141
- resolution : parts[6],
142
- });
143
- });
121
+ for (const anum of Object.keys(cra)) {
122
+
123
+ const [pi_name, prop, operator, value, award, reason, resolv]
124
+ = cra[anum].split(/(?:\s*\|\s*)/)
125
+
126
+ const ra = this.result_awards
127
+
128
+ if (!ra[pi_name]) ra[pi_name] = {}
129
+
130
+ if (!ra[pi_name][prop]) ra[pi_name][prop] = []
131
+
132
+ ra[pi_name][prop].push({ id: anum, operator, value, award, reason, resolv })
133
+ }
144
134
  }
145
135
 
146
136
  exports.check_result = function (connection, message) {
147
- const plugin = this;
148
- // connection.loginfo(plugin, message);
137
+
138
+ // connection.loginfo(this, message);
149
139
  // {"plugin":"karma","result":{"fail":"spamassassin.hits"}}
150
140
  // {"plugin":"geoip","result":{"country":"CN"}}
151
141
 
152
- const m = JSON.parse(message);
142
+ const m = JSON.parse(message)
153
143
  if (m && m.result && m.result.asn) {
154
- plugin.check_result_asn(m.result.asn, connection);
144
+ this.check_result_asn(m.result.asn, connection)
155
145
  }
156
- if (!plugin.result_awards[m.plugin]) return; // no awards for plugin
146
+ if (!this.result_awards[m.plugin]) return // no awards for plugin
157
147
 
158
- Object.keys(m.result).forEach(r => { // foreach result in mess
159
- if (r === 'emit') return; // r: pass, fail, skip, err, ...
148
+ for (const r of Object.keys(m.result)) { // each result in mess
149
+ if (r === 'emit') return // r: pass, fail, skip, err, ...
160
150
 
161
- const pi_prop = plugin.result_awards[m.plugin][r];
162
- if (!pi_prop) return; // no award for this plugin property
151
+ const pi_prop = this.result_awards[m.plugin][r]
152
+ if (!pi_prop) return // no award for this plugin property
163
153
 
164
- const thisResult = m.result[r];
154
+ const thisResult = m.result[r]
165
155
  // ignore empty arrays, objects, and strings
166
- if (Array.isArray(thisResult) && thisResult.length === 0) return;
156
+ if (Array.isArray(thisResult) && thisResult.length === 0) return
167
157
  if (typeof thisResult === 'object' && !Object.keys(thisResult).length) {
168
- return;
158
+ return
169
159
  }
170
- if (typeof thisResult === 'string' && !thisResult) return; // empty
160
+ if (typeof thisResult === 'string' && !thisResult) return // empty
171
161
 
172
162
  // do any award conditions match this result?
173
163
  for (let i=0; i < pi_prop.length; i++) { // each award...
174
- const thisAward = pi_prop[i];
164
+ const thisAward = pi_prop[i]
175
165
  // { id: '011', operator: 'equals', value: 'all_bad', award: '-2'}
176
- const thisResArr = plugin.result_as_array(thisResult);
166
+ const thisResArr = this.result_as_array(thisResult)
177
167
  switch (thisAward.operator) {
178
168
  case 'eq':
179
169
  case 'equal':
180
170
  case 'equals':
181
- plugin.check_result_equal(thisResArr, thisAward, connection);
182
- break;
171
+ this.check_result_equal(thisResArr, thisAward, connection)
172
+ break
183
173
  case 'match':
184
- plugin.check_result_match(thisResArr, thisAward, connection);
185
- break;
174
+ this.check_result_match(thisResArr, thisAward, connection)
175
+ break
186
176
  case 'lt':
187
- plugin.check_result_lt(thisResArr, thisAward, connection);
188
- break;
177
+ this.check_result_lt(thisResArr, thisAward, connection)
178
+ break
189
179
  case 'gt':
190
- plugin.check_result_gt(thisResArr, thisAward, connection);
191
- break;
180
+ this.check_result_gt(thisResArr, thisAward, connection)
181
+ break
192
182
  case 'length':
193
- plugin.check_result_length(thisResArr, thisAward, connection);
194
- break;
183
+ this.check_result_length(thisResArr, thisAward, connection)
184
+ break
195
185
  }
196
186
  }
197
- });
187
+ }
198
188
  }
199
189
 
200
190
  exports.result_as_array = function (result) {
201
191
 
202
- if (typeof result === 'string') return [result];
203
- if (typeof result === 'number') return [result];
204
- if (typeof result === 'boolean') return [result];
205
- if (Array.isArray(result)) return result;
192
+ if (typeof result === 'string') return [result]
193
+ if (typeof result === 'number') return [result]
194
+ if (typeof result === 'boolean') return [result]
195
+ if (Array.isArray(result)) return result
206
196
  if (typeof result === 'object') {
207
- const array = [];
197
+ const array = []
208
198
  Object.keys(result).forEach(tr => {
209
- array.push(result[tr]);
210
- });
211
- return array;
199
+ array.push(result[tr])
200
+ })
201
+ return array
212
202
  }
213
- this.loginfo('what format is result: ' + result);
214
- return result;
203
+ this.loginfo(`what format is result: ${result}`)
204
+ return result
215
205
  }
216
206
 
217
207
  exports.check_result_asn = function (asn, conn) {
218
- const plugin = this;
219
- if (!plugin.cfg.asn_awards) return;
220
- if (!plugin.cfg.asn_awards[asn]) return;
208
+ if (!this.cfg.asn_awards) return
209
+ if (!this.cfg.asn_awards[asn]) return
221
210
 
222
- conn.results.incr(plugin, {score: plugin.cfg.asn_awards[asn]});
223
- conn.results.push(plugin, {fail: 'asn_awards'});
211
+ conn.results.incr(this, {score: this.cfg.asn_awards[asn]})
212
+ conn.results.push(this, {fail: 'asn_awards'})
224
213
  }
225
214
 
226
215
  exports.check_result_lt = function (thisResult, thisAward, conn) {
227
- const plugin = this;
228
216
 
229
217
  for (let j=0; j < thisResult.length; j++) {
230
- const tr = parseFloat(thisResult[j]);
231
- if (tr >= parseFloat(thisAward.value)) continue;
232
- if (conn.results.has('karma', 'awards', thisAward.id)) continue;
218
+ const tr = parseFloat(thisResult[j])
219
+ if (tr >= parseFloat(thisAward.value)) continue
220
+ if (conn.results.has('karma', 'awards', thisAward.id)) continue
233
221
 
234
- conn.results.incr(plugin, {score: thisAward.award});
235
- conn.results.push(plugin, {awards: thisAward.id});
222
+ conn.results.incr(this, {score: thisAward.award})
223
+ conn.results.push(this, {awards: thisAward.id})
236
224
  }
237
225
  }
238
226
 
239
227
  exports.check_result_gt = function (thisResult, thisAward, conn) {
240
- const plugin = this;
241
228
 
242
229
  for (let j=0; j < thisResult.length; j++) {
243
- const tr = parseFloat(thisResult[j]);
244
- if (tr <= parseFloat(thisAward.value)) continue;
245
- if (conn.results.has('karma', 'awards', thisAward.id)) continue;
230
+ const tr = parseFloat(thisResult[j])
231
+ if (tr <= parseFloat(thisAward.value)) continue
232
+ if (conn.results.has('karma', 'awards', thisAward.id)) continue
246
233
 
247
- conn.results.incr(plugin, {score: thisAward.award});
248
- conn.results.push(plugin, {awards: thisAward.id});
234
+ conn.results.incr(this, {score: thisAward.award})
235
+ conn.results.push(this, {awards: thisAward.id})
249
236
  }
250
237
  }
251
238
 
252
239
  exports.check_result_equal = function (thisResult, thisAward, conn) {
253
- const plugin = this;
254
240
 
255
241
  for (let j=0; j < thisResult.length; j++) {
256
242
  if (thisAward.value === 'true') {
257
- if (!thisResult[j]) continue;
243
+ if (!thisResult[j]) continue
258
244
  }
259
245
  else {
260
- if (thisResult[j] != thisAward.value) continue;
246
+ if (thisResult[j] != thisAward.value) continue
261
247
  }
262
248
  if (!/auth/.test(thisAward.plugin)) {
263
249
  // only auth attempts are scored > 1x
264
- if (conn.results.has('karma', 'awards', thisAward.id)) continue;
250
+ if (conn.results.has('karma', 'awards', thisAward.id)) continue
265
251
  }
266
252
 
267
- conn.results.incr(plugin, {score: thisAward.award});
268
- conn.results.push(plugin, {awards: thisAward.id});
253
+ conn.results.incr(this, {score: thisAward.award})
254
+ conn.results.push(this, {awards: thisAward.id})
269
255
  }
270
256
  }
271
257
 
272
258
  exports.check_result_match = function (thisResult, thisAward, conn) {
273
- const plugin = this;
274
- const re = new RegExp(thisAward.value, 'i');
259
+ const re = new RegExp(thisAward.value, 'i')
275
260
 
276
261
  for (let i=0; i < thisResult.length; i++) {
277
- if (!re.test(thisResult[i])) continue;
278
- if (conn.results.has('karma', 'awards', thisAward.id)) continue;
262
+ if (!re.test(thisResult[i])) continue
263
+ if (conn.results.has('karma', 'awards', thisAward.id)) continue
279
264
 
280
- conn.results.incr(plugin, {score: thisAward.award});
281
- conn.results.push(plugin, {awards: thisAward.id});
265
+ conn.results.incr(this, {score: thisAward.award})
266
+ conn.results.push(this, {awards: thisAward.id})
282
267
  }
283
268
  }
284
269
 
285
270
  exports.check_result_length = function (thisResult, thisAward, conn) {
286
- const plugin = this;
287
271
 
288
272
  for (let j=0; j < thisResult.length; j++) {
289
- // let [operator, qty] = thisAward.value.split(/\s+/); // requires node 6
290
- const matches = thisAward.value.split(/\s+/);
291
- const operator = matches[0];
292
- const qty = matches[1];
273
+ const [operator, qty] = thisAward.value.split(/\s+/) // requires node 6+
293
274
 
294
275
  switch (operator) {
295
276
  case 'eq':
296
277
  case 'equal':
297
278
  case 'equals':
298
- if (parseInt(thisResult[j], 10) != parseInt(qty, 10)) continue;
299
- break;
279
+ if (parseInt(thisResult[j], 10) != parseInt(qty, 10)) continue
280
+ break
300
281
  case 'gt':
301
- if (parseInt(thisResult[j], 10) <= parseInt(qty, 10)) continue;
302
- break;
282
+ if (parseInt(thisResult[j], 10) <= parseInt(qty, 10)) continue
283
+ break
303
284
  case 'lt':
304
- if (parseInt(thisResult[j], 10) >= parseInt(qty, 10)) continue;
305
- break;
285
+ if (parseInt(thisResult[j], 10) >= parseInt(qty, 10)) continue
286
+ break
306
287
  default:
307
- conn.results.add(plugin, { err: 'invalid operator:' + operator });
308
- continue;
288
+ conn.results.add(this, { err: `invalid operator: ${operator}` })
289
+ continue
309
290
  }
310
291
 
311
- conn.results.incr(plugin, {score: thisAward.award});
312
- conn.results.push(plugin, {awards: thisAward.id});
292
+ conn.results.incr(this, {score: thisAward.award})
293
+ conn.results.push(this, {awards: thisAward.id })
313
294
  }
314
295
  }
315
296
 
316
297
  exports.check_result_exists = function (thisResult, thisAward, conn) {
317
- const plugin = this;
318
298
 
319
299
  /* eslint-disable no-unused-vars */
320
300
  for (const r of thisResult) {
321
- const [operator, qty] = thisAward.value.split(/\s+/);
301
+ const [operator, qty] = thisAward.value.split(/\s+/)
322
302
 
323
303
  switch (operator) {
324
304
  case 'any':
325
305
  case '':
326
- break;
306
+ break
327
307
  default:
328
- conn.results.add(plugin, { err: `invalid operator: ${operator}` });
329
- continue;
308
+ conn.results.add(this, { err: `invalid operator: ${operator}` })
309
+ continue
330
310
  }
331
311
 
332
- conn.results.incr(plugin, {score: thisAward.award});
333
- conn.results.push(plugin, {awards: thisAward.id});
312
+ conn.results.incr(this, {score: thisAward.award})
313
+ conn.results.push(this, {awards: thisAward.id})
334
314
  }
335
315
  }
336
316
 
337
317
  exports.apply_tarpit = function (connection, hook, score, next) {
338
- const plugin = this;
339
- if (!plugin.cfg.tarpit) { return next(); } // tarpit disabled in config
318
+
319
+ if (!this.cfg.tarpit) return next() // tarpit disabled in config
340
320
 
341
321
  // If tarpit is enabled on the reset_transaction hook, Haraka doesn't
342
322
  // wait. Then bad things happen, like a Haraka crash.
343
- if (utils.in_array(hook, ['reset_transaction','queue'])) return next();
323
+ if (utils.in_array(hook, ['reset_transaction','queue'])) return next()
344
324
 
345
325
  // no delay for senders with good karma
346
- const k = connection.results.get('karma');
347
- if (score === undefined) { score = parseFloat(k.score); }
348
- if (score >= 0) { return next(); }
326
+ const k = connection.results.get('karma')
327
+ if (score === undefined) score = parseFloat(k.score)
328
+ if (score >= 0) return next()
349
329
 
350
330
  // how long to delay?
351
- const delay = plugin.tarpit_delay(score, connection, hook, k);
352
- if (!delay) return next();
331
+ const delay = this.tarpit_delay(score, connection, hook, k)
332
+ if (!delay) return next()
353
333
 
354
- connection.logdebug(plugin, 'tarpitting '+hook+' for ' + delay + 's');
334
+ connection.logdebug(this, `tarpitting ${hook} for ${delay}s`)
355
335
  setTimeout(() => {
356
- connection.logdebug(plugin, 'tarpit '+hook+' end');
357
- next();
358
- }, delay * 1000);
336
+ connection.logdebug(this, `tarpit ${hook} end`)
337
+ next()
338
+ }, delay * 1000)
359
339
  }
360
340
 
361
341
  exports.tarpit_delay = function (score, connection, hook, k) {
362
- const plugin = this;
363
342
 
364
- if (plugin.cfg.tarpit.delay && parseFloat(plugin.cfg.tarpit.delay)) {
365
- connection.logdebug(plugin, 'static tarpit');
366
- return parseFloat(plugin.cfg.tarpit.delay);
343
+ if (this.cfg.tarpit.delay && parseFloat(this.cfg.tarpit.delay)) {
344
+ connection.logdebug(this, 'static tarpit')
345
+ return parseFloat(this.cfg.tarpit.delay)
367
346
  }
368
347
 
369
- const delay = score * -1; // progressive tarpit
348
+ const delay = score * -1 // progressive tarpit
370
349
 
371
350
  // detect roaming users based on MSA ports that require auth
372
351
  if (utils.in_array(connection.local.port, [587,465]) &&
373
352
  utils.in_array(hook, ['ehlo','connect'])) {
374
- return plugin.tarpit_delay_msa(connection, delay, k);
353
+ return this.tarpit_delay_msa(connection, delay, k)
375
354
  }
376
355
 
377
- const max = plugin.cfg.tarpit.max || 5;
356
+ const max = this.cfg.tarpit.max || 5
378
357
  if (delay > max) {
379
- connection.logdebug(plugin, 'tarpit capped to: ' + max);
380
- return max;
358
+ connection.logdebug(this, `tarpit capped to: ${max}`)
359
+ return max
381
360
  }
382
361
 
383
- return delay;
362
+ return delay
384
363
  }
385
364
 
386
365
  exports.tarpit_delay_msa = function (connection, delay, k) {
387
- const plugin = this;
388
- const trg = 'tarpit reduced for good';
366
+ const trg = 'tarpit reduced for good'
389
367
 
390
- delay = parseFloat(delay);
368
+ delay = parseFloat(delay)
391
369
 
392
370
  // Reduce delay for good history
393
- const history = ((k.good || 0) - (k.bad || 0));
371
+ const history = ((k.good || 0) - (k.bad || 0))
394
372
  if (history > 0) {
395
- delay = delay - 2;
396
- connection.logdebug(plugin, trg + ' history: ' + delay);
373
+ delay = delay - 2
374
+ connection.logdebug(this, `${trg} history: ${delay}`)
397
375
  }
398
376
 
399
377
  // Reduce delay for good ASN history
400
- let asn = connection.results.get('asn');
401
- if (!asn) { asn = connection.results.get('geoip'); }
378
+ let asn = connection.results.get('asn')
379
+ if (!asn) asn = connection.results.get('geoip')
402
380
  if (asn && asn.asn && k.neighbors > 0) {
403
- connection.logdebug(plugin, trg + ' neighbors: ' + delay);
404
- delay = delay - 2;
381
+ connection.logdebug(this, `${trg} neighbors: ${delay}`)
382
+ delay = delay - 2
405
383
  }
406
384
 
407
- const max = plugin.cfg.tarpit.max_msa || 2;
385
+ const max = this.cfg.tarpit.max_msa || 2
408
386
  if (delay > max) {
409
- connection.logdebug(plugin, 'tarpit capped at: ' + delay);
410
- delay = max;
387
+ connection.logdebug(this, `tarpit capped at: ${delay}`)
388
+ delay = max
411
389
  }
412
390
 
413
- return delay;
391
+ return delay
414
392
  }
415
393
 
416
394
  exports.should_we_skip = function (connection) {
417
- if (connection.remote.is_private) return true;
418
- if (connection.notes.disable_karma) return true;
419
- return false;
395
+ if (connection.remote.is_private) return true
396
+ if (connection.notes.disable_karma) return true
397
+ return false
420
398
  }
421
399
 
422
400
  exports.should_we_deny = function (next, connection, hook) {
423
- const plugin = this;
401
+ const r = connection.results.get('karma')
402
+ if (!r) return next()
424
403
 
425
- const r = connection.results.get('karma');
426
- if (!r) { return next(); }
404
+ this.check_awards(connection) // update awards first
427
405
 
428
- plugin.check_awards(connection); // update awards first
429
-
430
- const score = parseFloat(r.score);
406
+ const score = parseFloat(r.score)
431
407
  if (isNaN(score)) {
432
- connection.logerror(plugin, 'score is NaN');
433
- connection.results.add(plugin, {score: 0});
434
- return next();
408
+ connection.logerror(this, 'score is NaN')
409
+ connection.results.add(this, {score: 0})
410
+ return next()
435
411
  }
436
412
 
437
- let negative_limit = -5;
438
- if (plugin.cfg.thresholds && plugin.cfg.thresholds.negative) {
439
- negative_limit = parseFloat(plugin.cfg.thresholds.negative);
413
+ let negative_limit = -5
414
+ if (this.cfg.thresholds && this.cfg.thresholds.negative) {
415
+ negative_limit = parseFloat(this.cfg.thresholds.negative)
440
416
  }
441
417
 
442
418
  if (score > negative_limit) {
443
- return plugin.apply_tarpit(connection, hook, score, next);
419
+ return this.apply_tarpit(connection, hook, score, next)
444
420
  }
445
- if (!plugin.deny_hooks[hook]) {
446
- return plugin.apply_tarpit(connection, hook, score, next);
421
+ if (!this.deny_hooks[hook]) {
422
+ return this.apply_tarpit(connection, hook, score, next)
447
423
  }
448
424
 
449
- let rejectMsg = 'very bad karma score: {score}';
450
- if (plugin.cfg.deny && plugin.cfg.deny.message) {
451
- rejectMsg = plugin.cfg.deny.message;
425
+ let rejectMsg = 'very bad karma score: {score}'
426
+ if (this.cfg.deny && this.cfg.deny.message) {
427
+ rejectMsg = this.cfg.deny.message
452
428
  }
453
429
 
454
430
  if (/\{/.test(rejectMsg)) {
455
- rejectMsg = rejectMsg.replace(/\{score\}/, score);
456
- rejectMsg = rejectMsg.replace(/\{uuid\}/, connection.uuid);
431
+ rejectMsg = rejectMsg.replace(/\{score\}/, score)
432
+ rejectMsg = rejectMsg.replace(/\{uuid\}/, connection.uuid)
457
433
  }
458
434
 
459
- return plugin.apply_tarpit(connection, hook, score, () => {
460
- next(constants.DENY, rejectMsg);
461
- });
435
+ return this.apply_tarpit(connection, hook, score, () => {
436
+ next(constants.DENY, rejectMsg)
437
+ })
462
438
  }
463
439
 
464
440
  exports.hook_deny = function (next, connection, params) {
465
- const plugin = this;
466
-
467
- if (plugin.should_we_skip(connection)) return next();
441
+ if (this.should_we_skip(connection)) return next()
468
442
 
469
443
  // let pi_deny = params[0]; // (constants.deny, denysoft, ok)
470
444
  // let pi_message = params[1];
471
- const pi_name = params[2];
445
+ const pi_name = params[2]
472
446
  // let pi_function = params[3];
473
447
  // let pi_params = params[4];
474
- const pi_hook = params[5];
448
+ const pi_hook = params[5]
475
449
 
476
450
  // exceptions, whose 'DENY' should not be captured
477
451
  if (pi_name) {
478
- if (pi_name === 'karma') return next();
479
- if (plugin.deny_exclude_plugins[pi_name]) return next();
480
- }
481
- if (pi_hook && plugin.deny_exclude_hooks[pi_hook]) {
482
- return next();
452
+ if (pi_name === 'karma') return next()
453
+ if (this.deny_exclude_plugins[pi_name]) return next()
483
454
  }
455
+ if (pi_hook && this.deny_exclude_hooks[pi_hook]) return next()
484
456
 
485
- if (!connection.results) {
486
- return next(constants.OK); // resume the connection
487
- }
457
+ if (!connection.results) return next(constants.OK) // resume the connection
488
458
 
489
459
  // intercept any other denials
490
- connection.results.add(plugin, { msg: 'deny:' + pi_name });
491
- connection.results.incr(plugin, { score: -2 });
460
+ connection.results.add(this, { msg: `deny: ${pi_name}` })
461
+ connection.results.incr(this, { score: -2 })
492
462
 
493
- next(constants.OK); // resume the connection
463
+ next(constants.OK) // resume the connection
494
464
  }
495
465
 
496
466
  exports.hook_connect = function (next, connection) {
497
- const plugin = this;
467
+ if (this.should_we_skip(connection)) return next()
498
468
 
499
- if (plugin.should_we_skip(connection)) return next();
500
-
501
- const asnkey = plugin.get_asn_key(connection);
469
+ const asnkey = this.get_asn_key(connection)
502
470
  if (asnkey) {
503
- plugin.check_asn(connection, asnkey);
471
+ this.check_asn(connection, asnkey)
504
472
  }
505
- plugin.should_we_deny(next, connection, 'connect');
473
+ this.should_we_deny(next, connection, 'connect')
506
474
  }
507
475
 
508
476
  exports.hook_helo = function (next, connection) {
509
- const plugin = this;
510
-
511
- if (plugin.should_we_skip(connection)) return next();
477
+ if (this.should_we_skip(connection)) return next()
512
478
 
513
- plugin.should_we_deny(next, connection, 'helo');
479
+ this.should_we_deny(next, connection, 'helo')
514
480
  }
515
481
 
516
482
  exports.hook_ehlo = function (next, connection) {
517
- const plugin = this;
483
+ if (this.should_we_skip(connection)) return next()
518
484
 
519
- if (plugin.should_we_skip(connection)) return next();
520
-
521
- plugin.should_we_deny(next, connection, 'ehlo');
485
+ this.should_we_deny(next, connection, 'ehlo')
522
486
  }
523
487
 
524
488
  exports.hook_vrfy = function (next, connection) {
525
- const plugin = this;
526
-
527
- if (plugin.should_we_skip(connection)) return next();
489
+ if (this.should_we_skip(connection)) return next()
528
490
 
529
- plugin.should_we_deny(next, connection, 'vrfy');
491
+ this.should_we_deny(next, connection, 'vrfy')
530
492
  }
531
493
 
532
494
  exports.hook_noop = function (next, connection) {
533
- const plugin = this;
534
-
535
- if (plugin.should_we_skip(connection)) return next();
495
+ if (this.should_we_skip(connection)) return next()
536
496
 
537
- plugin.should_we_deny(next, connection, 'noop');
497
+ this.should_we_deny(next, connection, 'noop')
538
498
  }
539
499
 
540
500
  exports.hook_data = function (next, connection) {
541
- const plugin = this;
501
+ if (this.should_we_skip(connection)) return next()
542
502
 
543
- if (plugin.should_we_skip(connection)) return next();
544
-
545
- plugin.should_we_deny(next, connection, 'data');
503
+ this.should_we_deny(next, connection, 'data')
546
504
  }
547
505
 
548
506
  exports.hook_queue = function (next, connection) {
549
- const plugin = this;
550
-
551
- if (plugin.should_we_skip(connection)) return next();
507
+ if (this.should_we_skip(connection)) return next()
552
508
 
553
- plugin.should_we_deny(next, connection, 'queue');
509
+ this.should_we_deny(next, connection, 'queue')
554
510
  }
555
511
 
556
512
  exports.hook_reset_transaction = function (next, connection) {
557
- const plugin = this;
513
+ if (this.should_we_skip(connection)) return next()
558
514
 
559
- if (plugin.should_we_skip(connection)) return next();
560
-
561
- connection.results.add(plugin, {emit: true});
562
- plugin.should_we_deny(next, connection, 'reset_transaction');
515
+ connection.results.add(this, {emit: true})
516
+ this.should_we_deny(next, connection, 'reset_transaction')
563
517
  }
564
518
 
565
519
  exports.hook_unrecognized_command = function (next, connection, params) {
566
- const plugin = this;
567
520
 
568
- if (plugin.should_we_skip(connection)) return next();
521
+ if (this.should_we_skip(connection)) return next()
569
522
 
570
523
  // in case karma is in config/plugins before tls
571
- if (params[0].toUpperCase() === 'STARTTLS') return next();
524
+ if (params[0].toUpperCase() === 'STARTTLS') return next()
572
525
 
573
526
  // in case karma is in config/plugins before AUTH plugin(s)
574
- if (connection.notes.authenticating) return next();
527
+ if (connection.notes.authenticating) return next()
575
528
 
576
- connection.results.incr(plugin, {score: -1});
577
- connection.results.add(plugin, {fail: `cmd:(${params})`});
529
+ connection.results.incr(this, {score: -1})
530
+ connection.results.add(this, {fail: `cmd:(${params})`})
578
531
 
579
- return plugin.should_we_deny(next, connection, 'unrecognized_command');
532
+ return this.should_we_deny(next, connection, 'unrecognized_command')
580
533
  }
581
534
 
582
535
  exports.ip_history_from_redis = function (next, connection) {
583
- const plugin = this;
536
+ const plugin = this
584
537
 
585
- if (plugin.should_we_skip(connection)) return next();
538
+ if (plugin.should_we_skip(connection)) return next()
586
539
 
587
- const expire = (plugin.cfg.redis.expire_days || 60) * 86400; // to days
588
- const dbkey = 'karma|' + connection.remote.ip;
540
+ const expire = (plugin.cfg.redis.expire_days || 60) * 86400 // to days
541
+ const dbkey = `karma|${connection.remote.ip}`
589
542
 
590
543
  // redis plugin is emitting errors, no need to here
591
- if (!plugin.db) return next();
544
+ if (!plugin.db) return next()
592
545
 
593
546
  plugin.db.hgetall(dbkey, (err, dbr) => {
594
547
  if (err) {
595
- connection.results.add(plugin, {err: err});
596
- return next();
548
+ connection.results.add(plugin, { err })
549
+ return next()
597
550
  }
598
551
 
599
552
  if (dbr === null) {
600
- plugin.init_ip(dbkey, connection.remote.ip, expire);
601
- return next();
553
+ plugin.init_ip(dbkey, connection.remote.ip, expire)
554
+ return next()
602
555
  }
603
556
 
604
557
  plugin.db.multi()
605
558
  .hincrby(dbkey, 'connections', 1) // increment total conn
606
559
  .expire(dbkey, expire) // extend expiration
607
560
  .exec((err2, replies) => {
608
- if (err2) connection.results.add(plugin, {err: err2});
609
- });
561
+ if (err2) connection.results.add(plugin, {err: err2})
562
+ })
610
563
 
611
564
  const results = {
612
565
  good: dbr.good,
@@ -618,412 +571,381 @@ exports.ip_history_from_redis = function (next, connection) {
618
571
 
619
572
  // Careful: don't become self-fulfilling prophecy.
620
573
  if (parseInt(dbr.good) > 5 && parseInt(dbr.bad) === 0) {
621
- results.pass = 'all_good';
574
+ results.pass = 'all_good'
622
575
  }
623
576
  if (parseInt(dbr.bad) > 5 && parseInt(dbr.good) === 0) {
624
- results.fail = 'all_bad';
577
+ results.fail = 'all_bad'
625
578
  }
626
579
 
627
- connection.results.add(plugin, results);
580
+ connection.results.add(plugin, results)
628
581
 
629
- plugin.check_awards(connection);
630
- return next();
631
- });
582
+ plugin.check_awards(connection)
583
+ return next()
584
+ })
632
585
  }
633
586
 
634
587
  exports.hook_mail = function (next, connection, params) {
635
- const plugin = this;
636
588
 
637
- if (plugin.should_we_skip(connection)) return next();
589
+ if (this.should_we_skip(connection)) return next()
638
590
 
639
- plugin.check_spammy_tld(params[0], connection);
591
+ this.check_spammy_tld(params[0], connection)
640
592
 
641
593
  // look for invalid (RFC 5321,(2)821) space in envelope from
642
- const full_from = connection.current_line;
594
+ const full_from = connection.current_line
643
595
  if (full_from.toUpperCase().substring(0,11) !== 'MAIL FROM:<') {
644
- connection.loginfo(plugin, 'RFC ignorant env addr format: ' + full_from);
645
- connection.results.add(plugin, {fail: 'rfc5321.MailFrom'});
596
+ connection.loginfo(this, `RFC ignorant env addr format: ${full_from}`)
597
+ connection.results.add(this, {fail: 'rfc5321.MailFrom'})
646
598
  }
647
599
 
648
600
  // apply TLS awards (if defined)
649
- if (plugin.cfg.tls !== undefined) {
650
- if (plugin.cfg.tls.set && connection.tls.enabled) {
651
- connection.results.incr(plugin, {score: plugin.cfg.tls.set});
601
+ if (this.cfg.tls !== undefined) {
602
+ if (this.cfg.tls.set && connection.tls.enabled) {
603
+ connection.results.incr(this, {score: this.cfg.tls.set})
652
604
  }
653
- if (plugin.cfg.tls.unset && !connection.tls.enabled) {
654
- connection.results.incr(plugin, {score: plugin.cfg.tls.unset});
605
+ if (this.cfg.tls.unset && !connection.tls.enabled) {
606
+ connection.results.incr(this, {score: this.cfg.tls.unset})
655
607
  }
656
608
  }
657
609
 
658
- return plugin.should_we_deny(next, connection, 'mail');
610
+ return this.should_we_deny(next, connection, 'mail')
659
611
  }
660
612
 
661
613
  exports.hook_rcpt = function (next, connection, params) {
662
- const plugin = this;
663
614
 
664
- if (plugin.should_we_skip(connection)) return next();
615
+ if (this.should_we_skip(connection)) return next()
665
616
 
666
- const rcpt = params[0];
617
+ const rcpt = params[0]
667
618
 
668
619
  // hook_rcpt catches recipients that no rcpt_to plugin permitted
669
620
  // hook_rcpt_ok catches accepted recipients
670
621
 
671
622
  // odds of from_user=rcpt_user in ham: < 1%, in spam > 40%
672
623
  // 2015-05 30-day sample: 84% spam correlation
673
- const txn = connection.transaction;
674
- if (txn && txn.mail_from && txn.mail_from.user === rcpt.user) {
675
- connection.results.add(plugin, {fail: 'env_user_match'});
624
+ if (connection?.transaction?.mail_from?.user === rcpt.user) {
625
+ connection.results.add(this, {fail: 'env_user_match'})
676
626
  }
677
627
 
678
- plugin.check_syntax_RcptTo(connection);
628
+ this.check_syntax_RcptTo(connection)
679
629
 
680
- connection.results.add(plugin, {fail: 'rcpt_to'});
630
+ connection.results.add(this, {fail: 'rcpt_to'})
681
631
 
682
- return plugin.should_we_deny(next, connection, 'rcpt');
632
+ return this.should_we_deny(next, connection, 'rcpt')
683
633
  }
684
634
 
685
635
  exports.hook_rcpt_ok = function (next, connection, rcpt) {
686
- const plugin = this;
687
636
 
688
- if (plugin.should_we_skip(connection)) return next();
637
+ if (this.should_we_skip(connection)) return next()
689
638
 
690
- const txn = connection.transaction;
639
+ const txn = connection.transaction
691
640
  if (txn && txn.mail_from && txn.mail_from.user === rcpt.user) {
692
- connection.results.add(plugin, {fail: 'env_user_match'});
641
+ connection.results.add(this, {fail: 'env_user_match'})
693
642
  }
694
643
 
695
- plugin.check_syntax_RcptTo(connection);
644
+ this.check_syntax_RcptTo(connection)
696
645
 
697
- return plugin.should_we_deny(next, connection, 'rcpt');
646
+ return this.should_we_deny(next, connection, 'rcpt')
698
647
  }
699
648
 
700
649
  exports.hook_data_post = function (next, connection) {
701
650
  // goal: prevent delivery of spam before queue
702
- const plugin = this;
703
651
 
704
- if (plugin.should_we_skip(connection)) return next();
652
+ if (this.should_we_skip(connection)) return next()
705
653
 
706
- plugin.check_awards(connection); // update awards
654
+ this.check_awards(connection) // update awards
707
655
 
708
- const results = connection.results.collate(plugin);
709
- connection.logdebug(plugin, 'adding header: ' + results);
710
- connection.transaction.remove_header('X-Haraka-Karma');
711
- connection.transaction.add_header('X-Haraka-Karma', results);
656
+ const results = connection.results.collate(this)
657
+ connection.logdebug(this, `adding header: ${results}`)
658
+ connection.transaction.remove_header('X-Haraka-Karma')
659
+ connection.transaction.add_header('X-Haraka-Karma', results)
712
660
 
713
- return plugin.should_we_deny(next, connection, 'data_post');
661
+ return this.should_we_deny(next, connection, 'data_post')
714
662
  }
715
663
 
716
664
  exports.increment = function (connection, key, val) {
717
- const plugin = this;
718
- if (!plugin.db) return;
665
+ const plugin = this
666
+ if (!plugin.db) return
719
667
 
720
- plugin.db.hincrby('karma|' + connection.remote.ip, key, 1);
668
+ plugin.db.hincrby(`karma|${connection.remote.ip}`, key, 1)
721
669
 
722
- const asnkey = plugin.get_asn_key(connection);
723
- if (asnkey) plugin.db.hincrby(asnkey, key, 1);
670
+ const asnkey = plugin.get_asn_key(connection)
671
+ if (asnkey) plugin.db.hincrby(asnkey, key, 1)
724
672
  }
725
673
 
726
674
  exports.hook_disconnect = function (next, connection) {
727
- const plugin = this;
675
+ const plugin = this
728
676
 
729
- if (plugin.should_we_skip(connection)) return next();
677
+ plugin.redis_unsubscribe(connection)
730
678
 
731
- plugin.redis_unsubscribe(connection);
679
+ if (plugin.should_we_skip(connection)) return next()
732
680
 
733
- const k = connection.results.get('karma');
681
+ const k = connection.results.get('karma')
734
682
  if (!k || k.score === undefined) {
735
- connection.results.add(plugin, {err: 'karma results missing'});
736
- return next();
683
+ connection.results.add(plugin, {err: 'karma results missing'})
684
+ return next()
737
685
  }
738
686
 
739
687
  if (!plugin.cfg.thresholds) {
740
- plugin.check_awards(connection);
741
- connection.results.add(plugin, {msg: 'no action', emit: true });
742
- return next();
688
+ plugin.check_awards(connection)
689
+ connection.results.add(plugin, {msg: 'no action', emit: true })
690
+ return next()
743
691
  }
744
692
 
745
693
  if (k.score > (plugin.cfg.thresholds.positive || 3)) {
746
- plugin.increment(connection, 'good', 1);
694
+ plugin.increment(connection, 'good', 1)
747
695
  }
748
696
  if (k.score < 0) {
749
- plugin.increment(connection, 'bad', 1);
697
+ plugin.increment(connection, 'bad', 1)
750
698
  }
751
699
 
752
- connection.results.add(plugin, {emit: true });
753
- return next();
700
+ connection.results.add(plugin, {emit: true })
701
+ return next()
754
702
  }
755
703
 
756
704
  exports.get_award_loc_from_note = function (connection, award) {
757
- const plugin = this;
758
705
 
759
706
  if (connection.transaction) {
760
- const obj = plugin.assemble_note_obj(connection.transaction, award);
761
- if (obj) { return obj; }
707
+ const obj = this.assemble_note_obj(connection.transaction, award)
708
+ if (obj) return obj
762
709
  }
763
710
 
764
- // connection.logdebug(plugin, 'no txn note: ' + award);
765
- const obj = plugin.assemble_note_obj(connection, award);
766
- if (obj) return obj;
711
+ // connection.logdebug(this, `no txn note: ${award}`);
712
+ const obj = this.assemble_note_obj(connection, award)
713
+ if (obj) return obj
767
714
 
768
- // connection.logdebug(plugin, 'no conn note: ' + award);
769
- return;
715
+ // connection.logdebug(this, `no conn note: ${award}`);
716
+ return
770
717
  }
771
718
 
772
719
  exports.get_award_loc_from_results = function (connection, loc_bits) {
773
720
 
774
- let pi_name = loc_bits[1];
775
- let notekey = loc_bits[2];
721
+ let pi_name = loc_bits[1]
722
+ let notekey = loc_bits[2]
776
723
 
777
724
  if (phase_prefixes[pi_name]) {
778
- pi_name = loc_bits[1] + '.' + loc_bits[2];
779
- notekey = loc_bits[3];
725
+ pi_name = `${loc_bits[1]}.${loc_bits[2]}`
726
+ notekey = loc_bits[3]
780
727
  }
781
728
 
782
- let obj;
783
- if (connection.transaction) {
784
- obj = connection.transaction.results.get(pi_name);
785
- }
786
- if (!obj) {
787
- // connection.logdebug(plugin, 'no txn results: ' + pi_name);
788
- obj = connection.results.get(pi_name);
789
- }
790
- if (!obj) {
791
- // connection.logdebug(plugin, 'no conn results: ' + pi_name);
792
- return;
793
- }
729
+ let obj
730
+ if (connection.transaction) obj = connection.transaction.results.get(pi_name)
731
+
732
+ // connection.logdebug(plugin, `no txn results: ${pi_name}`);
733
+ if (!obj) obj = connection.results.get(pi_name)
734
+ if (!obj) return
794
735
 
795
- // connection.logdebug(plugin, 'found results for ' + pi_name +
796
- // ', ' + notekey);
797
- if (notekey) { return obj[notekey]; }
798
- return obj;
736
+ // connection.logdebug(plugin, `found results for ${pi_name}, ${notekey}`);
737
+ if (notekey) return obj[notekey]
738
+ return obj
799
739
  }
800
740
 
801
741
  exports.get_award_location = function (connection, award_key) {
802
742
  // based on award key, find the requested note or result
803
- const plugin = this;
804
- const bits = award_key.split('@');
805
- const loc_bits = bits[0].split('.');
806
- if (loc_bits.length === 1) { // ex: relaying
807
- return connection[bits[0]];
808
- }
743
+ const bits = award_key.split('@')
744
+ const loc_bits = bits[0].split('.')
745
+ if (loc_bits.length === 1) return connection[bits[0]] // ex: relaying
809
746
 
810
747
  if (loc_bits[0] === 'notes') { // ex: notes.spf_mail_helo
811
- return plugin.get_award_loc_from_note(connection, bits[0]);
748
+ return this.get_award_loc_from_note(connection, bits[0])
812
749
  }
813
750
 
814
- if (loc_bits[0] === 'results') { // ex: results.geoip.distance
815
- return plugin.get_award_loc_from_results(connection, loc_bits);
751
+ if (loc_bits[0] === 'results') { // ex: results.geoip.distance
752
+ return this.get_award_loc_from_results(connection, loc_bits)
816
753
  }
817
754
 
818
755
  // ex: transaction.results.spf
819
- if (connection.transaction &&
820
- loc_bits[0] === 'transaction' &&
821
- loc_bits[1] === 'results') {
822
- loc_bits.shift();
823
- return plugin.get_award_loc_from_results(connection.transaction, loc_bits);
756
+ if (connection.transaction && loc_bits[0] === 'transaction' && loc_bits[1] === 'results') {
757
+ loc_bits.shift()
758
+ return this.get_award_loc_from_results(connection.transaction, loc_bits)
824
759
  }
825
760
 
826
- connection.logdebug(plugin, 'unknown location for ' + award_key);
761
+ connection.logdebug(this, `unknown location for ${award_key}`)
827
762
  }
828
763
 
829
764
  exports.get_award_condition = function (note_key, note_val) {
830
- let wants;
831
- const keybits = note_key.split('@');
832
- if (keybits[1]) { wants = keybits[1]; }
765
+ let wants
766
+ const keybits = note_key.split('@')
767
+ if (keybits[1]) { wants = keybits[1] }
833
768
 
834
- const valbits = note_val.split(/\s+/);
835
- if (!valbits[1]) { return wants; }
836
- if (valbits[1] !== 'if') { return wants; } // no if condition
769
+ const valbits = note_val.split(/\s+/)
770
+ if (!valbits[1]) return wants
771
+ if (valbits[1] !== 'if') return wants // no if condition
837
772
 
838
773
  if (valbits[2].match(/^(equals|gt|lt|match)$/)) {
839
- if (valbits[3]) { wants = valbits[3]; }
774
+ if (valbits[3]) wants = valbits[3]
840
775
  }
841
- return wants;
776
+ return wants
842
777
  }
843
778
 
844
779
  exports.check_awards = function (connection) {
845
- const plugin = this;
846
- const karma = connection.results.get('karma');
847
- if (!karma ) return;
848
- if (!karma.todo) return;
780
+ const karma = connection.results.get('karma')
781
+ if (!karma?.todo) return
849
782
 
850
783
  for (const key in karma.todo) {
851
784
  // loc = terms
852
785
  // note_location [@wants] = award [conditions]
853
786
  // results.geoip.too_far = -1
854
787
  // results.geoip.distance@4000 = -1 if gt 4000
855
- const award_terms = karma.todo[key];
788
+ const award_terms = karma.todo[key]
856
789
 
857
- const note = plugin.get_award_location(connection, key);
858
- if (note === undefined) { continue; }
859
- let wants = plugin.get_award_condition(key, award_terms);
790
+ const note = this.get_award_location(connection, key)
791
+ if (note === undefined) continue
792
+ let wants = this.get_award_condition(key, award_terms)
860
793
 
861
794
  // test the desired condition
862
- const bits = award_terms.split(/\s+/);
863
- const award = parseFloat(bits[0]);
795
+ const bits = award_terms.split(/\s+/)
796
+ const award = parseFloat(bits[0])
864
797
  if (!bits[1] || bits[1] !== 'if') { // no if conditions
865
- if (!note) { continue; } // failed truth test
798
+ if (!note) continue // failed truth test
866
799
  if (!wants) { // no wants, truth matches
867
- plugin.apply_award(connection, key, award);
868
- delete karma.todo[key];
869
- continue;
800
+ this.apply_award(connection, key, award)
801
+ delete karma.todo[key]
802
+ continue
870
803
  }
871
- if (note !== wants) { continue; } // didn't match
804
+ if (note !== wants) continue // didn't match
872
805
  }
873
806
 
874
- // connection.loginfo(plugin, 'check_awards, case matching for: ' +
875
- // wants);
807
+ // connection.loginfo(this, `check_awards, case matching for: ${wants}`
876
808
 
877
809
  // the matching logic here is inverted, weeding out misses (continue)
878
810
  // Matches fall through (break) to the apply_award below.
879
- const condition = bits[2];
811
+ const condition = bits[2]
880
812
  switch (condition) {
881
813
  case 'equals':
882
- if (wants != note) continue;
883
- break;
814
+ if (wants != note) continue
815
+ break
884
816
  case 'gt':
885
- if (parseFloat(note) <= parseFloat(wants)) { continue; }
886
- break;
817
+ if (parseFloat(note) <= parseFloat(wants)) continue
818
+ break
887
819
  case 'lt':
888
- if (parseFloat(note) >= parseFloat(wants)) { continue; }
889
- break;
820
+ if (parseFloat(note) >= parseFloat(wants)) continue
821
+ break
890
822
  case 'match':
891
823
  if (Array.isArray(note)) {
892
- // connection.logerror(plugin, 'matching an array');
893
- if (new RegExp(wants, 'i').test(note)) { break; }
824
+ // connection.logerror(this, 'matching an array');
825
+ if (new RegExp(wants, 'i').test(note)) break
894
826
  }
895
- if (note.toString().match(new RegExp(wants, 'i'))) { break; }
896
- continue;
827
+ if (note.toString().match(new RegExp(wants, 'i'))) break
828
+ continue
897
829
  case 'length': {
898
- const operator = bits[3];
899
- if (bits[4]) { wants = bits[4]; }
830
+ const operator = bits[3]
831
+ if (bits[4]) { wants = bits[4] }
900
832
  switch (operator) {
901
833
  case 'gt':
902
- if (note.length <= parseFloat(wants)) { continue; }
903
- break;
834
+ if (note.length <= parseFloat(wants)) continue
835
+ break
904
836
  case 'lt':
905
- if (note.length >= parseFloat(wants)) { continue; }
906
- break;
837
+ if (note.length >= parseFloat(wants)) continue
838
+ break
907
839
  case 'equals':
908
- if (note.length !== parseFloat(wants)) { continue; }
909
- break;
840
+ if (note.length !== parseFloat(wants)) continue
841
+ break
910
842
  default:
911
- connection.logerror(plugin, 'length operator "' +
912
- operator + '" not supported.');
913
- continue;
843
+ connection.logerror(this, `length operator "${operator}" not supported.`)
844
+ continue
914
845
  }
915
- break;
846
+ break
916
847
  }
917
848
  case 'in': // if in pass whitelisted
918
849
  // let list = bits[3];
919
- if (bits[4]) { wants = bits[4]; }
920
- if (!Array.isArray(note)) { continue; }
921
- if (!wants) { continue; }
922
- if (note.indexOf(wants) !== -1) { break; } // found!
923
- continue;
850
+ if (bits[4]) { wants = bits[4] }
851
+ if (!Array.isArray(note)) continue
852
+ if (!wants) continue
853
+ if (note.indexOf(wants) !== -1) break // found!
854
+ continue
924
855
  default:
925
- continue;
856
+ continue
926
857
  }
927
- plugin.apply_award(connection, key, award);
928
- delete karma.todo[key];
858
+ this.apply_award(connection, key, award)
859
+ delete karma.todo[key]
929
860
  }
930
861
  }
931
862
 
932
863
  exports.apply_award = function (connection, nl, award) {
933
- const plugin = this;
934
- if (!award) { return; }
864
+ if (!award) return
935
865
  if (isNaN(award)) { // garbage in config
936
- connection.logerror(plugin, 'non-numeric award from: ' + nl + ':' +
937
- award);
938
- return;
866
+ connection.logerror(this, `non-numeric award from: ${nl}:${award}`)
867
+ return
939
868
  }
940
869
 
941
- const bits = nl.split('@'); nl = bits[0]; // strip off @... if present
870
+ const bits = nl.split('@'); nl = bits[0] // strip off @... if present
942
871
 
943
- connection.results.incr(plugin, {score: award});
944
- connection.logdebug(plugin, 'applied ' + nl + ':' + award);
872
+ connection.results.incr(this, {score: award})
873
+ connection.logdebug(this, `applied ${nl}:${award}`)
945
874
 
946
875
  let trimmed = nl.substring(0, 5) === 'notes' ? nl.substring(6) :
947
876
  nl.substring(0, 7) === 'results' ? nl.substring(8) :
948
877
  nl.substring(0,19) === 'transaction.results' ?
949
- nl.substring(20) : nl;
878
+ nl.substring(20) : nl
950
879
 
951
- if (trimmed.substring(0,7) === 'rcpt_to') trimmed = trimmed.substring(8);
952
- if (trimmed.substring(0,7) === 'mail_from') trimmed = trimmed.substring(10);
953
- if (trimmed.substring(0,7) === 'connect') trimmed = trimmed.substring(8);
954
- if (trimmed.substring(0,4) === 'data') trimmed = trimmed.substring(5);
880
+ if (trimmed.substring(0,7) === 'rcpt_to') trimmed = trimmed.substring(8)
881
+ if (trimmed.substring(0,7) === 'mail_from') trimmed = trimmed.substring(10)
882
+ if (trimmed.substring(0,7) === 'connect') trimmed = trimmed.substring(8)
883
+ if (trimmed.substring(0,4) === 'data') trimmed = trimmed.substring(5)
955
884
 
956
- if (award > 0) { connection.results.add(plugin, {pass: trimmed}); }
957
- if (award < 0) { connection.results.add(plugin, {fail: trimmed}); }
885
+ if (award > 0) connection.results.add(this, { pass: trimmed })
886
+ if (award < 0) connection.results.add(this, { fail: trimmed })
958
887
  }
959
888
 
960
889
  exports.check_spammy_tld = function (mail_from, connection) {
961
- const plugin = this;
962
- if (!plugin.cfg.spammy_tlds) return;
963
- if (mail_from.isNull()) return; // null sender (bounce)
890
+ if (!this.cfg.spammy_tlds) return
891
+ if (mail_from.isNull()) return // null sender (bounce)
964
892
 
965
- const from_tld = mail_from.host.split('.').pop();
966
- // connection.logdebug(plugin, 'from_tld: ' + from_tld);
893
+ const from_tld = mail_from.host.split('.').pop()
894
+ // connection.logdebug(this, `from_tld: ${from_tld}`);
967
895
 
968
- const tld_penalty = parseFloat(plugin.cfg.spammy_tlds[from_tld] || 0);
969
- if (tld_penalty === 0) return;
896
+ const tld_penalty = parseFloat(this.cfg.spammy_tlds[from_tld] || 0)
897
+ if (tld_penalty === 0) return
970
898
 
971
- connection.results.incr(plugin, {score: tld_penalty});
972
- connection.results.add(plugin, {fail: 'spammy.TLD'});
899
+ connection.results.incr(this, {score: tld_penalty})
900
+ connection.results.add(this, {fail: 'spammy.TLD'})
973
901
  }
974
902
 
975
903
  exports.check_syntax_RcptTo = function (connection) {
976
- const plugin = this;
977
-
978
904
  // look for an illegal (RFC 5321,(2)821) space in envelope recipient
979
- const full_rcpt = connection.current_line;
980
- if (full_rcpt.toUpperCase().substring(0,9) === 'RCPT TO:<') { return; }
905
+ const full_rcpt = connection.current_line
906
+ if (full_rcpt.toUpperCase().substring(0,9) === 'RCPT TO:<') return
981
907
 
982
- connection.loginfo(plugin, 'illegal envelope address format: ' +
983
- full_rcpt );
984
- connection.results.add(plugin, {fail: 'rfc5321.RcptTo'});
908
+ connection.loginfo(this, `illegal envelope address format: ${full_rcpt}`)
909
+ connection.results.add(this, {fail: 'rfc5321.RcptTo'})
985
910
  }
986
911
 
987
912
  exports.assemble_note_obj = function (prefix, key) {
988
- let note = prefix;
989
- const parts = key.split('.');
913
+ let note = prefix
914
+ const parts = key.split('.')
990
915
  while (parts.length > 0) {
991
- let next = parts.shift();
916
+ let next = parts.shift()
992
917
  if (phase_prefixes[next]) {
993
- next = next + '.' + parts.shift();
918
+ next = `${next}.${parts.shift()}`
994
919
  }
995
- note = note[next];
996
- if (note === null || note === undefined) { break; }
920
+ note = note[next]
921
+ if (note === null || note === undefined) break
997
922
  }
998
- return note;
923
+ return note
999
924
  }
1000
925
 
1001
926
  exports.check_asn = function (connection, asnkey) {
1002
- const plugin = this;
1003
- if (!plugin.db) return;
927
+ if (!this.db) return
1004
928
 
1005
- const report_as = { name: plugin.name };
929
+ const report_as = { name: this.name }
1006
930
 
1007
- if (plugin.cfg.asn.report_as) {
1008
- report_as.name = plugin.cfg.asn.report_as;
1009
- }
931
+ if (this.cfg.asn.report_as) report_as.name = this.cfg.asn.report_as
1010
932
 
1011
- plugin.db.hgetall(asnkey, (err, res) => {
933
+ this.db.hgetall(asnkey, (err, res) => {
1012
934
  if (err) {
1013
- connection.results.add(plugin, {err: err});
1014
- return;
935
+ connection.results.add(this, { err })
936
+ return
1015
937
  }
1016
938
 
1017
939
  if (res === null) {
1018
- const expire = (plugin.cfg.redis.expire_days || 60) * 86400; // days
1019
- plugin.init_asn(asnkey, expire);
1020
- return;
940
+ const expire = (this.cfg.redis.expire_days || 60) * 86400 // days
941
+ this.init_asn(asnkey, expire)
942
+ return
1021
943
  }
1022
944
 
1023
- plugin.db.hincrby(asnkey, 'connections', 1);
1024
- const asn_score = parseInt(res.good || 0) - (res.bad || 0);
945
+ this.db.hincrby(asnkey, 'connections', 1)
946
+ const asn_score = parseInt(res.good || 0) - (res.bad || 0)
1025
947
  const asn_results = {
1026
- asn_score: asn_score,
948
+ asn_score,
1027
949
  asn_connections: res.connections,
1028
950
  asn_good: res.good,
1029
951
  asn_bad: res.bad,
@@ -1032,49 +954,45 @@ exports.check_asn = function (connection, asnkey) {
1032
954
 
1033
955
  if (asn_score) {
1034
956
  if (asn_score < -5) {
1035
- asn_results.fail = 'asn:history';
957
+ asn_results.fail = 'asn:history'
1036
958
  }
1037
959
  else if (asn_score > 5) {
1038
- asn_results.pass = 'asn:history';
960
+ asn_results.pass = 'asn:history'
1039
961
  }
1040
962
  }
1041
963
 
1042
964
  if (parseInt(res.bad) > 5 && parseInt(res.good) === 0) {
1043
- asn_results.fail = 'asn:all_bad';
965
+ asn_results.fail = 'asn:all_bad'
1044
966
  }
1045
967
  if (parseInt(res.good) > 5 && parseInt(res.bad) === 0) {
1046
- asn_results.pass = 'asn:all_good';
968
+ asn_results.pass = 'asn:all_good'
1047
969
  }
1048
970
 
1049
- connection.results.add(report_as, asn_results);
1050
- });
971
+ connection.results.add(report_as, asn_results)
972
+ })
1051
973
  }
1052
974
 
1053
975
  exports.init_ip = function (dbkey, rip, expire) {
1054
- const plugin = this;
1055
- if (!plugin.db) return;
1056
- plugin.db.multi()
976
+ if (!this.db) return
977
+ this.db.multi()
1057
978
  .hmset(dbkey, {'bad': 0, 'good': 0, 'connections': 1})
1058
979
  .expire(dbkey, expire)
1059
- .exec();
980
+ .exec()
1060
981
  }
1061
982
 
1062
983
  exports.get_asn_key = function (connection) {
1063
- const plugin = this;
1064
- if (!plugin.cfg.asn.enable) { return; }
1065
- let asn = connection.results.get('asn');
1066
- if (!asn || !asn.asn) {
1067
- asn = connection.results.get('geoip');
1068
- }
1069
- if (!asn || !asn.asn || isNaN(asn.asn)) { return; }
1070
- return 'as' + asn.asn;
984
+ if (!this.cfg.asn.enable) return
985
+ let asn = connection.results.get('asn')
986
+ if (!asn || !asn.asn) asn = connection.results.get('geoip')
987
+ if (!asn || !asn.asn || isNaN(asn.asn)) return
988
+ return `as${asn.asn}`
1071
989
  }
1072
990
 
1073
991
  exports.init_asn = function (asnkey, expire) {
1074
- const plugin = this;
1075
- if (!plugin.db) return;
992
+ const plugin = this
993
+ if (!plugin.db) return
1076
994
  plugin.db.multi()
1077
995
  .hmset(asnkey, {'bad': 0, 'good': 0, 'connections': 1})
1078
996
  .expire(asnkey, expire * 2) // keep ASN longer
1079
- .exec();
997
+ .exec()
1080
998
  }