<commit_before>/*!
 * Node - Clam
 * Copyright(c) 2013 <NAME> <<EMAIL>>
 * MIT Licensed
 */

// Module dependencies.
var __ = require('underscore');
var fs = require('fs');
var exec = require('child_process').exec;
var execSync = require('child_process').execSync;
var spawn = require('child_process').spawn;
var os = require('os');
	
// ****************************************************************************
// NodeClam class definition
// -----
// @param	Object	options		Key => Value pairs to override default settings
// ****************************************************************************
function NodeClam(options) {
    options = options || {};
    
    this.default_scanner = 'clamdscan';
    
    // Configuration Settings
    this.defaults = Object.freeze({
        remove_infected: false,
        quarantine_infected: false,
        scan_log: null,
        debug_mode: false,
        file_list: null,
        scan_recursively: true,
        clamscan: {
            path: '/usr/bin/clamscan',
            scan_archives: true,
            db: null,
            active: true
        },
        clamdscan: {
            path: '/usr/bin/clamdscan',
            config_file: '/etc/clamd.conf',
            multiscan: true,
            reload_db: false,
            active: true
        },
        preference: this.default_scanner
    });
    
    this.settings = __.extend({},this.defaults);

    // Override defaults with user preferences
    if (options.hasOwnProperty('clamscan') && Object.keys(options.clamscan).length > 0) {
        this.settings.clamscan = __.extend({},this.settings.clamscan, options.clamscan);
        delete options.clamscan;
    }
    if (options.hasOwnProperty('clamdscan') && Object.keys(options.clamdscan).length > 0) {
        this.settings.clamdscan = __.extend({},this.settings.clamdscan, options.clamdscan);
        delete options.clamdscan;
    }
    this.settings = __.extend({},this.settings,options);
    
    // Backwards compatibilty section
    if (this.settings.quarantine_path && !__.isEmpty(this.settings.quarantine_path)) {
        this.settings.quarantine_infected = this.settings.quarantine_path;
    }
    
    // Determine whether to use clamdscan or clamscan
    this.scanner = this.default_scanner;
    if (typeof this.settings.preference !== 'string' || ['clamscan','clamdscan'].indexOf(this.settings.preference) === -1) {
        throw new Error("Invalid virus scanner preference defined!");
    }
    if (this.settings.preference === 'clamscan' && this.settings.clamscan.active === true) {
        this.scanner = 'clamscan';
    }
    
    // Check to make sure preferred scanner exists and actually is a clamscan binary
    if (!this.is_clamav_binary_sync(this.scanner)) {
        // Fall back to other option:
        if (this.scanner == 'clamdscan' && this.settings.clamscan.active === true && this.is_clamav_binary_sync('clamscan')) {
            this.scanner == 'clamscan';
        } else if (this.scanner == 'clamscan' && this.settings.clamdscan.active === true && this.is_clamav_binary_sync('clamdscan')) {
            this.scanner == 'clamdscan';
        } else {
            throw new Error("No valid & active virus scanning binaries are active and available!");
        }
    }
    
    // Make sure quarantine infected path exists at specified location
    if (!__.isEmpty(this.settings.quarantine_infected) && !fs.existsSync(this.settings.quarantine_infected)) {
        var err_msg = "Quarantine infected path (" + this.settings.quarantine_infected + ") is invalid.";
        this.settings.quarantine_infected = false;
        throw new Error(err_msg);
        
        if (this.settings.debug_mode)
            console.log("node-clam: " + err_msg);
    }
    
    // Make sure scan_log exists at specified location
    if (!__.isEmpty(this.settings.scan_log) && !fs.existsSync(this.settings.scan_log)) {
        var err_msg = "node-clam: Scan Log path (" + this.settings.scan_log + ") is invalid.";
        this.settings.scan_log = null;
        if (this.settings.debug_mode)
            console.log(err_msg);
    }
    
    // If using clamscan, make sure definition db exists at specified location
    if (this.scanner === 'clamscan') {
        if (!__.isEmpty(this.settings.clamscan.db) && !fs.existsSync(this.settings.db)) {
            var err_msg = "node-clam: Definitions DB path (" + this.db + ") is invalid.";
            this.db = null;
            if(this.settings.debug_mode)
                console.log(err_msg);
        }
    }
    
    // Build clam flags
    this.clam_flags = build_clam_flags(this.scanner, this.settings);
}

