<commit_before>'use strict';

const querystring = require('querystring');
const url = require('url');

// -----------------------------------------------------------------------------

class Parser {

    constructor(configuration) {
        this.setConfiguration(configuration);
        this.query = null;
        this.result = null;
    }

    parse(request) {
        this.query = {};
        this.initializeResult();
        this.parseRequest(request);
        return this.result;
    }

    initializeResult() {
        this.result = {
            options: {
                headers: {}
            },
            configuration: {}
        };
    }

    parseRequest(request) {
        // Split the string into an array of lines and reverse so that we can pop
        // lines off the stack and read them in order.
        let lines = request.split('\n');
        let line;
        lines.reverse();
        while (lines.length > 0) {
            line = lines.pop().trim();
            if (line !== '') {
                this.parseLine(line);
            } else if (this.parsedRequestLine()) {
                // The first empty line after the header section indicates the body.
                this.parseBody(lines);
                break;
            }
        }
        this.ensureUri();
        // Merge the parsed query parameters onto the request path.
        this.result.options.path = mergeQuery(this.result.options.path, this.query);
    }

    ensureUri() {
        if (!this.result.options.hostname) {
            let hostHeaderValue = valueForCaseInsensitiveKey(
                this.result.options.headers, 'Host');
            if (hostHeaderValue) {
                let parts = hostHeaderValue.split(':', 2);
                if (parts.length === 1) {
                    this.result.options.hostname = hostHeaderValue;
                } else {
                    this.result.options.hostname = parts[0];
                    this.result.options.port = parseInt(parts[1], 10);
                }
            }
        }
    }

    parseLine(line) {
        if (!this.parsedRequestLine()) {
            this.parseRequestLine(line);
        } else {
            this.parseHeaderSectionLine(line);
        }
    }

    parseRequestLine(line) {
        // Skip comments
        if (beginsWith(line, ['#', '//'])) {
            return;
        }
        const properties = [
            'protocol',
            'auth',
            'host',
            'port',
            'hostname',
            'path'
        ];
        let words = line.split(' ');
        let uri;

        if (words.length === 1) {
            // For one-word lines, use the default method; the word as the URI.
            this.result.options.method = 'GET';
            uri = this.parseUri(words[0]);
        } else {
            // For two-or-more-word lines, the first word is the method; The
            // second is the URI; others are ignored.
            this.result.options.method = words[0];
            uri = this.parseUri(words[1]);
        }
        // Copy specific properties from the parsed URI to the results.
        for (let i = 0; i < properties.length; ++i) {
            let property = properties[i];
            this.result.options[property] = uri[property];
        }
        // Ensure the options contain a protocol.
        if (!this.result.options.protocol) {
            this.result.options.protocol = normalizeProtocol('');
        }
    }

    parseUri(uri) {
        if (!this.isPathOnly(uri) && !this.containsProtocol(uri)) {
            uri = 'http://' + uri;
        }
        return url.parse(uri);
    }

    isPathOnly(uri) {
        return uri[0] === '/';
    }

    containsProtocol(uri) {
        return uri.includes('://');
    }

    parseHeaderSectionLine (line) {
        if (this.isCommentLine(line)) {
            // No-op
        } else if (this.isQueryLine(line)) {
            this.parseQueryLine(line);
        } else if (this.isOptionLine(line)) {
            this.parseOptionLine(line);
        } else {
            this.parseHeaderLine(line);
        }
    }

    isCommentLine(line) {
        return beginsWith(line, ['#', '//']);
    }

    isQueryLine(line) {
        return beginsWith(line, ['?', '&']);
    }

    parseQueryLine(line) {
        // Query parameter
        line = line.slice(1).trim();
        let separator = earliestSubstring([':','='], line);
        if (separator) {
            let words = line.split(separator);
            let key = words[0].trim();
            this.query[key] = words.slice(1).join(separator).trim();
        } else {
            this.query[line] = '';
        }
    }

    isOptionLine(line) {
        return line.charAt(0) == '@';
    }

    parseOptionLine(line) {
        // Query parameter
        line = line.slice(1).trim();
        let separator = earliestSubstring([':','='], line);
        if (separator) {
            let words = line.split(separator);
            let key = words[0].trim();
            let value = words.slice(1).join(separator).trim();
            try {
                value = JSON.parse(value);
            } catch (e) {
                // Do nothing. Retain unparsed value.
            }
            this.setResultOption(key, value);
        } else {
            this.setResultOption(line, true);
        }
    }

    setResultOption(key, value) {
        switch (key) {
            case 'protocol':
                this.result.options[key] = normalizeProtocol(value);
                break;
            case 'auth':
            case 'hostname':
            case 'port':
                this.result.options[key] = value;
                break;
            default:
                this.result.configuration[key] = value;
        }
    }

    parseHeaderLine(line) {
        let words = line.split(':');
        if (words.length > 1) {
            let key = words[0].trim();
            this.result.options.headers[key] = words.slice(1).join(':').trim();
        }
    }

    setConfiguration(configuration) {
        let userConfiguration = configuration || {};
        this.configuration = {};
        let properties = Object.keys(userConfiguration);
        for (let i = 0; i < properties.length; ++i) {
            let property = properties[i];
            this.configuration[property] = userConfiguration[property];
        }
    }

    parsedRequestLine() {
        return !!this.result.options.method;
    }

    parseBody(lines) {
        lines.reverse();
        let body = lines.join('\n').trim();
        // Return the lines to the original order.
        if (body) {
            if (this.isForm()) {
                this.result.options.headers['Content-type'] = 'application/x-www-form-urlencoded';
                this.result.body = this.parseForm(body);
            } else {
                this.result.body = body;
            }
            // Do not override content-length header if set explicitly
            if (!valueForCaseInsensitiveKey(this.result.options.headers, 'content-length')) {
                this.result.options.headers['Content-length'] = '' + this.result.body.length;
            }
        }
    }

    isForm() {
        return this.result.configuration.form === true;
    }

    parseForm(body) {
        let formParser = new FormParser(this.configuration);
        return formParser.parse(body);
    }
}

// -----------------------------------------------------------------------------

class FormParser {

    constructor(configuration) {
        this.setConfiguration(configuration);
        this.fields = {};
        this.multilineInProgress = false;
        this.multilineValue = null;
        this.multilineKey = null;
    }

    setConfiguration(configuration) {
        let userConfiguration = configuration;
        this.configuration = {
            multilineStart: '"""',
            multilineEnd: '"""'
        };
        let properties = Object.keys(userConfiguration);
        for (let i = 0; i < properties.length; ++i) {
            let property = properties[i];
            this.configuration[property] = userConfiguration[property];
        }
    }

    parse(body) {
        // Split the string into an array of lines and reverse so that we can pop
        // lines off the stack and read them in order.
        let lines = body.split('\n');
        lines.reverse();
        while (lines.length > 0) {
            let line = lines.pop();
            this.parseLine(line);
        }
        return querystring.stringify(this.fields);
    }

    parseLine(line) {
        if (this.multilineInProgress) {
            this.parseMultilineField(line);
        } else {
            this.parseField(line);
        }
    }

    isCommentLine(line) {
        return beginsWith(line, ['#', '//']);
    }

    parseField(originalLine) {
        // Trim left-side only as the right is needed to multiline fields.
        let line = originalLine.trimLeft();
        if (this.isCommentLine(line)) {
            return; // no-op
        }
        let key, pos, separator, value, words;
        separator = earliestSubstring([':','='], line);
        if (separator) {
            words = line.split(separator);
            key = words[0].trim();
            value = words[1];
            // Check if this value begins a multiline field.
            pos = value.indexOf(this.configuration.multilineStart);
            if (pos !== -1) {
                value = value.slice(pos + this.configuration.multilineStart.length);
                // Check if this value also ends a multiline field.
                pos = value.indexOf(this.configuration.multilineEnd);
                if (pos !== -1) {
                    // The entire multiline value is on one line.
                    value = value.slice(0, pos);
                } else {
                    // Start multiline.
                    this.multilineInProgress = true;
                    this.multilineKey = key;
                    this.multilineValue = value;
                    return;
                }
            }
            this.fields[key] = value.trim();
        }
    }

    parseMultilineField(line) {
        if (this.isEndOfMultilineField(line)) {
            this.completeMultilineField(line);
        } else {
            this.continueMultilineField(line);
        }
    }

    isEndOfMultilineField(line) {
        return line.indexOf(this.configuration.multilineEnd) !== -1;
    }

