<commit_before>// Copyright 2015, EMC, Inc.

'use strict';

module.exports = lookupServiceFactory;

lookupServiceFactory.$provide = 'Services.Lookup';
lookupServiceFactory.$inject = [
    'Promise',
    'Services.Waterline',
    'Services.Configuration',
    'Assert',
    'Errors',
    '_',
    'lru-cache',
    'ARPCache',
    'ChildProcess'
];

/**
 * Provides a LookupService singleton.
 * @private
 * @param  {Promise} Promise
 * @param  {Waterline} waterline
 * @param  {Assert} assert
 * @param  {Errors} errors
 * @param  {lodash} _
 * @param  {lru-cache} LRU Cache Package
 *
 * @return {LookupService} An instance of the LookupService.
 */
function lookupServiceFactory(
    Promise,
    waterline,
    configuration,
    assert,
    Errors,
    _,
    LRU,
    arpCache,
    ChildProcess
) {
    // TODO: stolen from http-service could stand to be a re-usable helper.
    /**
     * Get remote address of the client.
     * @private
     * @param {req} req from express
     * @returns {String|Undefined} either the ip of requester or undefined
     *                             if unavailable
     */
    function remoteAddress(req) {
        assert.ok(req, 'req');

        if(req.get("X-Real-IP")) {
            return req.get("X-Real-IP");
        }

        if (req.ip) {
            return req.ip;
        }

        if (req._remoteAddress) {
            return req._remoteAddress;
        }

        if (req.connection) {
            return req.connection.remoteAddress;
        }

        // TODO: This doesn't appear to ever get hit and causes an assertion
        // in the IP lookup due to undefined not being an IP so we need to investigate
        // what to do in this particular case (which again doesn't seem to have ever
        // actually occurred)
        return undefined;
    }

    /**
     * Provides functionality to correlate various identifiers
     * to their appropriate counterparts.
     * @constructor
     */
    function LookupService () {
        this.resetNodeIdCache();
        this.resetMacRequests();
    }

    /**
     * Reset all cached Node IDs.
     */
    LookupService.prototype.resetNodeIdCache = function () {
        if (this.nodeIdCache) {
            this.nodeIdCache.reset();
            return;
        }

        this.nodeIdCache = LRU({
            max: 500,
            maxAge: 30000
        });
    };

    LookupService.prototype.resetMacRequests = function () {
        if (configuration.get('externalLookupHelper', null)) {
            this.macRequests = {};
        } else {
            delete this.macRequests;
        }
    };

    /**
     * Look up an entry in the lookups collection by IP. Optionally fall back
     * to other mechanisms if we can't find an entry.
     * @private
     * @param {ip} IP address to look for
     * @returns lookups record
     */
    LookupService.prototype.lookupByIP = function (ip) {
        return this.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(ip);
        });
    };

    /**
     * Look up an entry in the lookups collection by MAC. Optionally fall back
     * to other mechanisms if we can't find an entry.
     * @private
     * @param {mac} MAC address to look for
     * @returns lookups record
     */
    LookupService.prototype.lookupByMAC = function (mac) {
        return this.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(mac);
        })
        .catch(function (error) {
            var helper = configuration.get('externalLookupHelper', null);
            if (helper != null) {
                return this.runExternalHelper(helper, mac).then(function () {
                    return waterline.lookups.findOneByTerm(mac);
                });
            }
            throw error;
        }.bind(this));
    };

    /**
     * Run an external helper script to fill in missing lookup entries.
     * Only one instance of the helper script is run at any given time for
     * any given MAC address. All other requests will be resolved when the
     * running instance returns.
     * @private
     * @param {helperPath} Path to the lookup script.
     * @param {mac} The MAC address we need information on.
     */
    LookupService.prototype.runExternalHelper = function (helperPath, mac) {
        if (mac in this.macRequests) {
            return new Promise(function (resolve, reject) {
                var responder = {resolve: resolve, reject: reject};
                this.macRequests[mac].push(responder);
            }.bind(this));
        }

        var helper = new ChildProcess(helperPath, [mac]);
        this.macRequests[mac] = [];
        return this.processHelperResults(helper).then(function () {
            this.handleHelperPromises(mac)
        }.bind(this));
    };

    /**
     * Process the output from an external lookup helper script.
     * The expected output is one or more lines of text of the form:
     *    <MAC address> <IP address>
     * Each entry will be turned into arguments for this.setIpAddress.
     * @private
     * @param {helper} The ChildProcess object representing the helper script.
     */
    LookupService.prototype.processHelperResults = function (helper) {
        var self = this;
        return helper.run().then(function (ret) {
            var promises = [];

            ret.stdout.split('\n').forEach(function (line) {
                if (!line) {
                    return;
                }

                var items = line.split(' ');
                promises.push(this.setIpAddress(items[1], items[0]));
            }.bind(this))

            return Promise.all(promises);
        }.bind(this));
    };

    /**
     * Clean up remaining helper script promises if any are waiting on the
     * completion of a lookup script.
     * @private
     * @param {mac} The MAC address that was being updated.
     */<commit_msg>Shift to using self rather than .bind().<commit_after>// Copyright 2015, EMC, Inc.

'use strict';

module.exports = lookupServiceFactory;