// ****************************************************************************
// Checks to see if a particular path contains a clamav binary
// -----
// NOTE: Not currently being used (maybe for future implementations)
// SEE: in_clamav_binary_sync()
// -----
// @param   String      scanner     Scanner (clamscan or clamdscan) to check
// @param   Function    cb          Callback function to call after check
// @return  VOID
// ****************************************************************************
NodeClam.prototype.is_clamav_binary = function(scanner, cb) {
    var path = this.settings[scanner].path || null;
    if (!path) {
        if (this.settings.debug_mode) {
            console.log("node-clam: Could not determine path for clamav binary.");
        }
        return cb(false);
    }
    
    var version_cmds = {
        clamdscan: path + ' -c ' + this.settings.clamdscan.config_file + ' --version',
        clamscan: path + ' --version'
    };
    
    fs.exists(path, function(exists) {
        if (exists === false) {
            if (this.settings.debug_mode) {
                console.log("node-clam: Could not verify the " + scanner + " binary.");
            }
            return cb(false);
        }
        exec(version_cmds[scanner], function(err, stdout, stderr) {
            if (stdout.toString().match(/ClamAV/) === null) {
                if (this.settings.debug_mode) {
                    console.log("node-clam: Could not verify the " + scanner + " binary.");
                }
                return cb(false);
            }
            return cb(true);
        })
    });
}