    continueMultilineField(line) {
        this.multilineValue += '\n' + line;
    }

    completeMultilineField(line) {
        let pos = line.indexOf(this.configuration.multilineEnd);
        this.fields[this.multilineKey] = this.multilineValue +
            '\n' + line.slice(0, pos);
        this.multilineInProgress = false;
    }
}

// -----------------------------------------------------------------------------

function normalizeProtocol(protocol) {
    if (protocol.slice(0, 5) === 'https') {
        return 'https:';
    } else {
        return 'http:';
    }
}

/**
 * Tests if a string begins with any of the string in the array of needles.
 *
 * @param  {string} haystack String to test
 * @param  {string[]} needles Array of string to look for
 * @return {boolean} True indicates the string begins with one of the needles.
 */
function beginsWith(haystack, needles) {
    let needle;
    for (let i = 0, u = needles.length; i < u; ++i) {
        needle = needles[i];
        if (haystack.substr(0, needle.length) === needle) {
            return true;
        }
    }
    return false;
}

/**
 * Given an array of substrings to search for (needles), return the substring
 * that occurs at the earliest location inside the haystack. If no needs
 * are present in the haystack, return undefined.
 *
 * @param {string[]} needles Strings to search for
 * @param {string} haystack String that should contains a needles
 * @return {string|undefined} The needles that occurs earlier in the haystack
 */
function earliestSubstring(needles, haystack) {
    let minPosition = haystack.length;
    let position, minNeedle = undefined;
    needles.forEach((needle) => {
        position = haystack.indexOf(needle);
        if (position !== -1) {
            minPosition = Math.min(minPosition, position);
            if (minPosition === position) {
                minNeedle = needle;
            }
        }
    });
    return minNeedle;
}

/**
 * Merge additional query parameters onto an existing request path and return
 * the combined path + query
 *
 * @param {string} path Request path, with or without a query
 * @param {Object} newQuery Query parameters to merge
 * @return {string} New path with query parameters merged
 */
function mergeQuery(path, newQuery) {
    let parsed, properties, query, queryString;
    // Return the original path if the query to merge is empty.
    if (Object.keys(newQuery).length === 0) {
        return path;
    }
    // Parse the original path.
    parsed = url.parse(path, true);
    // Merge the queries to form a new query.
    query = parsed.query;
    properties = Object.keys(newQuery);
    for (let i = 0, u = properties.length; i < u; ++i) {
        let property = properties[i];
        query[property] = newQuery[property];
    }
    queryString = querystring.stringify(query);
    path = parsed.pathname + '?' + queryString;
    return path;
}

function valueForCaseInsensitiveKey(object, key) {
    let normalKey = key.toLowerCase();
    let keys = Object.keys(object);
    for (let i = 0; i < keys.length; ++i) {
        let originalKey = keys[i];
        if (originalKey.toLowerCase() === normalKey) {
            return object[originalKey];
        }
    }
}

// -----------------------------------------------------------------------------

module.exports = Parser;
<commit_msg>Calculate request content-length as UTF-8<commit_after>'use strict';

const querystring = require('querystring');
const url = require('url');

// -----------------------------------------------------------------------------

class Parser {

    constructor(configuration) {
        this.setConfiguration(configuration);
        this.query = null;
        this.result = null;
    }

    parse(request) {
        this.query = {};
        this.initializeResult();
        this.parseRequest(request);
        return this.result;
    }

    initializeResult() {
        this.result = {
            options: {
                headers: {}
            },
            configuration: {}
        };
    }

    parseRequest(request) {
        // Split the string into an array of lines and reverse so that we can pop
        // lines off the stack and read them in order.
        let lines = request.split('\n');
        let line;
        lines.reverse();
        while (lines.length > 0) {
            line = lines.pop().trim();
            if (line !== '') {
                this.parseLine(line);
            } else if (this.parsedRequestLine()) {
                // The first empty line after the header section indicates the body.
                this.parseBody(lines);
                break;
            }
        }
        this.ensureUri();
        // Merge the parsed query parameters onto the request path.
        this.result.options.path = mergeQuery(this.result.options.path, this.query);
    }

    ensureUri() {
        if (!this.result.options.hostname) {
            let hostHeaderValue = valueForCaseInsensitiveKey(
                this.result.options.headers, 'Host');
            if (hostHeaderValue) {
                let parts = hostHeaderValue.split(':', 2);
                if (parts.length === 1) {
                    this.result.options.hostname = hostHeaderValue;
                } else {
                    this.result.options.hostname = parts[0];
                    this.result.options.port = parseInt(parts[1], 10);
                }
            }
        }
    }

    parseLine(line) {
        if (!this.parsedRequestLine()) {
            this.parseRequestLine(line);
        } else {
            this.parseHeaderSectionLine(line);
        }
    }

    parseRequestLine(line) {
        // Skip comments
        if (beginsWith(line, ['#', '//'])) {
            return;
        }
        const properties = [
            'protocol',
            'auth',
            'host',
            'port',
            'hostname',
            'path'
        ];
        let words = line.split(' ');
        let uri;

        if (words.length === 1) {
            // For one-word lines, use the default method; the word as the URI.
            this.result.options.method = 'GET';
            uri = this.parseUri(words[0]);
        } else {
            // For two-or-more-word lines, the first word is the method; The
            // second is the URI; others are ignored.
            this.result.options.method = words[0];
            uri = this.parseUri(words[1]);
        }
        // Copy specific properties from the parsed URI to the results.
        for (let i = 0; i < properties.length; ++i) {
            let property = properties[i];
            this.result.options[property] = uri[property];
        }
        // Ensure the options contain a protocol.
        if (!this.result.options.protocol) {
            this.result.options.protocol = normalizeProtocol('');
        }
    }

    parseUri(uri) {
        if (!this.isPathOnly(uri) && !this.containsProtocol(uri)) {
            uri = 'http://' + uri;
        }
        return url.parse(uri);
    }

    isPathOnly(uri) {
        return uri[0] === '/';
    }

    containsProtocol(uri) {
        return uri.includes('://');
    }

    parseHeaderSectionLine (line) {
        if (this.isCommentLine(line)) {
            // No-op
        } else if (this.isQueryLine(line)) {
            this.parseQueryLine(line);
        } else if (this.isOptionLine(line)) {
            this.parseOptionLine(line);
        } else {
            this.parseHeaderLine(line);
        }
    }

    isCommentLine(line) {
        return beginsWith(line, ['#', '//']);
    }

    isQueryLine(line) {
        return beginsWith(line, ['?', '&']);
    }

    parseQueryLine(line) {
        // Query parameter
        line = line.slice(1).trim();
        let separator = earliestSubstring([':','='], line);
        if (separator) {
            let words = line.split(separator);
            let key = words[0].trim();
            this.query[key] = words.slice(1).join(separator).trim();
        } else {
            this.query[line] = '';
        }
    }

    isOptionLine(line) {
        return line.charAt(0) == '@';
    }

    parseOptionLine(line) {
        // Query parameter
        line = line.slice(1).trim();
        let separator = earliestSubstring([':','='], line);
        if (separator) {
            let words = line.split(separator);
            let key = words[0].trim();
            let value = words.slice(1).join(separator).trim();
            try {
                value = JSON.parse(value);
            } catch (e) {
                // Do nothing. Retain unparsed value.
            }
            this.setResultOption(key, value);
        } else {
            this.setResultOption(line, true);
        }
    }

    setResultOption(key, value) {
        switch (key) {
            case 'protocol':
                this.result.options[key] = normalizeProtocol(value);
                break;
            case 'auth':
            case 'hostname':
            case 'port':
                this.result.options[key] = value;
                break;
            default:
                this.result.configuration[key] = value;
        }
    }

    parseHeaderLine(line) {
        let words = line.split(':');
        if (words.length > 1) {
            let key = words[0].trim();
            this.result.options.headers[key] = words.slice(1).join(':').trim();
        }
    }

    setConfiguration(configuration) {
        let userConfiguration = configuration || {};
        this.configuration = {};
        let properties = Object.keys(userConfiguration);
        for (let i = 0; i < properties.length; ++i) {
            let property = properties[i];
            this.configuration[property] = userConfiguration[property];
        }
    }

    parsedRequestLine() {
        return !!this.result.options.method;
    }

