<commit_before>
/*!
 * Connect - utils
 * Copyright(c) 2010 Sencha Inc.
 * Copyright(c) 2011 TJ Holowaychuk
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var crypto = require('crypto')
  , Path = require('path')
  , fs = require('fs');

/**
 * Flatten the given `arr`.
 *
 * @param {Array} arr
 * @return {Array}
 * @api private
 */

exports.flatten = function(arr, ret){
  var ret = ret || []
    , len = arr.length;
  for (var i = 0; i < len; ++i) {
    if (Array.isArray(arr[i])) {
      exports.flatten(arr[i], ret);
    } else {
      ret.push(arr[i]);
    }
  }
  return ret;
};

/**
 * Return md5 hash of the given string and optional encoding,
 * defaulting to hex.
 *
 *     utils.md5('wahoo');
 *     // => "e493298061761236c96b02ea6aa8a2ad"
 *
 * @param {String} str
 * @param {String} encoding
 * @return {String}
 * @api public
 */

exports.md5 = function(str, encoding){
  return crypto
    .createHash('md5')
    .update(str)
    .digest(encoding || 'hex');
};

/**
 * Merge object b with object a.
 *
 *     var a = { foo: 'bar' }
 *       , b = { bar: 'baz' };
 *
 *     utils.merge(a, b);
 *     // => { foo: 'bar', bar: 'baz' }
 *
 * @param {Object} a
 * @param {Object} b
 * @return {Object}
 * @api public
 */

exports.merge = function(a, b){
  if (a && b) {
    for (var key in b) {
      a[key] = b[key];
    }
  }
  return a;
};

/**
 * Escape the given string of `html`.
 *
 * @param {String} html
 * @return {String}
 * @api public
 */