// ****************************************************************************
// Checks to see if a particular path contains a clamav binary
// -----
// @param   String  scanner     Scanner (clamscan or clamdscan) to check
// @return  Boolean             TRUE: Is binary; FALSE: Not binary
// ****************************************************************************
NodeClam.prototype.is_clamav_binary_sync = function(scanner) {
    var path = this.settings[scanner].path || null;
    if (!path) {
        if (this.settings.testing_mode) {
            console.log("node-clam: Could not determine path for clamav binary.");
        }
        return false;
    }
    
    var version_cmds = {
        clamdscan: path + ' -c ' + this.settings.clamdscan.config_file + ' --version',
        clamscan: path + ' --version'
    };
    
    if (!fs.existsSync(path) || execSync(version_cmds[scanner]).toString().match(/ClamAV/) === null) {
        if (this.settings.testing_mode) {
            console.log("node-clam: Could not verify the " + scanner + " binary.");
        }
        return false;<commit_msg>Removed item that require node > 0.12.<commit_after>/*!
 * Node - Clam
 * Copyright(c) 2013 <NAME> <<EMAIL>>
 * MIT Licensed
 */

// Module dependencies.
var __ = require('underscore');
var fs = require('fs');
var exec = require('child_process').exec;
var execSync = require('child_process').execSync;
var spawn = require('child_process').spawn;
var os = require('os');
	
// ****************************************************************************
// NodeClam class definition
// -----
// @param	Object	options		Key => Value pairs to override default settings
// ****************************************************************************
function NodeClam(options) {
    options = options || {};
    
    this.default_scanner = 'clamdscan';
    
    // Configuration Settings
    this.defaults = Object.freeze({
        remove_infected: false,
        quarantine_infected: false,
        scan_log: null,
        debug_mode: false,
        file_list: null,
        scan_recursively: true,
        clamscan: {
            path: '/usr/bin/clamscan',
            scan_archives: true,
            db: null,
            active: true
        },
        clamdscan: {
            path: '/usr/bin/clamdscan',
            config_file: '/etc/clamd.conf',
            multiscan: true,
            reload_db: false,
            active: true
        },
        preference: this.default_scanner
    });
    
    this.settings = __.extend({},this.defaults);

    // Override defaults with user preferences
    if (options.hasOwnProperty('clamscan') && Object.keys(options.clamscan).length > 0) {
        this.settings.clamscan = __.extend({},this.settings.clamscan, options.clamscan);
        delete options.clamscan;
    }
    if (options.hasOwnProperty('clamdscan') && Object.keys(options.clamdscan).length > 0) {
        this.settings.clamdscan = __.extend({},this.settings.clamdscan, options.clamdscan);
        delete options.clamdscan;
    }
    this.settings = __.extend({},this.settings,options);
    
    // Backwards compatibilty section
    if (this.settings.quarantine_path && !__.isEmpty(this.settings.quarantine_path)) {
        this.settings.quarantine_infected = this.settings.quarantine_path;
    }
    
    // Determine whether to use clamdscan or clamscan
    this.scanner = this.default_scanner;
    if (typeof this.settings.preference !== 'string' || ['clamscan','clamdscan'].indexOf(this.settings.preference) === -1) {
        throw new Error("Invalid virus scanner preference defined!");
    }
    if (this.settings.preference === 'clamscan' && this.settings.clamscan.active === true) {
        this.scanner = 'clamscan';
    }
    
    // Check to make sure preferred scanner exists and actually is a clamscan binary
    if (!this.is_clamav_binary_sync(this.scanner)) {
        // Fall back to other option:
        if (this.scanner == 'clamdscan' && this.settings.clamscan.active === true && this.is_clamav_binary_sync('clamscan')) {
            this.scanner == 'clamscan';
        } else if (this.scanner == 'clamscan' && this.settings.clamdscan.active === true && this.is_clamav_binary_sync('clamdscan')) {
            this.scanner == 'clamdscan';
        } else {
            throw new Error("No valid & active virus scanning binaries are active and available!");
        }
    }
    
    // Make sure quarantine infected path exists at specified location
    if (!__.isEmpty(this.settings.quarantine_infected) && !fs.existsSync(this.settings.quarantine_infected)) {
        var err_msg = "Quarantine infected path (" + this.settings.quarantine_infected + ") is invalid.";
        this.settings.quarantine_infected = false;
        throw new Error(err_msg);
        
        if (this.settings.debug_mode)
            console.log("node-clam: " + err_msg);
    }
    
    // Make sure scan_log exists at specified location
    if (!__.isEmpty(this.settings.scan_log) && !fs.existsSync(this.settings.scan_log)) {
        var err_msg = "node-clam: Scan Log path (" + this.settings.scan_log + ") is invalid.";
        this.settings.scan_log = null;
        if (this.settings.debug_mode)
            console.log(err_msg);
    }
    
    // If using clamscan, make sure definition db exists at specified location
    if (this.scanner === 'clamscan') {
        if (!__.isEmpty(this.settings.clamscan.db) && !fs.existsSync(this.settings.db)) {
            var err_msg = "node-clam: Definitions DB path (" + this.db + ") is invalid.";
            this.db = null;
            if(this.settings.debug_mode)
                console.log(err_msg);
        }
    }
    
    // Build clam flags
    this.clam_flags = build_clam_flags(this.scanner, this.settings);
}

// ****************************************************************************
// Checks to see if a particular path contains a clamav binary
// -----
// NOTE: Not currently being used (maybe for future implementations)
// SEE: in_clamav_binary_sync()
// -----
// @param   String      scanner     Scanner (clamscan or clamdscan) to check
// @param   Function    cb          Callback function to call after check
// @return  VOID
// ****************************************************************************
NodeClam.prototype.is_clamav_binary = function(scanner, cb) {
    var path = this.settings[scanner].path || null;
    if (!path) {
        if (this.settings.debug_mode) {
            console.log("node-clam: Could not determine path for clamav binary.");
        }
        return cb(false);
    }
    
    var version_cmds = {
        clamdscan: path + ' -c ' + this.settings.clamdscan.config_file + ' --version',
        clamscan: path + ' --version'
    };
    
    fs.exists(path, function(exists) {
        if (exists === false) {
            if (this.settings.debug_mode) {
                console.log("node-clam: Could not verify the " + scanner + " binary.");
            }
            return cb(false);
        }
        exec(version_cmds[scanner], function(err, stdout, stderr) {
            if (stdout.toString().match(/ClamAV/) === null) {
                if (this.settings.debug_mode) {
                    console.log("node-clam: Could not verify the " + scanner + " binary.");
                }
                return cb(false);
            }
            return cb(true);
        })
    });
}

// ****************************************************************************
// Checks to see if a particular path contains a clamav binary
// -----
// @param   String  scanner     Scanner (clamscan or clamdscan) to check
// @return  Boolean             TRUE: Is binary; FALSE: Not binary
// ****************************************************************************
NodeClam.prototype.is_clamav_binary_sync = function(scanner) {
    var path = this.settings[scanner].path || null;
    if (!path) {
        if (this.settings.testing_mode) {
            console.log("node-clam: Could not determine path for clamav binary.");
        }
        return false;
    }
    
    var version_cmds = {
        clamdscan: path + ' -c ' + this.settings.clamdscan.config_file + ' --version',
        clamscan: path + ' --version'
    };
    
    /* 
     * Saving this line for version 1.0 release--the one that requires Node 0> .12 
     * if (!fs.existsSync(path) || execSync(version_cmds[scanner]).toString().match(/ClamAV/) === null) {
     */
    if (!fs.existsSync(path)) {
        if (this.settings.testing_mode) {
            console.log("node-clam: Could not verify the " + scanner + " binary.");
        }
        return false;
{'fraction_non_alphanumeric': 0.18467438105489775, 'fraction_numerical': 0.001480086114101184, 'mean_word_length': 2.501766784452297, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 17, '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': '1802420', 'n_tokens_mistral': 4247, 'n_tokens_neox': 4164, 'n_words': 959}