    parseBody(lines) {
        lines.reverse();
        let body = lines.join('\n').trim();
        // Return the lines to the original order.
        if (body) {
            if (this.isForm()) {
                this.result.options.headers['Content-type'] = 'application/x-www-form-urlencoded';
                this.result.body = this.parseForm(body);
            } else {
                this.result.body = body;
            }
            // Do not override content-length header if set explicitly
            if (!valueForCaseInsensitiveKey(this.result.options.headers, 'content-length')) {
                this.result.options.headers['Content-length'] = ''
                  + Buffer.byteLength(this.result.body, 'utf8')
            }
        }
    }

    isForm() {
        return this.result.configuration.form === true;
    }

    parseForm(body) {
        let formParser = new FormParser(this.configuration);
        return formParser.parse(body);
    }
}

// -----------------------------------------------------------------------------

class FormParser {

    constructor(configuration) {
        this.setConfiguration(configuration);
        this.fields = {};
        this.multilineInProgress = false;
        this.multilineValue = null;
        this.multilineKey = null;
    }

    setConfiguration(configuration) {
        let userConfiguration = configuration;
        this.configuration = {
            multilineStart: '"""',
            multilineEnd: '"""'
        };
        let properties = Object.keys(userConfiguration);
        for (let i = 0; i < properties.length; ++i) {
            let property = properties[i];
            this.configuration[property] = userConfiguration[property];
        }
    }

    parse(body) {
        // Split the string into an array of lines and reverse so that we can pop
        // lines off the stack and read them in order.
        let lines = body.split('\n');
        lines.reverse();
        while (lines.length > 0) {
            let line = lines.pop();
            this.parseLine(line);
        }
        return querystring.stringify(this.fields);
    }

    parseLine(line) {
        if (this.multilineInProgress) {
            this.parseMultilineField(line);
        } else {
            this.parseField(line);
        }
    }

    isCommentLine(line) {
        return beginsWith(line, ['#', '//']);
    }

    parseField(originalLine) {
        // Trim left-side only as the right is needed to multiline fields.
        let line = originalLine.trimLeft();
        if (this.isCommentLine(line)) {
            return; // no-op
        }
        let key, pos, separator, value, words;
        separator = earliestSubstring([':','='], line);
        if (separator) {
            words = line.split(separator);
            key = words[0].trim();
            value = words[1];
            // Check if this value begins a multiline field.
            pos = value.indexOf(this.configuration.multilineStart);
            if (pos !== -1) {
                value = value.slice(pos + this.configuration.multilineStart.length);
                // Check if this value also ends a multiline field.
                pos = value.indexOf(this.configuration.multilineEnd);
                if (pos !== -1) {
                    // The entire multiline value is on one line.
                    value = value.slice(0, pos);
                } else {
                    // Start multiline.
                    this.multilineInProgress = true;
                    this.multilineKey = key;
                    this.multilineValue = value;
                    return;
                }
            }
            this.fields[key] = value.trim();
        }
    }

    parseMultilineField(line) {
        if (this.isEndOfMultilineField(line)) {
            this.completeMultilineField(line);
        } else {
            this.continueMultilineField(line);
        }
    }

    isEndOfMultilineField(line) {
        return line.indexOf(this.configuration.multilineEnd) !== -1;
    }

    continueMultilineField(line) {
        this.multilineValue += '\n' + line;
    }

    completeMultilineField(line) {
        let pos = line.indexOf(this.configuration.multilineEnd);
        this.fields[this.multilineKey] = this.multilineValue +
            '\n' + line.slice(0, pos);
        this.multilineInProgress = false;
    }
}

// -----------------------------------------------------------------------------

function normalizeProtocol(protocol) {
    if (protocol.slice(0, 5) === 'https') {
        return 'https:';
    } else {
        return 'http:';
    }
}

/**
 * Tests if a string begins with any of the string in the array of needles.
 *
 * @param  {string} haystack String to test
 * @param  {string[]} needles Array of string to look for
 * @return {boolean} True indicates the string begins with one of the needles.
 */
function beginsWith(haystack, needles) {
    let needle;
    for (let i = 0, u = needles.length; i < u; ++i) {
        needle = needles[i];
        if (haystack.substr(0, needle.length) === needle) {
            return true;
        }
    }
    return false;
}

/**
 * Given an array of substrings to search for (needles), return the substring
 * that occurs at the earliest location inside the haystack. If no needs
 * are present in the haystack, return undefined.
 *
 * @param {string[]} needles Strings to search for
 * @param {string} haystack String that should contains a needles
 * @return {string|undefined} The needles that occurs earlier in the haystack
 */
function earliestSubstring(needles, haystack) {
    let minPosition = haystack.length;
    let position, minNeedle = undefined;
    needles.forEach((needle) => {
        position = haystack.indexOf(needle);
        if (position !== -1) {
            minPosition = Math.min(minPosition, position);
            if (minPosition === position) {
                minNeedle = needle;
            }
        }
    });
    return minNeedle;
}

/**
 * Merge additional query parameters onto an existing request path and return
 * the combined path + query
 *
 * @param {string} path Request path, with or without a query
 * @param {Object} newQuery Query parameters to merge
 * @return {string} New path with query parameters merged
 */
function mergeQuery(path, newQuery) {
    let parsed, properties, query, queryString;
    // Return the original path if the query to merge is empty.
    if (Object.keys(newQuery).length === 0) {
        return path;
    }
    // Parse the original path.
    parsed = url.parse(path, true);
    // Merge the queries to form a new query.
    query = parsed.query;
    properties = Object.keys(newQuery);
    for (let i = 0, u = properties.length; i < u; ++i) {
        let property = properties[i];
        query[property] = newQuery[property];
    }
    queryString = querystring.stringify(query);
    path = parsed.pathname + '?' + queryString;
    return path;
}

function valueForCaseInsensitiveKey(object, key) {
    let normalKey = key.toLowerCase();
    let keys = Object.keys(object);
    for (let i = 0; i < keys.length; ++i) {
        let originalKey = keys[i];
        if (originalKey.toLowerCase() === normalKey) {
            return object[originalKey];
        }
    }
}

// -----------------------------------------------------------------------------

module.exports = Parser;
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2014 Glayzzle (BSD3 License)
 * @authors https://github.com/glayzzle/php-parser/graphs/contributors
 * @url http://glayzzle.com
 */

var fs = require('fs');

/**
 * Expose the parser constructor
 */