exports.escape = function(html){
  return String(html)
    .replace(/&(?!\w+;)/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"');
};

/**
 * Return a unique identifier with the given `len`.
 *
 *     utils.uid(10);
 *     // => "FDaS435D2z"
 *
 * @param {Number} len
 * @return {String}
 * @api public
 */

exports.uid = function(len) {
  var buf = []
    , chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
    , charlen = chars.length;

  for (var i = 0; i < len; ++i) {
    buf.push(chars[getRandomInt(0, charlen - 1)]);
  }

  return buf.join('');
};

/**
 * Parse the given cookie string into an object.
 *
 * @param {String} str
 * @return {Object}
 * @api public
 */

exports.parseCookie = function(str){
  var obj = {}
    , pairs = str.split(/[;,] */);
  for (var i = 0, len = pairs.length; i < len; ++i) {
    var pair = pairs[i]
      , eqlIndex = pair.indexOf('=')
      , key = pair.substr(0, eqlIndex).trim().toLowerCase()
      , val = pair.substr(++eqlIndex, pair.length).trim();

    // quoted values
    if ('"' == val[0]) val = val.slice(1, -1);

    // only assign once
    if (undefined == obj[key]) {
      val = val.replace(/\+/g, ' ');
      try {
        obj[key] = decodeURIComponent(val);
      } catch (err) {
        if (err instanceof URIError) {
          obj[key] = val;
        } else {
          throw err;
        }
      }
    }
  }
  return obj;
};

/**
 * Serialize the given object into a cookie string.
 *
 *      utils.serializeCookie('name', 'tj', { httpOnly: true })
 *      // => "name=tj; httpOnly"
 *
 * @param {String} name
 * @param {String} val
 * @param {Object} obj
 * @return {String}
 * @api public
 */

exports.serializeCookie = function(name, val, obj){
  var pairs = [name + '=' + encodeURIComponent(val)]
    , obj = obj || {};

  if (obj.domain) pairs.push('domain=' + obj.domain);
  if (obj.path) pairs.push('path=' + obj.path);
  if (obj.expires) pairs.push('expires=' + obj.expires.toUTCString());
  if (obj.httpOnly) pairs.push('httpOnly');
  if (obj.secure) pairs.push('secure');

  return pairs.join('; ');
};

/**
 * Pause `data` and `end` events on the given `obj`.
 * Middleware performing async tasks _should_ utilize
 * this utility (or similar), to re-emit data once
 * the async operation has completed, otherwise these
 * events may be lost.
 *
 *      var pause = utils.pause(req);
 *      fs.readFile(path, function(){
 *         next();
 *         pause.resume();
 *      });
 *
 * @param {Object} obj
 * @return {Object}
 * @api public
 */

exports.pause = function(obj){
  var onData
    , onEnd
    , events = [];

  // buffer data
  obj.on('data', onData = function(data, encoding){
    events.push(['data', data, encoding]);
  });

  // buffer end
  obj.on('end', onEnd = function(data, encoding){
    events.push(['end', data, encoding]);
  });

  return {
    end: function(){
      obj.removeListener('data', onData);
      obj.removeListener('end', onEnd);
    },
    resume: function(){
      this.end();
      for (var i = 0, len = events.length; i < len; ++i) {
        obj.emit.apply(obj, events[i]);
      }
    }
  };
};

/**
 * Check `req` and `res` to see if it has been modified.
 *
 * @param {IncomingMessage} req
 * @param {ServerResponse} res
 * @return {Boolean}
 * @api public
 */

exports.modified = function(req, res, headers) {
  var headers = headers || res._headers || {}
    , modifiedSince = req.headers['if-modified-since']
    , lastModified = headers['last-modified']
    , noneMatch = req.headers['if-none-match']
    , etag = headers['etag'];

  if (noneMatch) noneMatch = noneMatch.split(/ *, */);

  // check If-None-Match
  if (noneMatch && etag && ~noneMatch.indexOf(etag)) {
    return false;
  }

  // check If-Modified-Since
  if (modifiedSince && lastModified) {
    modifiedSince = new Date(modifiedSince);
    lastModified = new Date(lastModified);
    // Ignore invalid dates
    if (!isNaN(modifiedSince.getTime())) {
      if (lastModified <= modifiedSince) return false;
    }
  }

  return true;
};

/**
 * Strip `Content-*` headers from `res`.
 *
 * @param {ServerResponse} res
 * @api public
 */

exports.removeContentHeaders = function(res){
  Object.keys(res._headers).forEach(function(field){
    if (0 == field.indexOf('content')) {
      res.removeHeader(field);
    }
  });
};

/**
 * Check if `req` is a conditional GET request.
 *
 * @param {IncomingMessage} req
 * @return {Boolean}
 * @api public
 */

exports.conditionalGET = function(req) {
  return req.headers['if-modified-since']
    || req.headers['if-none-match'];
};

/**
 * Respond with 403 "Forbidden".
 *
 * @param {ServerResponse} res
 * @api public
 */

exports.forbidden = function(res) {
  var body = 'Forbidden';
  res.setHeader('Content-Type', 'text/plain');
  res.setHeader('Content-Length', body.length);
  res.statusCode = 403;
  res.end(body);
};

/**
 * Respond with 401 "Unauthorized".
 *
 * @param {ServerResponse} res
 * @param {String} realm
 * @api public
 */

exports.unauthorized = function(res, realm) {
  res.statusCode = 401;
  res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"');
  res.end('Unauthorized');
};

/**
 * Respond with 400 "Bad Request".
 *
 * @param {ServerResponse} res
 * @api public
 */

exports.badRequest = function(res) {
  res.statusCode = 400;
  res.end('Bad Request');
};

/**
 * Respond with 304 "Not Modified".
 *
 * @param {ServerResponse} res
 * @param {Object} headers
 * @api public
 */

exports.notModified = function(res) {
  exports.removeContentHeaders(res);
  res.statusCode = 304;
  res.end();
};

/**
 * Return an ETag in the form of `"<size>-<mtime>"`
 * from the given `stat`.
 *
 * @param {Object} stat
 * @return {String}
 * @api public
 */

exports.etag = function(stat) {
  return '"' + stat.size + '-' + Number(stat.mtime) + '"';
};

/**
 * Parse "Range" header `str` relative to the given file `size`.
 *
 * @param {Number} size
 * @param {String} str
 * @return {Array}
 * @api public
 */

exports.parseRange = function(size, str){
  var valid = true;
  var arr = str.substr(6).split(',').map(function(range){
    var range = range.split('-')
      , start = parseInt(range[0], 10)
      , end = parseInt(range[1], 10);

    // -500
    if (isNaN(start)) {
      start = size - end;
      end = size - 1;
    // 500-
    } else if (isNaN(end)) {
      end = size - 1;
    }

    // Invalid
    if (isNaN(start) || isNaN(end) || start > end) valid = false;

    return { start: start, end: end };
  });
  return valid ? arr : undefined;
};

/**
 * Convert array-like object to an `Array`.
 *
 * node-bench measured "16.5 times faster than Array.prototype.slice.call()"
 *
 * @param {Object} obj
 * @return {Array}
 * @api public
 */

var toArray = exports.toArray = function(obj){
  var len = obj.length
    , arr = new Array(len);
  for (var i = 0; i < len; ++i) {
    arr[i] = obj[i];
  }
  return arr;
};

/**
 * Retrun a random int, used by `utils.uid()`
 *
 * @param {Number} min
 * @param {Number} max
 * @return {Number}
 * @api private
 */

function getRandomInt(min, max) {
  return Math.floor(Math.random() * (max - min + 1)) + min;
}
<commit_msg>refactored `uid()`<commit_after>
/*!
 * Connect - utils
 * Copyright(c) 2010 Sencha Inc.
 * Copyright(c) 2011 TJ Holowaychuk
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var crypto = require('crypto')
  , Path = require('path')
  , fs = require('fs');

/**
 * Flatten the given `arr`.
 *
 * @param {Array} arr
 * @return {Array}
 * @api private
 */

exports.flatten = function(arr, ret){
  var ret = ret || []
    , len = arr.length;
  for (var i = 0; i < len; ++i) {
    if (Array.isArray(arr[i])) {
      exports.flatten(arr[i], ret);
    } else {
      ret.push(arr[i]);
    }
  }
  return ret;
};

/**
 * Return md5 hash of the given string and optional encoding,
 * defaulting to hex.
 *
 *     utils.md5('wahoo');
 *     // => "e493298061761236c96b02ea6aa8a2ad"
 *
 * @param {String} str
 * @param {String} encoding
 * @return {String}
 * @api public
 */

exports.md5 = function(str, encoding){
  return crypto
    .createHash('md5')
    .update(str)
    .digest(encoding || 'hex');
};

/**
 * Merge object b with object a.
 *
 *     var a = { foo: 'bar' }
 *       , b = { bar: 'baz' };
 *
 *     utils.merge(a, b);
 *     // => { foo: 'bar', bar: 'baz' }
 *
 * @param {Object} a
 * @param {Object} b
 * @return {Object}
 * @api public
 */

exports.merge = function(a, b){
  if (a && b) {
    for (var key in b) {
      a[key] = b[key];
    }
  }
  return a;
};

/**
 * Escape the given string of `html`.
 *
 * @param {String} html
 * @return {String}
 * @api public
 */

exports.escape = function(html){
  return String(html)
    .replace(/&(?!\w+;)/g, '&')
    .replace(/</g, '<')
    .replace(/>/g, '>')
    .replace(/"/g, '"');
};

/**
 * Return a unique identifier with the given `len`.
 *
 *     utils.uid(10);
 *     // => "FDaS435D2z"
 *
 * @param {Number} len
 * @return {String}
 * @api public
 */

exports.uid = function(len) {
  var buf = []
    , chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
    , charlen = chars.length;

  for (var i = 0; i < len; ++i) {
    buf.push(chars[Math.random() * charlen | 0]);
  }

  return buf.join('');
};

/**
 * Parse the given cookie string into an object.
 *
 * @param {String} str
 * @return {Object}
 * @api public
 */

exports.parseCookie = function(str){
  var obj = {}
    , pairs = str.split(/[;,] */);
  for (var i = 0, len = pairs.length; i < len; ++i) {
    var pair = pairs[i]
      , eqlIndex = pair.indexOf('=')
      , key = pair.substr(0, eqlIndex).trim().toLowerCase()
      , val = pair.substr(++eqlIndex, pair.length).trim();

    // quoted values
    if ('"' == val[0]) val = val.slice(1, -1);

    // only assign once
    if (undefined == obj[key]) {
      val = val.replace(/\+/g, ' ');
      try {
        obj[key] = decodeURIComponent(val);
      } catch (err) {
        if (err instanceof URIError) {
          obj[key] = val;
        } else {
          throw err;
        }
      }
    }
  }
  return obj;
};

/**
 * Serialize the given object into a cookie string.
 *
 *      utils.serializeCookie('name', 'tj', { httpOnly: true })
 *      // => "name=tj; httpOnly"
 *
 * @param {String} name
 * @param {String} val
 * @param {Object} obj
 * @return {String}
 * @api public
 */

exports.serializeCookie = function(name, val, obj){
  var pairs = [name + '=' + encodeURIComponent(val)]
    , obj = obj || {};

  if (obj.domain) pairs.push('domain=' + obj.domain);
  if (obj.path) pairs.push('path=' + obj.path);
  if (obj.expires) pairs.push('expires=' + obj.expires.toUTCString());
  if (obj.httpOnly) pairs.push('httpOnly');
  if (obj.secure) pairs.push('secure');

  return pairs.join('; ');
};

/**
 * Pause `data` and `end` events on the given `obj`.
 * Middleware performing async tasks _should_ utilize
 * this utility (or similar), to re-emit data once
 * the async operation has completed, otherwise these
 * events may be lost.
 *
 *      var pause = utils.pause(req);
 *      fs.readFile(path, function(){
 *         next();
 *         pause.resume();
 *      });
 *
 * @param {Object} obj
 * @return {Object}
 * @api public
 */

exports.pause = function(obj){
  var onData
    , onEnd
    , events = [];

  // buffer data
  obj.on('data', onData = function(data, encoding){
    events.push(['data', data, encoding]);
  });

  // buffer end
  obj.on('end', onEnd = function(data, encoding){
    events.push(['end', data, encoding]);
  });

  return {
    end: function(){
      obj.removeListener('data', onData);
      obj.removeListener('end', onEnd);
    },
    resume: function(){
      this.end();
      for (var i = 0, len = events.length; i < len; ++i) {
        obj.emit.apply(obj, events[i]);
      }
    }
  };
};

/**
 * Check `req` and `res` to see if it has been modified.
 *
 * @param {IncomingMessage} req
 * @param {ServerResponse} res
 * @return {Boolean}
 * @api public
 */

exports.modified = function(req, res, headers) {
  var headers = headers || res._headers || {}
    , modifiedSince = req.headers['if-modified-since']
    , lastModified = headers['last-modified']
    , noneMatch = req.headers['if-none-match']
    , etag = headers['etag'];

  if (noneMatch) noneMatch = noneMatch.split(/ *, */);

  // check If-None-Match
  if (noneMatch && etag && ~noneMatch.indexOf(etag)) {
    return false;
  }

  // check If-Modified-Since
  if (modifiedSince && lastModified) {
    modifiedSince = new Date(modifiedSince);
    lastModified = new Date(lastModified);
    // Ignore invalid dates
    if (!isNaN(modifiedSince.getTime())) {
      if (lastModified <= modifiedSince) return false;
    }
  }

  return true;
};

/**
 * Strip `Content-*` headers from `res`.
 *
 * @param {ServerResponse} res
 * @api public
 */

exports.removeContentHeaders = function(res){
  Object.keys(res._headers).forEach(function(field){
    if (0 == field.indexOf('content')) {
      res.removeHeader(field);
    }
  });
};

/**
 * Check if `req` is a conditional GET request.
 *
 * @param {IncomingMessage} req
 * @return {Boolean}
 * @api public
 */

exports.conditionalGET = function(req) {
  return req.headers['if-modified-since']
    || req.headers['if-none-match'];
};

/**
 * Respond with 403 "Forbidden".
 *
 * @param {ServerResponse} res
 * @api public
 */

exports.forbidden = function(res) {
  var body = 'Forbidden';
  res.setHeader('Content-Type', 'text/plain');
  res.setHeader('Content-Length', body.length);
  res.statusCode = 403;
  res.end(body);
};

/**
 * Respond with 401 "Unauthorized".
 *
 * @param {ServerResponse} res
 * @param {String} realm
 * @api public
 */

exports.unauthorized = function(res, realm) {
  res.statusCode = 401;
  res.setHeader('WWW-Authenticate', 'Basic realm="' + realm + '"');
  res.end('Unauthorized');
};

/**
 * Respond with 400 "Bad Request".
 *
 * @param {ServerResponse} res
 * @api public
 */

exports.badRequest = function(res) {
  res.statusCode = 400;
  res.end('Bad Request');
};

/**
 * Respond with 304 "Not Modified".
 *
 * @param {ServerResponse} res
 * @param {Object} headers
 * @api public
 */

exports.notModified = function(res) {
  exports.removeContentHeaders(res);
  res.statusCode = 304;
  res.end();
};

/**
 * Return an ETag in the form of `"<size>-<mtime>"`
 * from the given `stat`.
 *
 * @param {Object} stat
 * @return {String}
 * @api public
 */

exports.etag = function(stat) {
  return '"' + stat.size + '-' + Number(stat.mtime) + '"';
};

/**
 * Parse "Range" header `str` relative to the given file `size`.
 *
 * @param {Number} size
 * @param {String} str
 * @return {Array}
 * @api public
 */

exports.parseRange = function(size, str){
  var valid = true;
  var arr = str.substr(6).split(',').map(function(range){
    var range = range.split('-')
      , start = parseInt(range[0], 10)
      , end = parseInt(range[1], 10);

    // -500
    if (isNaN(start)) {
      start = size - end;
      end = size - 1;
    // 500-
    } else if (isNaN(end)) {
      end = size - 1;
    }

    // Invalid
    if (isNaN(start) || isNaN(end) || start > end) valid = false;

    return { start: start, end: end };
  });
  return valid ? arr : undefined;
};

/**
 * Convert array-like object to an `Array`.
 *
 * node-bench measured "16.5 times faster than Array.prototype.slice.call()"
 *
 * @param {Object} obj
 * @return {Array}
 * @api public
 */

var toArray = exports.toArray = function(obj){
  var len = obj.length
    , arr = new Array(len);
  for (var i = 0; i < len; ++i) {
    arr[i] = obj[i];
  }
  return arr;
};
<|endoftext|>


<commit_before>const Zabbix = require('./api');
const {spawn} = require('child_process');
const debug = require('debug')('utils');

class Client extends Zabbix {

  static sender (options) {

    // eslint-disable-next-line max-statements
    return new Promise((resolve, reject) => {

      if (!Object.prototype.hasOwnProperty.call(options, 'path')) {

        options.port = 'zabbix_sender';

      } // eslint: path

      if (!Object.prototype.hasOwnProperty.call(options, 'server')) {

        reject(new Error('Please provide zabbix server or proxy name.'));

      } // eslint: server

      if (!Object.prototype.hasOwnProperty.call(options, 'port')) {

        options.port = '10051';

      } // eslint: port

      if (!Object.prototype.hasOwnProperty.call(options, 'host')) {

        reject(new Error('Please provide the zabbix host name as ' +
          'registered in Zabbix frontend.'));

      } // eslint: host

      if (!Object.prototype.hasOwnProperty.call(options, 'values')) {

        reject(new Error('Specify values each line contains whitespace ' +
          'delimited: - <key> <value>'));

      } // eslint: values

      resolve(options);

    }).then((val) => {

      const {path, server, port, host, values} = val;

      return new Promise((resolve, reject) => {

        debug('zabbix_sender options: %O', val);

        let stdout = '';
        let stderr = '';

        const cmd = spawn(path, [
          '-z',
          server,
          '-p',
          port,
          '-s',
          host,
          '-r',
          '-i',
          '-'
        ]);

        cmd.stdin.write(values);

        cmd.stdout.on('data', (data) => {
          stdout += data;
        });

        cmd.stderr.on('data', (data) => {
          stderr += data;
        });

        cmd.on('error', (err) => reject(new Error(err)));

        cmd.on('close', (code) => {
          if (code === 0) {
            resolve({
              stdout,
              stderr
            });
          } else {
            reject(new Error(`${path} process exited with code ${code}`));
          }
        });

        cmd.stdin.end();

      }); // eslint: promise

    }); // eslint: then

  } // eslint: sender

} // eslint: Class

module.exports = Client;
<commit_msg>code refactoring<commit_after>/**
 * Dependencies
 */
const {spawn} = require('child_process');
const debug = require('debug')('utils');
const Zabbix = require('./api');

/**
 * Extending Zabbix API Client with helper methods
 */
class Client extends Zabbix {

  /**
   * @param {Object} options - zabbix_sender options
   *
   * @returns {Promise} a promise which resolves to the http response.
   */
  static sender (options) {
    return new Promise((resolve, reject) => {
      options.path = options.path || 'zabbix_sender';
      options.port = options.port || '10051';
      options.server = options.server || reject(new Error('Please provide ' +
        'zabbix server or proxy name.'));
      options.host = options.host || reject(new Error('Please provide the ' +
        'zabbix host name as registered in Zabbix frontend.'));
      options.values = options.values || reject(new Error('Specify values ' +
        'each line contains whitespace delimited: - <key> <value>'));

      resolve(options);
    }).then((val) => {
      const {path, server, port, host, values} = val;

      return new Promise((resolve, reject) => {
        debug('zabbix_sender options: %O', val);

        let stdout = '';
        let stderr = '';

        // eslint-disable-next-line array-element-newline, max-len
        const cmd = spawn(path, ['-z', server, '-p', port, '-s', host, '-r', '-i', '-']);

        cmd.stdin.write(values);

        cmd.stdout.on('data', (data) => {
          stdout += data;
        });

        cmd.stderr.on('data', (data) => {
          stderr += data;
        });

        cmd.on('error', (err) => reject(new Error(err)));

        cmd.on('close', (code) => {
          if (code === 0) {
            resolve({
              stdout,
              stderr
            });
          } else {
            reject(new Error(`${path} process exited with code ${code}`));
          }
        });

        cmd.stdin.end();
      }); // eslint: then return promise
    }); // eslint: promise then
  } // eslint: sender
} // eslint: Class

module.exports = Client;
<|endoftext|>


<commit_before>exports.Response = function(response) {
    return {
        response: function() {
            return response
        },
        handleUserError: function(error) {
            response.send(error.message, { 'Content-Type': 'text/plain' }, 400)
        },
        handleServerError: function(error) {
            response.send("Internal error", { 'Content-Type': 'text/plain' }, 500)
            console.error(error.message)
        }
    }
}

exports.request = function(args, callback) {
    try {
        require('request')(args, function(error, response, body) {
            if (error) callback(error)
            if (response.statusCode >= 400) return callback(new Error("Error " + _res.statusCode))
            callback(null, response, body)
        })
    } catch (error) {
        callback(error)
    }
}
<commit_msg>First attempt to simplify error handling<commit_after>exports.Response = function(response) {
    return {
        response: function() {
            return response
        },
        handleUserError: function(error) {
            response.send(error.message, { 'Content-Type': 'text/plain' }, 400)
        },
        handleServerError: function(error) {
            response.send("Internal error", { 'Content-Type': 'text/plain' }, 500)
            console.error(error.message)
        }
    }
}

/**
 * Returns a function that handles callback response and throwed errors
 *
 * - Not null responses (wich value is casted to an array) are converted to a
 *   call to handledCallback(null[, ...])
 * - Nothing is done on null responses (to only handle errors)
 * - Throwed errors are converted to a call to handledCallback(error)
 */
exports.handledCallback = function(handledCallback, callback) {
    return function() {
        var response
        try {
            response = callback.apply(null, arguments)
            if (response != null) {
                if (! response instanceof Array) response = [response]
                // Add error ("no error") parameter
                response.unshift(null)
            }
        } catch (error) {
            var response = [error]
        }
        if (response != null) handledCallback.apply(null, response)
    }
}

exports.request = function(args, callback) {
    exports.handledCallback(callback, function() {
        require('request')(args, exports.handledCallback(callback, function(error, response, body) {
            if (error) throw error
            if (response.statusCode >= 400) throw new Error("Error " + response.statusCode)
            return [response, body]
        }))
    })()
}
<|endoftext|>


<commit_before>var _ = require('underscore')

var Utils = {}

Utils.AddSlash = function(string) {
  var last = string.substr(-1)
  if (last != '/') {
     string = string + '/'
  }
  return string
}

module.exports = Utils
<commit_msg>added Utils.IsHidden for checking hidden files<commit_after>var _ = require('underscore')

var Utils = {}

Utils.AddSlash = function(string) {
  var last = string.substr(-1)
  if (last != '/') {
     string = string + '/'
  }
  return string
}

Utils.IsHidden = function(string) {
  if (string.charAt(0) === '.') {
    return true
  }
  return false
}

module.exports = Utils
<|endoftext|>


<commit_before>/* jslint node: true */
'use strict'

var fs = require('fs')
var request = require('request')
var colors = require('colors/safe')
var notifications = require('../data/datacache').notifications
var packageJson = require('../package.json')
var sanitizeHtml = require('sanitize-html')
var jsSHA = require('jssha')
var Entities = require('html-entities').AllHtmlEntities
var config = require('config')
var entities = new Entities()

var months = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']

var ctfKey
fs.readFile('ctf.key', 'utf8', function (err, data) {
  if (err) {
    throw err
  }
  ctfKey = data
})

exports.queryResultToJson = function (data, status) {
  var wrappedData = {}
  if (data) {
    if (!data.length && data.dataValues) {
      wrappedData = data.dataValues
    } else if (data.length > 0) {
      wrappedData = []
      for (var i = 0; i < data.length; i++) {
        wrappedData.push(data[i].dataValues ? data[i].dataValues : data[i])
      }
    } else {
      wrappedData = data
    }
  }
  return {
    status: status || 'success',
    data: wrappedData
  }
}

exports.startsWith = function (str, prefix) {
  return str ? str.indexOf(prefix) === 0 : false
}

exports.endsWith = function (str, suffix) {
  return str ? str.indexOf(suffix, str.length - suffix.length) !== -1 : false
}

exports.contains = function (str, element) {
  return str ? str.indexOf(element) !== -1 : false
}

exports.containsEscaped = function (str, element) {
  return this.contains(str, element.replace(/"/g, '\\"'))
}

exports.containsOrEscaped = function (str, element) {
  return this.contains(str, element) || this.containsEscaped(str, element)
}

exports.unquote = function (str) {
  if (str && this.startsWith(str, '"') && this.endsWith(str, '"')) {
    return str.substring(1, str.length - 1)
  } else {
    return str
  }
}

exports.version = function (module) {
  if (module) {
    return packageJson.dependencies[module]
  } else {
    return packageJson.version
  }
}

exports.toHmac = function (text) {
  var shaObj = new jsSHA('SHA-1', 'TEXT') // eslint-disable-line new-cap
  shaObj.setHMACKey(ctfKey, 'TEXT')
  shaObj.update(text)
  return shaObj.getHMAC('HEX')
}

exports.solve = function (challenge) {
  var flag = this.toHmac(challenge.name)
  challenge.solved = true
  challenge.save().success(function () {
    challenge.description = entities.decode(sanitizeHtml(challenge.description, {
      allowedTags: [],
      allowedAttributes: []
    }))
    console.log(colors.green('Solved') + ' challenge ' + colors.cyan(challenge.name) + ' (' + challenge.description + ')')
    var notification = {challenge: challenge.name + ' (' + challenge.description + ')', flag: flag, hidden: !config.get('application.showChallengeSolvedNotifications')}
    notifications.push(notification)
    if (global.io) {
      global.io.emit('challenge solved', notification)
    }
  })
}

exports.notSolved = function (challenge) {
  return challenge && !challenge.solved
}

exports.toMMMYY = function (date) {
  var month = date.getMonth()
  var year = date.getFullYear()
  return months[month] + year.toString().substring(2, 4)
}

exports.downloadToFile = function (url, dest, cb) {
  var file = fs.createWriteStream(dest)
  var sendReq = request.get(url)

    // verify response code
  sendReq.on('response', function (response) {
    if (response.statusCode !== 200) {
      return cb('Response status was ' + response.statusCode)
    }
  })

    // check for request errors
  sendReq.on('error', function (err) {
    fs.unlink(dest)
    return cb(err.message)
  })

  sendReq.pipe(file)

  file.on('finish', function () {
    file.close(cb)  // close() is async, call cb after close completes.
  })

  file.on('error', function (err) { // Handle errors
    fs.unlink(dest) // Delete the file async. (But we don't check the result)
    return cb(err.message)
  })
}
<commit_msg>Make config callback optional<commit_after>/* jslint node: true */
'use strict'

var fs = require('fs')
var request = require('request')
var colors = require('colors/safe')
var notifications = require('../data/datacache').notifications
var packageJson = require('../package.json')
var sanitizeHtml = require('sanitize-html')
var jsSHA = require('jssha')
var Entities = require('html-entities').AllHtmlEntities
var config = require('config')
var entities = new Entities()

var months = ['JAN', 'FEB', 'MAR', 'APR', 'MAY', 'JUN', 'JUL', 'AUG', 'SEP', 'OCT', 'NOV', 'DEC']

var ctfKey
fs.readFile('ctf.key', 'utf8', function (err, data) {
  if (err) {
    throw err
  }
  ctfKey = data
})

exports.queryResultToJson = function (data, status) {
  var wrappedData = {}
  if (data) {
    if (!data.length && data.dataValues) {
      wrappedData = data.dataValues
    } else if (data.length > 0) {
      wrappedData = []
      for (var i = 0; i < data.length; i++) {
        wrappedData.push(data[i].dataValues ? data[i].dataValues : data[i])
      }
    } else {
      wrappedData = data
    }
  }
  return {
    status: status || 'success',
    data: wrappedData
  }
}

exports.startsWith = function (str, prefix) {
  return str ? str.indexOf(prefix) === 0 : false
}

exports.endsWith = function (str, suffix) {
  return str ? str.indexOf(suffix, str.length - suffix.length) !== -1 : false
}

exports.contains = function (str, element) {
  return str ? str.indexOf(element) !== -1 : false
}

exports.containsEscaped = function (str, element) {
  return this.contains(str, element.replace(/"/g, '\\"'))
}

exports.containsOrEscaped = function (str, element) {
  return this.contains(str, element) || this.containsEscaped(str, element)
}

exports.unquote = function (str) {
  if (str && this.startsWith(str, '"') && this.endsWith(str, '"')) {
    return str.substring(1, str.length - 1)
  } else {
    return str
  }
}

exports.version = function (module) {
  if (module) {
    return packageJson.dependencies[module]
  } else {
    return packageJson.version
  }
}

exports.toHmac = function (text) {
  var shaObj = new jsSHA('SHA-1', 'TEXT') // eslint-disable-line new-cap
  shaObj.setHMACKey(ctfKey, 'TEXT')
  shaObj.update(text)
  return shaObj.getHMAC('HEX')
}

exports.solve = function (challenge) {
  var flag = this.toHmac(challenge.name)
  challenge.solved = true
  challenge.save().success(function () {
    challenge.description = entities.decode(sanitizeHtml(challenge.description, {
      allowedTags: [],
      allowedAttributes: []
    }))
    console.log(colors.green('Solved') + ' challenge ' + colors.cyan(challenge.name) + ' (' + challenge.description + ')')
    var notification = {challenge: challenge.name + ' (' + challenge.description + ')', flag: flag, hidden: !config.get('application.showChallengeSolvedNotifications')}
    notifications.push(notification)
    if (global.io) {
      global.io.emit('challenge solved', notification)
    }
  })
}

exports.notSolved = function (challenge) {
  return challenge && !challenge.solved
}

exports.toMMMYY = function (date) {
  var month = date.getMonth()
  var year = date.getFullYear()
  return months[month] + year.toString().substring(2, 4)
}

exports.downloadToFile = function (url, dest, cb) {
  var file = fs.createWriteStream(dest)
  var sendReq = request.get(url)

  sendReq.on('response', function (response) {
    if (cb && response.statusCode !== 200) {
      return cb('Response status was ' + response.statusCode)
    }
  })

  sendReq.on('error', function (err) {
    fs.unlink(dest)
    if (cb) {
      return cb(err.message)
    } else {
      console.error('Problem downloading file: ' + err.message)
    }
  })

  sendReq.pipe(file)

  file.on('finish', function () {
    file.close(cb)
  })

  file.on('error', function (err) { // Handle errors
    fs.unlink(dest)
    if (cb) {
      return cb(err.message)
    } else {
      console.error('Problem writing downloaded file: ' + err.message)
    }
  })
}
<|endoftext|>


<commit_before>const fs = require('fs');
const path = require('path');

module.exports = {
  // SIZE
  size: function(thing) {
    if (thing !== undefined && thing !== null && thing !== NaN) return Object.keys(thing).length;
    else return 0;
  },
  // TOBOOLEAN
  toBoolean: function(thing) {
    if (thing === 'false' || thing <= 0)
      return false;
    if (thing === 'true' || thing >= 1)
      return true;
    if (thing === true || thing === false)
      return thing;
    return false;
  },
  is: function(thing, Type) {
    if (!typeof Type || Type === null || Type === undefined || Number.isNaN(Type) || !Type.prototype) return false;
    return Type.prototype.isPrototypeOf(Object(thing));
  },
  existsSync: function(file){
    try {
      fs.statSync(path.normalize(file));
      return true;
    } catch (e) {
      return false;
    }
  },
  isDirectorySync: function(file){
    try {
      var stat = fs.statSync(path.normalize(file));
      if (stat.isDirectory()) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  },
  isFileSync: function(file){
    try {
      var stat = fs.statSync(path.normalize(file));
      if (stat.isFile()) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  },
  getInt: function(thing, min = 0){
    thing = parseInt(thing);
    min = parseInt(min);

    if (Number.isNaN(thing)) {
      if (!Number.isNaN(min)) return min;
      else return 0;
    } else {
      if (!Number.isNaN(min)) {
        if (thing > min) return thing;
        else return min;
      } else return thing;
    }
  }
};
<commit_msg>changed literal object syntax to ES6<commit_after>/**
 * utils functions © 2016 Adrien Valcke
 *
 * size(thing)
 * toBoolean(thing)
 * is(thing, Type)
 * existsSync(pathToContent)
 * isDirectorySync(directory)
 * isFileSync(file)
 * getFilesSync(pathToContent, recursive = false)
 * getFiles(pathToContent, callback)
 * getInt(thing, min = 0)
 */

const fs = require('fs');
const path = require('path');

module.exports = {
  // size
  size(thing) {
    if (thing !== undefined && thing !== null && thing !== NaN) return Object.keys(thing).length;
    else return 0;
  },
  // toBoolean
  toBoolean(thing) {
    if (thing === 'false' || thing <= 0)
      return false;
    if (thing === 'true' || thing >= 1)
      return true;
    if (thing === true || thing === false)
      return thing;
    return false;
  },
  // is
  is(thing, Type) {
    if (!typeof Type || Type === null || Type === undefined || Number.isNaN(Type) || !Type.prototype) return false;
    return Type.prototype.isPrototypeOf(Object(thing));
  },
  // existsSync
  existsSync(pathToContent){
    try {
      fs.statSync(path.normalize(pathToContent));
      return true;
    } catch (e) {
      return false;
    }
  },
  // isDirectorySync
  isDirectorySync(directory){
    try {
      var stat = fs.statSync(path.normalize(directory));
      if (stat.isDirectory()) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  },
  // isFileSync
  isFileSync(file){
    try {
      var stat = fs.statSync(path.normalize(file));
      if (stat.isFile()) {
        return true;
      } else {
        return false;
      }
    } catch (e) {
      return false;
    }
  },
  // getFilesSync
  getFilesSync(pathToContent, recursive = false) {
    let files = [];
    recursive = this.toBoolean(recursive);

    pathToContent = path.normalize(pathToContent);

    if (this.isFileSync(pathToContent)) {
      files.push(pathToContent);
    } else if (this.isDirectorySync(pathToContent)) {
      let contents = fs.readdirSync(pathToContent, 'utf8');
      for(content of contents) {
        let contentPath = path.join(pathToContent, content);
        if (this.isFileSync(contentPath)) files.push(contentPath);
        else if (recursive && this.isDirectorySync(contentPath)) files = files.concat(this.getFilesSync(contentPath, recursive));
      }
    } else {
      console.error(`${colors.red('safe-regex-cli error: no such file or directory "' + pathToContent + '".')}`);
    }
    return files;
  },
  // getFiles Async
  getFiles(pathToContent, callback) {
    let filesArray = [];
    pathToContent = path.normalize(pathToContent);

    fs.stat(pathToContent, (err, stats) => {
      if (err) return callback(err);
      if (stats.isFile()) {
        filesArray.push(pathToContent);
        callback(null, filesArray);
      } else if (stats.isDirectory()) {
        fs.readdir(pathToContent, 'utf8', (err, files) => {
          for(let i = 0, len = files.length; i < len; i++) {
            ((i) => {
              let filePath = path.join(pathToContent, files[i]);
              fs.stat(filePath, (err, stats) => {
                if (err) return callback(err);
                if (stats.isFile()) filesArray.push(filePath);
                if (i === len - 1) callback(null, filesArray);
              });
            })(i);
          }
        });
      } else {
        callback(new Error(`${colors.red('safe-regex-cli error: no such file or directory "' + pathToContent + '".')}`));
      }
    });
  },
  // getInt
  getInt(thing, min = 0){
    thing = parseInt(thing);
    min = parseInt(min);

    if (Number.isNaN(thing)) {
      if (!Number.isNaN(min)) return min;
      else return 0;
    } else {
      if (!Number.isNaN(min)) {
        if (thing > min) return thing;
        else return min;
      } else return thing;
    }
  }
};
<|endoftext|>


<commit_before>var fs = require('fs');

var LINES_OF_CONTEXT = 7;

var getFunction = function (line) {
    return line.getFunctionName() ||
           line.getTypeName() + '.' + (line.getMethodName() || '<anonymous>');
};

var parseStack = function (stack, cb) {
    var frames = [],
        cache = {},
        callbacks = stack.length;

    stack.forEach(function (line, index) {
        var frame = {
            filename   : line.getFileName() || '',
            lineno     : line.getLineNumber(),
            'function' : getFunction(line)
        };
        var isInternal = line.isNative() ||
                         (frame.filename[0] !== '/' &&
                          frame.filename[0] !== '.');
        var parseLines = function (lines) {
            frame.pre_context = lines.slice(Math.max(0, frame.lineno-(LINES_OF_CONTEXT+1)), frame.lineno-1);
            frame.context_line = lines[frame.lineno-1];
            frame.post_context = lines.slice(frame.lineno, frame.lineno+LINES_OF_CONTEXT);
        }

        // in_app is all that's not an internal Node function or a module within node_modules
        frame.in_app = !isInternal && !~frame.filename.indexOf('node_modules/');

        // internal Node files are not full path names. Ignore them.
        if (isInternal) {
            frames[index] = frame;
            if (--callbacks === 0) cb(frames);
            return;
        }

        if (frame.filename in cache) {
            parseLines(cache[frame.filename]);
            if (--callbcaks === 0) cb(frames);
            return;
        }

        fs.readFile(frame.filename, function (err, file) {
            if (!err) {
                file = file.toString().split('\n');
                cache[frame.filename] = file;
                parseLines(file, frame);
            }
            frames[index] = frame;
            if (--callbacks === 0) cb(frames);
        });

        // console.log(line.getThis());
        // console.log(line.getTypeName());
        // console.log(line.getFunction().caller);
        // console.log(line.getFunction()+'');
        // console.log(line.getFunctionName());
        // console.log(line.getMethodName());
        // console.log(line.getFileName());
        // console.log(line.getLineNumber());
        // console.log(line.getColumnNumber());
        // console.log(line.getEvalOrigin());
        // console.log(line.isToplevel());
        // console.log(line.isNative());
        // console.log(line.isConstructor());
    });
};

module.exports.parseStack = parseStack;
<commit_msg>Enhance stack trace debug code<commit_after>var fs = require('fs');

var LINES_OF_CONTEXT = 7;

var getFunction = function (line) {
    return line.getFunctionName() ||
           line.getTypeName() + '.' + (line.getMethodName() || '<anonymous>');
};

var parseStack = function (stack, cb) {
    var frames = [],
        cache = {},
        callbacks = stack.length;

    stack.forEach(function (line, index) {
        var frame = {
            filename   : line.getFileName() || '',
            lineno     : line.getLineNumber(),
            'function' : getFunction(line)
        };
        var isInternal = line.isNative() ||
                         (frame.filename[0] !== '/' &&
                          frame.filename[0] !== '.');
        var parseLines = function (lines) {
            frame.pre_context = lines.slice(Math.max(0, frame.lineno-(LINES_OF_CONTEXT+1)), frame.lineno-1);
            frame.context_line = lines[frame.lineno-1];
            frame.post_context = lines.slice(frame.lineno, frame.lineno+LINES_OF_CONTEXT);
        }

        // in_app is all that's not an internal Node function or a module within node_modules
        frame.in_app = !isInternal && !~frame.filename.indexOf('node_modules/');

        // internal Node files are not full path names. Ignore them.
        if (isInternal) {
            frames[index] = frame;
            if (--callbacks === 0) cb(frames);
            return;
        }

        if (frame.filename in cache) {
            parseLines(cache[frame.filename]);
            if (--callbcaks === 0) cb(frames);
            return;
        }

        fs.readFile(frame.filename, function (err, file) {
            if (!err) {
                file = file.toString().split('\n');
                cache[frame.filename] = file;
                parseLines(file, frame);
            }
            frames[index] = frame;
            if (--callbacks === 0) cb(frames);
        });

        // See all functions at https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi
        // console.log(line.getThis());
        // console.log(line.getTypeName());
        // console.log(line.getFunction()+'');
        // console.log(line.getFunction() ? line.getFunction().caller : '<no caller>');
        // console.log(line.getFunctionName());
        // console.log(line.getMethodName());
        // console.log(line.getFileName());
        // console.log(line.getLineNumber());
        // console.log(line.getColumnNumber());
        // console.log(line.getEvalOrigin());
        // console.log(line.isToplevel());
        // console.log(line.isEval());
        // console.log(line.isNative());
        // console.log(line.isConstructor());
    });
};

module.exports.parseStack = parseStack;
<|endoftext|>


<commit_before>var controllerDir;
var appName;

function getAppName() {
    var parts = __dirname.replace(/\\/g, '/').split('/');
    return parts[parts.length - 2].split('.')[0];
}

// Get js-controller directory to load libs
function getControllerDir(isInstall) {
    var fs = require('fs');
    // Find the js-controller location
    var controllerDir = __dirname.replace(/\\/g, '/');
    controllerDir = controllerDir.split('/');
    if (controllerDir[controllerDir.length - 3] == 'adapter') {
        controllerDir.splice(controllerDir.length - 3, 3);
        controllerDir = controllerDir.join('/');
    } else if (controllerDir[controllerDir.length - 3] == 'node_modules') {
        controllerDir.splice(controllerDir.length - 3, 3);
        controllerDir = controllerDir.join('/');
        if (fs.existsSync(controllerDir + '/node_modules/' + appName + '.js-controller')) {
            controllerDir += '/node_modules/' + appName + '.js-controller';
        } else if (fs.existsSync(controllerDir + '/node_modules/' + appName.toLowerCase() + '.js-controller')) {
            controllerDir += '/node_modules/' + appName.toLowerCase() + '.js-controller';
        } else if (!fs.existsSync(controllerDir + '/controller.js')) {
            if (!isInstall) {
                console.log('Cannot find js-controller');
                process.exit(10);
            } else {
                process.exit();
            }
        }
    } else {
        if (!isInstall) {
            console.log('Cannot find js-controller');
            process.exit(10);
        } else {
            process.exit();
        }
    }
    return controllerDir;
}

// Read controller configuration file
function getConfig() {
    if (fs.existsSync(controllerDir + '/conf/' + appName + '.json')) {
        return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName + '.json'));
    } else if (fs.existsSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json')) {
        return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json'));
    } else {
        throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
    }
}
appName       = getAppName();
controllerDir = getControllerDir(typeof process != 'undefined' && process.argv && process.argv.indexOf('--install') != -1);

exports.controllerDir = controllerDir;
exports.getConfig =     getConfig;
exports.adapter =       require(controllerDir + '/lib/adapter.js');
exports.appName =       appName;
<commit_msg>update utils.js<commit_after>var controllerDir;
var appName;

function getAppName() {
    var parts = __dirname.replace(/\\/g, '/').split('/');
    return parts[parts.length - 2].split('.')[0];
}

// Get js-controller directory to load libs
function getControllerDir(isInstall) {
    var fs = require('fs');
    // Find the js-controller location
    var controllerDir = __dirname.replace(/\\/g, '/');
    controllerDir = controllerDir.split('/');
    if (controllerDir[controllerDir.length - 3] === 'adapter') {
        controllerDir.splice(controllerDir.length - 3, 3);
        controllerDir = controllerDir.join('/');
    } else if (controllerDir[controllerDir.length - 3] === 'node_modules') {
        controllerDir.splice(controllerDir.length - 3, 3);
        controllerDir = controllerDir.join('/');
        if (fs.existsSync(controllerDir + '/node_modules/' + appName + '.js-controller')) {
            controllerDir += '/node_modules/' + appName + '.js-controller';
        } else if (fs.existsSync(controllerDir + '/node_modules/' + appName.toLowerCase() + '.js-controller')) {
            controllerDir += '/node_modules/' + appName.toLowerCase() + '.js-controller';
        } else if (!fs.existsSync(controllerDir + '/controller.js')) {
            if (!isInstall) {
                console.log('Cannot find js-controller');
                process.exit(10);
            } else {
                process.exit();
            }
        }
    } else if (fs.existsSync(__dirname + '/../../node_modules/' + appName.toLowerCase() + '.js-controller')) {
        controllerDir.splice(controllerDir.length - 2, 2);
        return controllerDir.join('/') + '/node_modules/' + appName.toLowerCase() + '.js-controller';
    } else {
        if (!isInstall) {
            console.log('Cannot find js-controller');
            process.exit(10);
        } else {
            process.exit();
        }
    }
    return controllerDir;
}

// Read controller configuration file
function getConfig() {
    var fs = require('fs');
    if (fs.existsSync(controllerDir + '/conf/' + appName + '.json')) {
        return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName + '.json'));
    } else if (fs.existsSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json')) {
        return JSON.parse(fs.readFileSync(controllerDir + '/conf/' + appName.toLowerCase() + '.json'));
    } else {
        throw new Error('Cannot find ' + controllerDir + '/conf/' + appName + '.json');
    }
}
appName       = getAppName();
controllerDir = getControllerDir(typeof process !== 'undefined' && process.argv && process.argv.indexOf('--install') !== -1);

exports.controllerDir = controllerDir;
exports.getConfig =     getConfig;
exports.Adapter =       require(controllerDir + '/lib/adapter.js');
exports.appName =       appName;
<|endoftext|>


<commit_before>// Load modules

var Hoek = require('hoek');
var Sntp = require('sntp');
var Boom = require('boom');

// Declare internals

var internals = {};

// Import Hoek Utilities

internals.import = function () {

    for (var i in Hoek) {
        if (Hoek.hasOwnProperty(i)) {
            exports[i] = Hoek[i];
        }
    }
};

internals.import();

// Hawk version

exports.version = function () {

    return exports.loadPackage(__dirname + '/..').version;
};

// Extract host and port from request

exports.parseHost = function (req, hostHeaderName) {

    hostHeaderName = (hostHeaderName ? hostHeaderName.toLowerCase() : 'host');
    var hostHeader = req.headers[hostHeaderName];
    if (!hostHeader) {
        return null;
    }

    var hostHeaderRegex;
    if (hostHeader[0] === '[') {
        hostHeaderRegex = /^(?:(?:\r\n)?\s)*(\[[^\]]+\])(?::(\d+))?(?:(?:\r\n)?\s)*$/;      // IPv6
    }
    else {
        hostHeaderRegex = /^(?:(?:\r\n)?\s)*([^:]+)(?::(\d+))?(?:(?:\r\n)?\s)*$/;           // IPv4, hostname
    }

    var hostParts = hostHeader.match(hostHeaderRegex);

    if (!hostParts ||
        hostParts.length !== 3 ||
        !hostParts[1]) {

        return null;
    }

    return {
        name: hostParts[1],
        port: (hostParts[2] ? hostParts[2] : (req.connection && req.connection.encrypted ? 443 : 80))
    };
};

// Parse Content-Type header content

exports.parseContentType = function (header) {

    if (!header) {
        return '';
    }

    return header.split(';')[0].trim().toLowerCase();
};

// Convert node's  to request configuration object

exports.parseRequest = function (req, options) {

    if (!req.headers) {
        return req;
    }

    // Obtain host and port information

    if (!options.host || !options.port) {
        var host = exports.parseHost(req, options.hostHeaderName);
        if (!host) {
            return new Error('Invalid Host header');
        }
    }

    var request = {
        method: req.method,
        url: req.url,
        host: options.host || host.name,
        port: options.port || host.port,
        authorization: req.headers.authorization,
        contentType: req.headers['content-type'] || ''
    };

    return request;
};

exports.now = function () {

    return Sntp.now();
};

// Parse Hawk HTTP Authorization header

exports.parseAuthorizationHeader = function (header, keys) {

    keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg'];

    if (!header) {
        return Boom.unauthorized(null, 'Hawk');
    }

    var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/);       // Header: scheme[ something]
    if (!headerParts) {
        return Boom.badRequest('Invalid header syntax');
    }

    var scheme = headerParts[1];
    if (scheme.toLowerCase() !== 'hawk') {
        return Boom.unauthorized(null, 'Hawk');
    }

    var attributesString = headerParts[2];
    if (!attributesString) {
        return Boom.badRequest('Invalid header syntax');
    }

    var attributes = {};
    var errorMessage = '';
    var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {

        // Check valid attribute names

        if (keys.indexOf($1) === -1) {
            errorMessage = 'Unknown attribute: ' + $1;
            return;
        }

        // Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9

        if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {
            errorMessage = 'Bad attribute value: ' + $1;
            return;
        }

        // Check for duplicates

        if (attributes.hasOwnProperty($1)) {
            errorMessage = 'Duplicate attribute: ' + $1;
            return;
        }

        attributes[$1] = $2;
        return '';
    });

    if (verify !== '') {
        return Boom.badRequest(errorMessage || 'Bad header format');
    }

    return attributes;
};

exports.unauthorized = function (message) {

    return Boom.unauthorized(message, 'Hawk');
};

<commit_msg>Replace version method<commit_after>// Load modules

var Hoek = require('hoek');
var Sntp = require('sntp');
var Boom = require('boom');

// Declare internals

var internals = {};

// Import Hoek Utilities

internals.import = function () {

    for (var i in Hoek) {
        if (Hoek.hasOwnProperty(i)) {
            exports[i] = Hoek[i];
        }
    }
};

internals.import();

exports.version = require('../package.json').version;

// Extract host and port from request

exports.parseHost = function (req, hostHeaderName) {

    hostHeaderName = (hostHeaderName ? hostHeaderName.toLowerCase() : 'host');
    var hostHeader = req.headers[hostHeaderName];
    if (!hostHeader) {
        return null;
    }

    var hostHeaderRegex;
    if (hostHeader[0] === '[') {
        hostHeaderRegex = /^(?:(?:\r\n)?\s)*(\[[^\]]+\])(?::(\d+))?(?:(?:\r\n)?\s)*$/;      // IPv6
    }
    else {
        hostHeaderRegex = /^(?:(?:\r\n)?\s)*([^:]+)(?::(\d+))?(?:(?:\r\n)?\s)*$/;           // IPv4, hostname
    }

    var hostParts = hostHeader.match(hostHeaderRegex);

    if (!hostParts ||
        hostParts.length !== 3 ||
        !hostParts[1]) {

        return null;
    }

    return {
        name: hostParts[1],
        port: (hostParts[2] ? hostParts[2] : (req.connection && req.connection.encrypted ? 443 : 80))
    };
};

// Parse Content-Type header content

exports.parseContentType = function (header) {

    if (!header) {
        return '';
    }

    return header.split(';')[0].trim().toLowerCase();
};

// Convert node's  to request configuration object

exports.parseRequest = function (req, options) {

    if (!req.headers) {
        return req;
    }

    // Obtain host and port information

    if (!options.host || !options.port) {
        var host = exports.parseHost(req, options.hostHeaderName);
        if (!host) {
            return new Error('Invalid Host header');
        }
    }

    var request = {
        method: req.method,
        url: req.url,
        host: options.host || host.name,
        port: options.port || host.port,
        authorization: req.headers.authorization,
        contentType: req.headers['content-type'] || ''
    };

    return request;
};

exports.now = function () {

    return Sntp.now();
};

// Parse Hawk HTTP Authorization header

exports.parseAuthorizationHeader = function (header, keys) {

    keys = keys || ['id', 'ts', 'nonce', 'hash', 'ext', 'mac', 'app', 'dlg'];

    if (!header) {
        return Boom.unauthorized(null, 'Hawk');
    }

    var headerParts = header.match(/^(\w+)(?:\s+(.*))?$/);       // Header: scheme[ something]
    if (!headerParts) {
        return Boom.badRequest('Invalid header syntax');
    }

    var scheme = headerParts[1];
    if (scheme.toLowerCase() !== 'hawk') {
        return Boom.unauthorized(null, 'Hawk');
    }

    var attributesString = headerParts[2];
    if (!attributesString) {
        return Boom.badRequest('Invalid header syntax');
    }

    var attributes = {};
    var errorMessage = '';
    var verify = attributesString.replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) {

        // Check valid attribute names

        if (keys.indexOf($1) === -1) {
            errorMessage = 'Unknown attribute: ' + $1;
            return;
        }

        // Allowed attribute value characters: !#$%&'()*+,-./:;<=>?@[]^_`{|}~ and space, a-z, A-Z, 0-9

        if ($2.match(/^[ \w\!#\$%&'\(\)\*\+,\-\.\/\:;<\=>\?@\[\]\^`\{\|\}~]+$/) === null) {
            errorMessage = 'Bad attribute value: ' + $1;
            return;
        }

        // Check for duplicates

        if (attributes.hasOwnProperty($1)) {
            errorMessage = 'Duplicate attribute: ' + $1;
            return;
        }

        attributes[$1] = $2;
        return '';
    });

    if (verify !== '') {
        return Boom.badRequest(errorMessage || 'Bad header format');
    }

    return attributes;
};

exports.unauthorized = function (message) {

    return Boom.unauthorized(message, 'Hawk');
};

<|endoftext|>


<commit_before>var fs = require('fs'),
    path = require('path'),
    extend = require('soak').mixin,
    inherit = require('soak').inherit,
    EventEmitter = require('events').EventEmitter;

module.exports = {
  // Create a base observable object that inherits from EventEmitter. This
  // allows us to create new objects that can trigger events very easily.
  //
  // Example:
  //
  //   var Store = Observable.extend({
  //     save: function () {}
  //   });
  Observable: inherit(EventEmitter),

  // Helper function for creating an index file in a directory. It loads all
  // modules within the provided directory excluding the file provided as
  // "indexpath". These will then be returned.
  //
  // Example:
  //
  //   module.exports = utils.index(__dirname, __filename);
  index: function (dirname, indexpath) {
    var extensions = {js: 1},
        modules = {};

    indexpath = indexpath || path.join(dirname, 'index.js');

    // Load all exported objects into a single module.
    fs.readdirSync(dirname).forEach(function (filename) {
      var fullpath = path.join(dirname, filename),
          parts    = filename.split('.'),
          module, name;

      if (fullpath !== indexpath && extensions[parts.pop()] && '.' !== filename[0]) {
        name = parts.join('.');
        module = require(fullpath);

        // Grab the function name or module name if possible.
        if (typeof module.name === 'string') {
          name = module.name;
        }

        modules[name] = module;
      }
    });

    return modules;
  },

  // Takes an object and a series of method names. Each method will be
  // re-assigned to the object with the object bound as the methods scope. This
  // is generally useful for defining callback methods.
  //
  // An array of method names can also be passed.
  //
  // Example:
  //
  //   object = {onData: function () {}, onEnd: function () {});
  //   utils.bindAll(object, 'onData', 'onEnd');
  //
  //   // Same as doing.
  //   object.onData = object.onData.bind(object);
  //   object.onEnd  = object.onEnd.bind(object);
  bindAll: function (object /* args */) {
    var methods = [].slice.call(arguments, 1);
    if (arguments.length === 2 && Array.isArray(arguments[1])) {
      methods = arguments[1];
    }

    methods.forEach(function (method) {
      if (typeof object[method] === 'function') {
        object[method] = object[method].bind(object);
      }
    });

    return object;
  },

  // Extends the first argument with the properties of the subsequent
  // arguments.
  extend: extend,

  // Simplifies the process of extending existing objects.
  inherit: inherit,

  extract: function (obj /* keys */) {
    var keys = [].slice.call(arguments, 1),
        collected = {};

    keys.forEach(function (key) {
      if (obj[key]) {
        collected[key] = obj[key];
      }
    });

    return collected;
  },
  isAjax: function (req) {
    return (req.get('X-Requested-With') || '').toLowerCase() === 'xmlhttprequest';
  },
  shortcode: function () {
    var vowels = 'aeiou',
        consonants = 'bcdfghjklmnpqrstvwxyz',
        word = '', length = 6, index = 0, set;

    for (; index < length; index += 1) {
      set = (index % 2 === 0) ? vowels : consonants;
      word += set[Math.floor(Math.random() * set.length)];
    }

    return word;
  },
  re: {
    flatten: /\s+/g,
    body: /<body.*?>\s*(.*)/m,
    meta: /<meta name="description" content="(.*?)"/,
    tags: /<[^>]+>/g,
    title: /<title>(.*)<\/title>/
  },
  titleForBin: function (bin) {
    // TODO: Use HTML parser or JSDOM
    var html = (bin.html || '').replace(this.re.flatten, ' '), // flatten
        javascript = (bin.javascript || '').replace(this.re.flatten, ' ').trim(),
        matches = (html.match(this.re.meta) || []);

    if (matches.length === 2 && matches[1].trim()) {
      return matches[1];
    }

    matches = (html.match(this.re.body) || []);

    if (matches.length === 2 && matches[1].trim()) {
      return matches[1].replace(this.re.tags, '').trim();
    }

    // No title return JavaScript.
    if (javascript) {
      return javascript;
    }

    matches = (html.match(this.re.title) || []);
    if (matches.length === 2 && matches[1].trim()) {
      return matches[1];
    }
    return '';
  },
  since: function (date) {
    var diff    = (+new Date() - date) / 1000,
        message = 'a long time',
        timespan;

    if (diff < 60) {
      timespan = Math.floor(diff);
      message  = '{timespan} second';
    }
    else if (diff < 3600) {
      timespan =  Math.floor(diff / 60);
      message  = 'about {timespan} minute';
    }
    else if (diff < 86400) {
      timespan = Math.floor(diff / 3600);
      message  = 'around {timespan} hour';
    }
    else if (diff < 604800) {
      timespan = Math.floor(diff / 86400);
      message  = 'about {timespan} day';
    }
    else if (diff < 2419200) {
      timespan = Math.ceil(diff / 604800);
      message  = 'nearly {timespan} week';
    }
    else if (diff < 29030400) {
      timespan = Math.floor(diff / 2419200);
      message  = 'about {timespan} month';
    }

    if (timespan !== 1 && timespan) {
      message += 's';
    }

    return message.replace('{timespan}', timespan) + ' ago';
  },
  queryStringForBin: function (bin, defaults) {
    var params = [];
    ['html', 'javascript', 'css'].forEach(function (key) {
      if (bin[key] !== defaults[key]) {
        params.push(key);
      }
    });
    return params.join(',');
  }
};
<commit_msg>Add utils.gravatar() for creating gravatar urls<commit_after>var fs = require('fs'),
    path = require('path'),
    crypto = require('crypto'),
    extend = require('soak').mixin,
    inherit = require('soak').inherit,
    EventEmitter = require('events').EventEmitter;

module.exports = {
  // Create a base observable object that inherits from EventEmitter. This
  // allows us to create new objects that can trigger events very easily.
  //
  // Example:
  //
  //   var Store = Observable.extend({
  //     save: function () {}
  //   });
  Observable: inherit(EventEmitter),

  // Helper function for creating an index file in a directory. It loads all
  // modules within the provided directory excluding the file provided as
  // "indexpath". These will then be returned.
  //
  // Example:
  //
  //   module.exports = utils.index(__dirname, __filename);
  index: function (dirname, indexpath) {
    var extensions = {js: 1},
        modules = {};

    indexpath = indexpath || path.join(dirname, 'index.js');

    // Load all exported objects into a single module.
    fs.readdirSync(dirname).forEach(function (filename) {
      var fullpath = path.join(dirname, filename),
          parts    = filename.split('.'),
          module, name;

      if (fullpath !== indexpath && extensions[parts.pop()] && '.' !== filename[0]) {
        name = parts.join('.');
        module = require(fullpath);

        // Grab the function name or module name if possible.
        if (typeof module.name === 'string') {
          name = module.name;
        }

        modules[name] = module;
      }
    });

    return modules;
  },

  // Takes an object and a series of method names. Each method will be
  // re-assigned to the object with the object bound as the methods scope. This
  // is generally useful for defining callback methods.
  //
  // An array of method names can also be passed.
  //
  // Example:
  //
  //   object = {onData: function () {}, onEnd: function () {});
  //   utils.bindAll(object, 'onData', 'onEnd');
  //
  //   // Same as doing.
  //   object.onData = object.onData.bind(object);
  //   object.onEnd  = object.onEnd.bind(object);
  bindAll: function (object /* args */) {
    var methods = [].slice.call(arguments, 1);
    if (arguments.length === 2 && Array.isArray(arguments[1])) {
      methods = arguments[1];
    }

    methods.forEach(function (method) {
      if (typeof object[method] === 'function') {
        object[method] = object[method].bind(object);
      }
    });

    return object;
  },

  // Extends the first argument with the properties of the subsequent
  // arguments.
  extend: extend,

  // Simplifies the process of extending existing objects.
  inherit: inherit,

  extract: function (obj /* keys */) {
    var keys = [].slice.call(arguments, 1),
        collected = {};

    keys.forEach(function (key) {
      if (obj[key]) {
        collected[key] = obj[key];
      }
    });

    return collected;
  },
  isAjax: function (req) {
    return (req.get('X-Requested-With') || '').toLowerCase() === 'xmlhttprequest';
  },
  shortcode: function () {
    var vowels = 'aeiou',
        consonants = 'bcdfghjklmnpqrstvwxyz',
        word = '', length = 6, index = 0, set;

    for (; index < length; index += 1) {
      set = (index % 2 === 0) ? vowels : consonants;
      word += set[Math.floor(Math.random() * set.length)];
    }

    return word;
  },
  // Returns a gravatar url for the email address provided. An optional size
  // parameter can be provided to specify the size of the avatar to generate.
  gravatar: function (email, size) {
    email = (email || '').trim().toLowerCase();
    var hash = crypto.createHash('md5').update(email).digest('hex');
    return 'http://www.gravatar.com/avatar/' + hash + '?s=' + (size || 100);
  },
  re: {
    flatten: /\s+/g,
    body: /<body.*?>\s*(.*)/m,
    meta: /<meta name="description" content="(.*?)"/,
    tags: /<[^>]+>/g,
    title: /<title>(.*)<\/title>/
  },
  titleForBin: function (bin) {
    // TODO: Use HTML parser or JSDOM
    var html = (bin.html || '').replace(this.re.flatten, ' '), // flatten
        javascript = (bin.javascript || '').replace(this.re.flatten, ' ').trim(),
        matches = (html.match(this.re.meta) || []);

    if (matches.length === 2 && matches[1].trim()) {
      return matches[1];
    }

    matches = (html.match(this.re.body) || []);

    if (matches.length === 2 && matches[1].trim()) {
      return matches[1].replace(this.re.tags, '').trim();
    }

    // No title return JavaScript.
    if (javascript) {
      return javascript;
    }

    matches = (html.match(this.re.title) || []);
    if (matches.length === 2 && matches[1].trim()) {
      return matches[1];
    }
    return '';
  },
  since: function (date) {
    var diff    = (+new Date() - date) / 1000,
        message = 'a long time',
        timespan;

    if (diff < 60) {
      timespan = Math.floor(diff);
      message  = '{timespan} second';
    }
    else if (diff < 3600) {
      timespan =  Math.floor(diff / 60);
      message  = 'about {timespan} minute';
    }
    else if (diff < 86400) {
      timespan = Math.floor(diff / 3600);
      message  = 'around {timespan} hour';
    }
    else if (diff < 604800) {
      timespan = Math.floor(diff / 86400);
      message  = 'about {timespan} day';
    }
    else if (diff < 2419200) {
      timespan = Math.ceil(diff / 604800);
      message  = 'nearly {timespan} week';
    }
    else if (diff < 29030400) {
      timespan = Math.floor(diff / 2419200);
      message  = 'about {timespan} month';
    }

    if (timespan !== 1 && timespan) {
      message += 's';
    }

    return message.replace('{timespan}', timespan) + ' ago';
  },
  queryStringForBin: function (bin, defaults) {
    var params = [];
    ['html', 'javascript', 'css'].forEach(function (key) {
      if (bin[key] !== defaults[key]) {
        params.push(key);
      }
    });
    return params.join(',');
  }
};
<|endoftext|>


<commit_before>'use strict';

var Base = require('base-methods');
var utils = require('./utils');

function Views(options) {
  Base.call(this);
  this.options = options || {};
  this.define('View', this.options.View || require('./view'));
  utils.renameKey(this);
  utils.option(this);
  this.views = {};
}

Base.extend(Views, {
  use: function(fn) {
    fn.call(this, this);
    return this;
  },

  addView: function(key, value) {
    if (!value && typeof key === 'string') {
      return this.addView(this.renameKey(key), {path: key});
    }

    if (typeof value !== 'object') {
      throw new TypeError('expected value to be an object.');
    }

    var View = this.get('View');
    var view = !(value instanceof this.View)
      ? new View(value)
      : value;

    if (view.hasOwnProperty('options')) {
      this.option(view.options);
    }

    view.path = view.path || key;
    view.key = this.renameKey(view.key || key);

    var val = this.decorateView(view);
    this.emit('view', view.key, val);
    this.views[view.key] = val;
    return view;
  },

  addViews: function(views) {
    if (arguments.length > 1 && utils.isView(arguments[1])) {
      return this.addView.apply(this, arguments);
    }
    this.visit('addView', this.loader(views));
    return this;
  },

  getView: function(key) {
    return this.views[key] || this.views[this.renameKey(key)];
  },

  decorateView: function(view) {
    return view;
  },

  loader: function(views) {
    return views;
  },

  /**
   * Set view types for the collection.
   *
   * @param {String} `plural` e.g. `pages`
   * @param {Object} `options`
   * @api private
   */

  viewType: function() {
    this.options.viewType = utils.arrayify(this.options.viewType || []);
    if (this.options.viewType.length === 0) {
      this.options.viewType.push('renderable');
    }
    return this.options.viewType;
  }
});

utils.define(Views.prototype, 'count', {
  get: function() {
    return Object.keys(this.views).length;
  },
  set: function () {
    throw new Error('count is a read-only getter and cannot be defined.');
  }
});

module.exports = Views;
<commit_msg>some changes to `Views`:<commit_after>'use strict';

var Base = require('base-methods');
var utils = require('./utils');

function Views(options) {
  Base.call(this);
  this.options = options || {};
  this.define('View', this.options.View || require('./view'));
  utils.renameKey(this);
  utils.option(this);
  this.views = {};
}

Base.extend(Views, {
  use: function(fn) {
    fn.call(this, this, this.options);
    return this;
  },

  addView: function(key, value) {
    if (typeof value !== 'object' && typeof key === 'string') {
      return this.addView(this.renameKey(key), {path: key});
    }

    if (utils.isObject(key) && key.path) {
      return this.addView(key.path, key);
    }

    if (typeof value !== 'object') {
      throw new TypeError('expected value to be an object.');
    }

    var View = this.get('View');
    var view = !(value instanceof this.View)
      ? new View(value)
      : value;

    if (view.hasOwnProperty('options')) {
      this.option(view.options);
    }

    view.path = view.path || key;
    view.key = this.renameKey(view.key || key);

    var val = this.extendView(view);
    this.emit('view', view.key, val);
    this.views[view.key] = val;
    return view;
  },

  addViews: function(views) {
    if (arguments.length > 1 && utils.isView(arguments[1])) {
      return this.addView.apply(this, arguments);
    }
    if (Array.isArray(views)) {
      return this.addList(views);
    }
    this.visit('addView', this.loader(views));
    return this;
  },

  addList: function(list) {
    if (!Array.isArray(list)) {
      throw new TypeError('expected an array.');
    }
    list.forEach(this.addView.bind(this));
    return this;
  },

  getView: function(key) {
    return this.views[key] || this.views[this.renameKey(key)];
  },

  extendView: function(view) {
    return view;
  },

  loader: function(views) {
    return views;
  },

  /**
   * Set view types for the collection.
   *
   * @param {String} `plural` e.g. `pages`
   * @param {Object} `options`
   * @api private
   */

  viewType: function() {
    this.options.viewType = utils.arrayify(this.options.viewType || []);
    if (this.options.viewType.length === 0) {
      this.options.viewType.push('renderable');
    }
    return this.options.viewType;
  }
});

utils.define(Views.prototype, 'count', {
  get: function() {
    return Object.keys(this.views).length;
  },
  set: function () {
    throw new Error('count is a read-only getter and cannot be defined.');
  }
});

module.exports = Views;
<|endoftext|>


<commit_before>var fs   = require('fs');
var path = require('path');
var os = require('os');
var util = require('util');
var events = require('events');

var hasNativeRecursive = require('./has-native-recursive');
var is = require('./is');

var EVENT_UPDATE = 'update';
var EVENT_REMOVE = 'remove';

function makeArray(arr, offset) {
  return is.array(arr)
    ? arr : [].slice.call(arr, offset || 0);
}

function hasDup(arr) {
  return makeArray(arr).some(function(v, i, self) {
    return self.indexOf(v) !== i;
  });
}

function unique(arr) {
  return makeArray(arr).filter(function(v, i, self) {
    return self.indexOf(v) === i;
  });
}

function assign(obj/*, props */) {
  if (Object.assign) {
    return Object.assign.apply(Object, arguments);
  }
  return makeArray(arguments, 1)
    .reduce(function(mix, prop) {
      for (var name in prop) {
        if (prop.hasOwnProperty(name)) {
          mix[name] = prop[name];
        }
      }
      return mix;
    }, obj);
}

function guard(fn) {
  return function(arg, action) {
    if (is.func(fn)) {
      if (fn(arg)) action();
    } else {
      action();
    }
  }
}

function composeMessage(names) {
  return makeArray(names).map(function(n) {
    if (!is.exists(n)) return [EVENT_REMOVE, n];
    else return [EVENT_UPDATE, n];
  });
}

function getMessages(cache) {
  var dup = hasDup(cache.map(function(c) {
    return c.replace(/^[~#]+|[~#]+$/, '');
  }));

  // saving file from an editor maybe?
  if (dup) {
    var filtered = cache.filter(function(m) {
      return is.exists(m)
    });
    return composeMessage(unique(filtered));
  }
  else {
    return composeMessage(cache);
  }
}

function debounce(fn, delay) {
  var timer, cache = [];
  var info = fn.info;
  function handle() {
    getMessages(cache).forEach(function(msg) {
      fn.apply(null, msg);
    });
    timer = null;
    cache = [];
  }
  return function(evt, name) {
    if (is.nil(name)) {
      name = '';
    }
    cache.push(
      path.join(info.fpath, name)
    );
    if (!timer) {
      timer = setTimeout(handle, delay || 200);
    }
  }
}

function getSubDirectories(dir, fn) {
  if (is.directory(dir)) {
    fs.readdir(dir, function(err, all) {
      if (err) {
        // don't throw permission errors.
        if (!/^(EPERM|EACCES)$/.test(err.code)) throw err;
        else console.warn('Warning: Cannot access %s.', dir);
      }
      else if (is.array(all)) {
        all.forEach(function(f) {
          var sdir = path.join(dir, f);
          if (is.directory(sdir)) fn(sdir);
        });
      }
    });
  }
}

function Watcher() {
  events.EventEmitter.call(this);
  this.watchers = {};
}

util.inherits(Watcher, events.EventEmitter);

Watcher.prototype.expose = function() {
  var self = this;
  var methods = [
    'on', 'emit', 'close', 'isClosed', 'listeners', 'once',
    'setMaxListeners', 'getMaxListeners'
  ];
  return methods.reduce(function(expose, name) {
    expose[name] = function() {
      return self[name].apply(self, arguments);
    }
    return expose;
  }, {});
}

Watcher.prototype.isClosed = function() {
  return !Object.keys(this.watchers).length
}

Watcher.prototype.close = function(fullPath) {
  var self = this;
  if (fullPath) {
    var watcher = this.watchers[fullPath];
    if (watcher && watcher.close) {
      watcher.close();
      delete self.watchers[fullPath];
    }
    getSubDirectories(fullPath, function(fpath) {
      self.close(fpath);
    });
  } else {
    var self = this;
    Object.keys(self.watchers).forEach(function(fpath) {
      var watcher = self.watchers[fpath];
      if (watcher && watcher.close) {
        watcher.close();
      }
    });
    this.watchers = {};
  }
};

Watcher.prototype.add = function(watcher, info) {
  var self = this;
  info = info || {};
  var fullPath = path.resolve(info.fpath);
  this.watchers[fullPath] = watcher;

  var callback = function(evt, name) {
    if (info.options.recursive) {
      hasNativeRecursive(function(has) {
        if (!has) {
          var fullPath = path.resolve(name);
          // remove watcher on removal
          if (evt == EVENT_REMOVE) {
            self.close(fullPath);
          }
          // watch new created directory
          else if (is.directory(name) && !self.watchers[fullPath]) {
            var filterGuard = guard(info.options.filter);
            filterGuard(name, function() {
              self.watchDirectory(name, info.options);
            });
          }
        }
      });
    }

    // watch single file
    if (info.compareName) {
      if (info.compareName(name)) {
        self.emit('change', evt, name);
      }
    }
    // watch directory
    else {
      var filterGuard = guard(info.options.filter);
      filterGuard(name, function() {
        if (self.flag) self.flag = '';
        else self.emit('change', evt, name);
      });
    }
  };

  callback.info = info;

  watcher.on('error', function(err) {
    if (os.platform() == 'win32' && err.code == 'EPERM') {
      watcher.emit('change', EVENT_REMOVE, info.fpath && '');
      self.flag = 'windows-error';
      self.close(fullPath);
    } else {
     self.emit('error', err);
    }
  });

  watcher.on('change', debounce(callback));
}

Watcher.prototype.watchFile = function(file, options, fn) {
  var parent = path.join(file, '../');
  var opts = assign({}, options, {
    recursive: false,
    filter: null
  });

  var watcher = fs.watch(parent, opts);
  this.add(watcher, {
    type: 'file',
    fpath: parent,
    options: opts,
    compareName: function(n) {
      return is.sameFile(n, file);
    }
  });

  if (is.func(fn)) {
    this.on('change', fn);
  }
}

Watcher.prototype.watchDirectory = function(dir, options, fn) {
  var self = this;
  var watcher = fs.watch(dir, options);

  self.add(watcher, {
    type: 'dir',
    fpath: dir,
    options: options
  });

  if (is.func(fn)) {
    self.on('change', fn);
  }

  if (options.recursive) {
    hasNativeRecursive(function(has) {
      if (has) return false;
      getSubDirectories(dir, function(d) {
        var filterGuard = guard(options.filter);
        filterGuard(d, function() {
          self.watchDirectory(d, options);
        });
      });
    });
  }
}

function composeWatcher(watchers) {
  var watcher = new Watcher();
  watchers.forEach(function(w) {
    w.on('change', function(evt, name) {
      watcher.emit('change', evt, name);
    });
  });
  watcher.close = function() {
    watchers.forEach(function(w) {
      w.close();
    });
  }
  return watcher;
}

function watch(fpath, options, fn) {
  var watcher = new Watcher();

  if (is.array(fpath)) {
    return composeWatcher(unique(fpath).map(function(f) {
      return watch(f, options, fn);
    }));
  };

  if (!is.exists(fpath)) {
    watcher.emit('error',
      new Error(fpath + ' does not exist.')
    );
  }

  if (is.func(options)) {
    fn = options;
    options = {};
  }

  if (arguments.length < 2) {
    options = {};
  }

  if (is.file(fpath)) {
    watcher.watchFile(fpath, options, fn);
  }

  else if (is.directory(fpath)) {
    watcher.watchDirectory(fpath, options, fn);
  }

  return watcher.expose();
}

module.exports = watch;
<commit_msg>proxy error events in composed watcher<commit_after>var fs   = require('fs');
var path = require('path');
var os = require('os');
var util = require('util');
var events = require('events');

var hasNativeRecursive = require('./has-native-recursive');
var is = require('./is');

var EVENT_UPDATE = 'update';
var EVENT_REMOVE = 'remove';

function makeArray(arr, offset) {
  return is.array(arr)
    ? arr : [].slice.call(arr, offset || 0);
}

function hasDup(arr) {
  return makeArray(arr).some(function(v, i, self) {
    return self.indexOf(v) !== i;
  });
}

function unique(arr) {
  return makeArray(arr).filter(function(v, i, self) {
    return self.indexOf(v) === i;
  });
}

function assign(obj/*, props */) {
  if (Object.assign) {
    return Object.assign.apply(Object, arguments);
  }
  return makeArray(arguments, 1)
    .reduce(function(mix, prop) {
      for (var name in prop) {
        if (prop.hasOwnProperty(name)) {
          mix[name] = prop[name];
        }
      }
      return mix;
    }, obj);
}

function guard(fn) {
  return function(arg, action) {
    if (is.func(fn)) {
      if (fn(arg)) action();
    } else {
      action();
    }
  }
}

function composeMessage(names) {
  return makeArray(names).map(function(n) {
    if (!is.exists(n)) return [EVENT_REMOVE, n];
    else return [EVENT_UPDATE, n];
  });
}

function getMessages(cache) {
  var dup = hasDup(cache.map(function(c) {
    return c.replace(/^[~#]+|[~#]+$/, '');
  }));

  // saving file from an editor maybe?
  if (dup) {
    var filtered = cache.filter(function(m) {
      return is.exists(m)
    });
    return composeMessage(unique(filtered));
  }
  else {
    return composeMessage(cache);
  }
}

function debounce(fn, delay) {
  var timer, cache = [];
  var info = fn.info;
  function handle() {
    getMessages(cache).forEach(function(msg) {
      fn.apply(null, msg);
    });
    timer = null;
    cache = [];
  }
  return function(evt, name) {
    if (is.nil(name)) {
      name = '';
    }
    cache.push(
      path.join(info.fpath, name)
    );
    if (!timer) {
      timer = setTimeout(handle, delay || 200);
    }
  }
}

function getSubDirectories(dir, fn) {
  if (is.directory(dir)) {
    fs.readdir(dir, function(err, all) {
      if (err) {
        // don't throw permission errors.
        if (!/^(EPERM|EACCES)$/.test(err.code)) throw err;
        else console.warn('Warning: Cannot access %s.', dir);
      }
      else if (is.array(all)) {
        all.forEach(function(f) {
          var sdir = path.join(dir, f);
          if (is.directory(sdir)) fn(sdir);
        });
      }
    });
  }
}

function Watcher() {
  events.EventEmitter.call(this);
  this.watchers = {};
}

util.inherits(Watcher, events.EventEmitter);

Watcher.prototype.expose = function() {
  var self = this;
  var methods = [
    'on', 'emit', 'close', 'isClosed', 'listeners', 'once',
    'setMaxListeners', 'getMaxListeners'
  ];
  return methods.reduce(function(expose, name) {
    expose[name] = function() {
      return self[name].apply(self, arguments);
    }
    return expose;
  }, {});
}

Watcher.prototype.isClosed = function() {
  return !Object.keys(this.watchers).length
}

Watcher.prototype.close = function(fullPath) {
  var self = this;
  if (fullPath) {
    var watcher = this.watchers[fullPath];
    if (watcher && watcher.close) {
      watcher.close();
      delete self.watchers[fullPath];
    }
    getSubDirectories(fullPath, function(fpath) {
      self.close(fpath);
    });
  } else {
    var self = this;
    Object.keys(self.watchers).forEach(function(fpath) {
      var watcher = self.watchers[fpath];
      if (watcher && watcher.close) {
        watcher.close();
      }
    });
    this.watchers = {};
  }
};

Watcher.prototype.add = function(watcher, info) {
  var self = this;
  info = info || {};
  var fullPath = path.resolve(info.fpath);
  this.watchers[fullPath] = watcher;

  var callback = function(evt, name) {
    if (info.options.recursive) {
      hasNativeRecursive(function(has) {
        if (!has) {
          var fullPath = path.resolve(name);
          // remove watcher on removal
          if (evt == EVENT_REMOVE) {
            self.close(fullPath);
          }
          // watch new created directory
          else if (is.directory(name) && !self.watchers[fullPath]) {
            var filterGuard = guard(info.options.filter);
            filterGuard(name, function() {
              self.watchDirectory(name, info.options);
            });
          }
        }
      });
    }

    // watch single file
    if (info.compareName) {
      if (info.compareName(name)) {
        self.emit('change', evt, name);
      }
    }
    // watch directory
    else {
      var filterGuard = guard(info.options.filter);
      filterGuard(name, function() {
        if (self.flag) self.flag = '';
        else self.emit('change', evt, name);
      });
    }
  };

  callback.info = info;

  watcher.on('error', function(err) {
    if (os.platform() == 'win32' && err.code == 'EPERM') {
      watcher.emit('change', EVENT_REMOVE, info.fpath && '');
      self.flag = 'windows-error';
      self.close(fullPath);
    } else {
     self.emit('error', err);
    }
  });

  watcher.on('change', debounce(callback));
}

Watcher.prototype.watchFile = function(file, options, fn) {
  var parent = path.join(file, '../');
  var opts = assign({}, options, {
    recursive: false,
    filter: null
  });

  var watcher = fs.watch(parent, opts);
  this.add(watcher, {
    type: 'file',
    fpath: parent,
    options: opts,
    compareName: function(n) {
      return is.sameFile(n, file);
    }
  });

  if (is.func(fn)) {
    this.on('change', fn);
  }
}

Watcher.prototype.watchDirectory = function(dir, options, fn) {
  var self = this;
  var watcher = fs.watch(dir, options);

  self.add(watcher, {
    type: 'dir',
    fpath: dir,
    options: options
  });

  if (is.func(fn)) {
    self.on('change', fn);
  }

  if (options.recursive) {
    hasNativeRecursive(function(has) {
      if (has) return false;
      getSubDirectories(dir, function(d) {
        var filterGuard = guard(options.filter);
        filterGuard(d, function() {
          self.watchDirectory(d, options);
        });
      });
    });
  }
}

function composeWatcher(watchers) {
  var watcher = new Watcher();
  watchers.forEach(function(w) {
    w.on('change', function(evt, name) {
      watcher.emit('change', evt, name);
    });
    w.on('error', function(err) {
      watcher.emit('error', err);
    });
  });
  watcher.close = function() {
    watchers.forEach(function(w) {
      w.close();
    });
  }
  return watcher;
}

function watch(fpath, options, fn) {
  var watcher = new Watcher();

  if (is.array(fpath)) {
    return composeWatcher(unique(fpath).map(function(f) {
      return watch(f, options, fn);
    }));
  };

  if (!is.exists(fpath)) {
    watcher.emit('error',
      new Error(fpath + ' does not exist.')
    );
  }

  if (is.func(options)) {
    fn = options;
    options = {};
  }

  if (arguments.length < 2) {
    options = {};
  }

  if (is.file(fpath)) {
    watcher.watchFile(fpath, options, fn);
  }

  else if (is.directory(fpath)) {
    watcher.watchDirectory(fpath, options, fn);
  }

  return watcher.expose();
}

module.exports = watch;
<|endoftext|>