lookupServiceFactory.$provide = 'Services.Lookup';
lookupServiceFactory.$inject = [
    'Promise',
    'Services.Waterline',
    'Services.Configuration',
    'Assert',
    'Errors',
    '_',
    'lru-cache',
    'ARPCache',
    'ChildProcess'
];

/**
 * Provides a LookupService singleton.
 * @private
 * @param  {Promise} Promise
 * @param  {Waterline} waterline
 * @param  {Assert} assert
 * @param  {Errors} errors
 * @param  {lodash} _
 * @param  {lru-cache} LRU Cache Package
 *
 * @return {LookupService} An instance of the LookupService.
 */
function lookupServiceFactory(
    Promise,
    waterline,
    configuration,
    assert,
    Errors,
    _,
    LRU,
    arpCache,
    ChildProcess
) {
    // TODO: stolen from http-service could stand to be a re-usable helper.
    /**
     * Get remote address of the client.
     * @private
     * @param {req} req from express
     * @returns {String|Undefined} either the ip of requester or undefined
     *                             if unavailable
     */
    function remoteAddress(req) {
        assert.ok(req, 'req');

        if(req.get("X-Real-IP")) {
            return req.get("X-Real-IP");
        }

        if (req.ip) {
            return req.ip;
        }

        if (req._remoteAddress) {
            return req._remoteAddress;
        }

        if (req.connection) {
            return req.connection.remoteAddress;
        }

        // TODO: This doesn't appear to ever get hit and causes an assertion
        // in the IP lookup due to undefined not being an IP so we need to investigate
        // what to do in this particular case (which again doesn't seem to have ever
        // actually occurred)
        return undefined;
    }

    /**
     * Provides functionality to correlate various identifiers
     * to their appropriate counterparts.
     * @constructor
     */
    function LookupService () {
        this.resetNodeIdCache();
        this.resetMacRequests();
    }

    /**
     * Reset all cached Node IDs.
     */
    LookupService.prototype.resetNodeIdCache = function () {
        if (this.nodeIdCache) {
            this.nodeIdCache.reset();
            return;
        }

        this.nodeIdCache = LRU({
            max: 500,
            maxAge: 30000
        });
    };

    LookupService.prototype.resetMacRequests = function () {
        if (configuration.get('externalLookupHelper', null)) {
            this.macRequests = {};
        } else {
            delete this.macRequests;
        }
    };

    /**
     * Look up an entry in the lookups collection by IP. Optionally fall back
     * to other mechanisms if we can't find an entry.
     * @private
     * @param {ip} IP address to look for
     * @returns lookups record
     */
    LookupService.prototype.lookupByIP = function (ip) {
        return this.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(ip);
        });
    };

    /**
     * Look up an entry in the lookups collection by MAC. Optionally fall back
     * to other mechanisms if we can't find an entry.
     * @private
     * @param {mac} MAC address to look for
     * @returns lookups record
     */
    LookupService.prototype.lookupByMAC = function (mac) {
        var self = this;

        return this.validateArpCache()
        .then(function() {
            return waterline.lookups.findOneByTerm(mac);
        })
        .catch(function (error) {
            var helper = configuration.get('externalLookupHelper', null);
            if (helper != null) {
                return self.runExternalHelper(helper, mac).then(function () {
                    return waterline.lookups.findOneByTerm(mac);
                });
            }
            throw error;
        });
    };

    /**
     * Run an external helper script to fill in missing lookup entries.
     * Only one instance of the helper script is run at any given time for
     * any given MAC address. All other requests will be resolved when the
     * running instance returns.
     * @private
     * @param {helperPath} Path to the lookup script.
     * @param {mac} The MAC address we need information on.
     */
    LookupService.prototype.runExternalHelper = function (helperPath, mac) {
        var self = this;

        if (mac in this.macRequests) {
            return new Promise(function (resolve, reject) {
                var responder = {resolve: resolve, reject: reject};
                self.macRequests[mac].push(responder);
            });
        }

        var helper = new ChildProcess(helperPath, [mac]);
        this.macRequests[mac] = [];
        return this.processHelperResults(helper).then(function () {
            self.handleHelperPromises(mac)
        });
    };

    /**
     * Process the output from an external lookup helper script.
     * The expected output is one or more lines of text of the form:
     *    <MAC address> <IP address>
     * Each entry will be turned into arguments for this.setIpAddress.
     * @private
     * @param {helper} The ChildProcess object representing the helper script.
     */
    LookupService.prototype.processHelperResults = function (helper) {
        var self = this;

        return helper.run().then(function (ret) {
            var promises = [];

            ret.stdout.split('\n').forEach(function (line) {
                if (!line) {
                    return;
                }

                var items = line.split(' ');
                promises.push(self.setIpAddress(items[1], items[0]));
            });

            return Promise.all(promises);
        });
    };

    /**
     * Clean up remaining helper script promises if any are waiting on the
     * completion of a lookup script.
     * @private
     * @param {mac} The MAC address that was being updated.
     */
{'fraction_non_alphanumeric': 0.1033204112590595, 'fraction_numerical': 0.002359683128265633, 'mean_word_length': 1.9512559064909227, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '623326', 'n_tokens_mistral': 3261, 'n_tokens_neox': 3140, 'n_words': 977}