module.exports = function(engine) {

  var tokens = engine.tokens.names;
  var names = engine.tokens.values;
  var EOF = engine.lexer.EOF;

  // check if argument is a number
  function isNumber(n) {
    return n != '.' && n != ',' && !isNaN(parseFloat(n)) && isFinite(n);
  }

  // private helper : gets a token name
  function getTokenName(token) {
    if (!isNumber(token)) {
      return "'" + token + "'";
    } else {
      if (token == 1) return 'the end of file (EOF)';
      return names[token];
    }
  }

  /**
   * Graceful decorator
   */
  var _gracefulDecorator = function(fn) {
    try {
      this._currentNode = this._gracefulProxy[fn].apply(
        this,
        Array.prototype.slice.call(arguments, 1)
      );
      return this._currentNode;
    } catch(e) {
      if (this.lastError) {
        this.next(); // ignore token & go next
        var errorNode = [
          'error',
          this.lastError.tokenName,
          this.lastError.expected,
          this.lastError.line
        ];
        // force to append the error node
        if (this.ast.length < 3) {
          this.ast.push([]);
        }
        this.ast[2].push(errorNode);
        // return the node
        return errorNode;
      } else {
        throw e;  // not a parsing error
      }
    }
  };

  /**
   * The basic parser api
   */
  var api = {
    // Private vars, do not use directly
    _gracefulProxy: {},
    _graceful: false,
    // the lexer
    lexer: engine.lexer,
    token: null,
    prev: null,
    debug: false,
    locations: false,
    extractDoc: false,
    suppressErrors: false,
    lastError: false,
    startAt: [],
    entries: {
      'SCALAR': [
        tokens.T_CONSTANT_ENCAPSED_STRING,
        tokens.T_START_HEREDOC,
        tokens.T_LNUMBER,
        tokens.T_DNUMBER,
        tokens.T_STRING,
        tokens.T_ARRAY,'[',
        tokens.T_CLASS_C,
        tokens.T_TRAIT_C,
        tokens.T_FUNC_C,
        tokens.T_METHOD_C,
        tokens.T_LINE,
        tokens.T_FILE,
        tokens.T_DIR,
        tokens.T_NS_C,
        '"',
        '-',
        tokens.T_NS_SEPARATOR
      ],
      'T_MAGIC_CONST': [
          tokens.T_CLASS_C,
          tokens.T_TRAIT_C,
          tokens.T_FUNC_C,
          tokens.T_METHOD_C,
          tokens.T_LINE,
          tokens.T_FILE,
          tokens.T_DIR,
          tokens.T_NS_C
      ],
      'T_MEMBER_FLAGS': [
        tokens.T_PUBLIC,
        tokens.T_PRIVATE,
        tokens.T_PROTECTED,
        tokens.T_STATIC,
        tokens.T_ABSTRACT,
        tokens.T_FINAL
      ],
      'VARIABLE': [
        tokens.T_VARIABLE,
        '$',
        tokens.T_NS_SEPARATOR,
        tokens.T_STRING,
        tokens.T_STATIC
      ],
      'EOS': [
        ';',
        tokens.T_CLOSE_TAG,
        EOF,
        tokens.T_INLINE_HTML
      ],
      'EXPR': [
        '@','-','+','!','~','(','`',
        tokens.T_LIST,
        tokens.T_CLONE,
        tokens.T_INC,
        tokens.T_DEC,
        tokens.T_NEW,
        tokens.T_ISSET,
        tokens.T_EMPTY,
        tokens.T_INCLUDE,
        tokens.T_INCLUDE_ONCE,
        tokens.T_REQUIRE,
        tokens.T_REQUIRE_ONCE,
        tokens.T_EVAL,
        tokens.T_INT_CAST,
        tokens.T_DOUBLE_CAST,
        tokens.T_STRING_CAST,
        tokens.T_ARRAY_CAST,
        tokens.T_OBJECT_CAST,
        tokens.T_BOOL_CAST,
        tokens.T_UNSET_CAST,
        tokens.T_EXIT,
        tokens.T_PRINT,
        tokens.T_YIELD,
        tokens.T_STATIC,
        tokens.T_FUNCTION,
        // using VARIABLES :
        tokens.T_VARIABLE,
        '$',
        tokens.T_NS_SEPARATOR,
        tokens.T_STRING,
        // using SCALAR :
        tokens.T_STRING, // @see variable.js line 45 > conflict with variable = shift/reduce :)
        tokens.T_CONSTANT_ENCAPSED_STRING,
        tokens.T_START_HEREDOC,
        tokens.T_LNUMBER,
        tokens.T_DNUMBER,
        tokens.T_ARRAY,'[',
        tokens.T_CLASS_C,
        tokens.T_TRAIT_C,
        tokens.T_FUNC_C,
        tokens.T_METHOD_C,
        tokens.T_LINE,
        tokens.T_FILE,
        tokens.T_DIR,
        tokens.T_NS_C
      ]
    }
    /** main entry point : converts a source code to AST **/
    ,parse: function(code) {
      this.lastError = false;
      this.lexer.setInput(code);
      this.lexer.comment_tokens = this.extractDoc;
      this.length = this.lexer._input.length;
      this.nextWithComments();
      this.ast = ['program', []];
      while(this.token != EOF) {
        var node = this.read_start();
        if (node !== null) {
          if (typeof node[0] !== 'string') {
            node.forEach(function(item) {
              this.ast[1].push(item);
            }.bind(this));
          } else {
            this.ast[1].push(node);
          }
        }
      }
      return this.ast;
    }
    /** handling errors **/
    ,error: function(expect) {
      token = getTokenName(this.token);
      var msgExpect = '';
      if (expect) {
        msgExpect = ', expecting ';
        if (Array.isArray(expect)) {
          for(var i = 0; i < expect.length; i++) {
            expect[i] = getTokenName(expect[i]);
          }
          msgExpect += expect.join(', ');
        } else {
          msgExpect += getTokenName(expect);
        }
      }
      this.lastError = {
        token: this.token,
        tokenName: token,
        expected: expect,
        messageExpected: msgExpect,
        message: 'Parse Error : unexpected ' + token + msgExpect + ' at line ' + this.lexer.yylloc.first_line,
        line: this.lexer.yylloc.first_line
      };
      if (this.suppressErrors && !this._gracefull) {
        this.token = EOF;
      } else {
        throw new Error(this.lastError.message);
      }
    }
    /**
     * enable / disable the graceful mode
     */
    ,graceful: function(mode) {
      if (this._graceful !== mode) {
        if (mode) {
          // enable the graceful mode
          this._gracefulProxy = {};
          for(var i in this) {
            var cb = this[i];
            if (typeof cb === 'function') {
              this._gracefulProxy[i] = cb;
              this[i] = _gracefulDecorator.bind(this, i);
            }
          }
        } else {
          // disable the graceful mode
          for(var i in this._gracefulProxy) {
            this[i] = this._gracefulProxy[i];
          }
        }
        this._graceful = mode;
      }
      return this;

    }
    /**
     * Creates a new AST node
     */
    ,node: function(name) {
      var startAt = null;
      if (this.locations === true) {
        startAt = [
          this.lexer.yylloc.first_line,
          this.lexer.yylloc.first_column,
          this.length - this.lexer._input.length - this.lexer.yytext.length
        ];
      }
      return function() {
        var result =  Array.prototype.slice.call(arguments);
        if (name && name.constructor === Array) {
          if (this.locations === true) {
            name[2] = [
              this.prev[0],
              this.prev[1],
              this.prev[2]
            ];
            Array.prototype.push.apply(name[3], result);
          } else {
            Array.prototype.push.apply(name, result);
          }
          result = name;
        } else {
          if (name) {
            result.unshift(name);
          }
          if (this.locations === true) {
            result = [
              'position',
              startAt,
              [
                this.prev[0],
                this.prev[1],
                this.prev[2]
              ],
              result
            ];
          }
        }
        return result;
      }.bind(this);
    }
    /** expects an end of statement or end of file **/
    ,expectEndOfStatement: function() {
      if (this.token === ';') {
        this.nextWithComments();
        if (this.token === tokens.T_CLOSE_TAG) {
          // strip close tag (statement already closed with ';')
          this.nextWithComments();
        }
      } else if (this.token === tokens.T_CLOSE_TAG) {
        this.nextWithComments();
      } else if (this.token !== tokens.T_INLINE_HTML && this.token !== EOF) {
        this.error(';');
      }
      return this;
    }
    /** outputs some debug information on current token **/
    ,showlog: function() {
      var stack = (new Error()).stack.split('\n');
      console.log(
        'Line '
        + this.lexer.yylloc.first_line
        + ' : '
        + getTokenName(this.token)
        + ">" + this.lexer.yytext + "<"
        + ' @' + stack[3].trim()
      );
      return this;
    }
    /** force to expect specified token **/
    ,expect: function(token) {
      if (!this.lastError) {
        if (Array.isArray(token)) {
          if (token.indexOf(this.token) === -1) {
            this.error(token);
          }
        } else if (this.token != token) {
          this.error(token);
        }
      }
      return this;
    }
    /**returns the current token contents **/
    ,text: function() {
      return this.lexer.yytext;
    }
    /** consume the next token **/
    ,next: function() {
      this.nextWithComments();
      if (this.debug) this.showlog();
      while(this.token === tokens.T_COMMENT || this.token === tokens.T_DOC_COMMENT) {
        // IGNORE COMMENTS
        this.nextWithComments();
      }
      return this;
    }
    /** consume comments (if found) **/
    ,ignoreComments: function() {
      if (this.debug) this.showlog();
      while(this.token === tokens.T_COMMENT || this.token === tokens.T_DOC_COMMENT) {
        // IGNORE COMMENTS
        this.nextWithComments();
      }
      return this;
    }
    /** consume the next token (including doc) **/
    ,nextWithComments: function() {
      this.prev = [
        this.lexer.yylloc.first_line,
        this.lexer.yylloc.first_column,
        this.lexer.offset
      ];
      this.token = this.lexer.lex() || EOF;
      if (this.debug) this.showlog();
      return this;
    }
    /**
     * Check if token is of specified type
     */
    ,is: function(type) {
      if (Array.isArray(type)) {
        return type.indexOf(this.token) !== -1;
      } else {
        return this.entries[type].indexOf(this.token) != -1;
      }
    }
    /** convert an token to ast **/
    ,read_token: function() {
      var result = this.token;
      if (isNumber(result)) {
        result = [result, this.text(), this.lexer.yylloc.first_line];
      }
      this.next();
      return result;
    }
    /**
     * Helper : reads a list of tokens / sample : T_STRING ',' T_STRING ...
     * <ebnf>
     * list ::= separator? ( item separator )* item
     * </ebnf>
     */
    ,read_list: function(item, separator, preserveFirstSeparator) {
      var result = [];

      if (this.token == separator) {
        if (preserveFirstSeparator) result.push('');
        this.next();
      }

      if (typeof (item) === "function") {
        do {
          result.push(item.apply(this, []));
          if (this.token != separator) {
            break;
          }
        } while(this.next().token != EOF);
      } else {
        result.push(this.expect(item).text());
        while (this.next().token != EOF) {
          if (this.token != separator) break;
          // trim current separator & check item
          if (this.next().token != item) break;
          result.push(this.text());
        }
      }
      return result;
    }
  };

  // extends the parser with syntax files
  [
    require('./parser/array.js')(api, tokens, EOF),
    require('./parser/class.js')(api, tokens, EOF),
    require('./parser/expr.js')(api, tokens, EOF),
    require('./parser/function.js')(api, tokens, EOF),
    require('./parser/if.js')(api, tokens, EOF),
    require('./parser/loops.js')(api, tokens, EOF),
    require('./parser/main.js')(api, tokens, EOF),
    require('./parser/namespace.js')(api, tokens, EOF),
    require('./parser/scalar.js')(api, tokens, EOF),
    require('./parser/statement.js')(api, tokens, EOF),
    require('./parser/switch.js')(api, tokens, EOF),
    require('./parser/try.js')(api, tokens, EOF),
    require('./parser/comment.js')(api, tokens, EOF),
    require('./parser/variable.js')(api, tokens, EOF)
  ].forEach(function (ext) {
    for(var k in ext) {
      api[k] = ext[k];
    }
  });

  return api;
};
<commit_msg>fix error message (more like php one)<commit_after>/**
 * Copyright (C) 2014 Glayzzle (BSD3 License)
 * @authors https://github.com/glayzzle/php-parser/graphs/contributors
 * @url http://glayzzle.com
 */

