<commit_before>'use strict';
// SMTP client object and class. This allows for every part of the client
// protocol to be hooked for different levels of control, such as
// smtp_forward and smtp_proxy queue plugins.

var events = require('events');
var util = require('util');
var generic_pool = require('generic-pool');
var line_socket = require('./line_socket');
var logger = require('./logger');
var uuid = require('./utils').uuid;
var utils = require('./utils');
var config = require('./config');
var tls_socket = require('./tls_socket');
var ipaddr      = require('ipaddr.js');

var smtp_regexp = /^([0-9]{3})([ -])(.*)/;
var STATE = {
    IDLE: 1,
    ACTIVE: 2,
    RELEASED: 3,
    DESTROYED: 4,
};

function SMTPClient(port, host, connect_timeout, idle_timeout) {
    events.EventEmitter.call(this);
    this.uuid = uuid();
    this.connect_timeout = parseInt(connect_timeout) || 30;
    this.socket = line_socket.connect(port, host);
    this.socket.setTimeout(this.connect_timeout * 1000);
    this.socket.setKeepAlive(true);
    this.state = STATE.IDLE;
    this.command = 'greeting';
    this.response = [];
    this.connected = false;
    this.authenticated = false;
    this.auth_capabilities = [];
    var client = this;

    this.socket.on('line', function (line) {
        client.emit('server_protocol', line);
        var matches = smtp_regexp.exec(line);
        if (!matches) {
            client.emit('error', client.uuid + ': Unrecognised response from upstream server: ' + line);
            client.destroy();
            return;
        }

        var code = matches[1];
        var cont = matches[2];
        var msg = matches[3];

        client.response.push(msg);
        if (cont !== ' ') {
            return;
        }

        if (client.command === 'auth') {
            if (code.match(/^3/) && cont === 'VXNlcm5hbWU6') {
                client.emit('auth_username');
                return;
            }
            else if (code.match(/^3/) && cont === 'UGFzc3dvcmQ6') {
                client.emit('auth_password');
                return;
            }
        }

        if (client.command === 'ehlo') {
            if (code.match(/^5/)) {
                // Handle fallback to HELO if EHLO is rejected
                client.emit('greeting', 'HELO');
                return;
            }
            client.emit('capabilities');
            if (client.command !== 'ehlo') {
                return;
            }
        }
        if (client.command === 'xclient' && code.match(/^5/)) {
            // XCLIENT command was rejected (no permission?)
            // Carry on without XCLIENT
            client.command = 'helo';
        }
        else if (code.match(/^[45]/)) {
            client.emit('bad_code', code, client.response.join(' '));
            if (client.state !== STATE.ACTIVE) {
                return;
            }
        }
        switch (client.command) {
            case 'xclient':
                client.xclient = true;
                client.emit('xclient', 'EHLO');
                break;
            case 'starttls':
                this.upgrade(this.tls_options);
                break;
            case 'greeting':
                client.connected = true;
                client.emit('greeting', 'EHLO');
                break;
            case 'ehlo':
                client.emit('helo');
                break;
            case 'helo':
            case 'mail':
            case 'rcpt':
            case 'data':
            case 'dot':
            case 'rset':
            case 'auth':
                client.emit(client.command);
                break;
            case 'quit':
                client.emit('quit');
                client.destroy();
                break;
            default:
                throw new Error("Unknown command: " + client.command);
        }
    });

    this.socket.on('connect', function () {
        // Remove connection timeout and set idle timeout
        client.socket.setTimeout(((idle_timeout) ? idle_timeout : 300) * 1000);
        client.remote_ip = ipaddr.process(client.socket.address()).toString();
    });

    var closed = function (msg) {
        return function (error) {
            if (!error) {
                error = '';
            }
            var errMsg = client.uuid + ': SMTP connection ' + msg + ' ' + error;
            switch (client.state) {
                case STATE.ACTIVE:
                case STATE.IDLE:
                case STATE.RELEASED:
                    client.destroy();
                    break;
                default:<commit_msg>Fix remote_ip in smtp_client<commit_after>'use strict';
// SMTP client object and class. This allows for every part of the client
// protocol to be hooked for different levels of control, such as
// smtp_forward and smtp_proxy queue plugins.

var events = require('events');
var util = require('util');
var generic_pool = require('generic-pool');
var line_socket = require('./line_socket');
var logger = require('./logger');
var uuid = require('./utils').uuid;
var utils = require('./utils');
var config = require('./config');
var tls_socket = require('./tls_socket');
var ipaddr      = require('ipaddr.js');

var smtp_regexp = /^([0-9]{3})([ -])(.*)/;
var STATE = {
    IDLE: 1,
    ACTIVE: 2,
    RELEASED: 3,
    DESTROYED: 4,
};

function SMTPClient(port, host, connect_timeout, idle_timeout) {
    events.EventEmitter.call(this);
    this.uuid = uuid();
    this.connect_timeout = parseInt(connect_timeout) || 30;
    this.socket = line_socket.connect(port, host);
    this.socket.setTimeout(this.connect_timeout * 1000);
    this.socket.setKeepAlive(true);
    this.state = STATE.IDLE;
    this.command = 'greeting';
    this.response = [];
    this.connected = false;
    this.authenticated = false;
    this.auth_capabilities = [];
    var client = this;

    this.socket.on('line', function (line) {
        client.emit('server_protocol', line);
        var matches = smtp_regexp.exec(line);
        if (!matches) {
            client.emit('error', client.uuid + ': Unrecognised response from upstream server: ' + line);
            client.destroy();
            return;
        }

        var code = matches[1];
        var cont = matches[2];
        var msg = matches[3];

        client.response.push(msg);
        if (cont !== ' ') {
            return;
        }

        if (client.command === 'auth') {
            if (code.match(/^3/) && cont === 'VXNlcm5hbWU6') {
                client.emit('auth_username');
                return;
            }
            else if (code.match(/^3/) && cont === 'UGFzc3dvcmQ6') {
                client.emit('auth_password');
                return;
            }
        }

        if (client.command === 'ehlo') {
            if (code.match(/^5/)) {
                // Handle fallback to HELO if EHLO is rejected
                client.emit('greeting', 'HELO');
                return;
            }
            client.emit('capabilities');
            if (client.command !== 'ehlo') {
                return;
            }
        }
        if (client.command === 'xclient' && code.match(/^5/)) {
            // XCLIENT command was rejected (no permission?)
            // Carry on without XCLIENT
            client.command = 'helo';
        }
        else if (code.match(/^[45]/)) {
            client.emit('bad_code', code, client.response.join(' '));
            if (client.state !== STATE.ACTIVE) {
                return;
            }
        }
        switch (client.command) {
            case 'xclient':
                client.xclient = true;
                client.emit('xclient', 'EHLO');
                break;
            case 'starttls':
                this.upgrade(this.tls_options);
                break;
            case 'greeting':
                client.connected = true;
                client.emit('greeting', 'EHLO');
                break;
            case 'ehlo':
                client.emit('helo');
                break;
            case 'helo':
            case 'mail':
            case 'rcpt':
            case 'data':
            case 'dot':
            case 'rset':
            case 'auth':
                client.emit(client.command);
                break;
            case 'quit':
                client.emit('quit');
                client.destroy();
                break;
            default:
                throw new Error("Unknown command: " + client.command);
        }
    });

    this.socket.on('connect', function () {
        // Remove connection timeout and set idle timeout
        client.socket.setTimeout(((idle_timeout) ? idle_timeout : 300) * 1000);
        client.remote_ip = ipaddr.process(client.socket.remoteAddress).toString();
    });

    var closed = function (msg) {
        return function (error) {
            if (!error) {
                error = '';
            }
            var errMsg = client.uuid + ': SMTP connection ' + msg + ' ' + error;
            switch (client.state) {
                case STATE.ACTIVE:
                case STATE.IDLE:
                case STATE.RELEASED:
                    client.destroy();
                    break;
                default:
{'fraction_non_alphanumeric': 0.13677447457257977, 'fraction_numerical': 0.007187193727540019, 'mean_word_length': 1.5855855855855856, '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': '6149879', 'n_tokens_mistral': 2626, 'n_tokens_neox': 2501, 'n_words': 590}