index.js
// dcc client// http://www.dcc-servers.net/dcc/dcc-tree/dccifd.html const net = require('net'); exports.register = function () { this.load_dcc_ini();} exports.load_dcc_ini = function () { const plugin = this; plugin.cfg = plugin.config.get('dcc.ini', () => { plugin.load_dcc_ini(); });} exports.get_host = function (host) { switch (host) { case 'Unknown': case 'NXDOMAIN': case 'DNSERROR': case undefined: return undefined; default: return host; }} exports.should_train = function (txn) { if (txn.notes.training_mode && txn.notes.training_mode === 'spam') return ' spam'; return '';} exports.human_result = function (code) { switch (code) { case 'A': return 'Accept'; case 'G': return 'Greylist'; case 'R': return 'Reject'; case 'S': return 'Accept some'; case 'T': return 'Temp fail'; }} exports.get_result = function (c, result) { const plugin = this; // Get result code switch (result) { case 'A': // Accept, fall through case 'G': // Greylist, fall through case 'R': // Reject, fall through case 'S': // Accept for some recipients, fall through case 'T': // Temporary failure break; default: c.logerror(plugin, 'invalid result: ' + result); break; } return result;} exports.human_disposition = function (code) { switch (code) { case 'A': return 'Accept'; case 'G': return 'Greylist/Discard'; case 'R': return 'Reject'; }} exports.get_disposition = function (c, disposition) { const plugin = this; switch (disposition) { case 'A': // Deliver the message case 'G': // Discard the message during greylist embargo case 'R': // Discard the message as spam break; default: c.logerror(plugin, 'invalid disposition: ' + disposition); break; } return disposition;} exports.get_request_headers = function (conn, training) { const plugin = this; const txn = conn.transaction; const host = plugin.get_host(conn.remote.host); const headers = [ 'header' + training, conn.remote.ip + ((host) ? '\r' + host : ''), conn.hello.host, txn.mail_from.address(), txn.rcpt_to.map((rcpt) => { return rcpt.address(); }).join('\r'), ].join('\n'); conn.logdebug(plugin, 'sending protocol headers: ' + headers); return headers + '\n\n';} Function `get_response_headers` has a Cognitive Complexity of 12 (exceeds 5 allowed). Consider refactoring.exports.get_response_headers = function (c, rl) { // Read headers const headers = []; for (let i=0; i<rl.length; i++) { if (/^\s/.test(rl[i]) && headers.length) { // Continuation headers[headers.length-1] += rl[i]; } else { if (rl[i]) headers.push(rl[i]); } } c.logdebug(this, 'found ' + headers.length + ' headers'); for (let h=0; h<headers.length; h++) { const header = headers[h].toString('utf8').trim(); let match; if ((match = /^([^: ]+):\s*((?:.|[\r\n])+)/.exec(header))) { c.transaction.add_header(match[1], match[2]); } else { c.logerror(this, 'header did not match regexp: ' + header); } } return headers;} Function `hook_data_post` has 45 lines of code (exceeds 25 allowed). Consider refactoring.exports.hook_data_post = function (next, connection) { const plugin = this; // Fix-up rDNS for DCC const training = plugin.should_train(connection.transaction); let response = ''; let client; function onConnect () { connection.logdebug(plugin, 'connected to dcc'); this.write(plugin.get_request_headers(connection, training) , () => { connection.transaction.message_stream.pipe(client); }); } const c = plugin.cfg.dccifd; if (c.path) { client = net.createConnection(c.path, onConnect); } else { client = net.createConnection(c.port, c.host, onConnect); } client .on('error', function (err) { connection.logerror(plugin, err.message); return next(); }) .on('data', function (chunk) { response += chunk.toString('utf8'); }) .on('end', function () { const rl = response.split("\n"); if (rl.length < 2) { connection.logwarn(plugin, 'invalid response: ' + response + 'length=' + rl.length); return next(); } connection.logdebug(plugin, 'got response: ' + response); const result = plugin.get_result(connection, rl.shift()); const disposition = plugin.get_disposition(connection, rl.shift()); const headers = plugin.get_response_headers(connection, rl); connection.transaction.results.add(plugin, { 'training': (training ? true : false), 'result': plugin.human_result(result), 'disposition': plugin.human_disposition(disposition), headers, }); connection.loginfo(plugin, 'training=' + (training ? 'Y' : 'N') + ` result=${result} disposition=${disposition} headers=${headers.length}`); return next(); });}