var fs = require('fs');

/**
 * Expose the parser constructor
 */
module.exports = function(engine) {

  var tokens = engine.tokens.names;
  var names = engine.tokens.values;
  var EOF = engine.lexer.EOF;

  // check if argument is a number
  function isNumber(n) {
    return n != '.' && n != ',' && !isNaN(parseFloat(n)) && isFinite(n);
  }

  // private helper : gets a token name
  function getTokenName(token) {
    if (!isNumber(token)) {
      return "'" + token + "'";
    } else {
      if (token == 1) return 'the end of file (EOF)';
      return names[token];
    }
  }

  /**
   * Graceful decorator
   */
  var _gracefulDecorator = function(fn) {
    try {
      this._currentNode = this._gracefulProxy[fn].apply(
        this,
        Array.prototype.slice.call(arguments, 1)
      );
      return this._currentNode;
    } catch(e) {
      if (this.lastError) {
        this.next(); // ignore token & go next
        var errorNode = [
          'error',
          this.lastError.tokenName,
          this.lastError.expected,
          this.lastError.line
        ];
        // force to append the error node
        if (this.ast.length < 3) {
          this.ast.push([]);
        }
        this.ast[2].push(errorNode);
        // return the node
        return errorNode;
      } else {
        throw e;  // not a parsing error
      }
    }
  };

  /**
   * The basic parser api
   */
  var api = {
    // Private vars, do not use directly
    _gracefulProxy: {},
    _graceful: false,
    // the lexer
    lexer: engine.lexer,
    token: null,
    prev: null,
    debug: false,
    locations: false,
    extractDoc: false,
    suppressErrors: false,
    lastError: false,
    startAt: [],
    entries: {
      'SCALAR': [
        tokens.T_CONSTANT_ENCAPSED_STRING,
        tokens.T_START_HEREDOC,
        tokens.T_LNUMBER,
        tokens.T_DNUMBER,
        tokens.T_STRING,
        tokens.T_ARRAY,'[',
        tokens.T_CLASS_C,
        tokens.T_TRAIT_C,
        tokens.T_FUNC_C,
        tokens.T_METHOD_C,
        tokens.T_LINE,
        tokens.T_FILE,
        tokens.T_DIR,
        tokens.T_NS_C,
        '"',
        '-',
        tokens.T_NS_SEPARATOR
      ],
      'T_MAGIC_CONST': [
          tokens.T_CLASS_C,
          tokens.T_TRAIT_C,
          tokens.T_FUNC_C,
          tokens.T_METHOD_C,
          tokens.T_LINE,
          tokens.T_FILE,
          tokens.T_DIR,
          tokens.T_NS_C
      ],
      'T_MEMBER_FLAGS': [
        tokens.T_PUBLIC,
        tokens.T_PRIVATE,
        tokens.T_PROTECTED,
        tokens.T_STATIC,
        tokens.T_ABSTRACT,
        tokens.T_FINAL
      ],
      'VARIABLE': [
        tokens.T_VARIABLE,
        '$',
        tokens.T_NS_SEPARATOR,
        tokens.T_STRING,
        tokens.T_STATIC
      ],
      'EOS': [
        ';',
        tokens.T_CLOSE_TAG,
        EOF,
        tokens.T_INLINE_HTML
      ],
      'EXPR': [
        '@','-','+','!','~','(','`',
        tokens.T_LIST,
        tokens.T_CLONE,
        tokens.T_INC,
        tokens.T_DEC,
        tokens.T_NEW,
        tokens.T_ISSET,
        tokens.T_EMPTY,
        tokens.T_INCLUDE,
        tokens.T_INCLUDE_ONCE,
        tokens.T_REQUIRE,
        tokens.T_REQUIRE_ONCE,
        tokens.T_EVAL,
        tokens.T_INT_CAST,
        tokens.T_DOUBLE_CAST,
        tokens.T_STRING_CAST,
        tokens.T_ARRAY_CAST,
        tokens.T_OBJECT_CAST,
        tokens.T_BOOL_CAST,
        tokens.T_UNSET_CAST,
        tokens.T_EXIT,
        tokens.T_PRINT,
        tokens.T_YIELD,
        tokens.T_STATIC,
        tokens.T_FUNCTION,
        // using VARIABLES :
        tokens.T_VARIABLE,
        '$',
        tokens.T_NS_SEPARATOR,
        tokens.T_STRING,
        // using SCALAR :
        tokens.T_STRING, // @see variable.js line 45 > conflict with variable = shift/reduce :)
        tokens.T_CONSTANT_ENCAPSED_STRING,
        tokens.T_START_HEREDOC,
        tokens.T_LNUMBER,
        tokens.T_DNUMBER,
        tokens.T_ARRAY,'[',
        tokens.T_CLASS_C,
        tokens.T_TRAIT_C,
        tokens.T_FUNC_C,
        tokens.T_METHOD_C,
        tokens.T_LINE,
        tokens.T_FILE,
        tokens.T_DIR,
        tokens.T_NS_C
      ]
    }
    /** main entry point : converts a source code to AST **/
    ,parse: function(code) {
      this.lastError = false;
      this.lexer.setInput(code);
      this.lexer.comment_tokens = this.extractDoc;
      this.length = this.lexer._input.length;
      this.nextWithComments();
      this.ast = ['program', []];
      while(this.token != EOF) {
        var node = this.read_start();
        if (node !== null) {
          if (typeof node[0] !== 'string') {
            node.forEach(function(item) {
              this.ast[1].push(item);
            }.bind(this));
          } else {
            this.ast[1].push(node);
          }
        }
      }
      return this.ast;
    }
    /** handling errors **/
    ,error: function(expect) {
      token = getTokenName(this.token);
      if (isNumber(this.token)) {
        var symbol = this.text();
        if (symbol.length > 10) {
          symbol = symbol.substring(0, 7) + '...';
        }
        token = '\''+symbol+'\' ('+token+')';
      }
      var msgExpect = '';
      if (expect) {
        msgExpect = ', expecting ';
        if (Array.isArray(expect)) {
          for(var i = 0; i < expect.length; i++) {
            expect[i] = getTokenName(expect[i]);
          }
          msgExpect += expect.join(', ');
        } else {
          msgExpect += getTokenName(expect);
        }
      }
      this.lastError = {
        token: this.token,
        tokenName: token,
        expected: expect,
        messageExpected: msgExpect,
        message: 'Parse Error : syntax error, unexpected ' + token + msgExpect + ' on line ' + this.lexer.yylloc.first_line,
        line: this.lexer.yylloc.first_line
      };
      if (this.suppressErrors && !this._gracefull) {
        this.token = EOF;
      } else {
        throw new Error(this.lastError.message);
      }
    }
    /**
     * enable / disable the graceful mode
     */
    ,graceful: function(mode) {
      if (this._graceful !== mode) {
        if (mode) {
          // enable the graceful mode
          this._gracefulProxy = {};
          for(var i in this) {
            var cb = this[i];
            if (typeof cb === 'function') {
              this._gracefulProxy[i] = cb;
              this[i] = _gracefulDecorator.bind(this, i);
            }
          }
        } else {
          // disable the graceful mode
          for(var i in this._gracefulProxy) {
            this[i] = this._gracefulProxy[i];
          }
        }
        this._graceful = mode;
      }
      return this;

    }
    /**
     * Creates a new AST node
     */
    ,node: function(name) {
      var startAt = null;
      if (this.locations === true) {
        startAt = [
          this.lexer.yylloc.first_line,
          this.lexer.yylloc.first_column,
          this.length - this.lexer._input.length - this.lexer.yytext.length
        ];
      }
      return function() {
        var result =  Array.prototype.slice.call(arguments);
        if (name && name.constructor === Array) {
          if (this.locations === true) {
            name[2] = [
              this.prev[0],
              this.prev[1],
              this.prev[2]
            ];
            Array.prototype.push.apply(name[3], result);
          } else {
            Array.prototype.push.apply(name, result);
          }
          result = name;
        } else {
          if (name) {
            result.unshift(name);
          }
          if (this.locations === true) {
            result = [
              'position',
              startAt,
              [
                this.prev[0],
                this.prev[1],
                this.prev[2]
              ],
              result
            ];
          }
        }
        return result;
      }.bind(this);
    }
    /** expects an end of statement or end of file **/
    ,expectEndOfStatement: function() {
      if (this.token === ';') {
        this.nextWithComments();
        if (this.token === tokens.T_CLOSE_TAG) {
          // strip close tag (statement already closed with ';')
          this.nextWithComments();
        }
      } else if (this.token === tokens.T_CLOSE_TAG) {
        this.nextWithComments();
      } else if (this.token !== tokens.T_INLINE_HTML && this.token !== EOF) {
        this.error(';');
      }
      return this;
    }
    /** outputs some debug information on current token **/
    ,showlog: function() {
      var stack = (new Error()).stack.split('\n');
      console.log(
        'Line '
        + this.lexer.yylloc.first_line
        + ' : '
        + getTokenName(this.token)
        + ">" + this.lexer.yytext + "<"
        + ' @' + stack[3].trim()
      );
      return this;
    }
    /** force to expect specified token **/
    ,expect: function(token) {
      if (!this.lastError) {
        if (Array.isArray(token)) {
          if (token.indexOf(this.token) === -1) {
            this.error(token);
          }
        } else if (this.token != token) {
          this.error(token);
        }
      }
      return this;
    }
    /**returns the current token contents **/
    ,text: function() {
      return this.lexer.yytext;
    }
    /** consume the next token **/
    ,next: function() {
      this.nextWithComments();
      if (this.debug) this.showlog();
      while(this.token === tokens.T_COMMENT || this.token === tokens.T_DOC_COMMENT) {
        // IGNORE COMMENTS
        this.nextWithComments();
      }
      return this;
    }
    /** consume comments (if found) **/
    ,ignoreComments: function() {
      if (this.debug) this.showlog();
      while(this.token === tokens.T_COMMENT || this.token === tokens.T_DOC_COMMENT) {
        // IGNORE COMMENTS
        this.nextWithComments();
      }
      return this;
    }
    /** consume the next token (including doc) **/
    ,nextWithComments: function() {
      this.prev = [
        this.lexer.yylloc.first_line,
        this.lexer.yylloc.first_column,
        this.lexer.offset
      ];
      this.token = this.lexer.lex() || EOF;
      if (this.debug) this.showlog();
      return this;
    }
    /**
     * Check if token is of specified type
     */
    ,is: function(type) {
      if (Array.isArray(type)) {
        return type.indexOf(this.token) !== -1;
      } else {
        return this.entries[type].indexOf(this.token) != -1;
      }
    }
    /** convert an token to ast **/
    ,read_token: function() {
      var result = this.token;
      if (isNumber(result)) {
        result = [result, this.text(), this.lexer.yylloc.first_line];
      }
      this.next();
      return result;
    }
    /**
     * Helper : reads a list of tokens / sample : T_STRING ',' T_STRING ...
     * <ebnf>
     * list ::= separator? ( item separator )* item
     * </ebnf>
     */
    ,read_list: function(item, separator, preserveFirstSeparator) {
      var result = [];

      if (this.token == separator) {
        if (preserveFirstSeparator) result.push('');
        this.next();
      }

      if (typeof (item) === "function") {
        do {
          result.push(item.apply(this, []));
          if (this.token != separator) {
            break;
          }
        } while(this.next().token != EOF);
      } else {
        result.push(this.expect(item).text());
        while (this.next().token != EOF) {
          if (this.token != separator) break;
          // trim current separator & check item
          if (this.next().token != item) break;
          result.push(this.text());
        }
      }
      return result;
    }
  };

  // extends the parser with syntax files
  [
    require('./parser/array.js')(api, tokens, EOF),
    require('./parser/class.js')(api, tokens, EOF),
    require('./parser/expr.js')(api, tokens, EOF),
    require('./parser/function.js')(api, tokens, EOF),
    require('./parser/if.js')(api, tokens, EOF),
    require('./parser/loops.js')(api, tokens, EOF),
    require('./parser/main.js')(api, tokens, EOF),
    require('./parser/namespace.js')(api, tokens, EOF),
    require('./parser/scalar.js')(api, tokens, EOF),
    require('./parser/statement.js')(api, tokens, EOF),
    require('./parser/switch.js')(api, tokens, EOF),
    require('./parser/try.js')(api, tokens, EOF),
    require('./parser/comment.js')(api, tokens, EOF),
    require('./parser/variable.js')(api, tokens, EOF)
  ].forEach(function (ext) {
    for(var k in ext) {
      api[k] = ext[k];
    }
  });

  return api;
};
<|endoftext|>


