<commit_before>// Call spamassassin via spamd

var sock = require('./line_socket');
var utils = require('./utils');

var defaults = {
    spamd_socket: 'localhost:783',
    max_size:     500000,
    old_headers_action: "rename",
    subject_prefix: "*** SPAM ***",
};

exports.register = function () {
    var plugin = this;
    var load_config = function () {
        plugin.loginfo("loading spamassassin.ini");
        plugin.cfg = plugin.config.get('spamassassin.ini', load_config);

        for (var key in defaults) {
            if (plugin.cfg.main[key]) continue;
            plugin.cfg.main[key] = defaults[key];
        }

        ['reject_threshold', 'relay_reject_threshold',
        'munge_subject_threshold', 'max_size'].forEach(function (item) {
            if (!plugin.cfg.main[item]) return;
            plugin.cfg.main[item] = Number(plugin.cfg.main[item]);
        });
    };
    load_config();
};

exports.hook_data_post = function (next, connection) {
    var plugin = this;
    if (plugin.msg_too_big(connection)) return next();

    var username        = plugin.get_spamd_username(connection);
    var headers         = plugin.get_spamd_headers(connection, username);
    var socket          = plugin.get_spamd_socket(next, connection);
    socket.is_connected = false;
    var results_timeout = parseInt(plugin.cfg.main.results_timeout) || 300;

    socket.on('connect', function () {
        if (!connection.transaction) {
            socket.end();
            return next();
        }
        this.is_connected = true;
        // Reset timeout
        this.setTimeout(results_timeout * 1000);
        socket.write(headers.join("\r\n") + "\r\n");
        connection.transaction.message_stream.pipe(socket);
    });

    var spamd_response = { headers: {} };
    var state = 'line0';
    var last_header;

    socket.on('line', function (line) {
        connection.logprotocol(plugin, "Spamd C: " + line + ' state=' + state);
        line = line.replace(/\r?\n/, '');
        if (state === 'line0') {
            spamd_response.line0 = line;
            state = 'response';
        }
        else if (state === 'response') {
            if (line.match(/\S/)) {
                var matches;
                if (matches = line.match(/Spam: (True|False) ; (-?\d+\.\d) \/ (-?\d+\.\d)/)) {
                    spamd_response.flag = matches[1];
                    spamd_response.score = matches[2];
                    spamd_response.hits = matches[2];  // backwards compat
                    spamd_response.reqd = matches[3];
                    spamd_response.flag = spamd_response.flag === 'True' ? 'Yes' : 'No';
                }
            }
            else {
                state = 'headers';
            }
        }
        else if (state === 'headers') {
            var m;
            if (m = line.match(/^X-Spam-([\x21-\x39\x3B-\x7E]+):\s+(.*)/)) {
                connection.logdebug(plugin, "header: " + line);
                last_header = m[1];
                spamd_response.headers[m[1]] = m[2];
                return;
            }
            var fold;
            if (last_header && (fold = line.match(/^(\s+.*)/))) {
                spamd_response.headers[last_header] += "\r\n" + fold[1];
                return;
            }
            last_header = '';
        }
    });

    socket.on('end', function () {
        // Abort if the transaction is gone<commit_msg>Make space between header and field optional in SpamAssassin plugin<commit_after>// Call spamassassin via spamd

var sock = require('./line_socket');
var utils = require('./utils');

var defaults = {
    spamd_socket: 'localhost:783',
    max_size:     500000,
    old_headers_action: "rename",
    subject_prefix: "*** SPAM ***",
};

exports.register = function () {
    var plugin = this;
    var load_config = function () {
        plugin.loginfo("loading spamassassin.ini");
        plugin.cfg = plugin.config.get('spamassassin.ini', load_config);

        for (var key in defaults) {
            if (plugin.cfg.main[key]) continue;
            plugin.cfg.main[key] = defaults[key];
        }

        ['reject_threshold', 'relay_reject_threshold',
        'munge_subject_threshold', 'max_size'].forEach(function (item) {
            if (!plugin.cfg.main[item]) return;
            plugin.cfg.main[item] = Number(plugin.cfg.main[item]);
        });
    };
    load_config();
};

exports.hook_data_post = function (next, connection) {
    var plugin = this;
    if (plugin.msg_too_big(connection)) return next();

    var username        = plugin.get_spamd_username(connection);
    var headers         = plugin.get_spamd_headers(connection, username);
    var socket          = plugin.get_spamd_socket(next, connection);
    socket.is_connected = false;
    var results_timeout = parseInt(plugin.cfg.main.results_timeout) || 300;

    socket.on('connect', function () {
        if (!connection.transaction) {
            socket.end();
            return next();
        }
        this.is_connected = true;
        // Reset timeout
        this.setTimeout(results_timeout * 1000);
        socket.write(headers.join("\r\n") + "\r\n");
        connection.transaction.message_stream.pipe(socket);
    });

    var spamd_response = { headers: {} };
    var state = 'line0';
    var last_header;

    socket.on('line', function (line) {
        connection.logprotocol(plugin, "Spamd C: " + line + ' state=' + state);
        line = line.replace(/\r?\n/, '');
        if (state === 'line0') {
            spamd_response.line0 = line;
            state = 'response';
        }
        else if (state === 'response') {
            if (line.match(/\S/)) {
                var matches;
                if (matches = line.match(/Spam: (True|False) ; (-?\d+\.\d) \/ (-?\d+\.\d)/)) {
                    spamd_response.flag = matches[1];
                    spamd_response.score = matches[2];
                    spamd_response.hits = matches[2];  // backwards compat
                    spamd_response.reqd = matches[3];
                    spamd_response.flag = spamd_response.flag === 'True' ? 'Yes' : 'No';
                }
            }
            else {
                state = 'headers';
            }
        }
        else if (state === 'headers') {
            var m;
            if (m = line.match(/^X-Spam-([\x21-\x39\x3B-\x7E]+):\s*(.*)/)) {
                connection.logdebug(plugin, "header: " + line);
                last_header = m[1];
                spamd_response.headers[m[1]] = m[2];
                return;
            }
            var fold;
            if (last_header && (fold = line.match(/^(\s+.*)/))) {
                spamd_response.headers[last_header] += "\r\n" + fold[1];
                return;
            }
            last_header = '';
        }
    });

    socket.on('end', function () {
        // Abort if the transaction is gone
{'fraction_non_alphanumeric': 0.15158568519057317, 'fraction_numerical': 0.009601396566773349, 'mean_word_length': 1.8315485996705108, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3590323', 'n_tokens_mistral': 2096, 'n_tokens_neox': 2015, 'n_words': 394}