<commit_before>import url from 'url';
import ldap from 'ldapjs';
import _ from 'lodash';
import Debug from 'debug';

var debug = Debug('LDAPClient');

class LDAPClient {
  constructor(cfg) {
    let tlsOptions = {
      cert:   cfg.cert,
      key:    cfg.key,
    };
    let port = url.parse(cfg.url).port;
    if (port) {
      tlsOptions.port = port;
    }

    this.client = ldap.createClient({
      url: cfg.url,
      tlsOptions,
      timeout: 10 * 1000,
      reconnect: true,
    });

    /* This promise acts as a lock; pending = locked, fulfilled = unlocked.  It
     * is never rejected. */
    this.lock = Promise.resolve();
  }

  /** To perform a set of operations bound as a particular user, retrying in
   * the face of failure, call this method with a function that begins by
   * calling this.bind.  No other use of the connection will be permitted while
   * those operations are in progress, so the connection cannot be re-bound.
   * The client is passed to the operations function.  You can call other
   * methods of this client from there.
   */
  async operate(operations, tries) {
    let attempts = 0;
    tries = tries || 5;
    while (true) {
      attempts++;
      try {
        /* do a little dance here to make sure that we see exceptions inside
         * this promise, but the lock promise is not rejected */
        await new Promise((accept, reject) => {
          this.lock = this.lock
            .then(() => operations(this))
            .then(accept, reject);
        });
        break;
      } catch (err) {
        if (attempts >= tries) {
          debug('error performing LDAP operation; failing', err);
          throw err;
        } else {
          debug('error performing LDAP operation; retrying', err);
        }
      }
    }
  }

  bind(user, password) {
    debug(`bind(${user}, <password>)`);
    return new Promise((accept, reject) => this.client.bind(
      user, password, err => {
        err ? reject(err) : accept();
      }));
  }

  async search(base, options) {
    debug(`search(${base}, ${JSON.stringify(options)})`);
    let entries = [];
    let res = await new Promise((accept, reject) => this.client.search(
      base, options, (err, res) => {
        err ? reject(err) : accept(res);
      }));
    return await new Promise((accept, reject) => {
      res.on('searchEntry', entry => {
        entries.push(entry);
      });
      res.on('error', (err) => {
        reject(err);
      });
      res.on('end', result => {
        if (result.status !== 0) {
          return reject(new Error('LDAP error, got status: ' + result.status));
        }
        return accept(entries);
      });
    });
  }

  async dnForEmail(email) {
    debug(`dnForEmail(${email})`);
    let userDn;
    let entries = await this.search(
      'dc=mozilla', {
        scope: 'sub',
        filter: '(&(objectClass=inetOrgPerson)(mail=' + email + '))',
        attributes: [],
        timeLimit: 10,
      });
    if (entries && entries.length === 1) {
      return entries[0].object.dn;
    }
  }
}

module.exports = LDAPClient;
<commit_msg>Drop and re-establish LDAP connections on error<commit_after>import url from 'url';
import ldap from 'ldapjs';
import _ from 'lodash';
import Debug from 'debug';

var debug = Debug('LDAPClient');

class LDAPClient {
  constructor(cfg) {
    let tlsOptions = {
      cert:   cfg.cert,
      key:    cfg.key,
    };
    let port = url.parse(cfg.url).port;
    if (port) {
      tlsOptions.port = port;
    }

    this.client = null;
    this.createClientArgs = {
      url: cfg.url,
      tlsOptions,
      timeout: 10 * 1000,
      reconnect: true,
    };

    /* This promise acts as a lock; pending = locked, fulfilled = unlocked.  It
     * is never rejected. */
    this.lock = Promise.resolve();
  }

  /** To perform a set of operations bound as a particular user, retrying in
   * the face of failure, call this method with a function that begins by
   * calling this.bind.  No other use of the connection will be permitted while
   * those operations are in progress, so the connection cannot be re-bound.
   * The client is passed to the operations function.  You can call other
   * methods of this client from there.
   */
  async operate(operations, tries) {
    let attempts = 0;
    tries = tries || 5;
    while (true) {
      attempts++;
      try {
        if (!this.client) {
          this.client = ldap.createClient(this.createClientArgs);
        }

        /* do a little dance here to make sure that we see exceptions inside
         * this promise, but the lock promise is not rejected */
        await new Promise((accept, reject) => {
          this.lock = this.lock
            .then(() => operations(this))
            .then(accept, reject);
        });
        break;
      } catch (err) {
        if (attempts >= tries) {
          debug('error performing LDAP operation; failing', err);
          throw err;
        } else {
          debug('error performing LDAP operation; retrying (with fresh connection)', err);
          this.client = null;
        }
      }
    }
  }

  bind(user, password) {
    debug(`bind(${user}, <password>)`);
    return new Promise((accept, reject) => this.client.bind(
      user, password, err => {
        err ? reject(err) : accept();
      }));
  }

  async search(base, options) {
    debug(`search(${base}, ${JSON.stringify(options)})`);
    let entries = [];
    let res = await new Promise((accept, reject) => this.client.search(
      base, options, (err, res) => {
        err ? reject(err) : accept(res);
      }));
    return await new Promise((accept, reject) => {
      res.on('searchEntry', entry => {
        entries.push(entry);
      });
      res.on('error', (err) => {
        reject(err);
      });
      res.on('end', result => {
        if (result.status !== 0) {
          return reject(new Error('LDAP error, got status: ' + result.status));
        }
        return accept(entries);
      });
    });
  }

  async dnForEmail(email) {
    debug(`dnForEmail(${email})`);
    let userDn;
    let entries = await this.search(
      'dc=mozilla', {
        scope: 'sub',
        filter: '(&(objectClass=inetOrgPerson)(mail=' + email + '))',
        attributes: [],
        timeLimit: 10,
      });
    if (entries && entries.length === 1) {
      return entries[0].object.dn;
    }
  }
}

module.exports = LDAPClient;
<|endoftext|>


<commit_before>var commands = {};
var social = {
	github:'https://github.com/VideahGams',
	twitter:'https://twitter.com/videah_',
	keybase:'https://keybase.io/videah',
	steam:'https://steamcommunity.com/id/videahgams',
	blog:'https://blog.videah.xyz'
}

commands.help = function() {
	var output = "
" +
		"
" +
		"
help - display this help.
" +
		"
echo - display a line of text.
" +
		"
intro - display site welcome screen.
" +
		"
social - display social command list.
" +
		"
cowsay - speaking cow.
" +
		// "
clear - clear the terminal screen.
" +
		"

";
	return output;
};

commands.intro = function() {
	var output = "" +
		"
" +
			"" +
			"
Hello, I am videah.
" +
			"
This is my personal site, feel free to look around.
" +
			"
Run the command help to get a list of commands.
" +
		"
"
	return output;
};

commands.social = function() {
	var output = "
" +
		"Enter one of these commands to redirect to my social media." +
		"
" +
		"
github - my github where i put my projects n stuff.
" +
		"
twitter - where i ramble about shit, including politics, so be warned.
" +
		"
keybase - if you're a cryptonut and want to send me classified docs or some shit.
" +
		"
steam - hey gamers.
" +
		"
blog - my dumb blog that i rarely post to.
" +
		"
" +
		"Here's some other social stuff." +
		"
" +
		"
Email - email@videah.xyz
" +
		"
IRC - nick is videah, i lurk freenode and OFTC
" +
		"
Bitcoin - 1fJCHBDweNz7bqaaQ1Q7uNBV4ZcKY6XB8 - give me cyber money
" +
		"
Signal - email me for it
" +
		"
" +
		"
";
	return output;
};

for (var key in social) {

	if (!social.hasOwnProperty(key)) { continue; }

	commands[key] = function(args) {
		window.location.href = social[args[0]];
		return "
" +
			"Redirecting to " + args[0] + " ..."
		"
"
	}

}

commands.echo = function(args) {
	var output = ""
	for(var i = 1; i < args.length; i++) {
		output += args[i] + "\n";
	}
	return output;
};

commands.cowsay = function(args) {

	var input = args.splice(1)
	input = input.join();
	input = input.replace(/,/g , " ");
	input = input.trim();

	if (!input) {return ""};

	var output = "
 " +
		"_".repeat(input.length + 2) +
	"
" +

	"
< " + input + " >
" +

	"
 " +
		"-".repeat(input.length + 2) +
	"

" +

	// God fucking help us
	"
        \\   ^__^
" +
	"
         \\  (oo)\\_______
" +
	"
            (__)\\       )\\/\\
" +
	"
                ||----w | " +
	"
                ||     || "

	return output;

}

// Hacky shit, i hate js
function focusOnClick(i) {
	var allspans = document.getElementsByClassName(i);
	var currentline = allspans[allspans.length - 1];
	currentline.focus();
}

Terminal.init(document.getElementById("terminal"), commands);<commit_msg>update some info<commit_after>var commands = {};
var social = {
	github:'https://github.com/videah',
	twitter:'https://twitter.com/videah_',
	keybase:'https://keybase.io/videah',
	steam:'https://steamcommunity.com/id/videah',
	blog:'https://blog.videah.xyz'
}

commands.help = function() {
	var output = "
" +
		"
" +
		"
help - display this help.
" +
		"
echo - display a line of text.
" +
		"
intro - display site welcome screen.
" +
		"
social - display social command list.
" +
		"
cowsay - speaking cow.
" +
		// "
clear - clear the terminal screen.
" +
		"

";
	return output;
};

commands.intro = function() {
	var output = "" +
		"
" +
			"" +
			"
Hello, I am videah.
" +
			"
This is my personal site, feel free to look around.
" +
			"
Run the command help to get a list of commands.
" +
		"
"
	return output;
};

commands.social = function() {
	var output = "
" +
		"Enter one of these commands to redirect to my social media." +
		"
" +
		"
github - my github where i put my projects n stuff.
" +
		"
twitter - where i ramble about shit, including politics, so be warned.
" +
		"
keybase - if you're a cryptonut and want to send me classified docs or some shit.
" +
		"
steam - hey gamers.
" +
		"
blog - my dumb blog that i rarely post to.
" +
		"
" +
		"Here's some other social stuff." +
		"
" +
		"
Email - email@videah.xyz
" +
		"
IRC - nick is videah, i lurk freenode and OFTC
" +
		"
Bitcoin - 1Jqq96HVZ2AKijjxNfYi2SaKaW78keWEMH - give me cyber money
" +
		"
Signal - email me for it
" +
		"
" +
		"
";
	return output;
};

for (var key in social) {

	if (!social.hasOwnProperty(key)) { continue; }

	commands[key] = function(args) {
		window.location.href = social[args[0]];
		return "
" +
			"Redirecting to " + args[0] + " ..."
		"
"
	}

}

commands.echo = function(args) {
	var output = ""
	for(var i = 1; i < args.length; i++) {
		output += args[i] + "\n";
	}
	return output;
};

commands.cowsay = function(args) {

	var input = args.splice(1)
	input = input.join();
	input = input.replace(/,/g , " ");
	input = input.trim();

	if (!input) {return ""};

	var output = "
 " +
		"_".repeat(input.length + 2) +
	"
" +

	"
< " + input + " >
" +

	"
 " +
		"-".repeat(input.length + 2) +
	"

" +

	// God fucking help us
	"
        \\   ^__^
" +
	"
         \\  (oo)\\_______
" +
	"
            (__)\\       )\\/\\
" +
	"
                ||----w | " +
	"
                ||     || "

	return output;

}

// Hacky shit, i hate js
function focusOnClick(i) {
	var allspans = document.getElementsByClassName(i);
	var currentline = allspans[allspans.length - 1];
	currentline.focus();
}

Terminal.init(document.getElementById("terminal"), commands);
<|endoftext|>


<commit_before>odoo.define('jim_telesale.models2', function (require) {
"use strict";
var TsModels = require('telesale.models');

// Overwrite backbone TsModel function to get extra fields lqdr and route_name;
TsModels.TsModel.prototype._get_product_fields = function(){
    return  ['display_name', 'default_code', 'list_price', 'standard_price', 'uom_id', 'taxes_id', 'weight', 'lqdr', 'route_name']}
});
<commit_msg>[IMP]jim_telesale: Mejora al heredar backbone<commit_after>odoo.define('jim_telesale.models2', function (require) {
"use strict";
var TsModels = require('telesale.models');
var TsModelSuper = TsModels.TsModel

TsModels.TsModel = TsModels.TsModel.extend({
    _get_product_fields: function(){
        var res = TsModelSuper.prototype._get_product_fields.call(this,{});
        res.push('lqdr', 'route_name')
        return res
    },
});

});
<|endoftext|>


<commit_before>Spamness.Message = {};

Spamness.Message.displayScoreHeader = function() {
    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                    .getService(Components.interfaces.nsIPrefBranch);
    var showScore = prefs.getBoolPref("extensions.spamness.display.messageScore");
    var rowEl = document.getElementById("expandedSpamnessRow");
    var hdrEl = document.getElementById("spamnessScoreHeader");
    if (!showScore) {
        rowEl.collapsed = true;
        return;
    } else {
        rowEl.collapsed = false;
    }

    var header = Spamness.getHeaderName(prefs);
    var uri = gMessageDisplay.folderDisplay.selectedMessageUris[0];

    if (uri == null)
        return;

    var hdr = gDBView.msgFolder.GetMessageHeader(gDBView.getKeyAt(gDBView.currentlyDisplayedMessage));

    if (hdr == null || hdr.getStringProperty(header) == null)
        return;

    var parsed = Spamness.parseHeader(hdr.getStringProperty(header));
    rowEl.collapsed = (parsed == null);
    hdrEl.headerValue = (parsed != null) ? parsed.getScore() + " (Bayes " + parsed.getBayes() + ", Fuzzy " + parsed.getFuzzy() + ")" : "";
    hdrEl.valid = true;
};

Spamness.Message.displayRulesHeader = function() {
    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                    .getService(Components.interfaces.nsIPrefBranch);
    var showRules = prefs.getBoolPref("extensions.spamness.display.messageRules");
    var rowEl = document.getElementById("expandedSpamnessRulesRow");
    var hdrEl = document.getElementById("expandedSpamnessRulesBox");
    if (!showRules) {
        rowEl.collapsed = true;
        return;
    } else {
        if (hdrEl.clearHeaderValues)
            hdrEl.clearHeaderValues();
        rowEl.collapsed = false;
    }

    var header = Spamness.getHeaderName(prefs);
    var uri = gMessageDisplay.folderDisplay.selectedMessageUris[0];

    if (uri == null)
        return;

    var hdr = gDBView.msgFolder.GetMessageHeader(gDBView.getKeyAt(gDBView.currentlyDisplayedMessage));

    if (hdr == null || hdr.getStringProperty(header) == null)
        return;

    var parsed = Spamness.parseHeader(hdr.getStringProperty(header));

    var rules = (parsed == null) ? [] : parsed.getRules();
    rowEl.collapsed = (rules.length == 0);
    if (parsed != null && rules.length > 0) {
        for (var i = 0; i < rules.length; i++) {
            var link = {};
            link.displayText = rules[i];
            link.class = Spamness.getMetricClass(rules[i]);
            hdrEl.addLinkView(link);
        }
        hdrEl.valid = true;
        hdrEl.buildViews();
    }
};

Spamness.Message.onLoad = function() {
    var listener = {};
    listener.onStartHeaders = function() {};
    listener.onEndHeaders = function() {
        Spamness.Message.displayScoreHeader();
        Spamness.Message.displayRulesHeader();
    };
    gMessageListeners.push(listener);
};

Spamness.Message.onUnload = function() {
    window.removeEventListener("load", Spamness.Message.onLoad, false);
    window.removeEventListener("unload", Spamness.Message.onUnload, false);
};

window.addEventListener("load", Spamness.Message.onLoad, false);
window.addEventListener("unload", Spamness.Message.onUnload, false);
<commit_msg>Fix crash and hide headers when open eml file.<commit_after>Spamness.Message = {};

Spamness.Message.displayScoreHeader = function() {
    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                    .getService(Components.interfaces.nsIPrefBranch);
    var showScore = prefs.getBoolPref("extensions.spamness.display.messageScore");
    var rowEl = document.getElementById("expandedSpamnessRow");
    var hdrEl = document.getElementById("spamnessScoreHeader");

    rowEl.collapsed = true;

    if (!showScore)
        return;

    var header = Spamness.getHeaderName(prefs);
    var uri = gMessageDisplay.folderDisplay.selectedMessageUris[0];

//    if (uri == null)
//        return;

    if (gDBView.msgFolder == null)
        return;

    var hdr = gDBView.msgFolder.GetMessageHeader(gDBView.getKeyAt(gDBView.currentlyDisplayedMessage));

    if (hdr == null || hdr.getStringProperty(header) == null)
        return;

    var parsed = Spamness.parseHeader(hdr.getStringProperty(header));
    rowEl.collapsed = (parsed == null);
    hdrEl.headerValue = (parsed != null) ? parsed.getScore() + " (Bayes " + parsed.getBayes() + ", Fuzzy " + parsed.getFuzzy() + ")" : "";
    hdrEl.valid = true;
};

Spamness.Message.displayRulesHeader = function() {
    var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                    .getService(Components.interfaces.nsIPrefBranch);
    var showRules = prefs.getBoolPref("extensions.spamness.display.messageRules");
    var rowEl = document.getElementById("expandedSpamnessRulesRow");
    var hdrEl = document.getElementById("expandedSpamnessRulesBox");

    rowEl.collapsed = true;

    if (!showRules) {
        return;
    } else {
        if (hdrEl.clearHeaderValues)
            hdrEl.clearHeaderValues();
    }

    var header = Spamness.getHeaderName(prefs);
    var uri = gMessageDisplay.folderDisplay.selectedMessageUris[0];

//    if (uri == null)
//        return;

    if (gDBView.msgFolder == null)
        return;

    var hdr = gDBView.msgFolder.GetMessageHeader(gDBView.getKeyAt(gDBView.currentlyDisplayedMessage));

    if (hdr == null || hdr.getStringProperty(header) == null)
        return;

    var parsed = Spamness.parseHeader(hdr.getStringProperty(header));

    var rules = (parsed == null) ? [] : parsed.getRules();
    rowEl.collapsed = (rules.length == 0);
    if (parsed != null && rules.length > 0) {
        for (var i = 0; i < rules.length; i++) {
            var link = {};
            link.displayText = rules[i];
            link.class = Spamness.getMetricClass(rules[i]);
            hdrEl.addLinkView(link);
        }
        hdrEl.valid = true;
        hdrEl.buildViews();
    }
};

Spamness.Message.onLoad = function() {
    var listener = {};
    listener.onStartHeaders = function() {};
    listener.onEndHeaders = function() {
        Spamness.Message.displayScoreHeader();
        Spamness.Message.displayRulesHeader();
    };
    gMessageListeners.push(listener);
};

Spamness.Message.onUnload = function() {
    window.removeEventListener("load", Spamness.Message.onLoad, false);
    window.removeEventListener("unload", Spamness.Message.onUnload, false);
};

window.addEventListener("load", Spamness.Message.onLoad, false);
window.addEventListener("unload", Spamness.Message.onUnload, false);
<|endoftext|>