<commit_before>/*
 * Copyright 2016 Pascal Mainini
 * Licensed under MIT license, see included file LICENSE or
 * http://opensource.org/licenses/MIT
 */

/**
 * This module implements the basic decomposition (DEC) algorithm for blinding
 * of the modexp-params.
 * Functions in this module should not be called directly, instead calls should
 * be made via FamodulusClient-object.
 * @see famodulus-client/client
 * @module famodulus-client/dec
 */

'use strict';
const _I = require('BigInt');
const util = require('./util.js');
const direct = require('./direct.js');

  "use strict";

  var MAX_BITS_PER_INTEGER = 52; // bit size of mantissa of JS numbers
  var BITS_PER_INTEGER = 0; // max bit size to perform integer multiplications
  for (BITS_PER_INTEGER=0; (1<<(BITS_PER_INTEGER+1)) > (1<<BITS_PER_INTEGER); BITS_PER_INTEGER++);
  BITS_PER_INTEGER>>=1;

  var LEADING_ZEROS = [""]; // precomputation of strings with leading zeros
  for (var i=1;i<MAX_BITS_PER_INTEGER;i++) {
    LEADING_ZEROS.push(LEADING_ZEROS[LEADING_ZEROS.length-1]+"0");
  }

  function bigInt2Hex(x) {
    return bin2Hex(bigInt2Bin(x));
  }

  function hex2BigInt(h) {
    return bin2BigInt(hex2Bin(h));
  }

  function bigInt2Bin(x) {
    var s = "";
    for (var i=x.length-2;i>=0;i--) {
      var b = x[i].toString(2);
      if (i<x.length-2) {
        s += LEADING_ZEROS[BITS_PER_INTEGER-b.length];
      }
      s += b;
    }
    return s;
  }

  function bin2Hex(b) {
    var h = "";
    var r = b.length % MAX_BITS_PER_INTEGER;
    if (r>0) r = r - MAX_BITS_PER_INTEGER;
    for (var i=r;i<b.length;i=i+MAX_BITS_PER_INTEGER) {
      var g = parseInt(b.substring(i,i+MAX_BITS_PER_INTEGER),2).toString(16);
      if (i>r) {
        h += LEADING_ZEROS[(MAX_BITS_PER_INTEGER/4)-g.length];
      }
      h += g;
    }
    return h;
  }

  function bin2BigInt(b) {
    var x = [];
    for(var i=b.length; i>0; i=i-BITS_PER_INTEGER) {
      x.push(parseInt(b.substring(i-BITS_PER_INTEGER,i),2));
    }
    x.push(0);
    return x;
  }

  function hex2Bin(h) {
    var b = "";
    var r = h.length % (MAX_BITS_PER_INTEGER/4);
    if (r>0) r = r - (MAX_BITS_PER_INTEGER/4);
    for (var i=r;i<h.length;i=i+MAX_BITS_PER_INTEGER/4) {
      var g = parseInt(h.substring(i,i+(MAX_BITS_PER_INTEGER/4)),16).toString(2);
      if (i>r) {
        b += LEADING_ZEROS[MAX_BITS_PER_INTEGER-g.length];
      }
      b += g;
    }
    return b;
  }

/**
 * Calculate a single or multiple modexp(s) with blinded exponent on two servers. If options.checked
 * is true, for each modexp, an identical modexp will be sent additionally to both servers and an error
 * will be raised if they are not identical. The order of the modexps and of the calls to the servers is randomized.
 *
 * Required options are 'servers' containing two full URIs to a famodulus modexp-API and 'brief',
 * indicating if a brief response is desired.
 *
 * @param {Object[]} modexps    An Array with modexps in the form {b:'base', e:'exponent', m:'modulus'}
 *                              (values are hexadecimal and may be undefined)
 * @param {Object} defaults     Default params given as {b:'base', e:'exponent', m:'modulus'}, applied if
 *                              omitted in any modexp (values are hexadecimal and may be undefined)
 * @param {Object} options      Options, requires 'servers' and 'brief'
 * @returns {Promise}           A promise to the remote modexp calculation.
 */
function decExponent (modexps, defaults, options) {
  // fill in default values for each modexp, where missing
  let data;
  if (defaults.b || defaults.e || defaults.m) {
    data = modexps.map(modexp =>
      ({
        b: modexp.b || defaults.b,
        e: modexp.e || defaults.e,
        m: modexp.m || defaults.m
      }));
  } else data = modexps;

  // ======= 1.) split exponents and prepare a list with modexps for each server =======

  let moduli = [];            // moduli of modexps may be different, we need to remember them for final multiplication
  let modexpsB = [];          // modexps with exponent b
  let modexpsC = [];          // modexps with exponent c
  for (let modexp of data) {
    let a = hex2BigInt(modexp.e);                       // a <- exponent
    let p = hex2BigInt(modexp.m);                       // p <- modulus
    let q = _I.sub(p, util.ONE);                        // q <- p-1 (p is prime by definition!)
    let b = util.random(q);                             // b <- random in Z_q
    let c = _I.mod(_I.sub(_I.add(a, q), b), q);         // c <- (a+q)-b mod q, we add q first to ensure c > 0
    //                                                                (BigInt returns 2s-complement otherwise)

    // sanity check, a has to be in Z_q
    if (_I.greater(a, q) || _I.equals(a, q)) return Promise.reject(new RangeError('Exponent not in Z_q!'));

    // add modexp to lists
    moduli.push(p);
    modexpsB.push({b: modexp.b, e: bigInt2Hex(b), m: modexp.m});
    modexpsC.push({b: modexp.b, e: bigInt2Hex(c), m: modexp.m});

    // if checked calculations are desired, add a modexp with randomized
    // exponent but same base and modulus for each server
    if (options.checked) {
      moduli.push(p);
      let rStr = bigInt2Hex(util.random(q));
      modexpsB.push({b: modexp.b, e: rStr, m: modexp.m});
      modexpsC.push({b: modexp.b, e: rStr, m: modexp.m});
    }
  }

  // ======= 2.) Randomize order of modexps and send them in random order to servers =======

  let listShuffle = util.randomList(moduli.length);
  let servers = util.shuffleList(options.servers);

  return Promise.resolve(Promise.all([
    direct.direct(util.shuffleList(modexpsB, listShuffle), {}, {server: servers[0]}),
    direct.direct(util.shuffleList(modexpsC, listShuffle), {}, {server: servers[1]})
  ]).then(data => {
    if (data[0].length !== data[1].length) throw new Error('Inequal amount of results obtained!');

    // ======= 3.) having obtained all results, multiply them ( u^b*u^c mod p ) and return =======

    let res1 = util.unshuffleList(data[0], listShuffle);
    let res2 = util.unshuffleList(data[1], listShuffle);

    // check if the test-values for checkability correspond
    if (options.checked) {
      let dataLength = res1.length / 2;
      for (let i = 0; i < dataLength; i++) {
        if (res1.splice(i + 1, 1)[0].r !== res2.splice(i + 1, 1)[0].r) throw new Error('Checking failed!');
      }
    }

    // multiply results and return them
    let results = new Array(res1.length);
    for (let i = 0; i < res1.length; i++) {
      if (options.brief) results[i] = {};
      else results[i] = {b: modexps[i].b, e: modexps[i].e, m: modexps[i].m};
      results[i].r = bigInt2Hex(_I.multMod(hex2BigInt(res1[i].r), hex2BigInt(res2[i].r), moduli[i]));
    }

    return results;
  }));
}

module.exports = {decExponent: decExponent};
<commit_msg>re-added linter comment<commit_after>/*
 * Copyright 2016 Pascal Mainini
 * Licensed under MIT license, see included file LICENSE or
 * http://opensource.org/licenses/MIT
 */

/**
 * This module implements the basic decomposition (DEC) algorithm for blinding
 * of the modexp-params.
 * Functions in this module should not be called directly, instead calls should
 * be made via FamodulusClient-object.
 * @see famodulus-client/client
 * @module famodulus-client/dec
 */

/* global Promise */
'use strict';

const _I = require('BigInt');
const util = require('./util.js');
const direct = require('./direct.js');

  var MAX_BITS_PER_INTEGER = 52; // bit size of mantissa of JS numbers
  var BITS_PER_INTEGER = 0; // max bit size to perform integer multiplications
  for (BITS_PER_INTEGER=0; (1<<(BITS_PER_INTEGER+1)) > (1<<BITS_PER_INTEGER); BITS_PER_INTEGER++);
  BITS_PER_INTEGER>>=1;

  var LEADING_ZEROS = [""]; // precomputation of strings with leading zeros
  for (var i=1;i<MAX_BITS_PER_INTEGER;i++) {
    LEADING_ZEROS.push(LEADING_ZEROS[LEADING_ZEROS.length-1]+"0");
  }

  function bigInt2Hex(x) {
    return bin2Hex(bigInt2Bin(x));
  }

  function hex2BigInt(h) {
    return bin2BigInt(hex2Bin(h));
  }

  function bigInt2Bin(x) {
    var s = "";
    for (var i=x.length-2;i>=0;i--) {
      var b = x[i].toString(2);
      if (i<x.length-2) {
        s += LEADING_ZEROS[BITS_PER_INTEGER-b.length];
      }
      s += b;
    }
    return s;
  }

  function bin2Hex(b) {
    var h = "";
    var r = b.length % MAX_BITS_PER_INTEGER;
    if (r>0) r = r - MAX_BITS_PER_INTEGER;
    for (var i=r;i<b.length;i=i+MAX_BITS_PER_INTEGER) {
      var g = parseInt(b.substring(i,i+MAX_BITS_PER_INTEGER),2).toString(16);
      if (i>r) {
        h += LEADING_ZEROS[(MAX_BITS_PER_INTEGER/4)-g.length];
      }
      h += g;
    }
    return h;
  }

  function bin2BigInt(b) {
    var x = [];
    for(var i=b.length; i>0; i=i-BITS_PER_INTEGER) {
      x.push(parseInt(b.substring(i-BITS_PER_INTEGER,i),2));
    }
    x.push(0);
    return x;
  }

  function hex2Bin(h) {
    var b = "";
    var r = h.length % (MAX_BITS_PER_INTEGER/4);
    if (r>0) r = r - (MAX_BITS_PER_INTEGER/4);
    for (var i=r;i<h.length;i=i+MAX_BITS_PER_INTEGER/4) {
      var g = parseInt(h.substring(i,i+(MAX_BITS_PER_INTEGER/4)),16).toString(2);
      if (i>r) {
        b += LEADING_ZEROS[MAX_BITS_PER_INTEGER-g.length];
      }
      b += g;
    }
    return b;
  }

/**
 * Calculate a single or multiple modexp(s) with blinded exponent on two servers. If options.checked
 * is true, for each modexp, an identical modexp will be sent additionally to both servers and an error
 * will be raised if they are not identical. The order of the modexps and of the calls to the servers is randomized.
 *
 * Required options are 'servers' containing two full URIs to a famodulus modexp-API and 'brief',
 * indicating if a brief response is desired.
 *
 * @param {Object[]} modexps    An Array with modexps in the form {b:'base', e:'exponent', m:'modulus'}
 *                              (values are hexadecimal and may be undefined)
 * @param {Object} defaults     Default params given as {b:'base', e:'exponent', m:'modulus'}, applied if
 *                              omitted in any modexp (values are hexadecimal and may be undefined)
 * @param {Object} options      Options, requires 'servers' and 'brief'
 * @returns {Promise}           A promise to the remote modexp calculation.
 */
function decExponent (modexps, defaults, options) {
  // fill in default values for each modexp, where missing
  let data;
  if (defaults.b || defaults.e || defaults.m) {
    data = modexps.map(modexp =>
      ({
        b: modexp.b || defaults.b,
        e: modexp.e || defaults.e,
        m: modexp.m || defaults.m
      }));
  } else data = modexps;

  // ======= 1.) split exponents and prepare a list with modexps for each server =======

  let moduli = [];            // moduli of modexps may be different, we need to remember them for final multiplication
  let modexpsB = [];          // modexps with exponent b
  let modexpsC = [];          // modexps with exponent c
  for (let modexp of data) {
    let a = hex2BigInt(modexp.e);                       // a <- exponent
    let p = hex2BigInt(modexp.m);                       // p <- modulus
    let q = _I.sub(p, util.ONE);                        // q <- p-1 (p is prime by definition!)
    let b = util.random(q);                             // b <- random in Z_q
    let c = _I.mod(_I.sub(_I.add(a, q), b), q);         // c <- (a+q)-b mod q, we add q first to ensure c > 0
    //                                                                (BigInt returns 2s-complement otherwise)

    // sanity check, a has to be in Z_q
    if (_I.greater(a, q) || _I.equals(a, q)) return Promise.reject(new RangeError('Exponent not in Z_q!'));

    // add modexp to lists
    moduli.push(p);
    modexpsB.push({b: modexp.b, e: bigInt2Hex(b), m: modexp.m});
    modexpsC.push({b: modexp.b, e: bigInt2Hex(c), m: modexp.m});

    // if checked calculations are desired, add a modexp with randomized
    // exponent but same base and modulus for each server
    if (options.checked) {
      moduli.push(p);
      let rStr = bigInt2Hex(util.random(q));
      modexpsB.push({b: modexp.b, e: rStr, m: modexp.m});
      modexpsC.push({b: modexp.b, e: rStr, m: modexp.m});
    }
  }

  // ======= 2.) Randomize order of modexps and send them in random order to servers =======

  let listShuffle = util.randomList(moduli.length);
  let servers = util.shuffleList(options.servers);

  return Promise.resolve(Promise.all([
    direct.direct(util.shuffleList(modexpsB, listShuffle), {}, {server: servers[0]}),
    direct.direct(util.shuffleList(modexpsC, listShuffle), {}, {server: servers[1]})
  ]).then(data => {
    if (data[0].length !== data[1].length) throw new Error('Inequal amount of results obtained!');

    // ======= 3.) having obtained all results, multiply them ( u^b*u^c mod p ) and return =======

    let res1 = util.unshuffleList(data[0], listShuffle);
    let res2 = util.unshuffleList(data[1], listShuffle);

    // check if the test-values for checkability correspond
    if (options.checked) {
      let dataLength = res1.length / 2;
      for (let i = 0; i < dataLength; i++) {
        if (res1.splice(i + 1, 1)[0].r !== res2.splice(i + 1, 1)[0].r) throw new Error('Checking failed!');
      }
    }

    // multiply results and return them
    let results = new Array(res1.length);
    for (let i = 0; i < res1.length; i++) {
      if (options.brief) results[i] = {};
      else results[i] = {b: modexps[i].b, e: modexps[i].e, m: modexps[i].m};
      results[i].r = bigInt2Hex(_I.multMod(hex2BigInt(res1[i].r), hex2BigInt(res2[i].r), moduli[i]));
    }

    return results;
  }));
}

module.exports = {decExponent: decExponent};
<|endoftext|>


<commit_before>import { compilePathTemplate } from '@folio/stripes-connect/RESTResource/RESTResource';
import { filters2cql } from '../lib/FilterGroups';
import { removeNsKeys } from './nsQueryFunctions';

// failOnCondition can take values:
//      0: do not fail even if query and filters and empty
//      1: fail if query is empty, whatever the filter state
//      2: fail if both query and filters and empty.
//
// For compatibility, false and true may be used for 0 and 1 respectively.
//
function makeQueryFunction(findAll, queryTemplate, sortMap, filterConfig, failOnCondition, nsParams) {
  return (queryParams, pathComponents, resourceValues, logger) => {
    const resourceQuery = removeNsKeys(resourceValues.query, nsParams);
    const nsQueryParams = removeNsKeys(queryParams, nsParams);
    const { qindex, filters, query, sort } = resourceQuery || {};

    if ((query === undefined || query === '') &&
        (failOnCondition === 1 || failOnCondition === true)) {
      return null;
    }
    if ((query === undefined || query === '') &&
        (filters === undefined || filters === '') &&
        (failOnCondition === 2)) {
      return null;
    }

    // This check should remain '$QUERY' until all uses of the $QUERY syntax have been removed from stripes modules
    if (queryTemplate.includes('$QUERY')) {
      // eslint-disable-next-line max-len
      console.warn('Use of "$QUERY" in the queryTemplate is deprecated. Use the "%{query.query}" syntax instead, as found at https://github.com/folio-org/stripes-connect/blob/master/doc/api.md#text-substitution');
      queryTemplate = queryTemplate.replace(/\$QUERY/g, '?{query}'); // eslint-disable-line no-param-reassign
    }

    let cql;
    if (query && qindex) {
      const t = qindex.split('/', 2);
      if (t.length === 1) {
        cql = `${qindex}="${query}*"`;
      } else {
        cql = `${t[0]} =\${t[1]} "${query}*"`;
      }
    } else if (query) {
      cql = compilePathTemplate(queryTemplate, nsQueryParams, pathComponents, { query: resourceQuery });
      if (cql === null) {
        // Some part of the template requires something that we don't have.
        return null;
      }
    }

    const filterCql = filters2cql(filterConfig, filters);
    if (filterCql) {
      if (cql) {
        cql = `(${cql}) and ${filterCql}`;
      } else {
        cql = filterCql;
      }
    }

    if (sort) {
      const sortIndexes = sort.split(',').map((sort1) => {
        let reverse = false;
        if (sort1.startsWith('-')) {
          sort1 = sort1.substr(1); // eslint-disable-line no-param-reassign
          reverse = true;
        }
        let sortIndex = sortMap[sort1] || sort1;
        if (reverse) {
          sortIndex = sortIndex.replace(' ', '/sort.descending ') + '/sort.descending';
        }
        return sortIndex;
      });

      if (cql === undefined) cql = findAll;
      cql = `(${cql}) sortby ${sortIndexes.join(' ')}`;
    }

    logger.log('mquery', `query='${query}' filters='${filters}' sort='${sort}' -> ${cql}`);

    return cql;
  };
}

export default makeQueryFunction;
<commit_msg>Correct handling of relation modifiers<commit_after>import { compilePathTemplate } from '@folio/stripes-connect/RESTResource/RESTResource';
import { filters2cql } from '../lib/FilterGroups';
import { removeNsKeys } from './nsQueryFunctions';

// failOnCondition can take values:
//      0: do not fail even if query and filters and empty
//      1: fail if query is empty, whatever the filter state
//      2: fail if both query and filters and empty.
//
// For compatibility, false and true may be used for 0 and 1 respectively.
//
function makeQueryFunction(findAll, queryTemplate, sortMap, filterConfig, failOnCondition, nsParams) {
  return (queryParams, pathComponents, resourceValues, logger) => {
    const resourceQuery = removeNsKeys(resourceValues.query, nsParams);
    const nsQueryParams = removeNsKeys(queryParams, nsParams);
    const { qindex, filters, query, sort } = resourceQuery || {};

    if ((query === undefined || query === '') &&
        (failOnCondition === 1 || failOnCondition === true)) {
      return null;
    }
    if ((query === undefined || query === '') &&
        (filters === undefined || filters === '') &&
        (failOnCondition === 2)) {
      return null;
    }

    // This check should remain '$QUERY' until all uses of the $QUERY syntax have been removed from stripes modules
    if (queryTemplate.includes('$QUERY')) {
      // eslint-disable-next-line max-len
      console.warn('Use of "$QUERY" in the queryTemplate is deprecated. Use the "%{query.query}" syntax instead, as found at https://github.com/folio-org/stripes-connect/blob/master/doc/api.md#text-substitution');
      queryTemplate = queryTemplate.replace(/\$QUERY/g, '?{query}'); // eslint-disable-line no-param-reassign
    }

    let cql;
    if (query && qindex) {
      const t = qindex.split('/', 2);
      if (t.length === 1) {
        cql = `${qindex}="${query}*"`;
      } else {
        cql = `${t[0]} =\/${t[1]} "${query}*"`;
      }
    } else if (query) {
      cql = compilePathTemplate(queryTemplate, nsQueryParams, pathComponents, { query: resourceQuery });
      if (cql === null) {
        // Some part of the template requires something that we don't have.
        return null;
      }
    }

    const filterCql = filters2cql(filterConfig, filters);
    if (filterCql) {
      if (cql) {
        cql = `(${cql}) and ${filterCql}`;
      } else {
        cql = filterCql;
      }
    }

    if (sort) {
      const sortIndexes = sort.split(',').map((sort1) => {
        let reverse = false;
        if (sort1.startsWith('-')) {
          sort1 = sort1.substr(1); // eslint-disable-line no-param-reassign
          reverse = true;
        }
        let sortIndex = sortMap[sort1] || sort1;
        if (reverse) {
          sortIndex = sortIndex.replace(' ', '/sort.descending ') + '/sort.descending';
        }
        return sortIndex;
      });

      if (cql === undefined) cql = findAll;
      cql = `(${cql}) sortby ${sortIndexes.join(' ')}`;
    }

    logger.log('mquery', `query='${query}' filters='${filters}' sort='${sort}' -> ${cql}`);

    return cql;
  };
}

export default makeQueryFunction;
<|endoftext|>


<commit_before>var fs = require('fs');
var Path = require('path');
var debug = require('debug')('express-dom');
var Pool = require('./pool');
var URL = require('url');
var BufferList = require('bl');
var pify = require('pify');
var clone = require('clone');

var pool;
var dom = module.exports = function() {
	if (!pool) {
		pool = new Pool(dom.pool, dom); // do not use instance options
		dom.acquire = pool.acquire.bind(pool);
		dom.release = pool.release.bind(pool);
	}
	var h = new Handler(Array.from(arguments));
	return h.chainable;
};

dom.plugins = require('./plugins');
dom.helpers = require('./helpers');

dom.pool = {
	max: 8,
	destroyTimeout: 600000,
	idleTimeout: 180000
};

dom.settings = {
	helpers: [
		dom.helpers.view
	],
	prepare: {
		plugins: [
			dom.plugins.hide,
			dom.plugins.noreq,
			dom.plugins.html
		]
	},
	load: {
		plugins: [
			dom.plugins.hide,
			dom.plugins.nomedia,
			dom.plugins.prerender,
			dom.plugins.redirect,
			dom.plugins.referrer,
			dom.plugins.html
		]
	},
	console: process.env.NODE_ENV != "production",
	stall: 15000,
	allow: "same-origin",
	navigation: false,
	develop: !!process.env.DEVELOP
};

function Handler(args) {
	var h = this;
	var view = args[0];
	if (typeof view != "function") args.shift();
	else view = null;
	h.view = view;
	debug("new handler", view);
	h.settings = emptySettings(dom.settings);
	h.settings.helpers = args.concat(dom.settings.helpers);
	h.settings.prepare = emptySettings(dom.settings, true);
	h.settings.load = emptySettings(dom.settings, true);

	h.chainable = h.middleware.bind(h);
	h.chainable.prepare = function(opts) {
		initOpts(h.settings.prepare, opts, Array.from(arguments));
		return h.chainable;
	};
	h.chainable.load = function(opts) {
		if (dom.settings.develop) {
			console.warn("develop mode is ON");
			var args = Array.from(arguments);
			if (typeof args[0] != "function") args.shift();
			console.info("Rewiring", args.length, "appended load plugins");
			h.chainable.prepare.apply(this, args);
		} else {
			initOpts(h.settings.load, opts, Array.from(arguments));
		}
		return h.chainable;
	};
}

function initOpts(settings, opts, args) {
	if (typeof opts == "function") opts = null;
	else args.shift();
	Object.assign(settings, opts);
	var plugins = settings.plugins;
	if (typeof plugins == "function") plugins = [plugins];
	settings.plugins = args.concat(plugins);
}

Handler.prototype.middleware = function(req, res, next) {
	var h = this;
	// per request settings
	var loc = URL.parse(
		(req.get('X-Forwarded-Proto') || req.protocol) + '://' + req.headers.host
	);
	var settings = clone(h.settings);
	settings.location = {
		protocol: loc.protocol,
		hostname: loc.hostname,
		port: loc.port,
		pathname: req.path,
		query: req.query
	};
	settings.view = h.view;

	var p = Promise.resolve();
	// ensure helpers can call prepare/load that won't affect h.settings
	var rh = {
		settings: settings,
		load: h.chainable.load.bind(rh),
		prepare: h.chainable.prepare.bind(rh)
	};
	settings.helpers.forEach(function(helper) {
		p = p.then(function() {
			return helper(rh, settings, req, res);
		}).then(function() {
			if (res.statusCode != 200) return Promise.reject(res.statusCode);
		});
	});
	p.then(function() {
		debug('view loaded for', settings.location.pathname);
		if (settings.prepare.plugins.length == 0) return;
		return h.runMethod('prepare', settings, req, res);
	}).then(function() {
		if (settings.load.plugins.length == 0) return;
		return h.runMethod('load', settings, req, res);
	}).then(function() {
		var data = settings.input;
		// hopefully free faster that memory
		delete settings.input;
		if (!res.get('Content-Type')) {
			res.set('Content-Type', "text/html");
		}
		var status = res.statusCode;
		if (data === false) {
			debug("data handled by plugin");
			return;
		}
		if (!data) {
			if (!settings.prepare && !settings.load) {
				return res.sendStatus(501);
			} else {
				return res.sendStatus(404);
			}
		}
		var isPath = true, isStream = false;
		if (Buffer.isBuffer(data)) {
			isPath = false;
		} else if (typeof data.pipe == "function") {
			isPath = false;
			isStream = true;
		} else if (typeof data != "string") {
			data = data.toString();
		}
		if (isPath && data.startsWith('<')) {
			isPath = false;
		}
		if (isPath) {
			res.sendFile(data);
		} else {
			if (status) res.status(status);
			if (isStream) {
				data.pipe(res);
			} else {
				res.send(data);
			}
		}
	}).catch(next);
};

Handler.prototype.runMethod = function(method, settings, request, response) {
	var h = this;
	return pify(dom.acquire)().then(function(page) {
		var mSettings = settings[method];
		var plugins = mSettings.plugins;
		var p = Promise.resolve();
		plugins.forEach(function(plugin) {
			p = p.then(function() {
				return plugin(page, mSettings, request, response);
			}).then(function() {
				if (response.statusCode != 200) return Promise.reject(response.statusCode);
			});
		});
		return p.then(function() {
			var ip = pify(function(cb) {
				// it would be nice if page.when was returning a promise,
				// but it returns page for listener chainability
				page.when('idle', function(wcb) {
					debug("release idle page", method, page.uri);
					page.pingTime = Date.now();
					wcb();
					dom.release(page, function(err) {
						if (err) console.error("dom.release", err);
					}); // no need to wait for the release
					cb();
				});
			})();
			settings.location = mSettings.location;
			settings.input = mSettings.input;
			var url = URL.format(settings.location);
			debug(method, url);
			page[method](url, Objet.assign({content: settings.input}, mSettings);
			return ip;
		}).then(function() {
			if (mSettings.output) settings.input = mSettings.output;
		});
	});
};

function emptySettings(defaults, withArrays) {
	// use defaults but not .load, .prepare sub global settings
	var obj = Object.assign({}, defaults);
	delete obj.load;
	delete obj.prepare;
	obj = clone(obj);
	if (withArrays) {
		if (!obj.filters) obj.filters = [];
		if (!obj.plugins) obj.plugins = [];
		if (!obj.scripts) obj.scripts = [];
	}
	return obj;
}
<commit_msg>Per-request settings fixes<commit_after>var fs = require('fs');
var Path = require('path');
var debug = require('debug')('express-dom');
var Pool = require('./pool');
var URL = require('url');
var BufferList = require('bl');
var pify = require('pify');
var clone = require('clone');

var pool;
var dom = module.exports = function() {
	if (!pool) {
		pool = new Pool(dom.pool, dom); // do not use instance options
		dom.acquire = pool.acquire.bind(pool);
		dom.release = pool.release.bind(pool);
	}
	var h = new Handler(Array.from(arguments));
	return h.chainable;
};

dom.plugins = require('./plugins');
dom.helpers = require('./helpers');

dom.pool = {
	max: 8,
	destroyTimeout: 600000,
	idleTimeout: 180000
};

dom.settings = {
	helpers: [
		dom.helpers.view
	],
	prepare: {
		plugins: [
			dom.plugins.hide,
			dom.plugins.noreq,
			dom.plugins.html
		]
	},
	load: {
		plugins: [
			dom.plugins.hide,
			dom.plugins.nomedia,
			dom.plugins.prerender,
			dom.plugins.redirect,
			dom.plugins.referrer,
			dom.plugins.html
		]
	},
	console: process.env.NODE_ENV != "production",
	stall: 15000,
	allow: "same-origin",
	navigation: false,
	develop: !!process.env.DEVELOP
};

function Handler(args) {
	var h = this;
	var view = args[0];
	if (typeof view != "function") args.shift();
	else view = null;
	h.view = view;
	debug("new handler", view);
	h.settings = emptySettings(dom.settings);
	h.settings.helpers = args.concat(dom.settings.helpers);
	h.settings.prepare = emptySettings(dom.settings, true);
	h.settings.load = emptySettings(dom.settings, true);

	h.chainable = h.middleware.bind(h);
	h.chainable.prepare = h.prepare.bind(h);
	h.chainable.load = h.load.bind(h);
}

Handler.prototype.prepare = function(opts) {
	initOpts(this.settings.prepare, dom.settings.prepare, opts, Array.from(arguments));
	return this.chainable;
};

Handler.prototype.load = function(opts) {
	if (dom.settings.develop) {
		console.warn("develop mode is ON");
		var args = Array.from(arguments);
		if (typeof args[0] != "function") args.shift();
		console.info("Rewiring", args.length, "appended load plugins");
		this.chainable.prepare.apply(this, args);
	} else {
		initOpts(this.settings.load, dom.settings.load, opts, Array.from(arguments));
	}
	return this.chainable;
};

function initOpts(settings, defaults, opts, args) {
	if (typeof opts == "function") opts = null;
	else args.shift();
	Object.assign(settings, defaults, opts);
	var plugins = settings.plugins;
	if (typeof plugins == "function") plugins = [plugins];
	settings.plugins = args.concat(plugins);
}

Handler.prototype.middleware = function(req, res, next) {
	var h = this;
	// per request settings
	var loc = URL.parse(
		(req.get('X-Forwarded-Proto') || req.protocol) + '://' + req.headers.host
	);
	var settings = clone(h.settings);
	settings.location = {
		protocol: loc.protocol,
		hostname: loc.hostname,
		port: loc.port,
		pathname: req.path,
		query: req.query
	};
	settings.view = h.view;

	var p = Promise.resolve();
	// ensure helpers can call prepare/load that won't affect h.settings
	var rh = {
		settings: settings
	};
	rh.load = h.load.bind(rh);
	rh.prepare = h.prepare.bind(rh);

	settings.helpers.forEach(function(helper) {
		p = p.then(function() {
			return helper(rh, settings, req, res);
		}).then(function() {
			if (res.statusCode != 200) return Promise.reject(res.statusCode);
		});
	});
	p.then(function() {
		debug('view loaded for', settings.location.pathname, "with", settings.prepare.plugins.length, "prepare plugins, and", settings.load.plugins.length, "load plugins");

		if (settings.prepare.plugins.length == 0) return;
		return h.runMethod('prepare', settings, req, res);
	}).then(function() {
		if (settings.load.plugins.length == 0) return;
		return h.runMethod('load', settings, req, res);
	}).then(function() {
		var data = settings.input;
		// hopefully free faster that memory
		delete settings.input;
		if (!res.get('Content-Type')) {
			res.set('Content-Type', "text/html");
		}
		var status = res.statusCode;
		if (data === false) {
			debug("data handled by plugin");
			return;
		}
		if (!data) {
			if (!settings.prepare && !settings.load) {
				return res.sendStatus(501);
			} else {
				return res.sendStatus(404);
			}
		}
		var isPath = true, isStream = false;
		if (Buffer.isBuffer(data)) {
			isPath = false;
		} else if (typeof data.pipe == "function") {
			isPath = false;
			isStream = true;
		} else if (typeof data != "string") {
			data = data.toString();
		}
		if (isPath && data.startsWith('<')) {
			isPath = false;
		}
		if (isPath) {
			res.sendFile(data);
		} else {
			if (status) res.status(status);
			if (isStream) {
				data.pipe(res);
			} else {
				res.send(data);
			}
		}
	}).catch(next);
};

Handler.prototype.runMethod = function(method, settings, request, response) {
	var h = this;
	return pify(dom.acquire)().then(function(page) {
		var mSettings = Object.assign({}, settings, settings[method]);
		mSettings.location = settings.location;
		var plugins = mSettings.plugins;
		delete mSettings.plugins;
		var p = Promise.resolve();
		plugins.forEach(function(plugin) {
			p = p.then(function() {
				return plugin(page, mSettings, request, response);
			}).then(function() {
				if (response.statusCode != 200) return Promise.reject(response.statusCode);
			});
		});
		return p.then(function() {
			var ip = pify(function(cb) {
				// it would be nice if page.when was returning a promise,
				// but it returns page for listener chainability
				page.when('idle', function(wcb) {
					debug("release idle page", method, page.uri);
					page.pingTime = Date.now();
					wcb();
					dom.release(page, function(err) {
						if (err) console.error("dom.release", err);
					}); // no need to wait for the release
					cb();
				});
			})();
			settings.location = mSettings.location;
			settings.input = mSettings.input;
			delete mSettings.location;
			delete mSettings.input;
			var url = URL.format(settings.location);
			debug(method, url);
			page[method](url, Object.assign({content: settings.input}, mSettings));
			return ip;
		}).then(function() {
			if (mSettings.output) {
				settings.input = mSettings.output;
			}
		});
	});
};

function emptySettings(defaults, withArrays) {
	// use defaults but not .load, .prepare sub global settings
	var obj = Object.assign({}, defaults);
	delete obj.load;
	delete obj.prepare;
	obj = clone(obj);
	if (withArrays) {
		if (!obj.filters) obj.filters = [];
		if (!obj.plugins) obj.plugins = [];
		if (!obj.scripts) obj.scripts = [];
	}
	return obj;
}
<|endoftext|>


<commit_before>;(function () {
  "use strict";

  var root = this

  var CryptoJS, BigInt, Worker, WWPath, HLP
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = DSA
    CryptoJS = require('../vendor/crypto.js')
    BigInt = require('../vendor/bigint.js')
    Worker = require('webworker-threads').Worker
    WWPath = require('path').join(__dirname, '/dsa-webworker.js')
    HLP = require('./helpers.js')
  } else {
    // copy over and expose internals
    Object.keys(root.DSA).forEach(function (k) {
      DSA[k] = root.DSA[k]
    })
    root.DSA = DSA
    CryptoJS = root.CryptoJS
    BigInt = root.BigInt
    Worker = root.Worker
    WWPath = 'dsa-webworker.js'
    HLP = DSA.HLP
  }

  var ZERO = BigInt.str2bigInt('0', 10)
    , ONE = BigInt.str2bigInt('1', 10)
    , TWO = BigInt.str2bigInt('2', 10)
    , KEY_TYPE = '\x00\x00'

  var DEBUG = false
  function timer() {
    var start = (new Date()).getTime()
    return function (s) {
      if (!DEBUG || typeof console === 'undefined') return
      var t = (new Date()).getTime()
      console.log(s + ': ' + (t - start))
      start = t
    }
  }

  function makeRandom(min, max) {
    var c = BigInt.randBigInt(BigInt.bitSize(max))
    if (!HLP.between(c, min, max)) return makeRandom(min, max)
    return c
  }

  // altered BigInt.randProbPrime()
  // n rounds of Miller Rabin (after trial division with small primes)
  var rpprb = []
  function isProbPrime(k, n) {
    var i, B = 30000, l = BigInt.bitSize(k)
    var primes = BigInt.primes

    if (primes.length === 0)
      primes = BigInt.findPrimes(B)

    if (rpprb.length != k.length)
      rpprb = BigInt.dup(k)

    // check ans for divisibility by small primes up to B
    for (i = 0; (i < primes.length) && (primes[i] <= B); i++)
      if (BigInt.modInt(k, primes[i]) === 0 && !BigInt.equalsInt(k, primes[i]))
        return 0

    // do n rounds of Miller Rabin, with random bases less than k
    for (i = 0; i < n; i++) {
      BigInt.randBigInt_(rpprb, l, 0)
      while(!BigInt.greater(k, rpprb))  // pick a random rpprb that's < k
        BigInt.randBigInt_(rpprb, l, 0)
      if (!BigInt.millerRabin(k, rpprb))
        return 0
    }

    return 1
  }

  var bit_lengths = {
      '1024': { N: 160, repeat: 40 }  // 40x should give 2^-80 confidence
    , '2048': { N: 224, repeat: 56 }
  }

  var primes = {}

  // follows go lang http://golang.org/src/pkg/crypto/dsa/dsa.go
  // fips version was removed in 0c99af0df3e7
  function generatePrimes(bit_length) {

    var t = timer()  // for debugging

    // number of MR tests to perform
    var repeat = bit_lengths[bit_length].repeat

    var N = bit_lengths[bit_length].N

    var LM1 = BigInt.twoToThe(bit_length - 1)
    var bl4 = 4 * bit_length
    var brk = false

    var q, p, rem, counter
    for (;;) {

      q = BigInt.randBigInt(N, 1)
      q[0] |= 1

      if (!isProbPrime(q, repeat)) continue
      t('q')

      for (counter = 0; counter < bl4; counter++) {
        p = BigInt.randBigInt(bit_length, 1)
        p[0] |= 1

        rem = BigInt.mod(p, q)
        rem = BigInt.sub(rem, ONE)
        p = BigInt.sub(p, rem)

        if (BigInt.greater(LM1, p)) continue
        if (!isProbPrime(p, repeat)) continue

        t('p')
        primes[bit_length] = { p: p, q: q }
        brk = true
        break
      }

      if (brk) break
    }

    var h = BigInt.dup(TWO)
    var pm1 = BigInt.sub(p, ONE)
    var e = BigInt.multMod(pm1, BigInt.inverseMod(q, p), p)

    var g
    for (;;) {
      g = BigInt.powMod(h, e, p)
      if (BigInt.equals(g, ONE)) {
        h = BigInt.add(h, ONE)
        continue
      }
      primes[bit_length].g = g
      t('g')
      return
    }

    throw new Error('Unreachable!')
  }

  function DSA(obj, opts) {
    if (!(this instanceof DSA)) return new DSA(obj, opts)

    // options
    opts = opts || {}

    // inherit
    if (obj) {
      var self = this
      ;['p', 'q', 'g', 'y', 'x'].forEach(function (prop) {
        self[prop] = obj[prop]
      })
      this.type = obj.type || KEY_TYPE
      return
    }

    // default to 1024
    var bit_length = parseInt(opts.bit_length ? opts.bit_length : 1024, 10)

    if (!bit_lengths[bit_length])
      throw new Error('Unsupported bit length.')

    // set primes
    if (!primes[bit_length])
      generatePrimes(bit_length)

    this.p = primes[bit_length].p
    this.q = primes[bit_length].q
    this.g = primes[bit_length].g

    // key type
    this.type = KEY_TYPE

    // private key
    this.x = makeRandom(ZERO, this.q)

    // public keys (p, q, g, y)
    this.y = BigInt.powMod(this.g, this.x, this.p)

    // nocache?
    if (opts.nocache) primes[bit_length] = null
  }

  DSA.prototype = {

    constructor: DSA,

    packPublic: function () {
      var str = this.type
      str += HLP.packMPI(this.p)
      str += HLP.packMPI(this.q)
      str += HLP.packMPI(this.g)
      str += HLP.packMPI(this.y)
      return str
    },

    packPrivate: function () {
      var str = this.packPublic() + HLP.packMPI(this.x)
      str = CryptoJS.enc.Latin1.parse(str)
      return str.toString(CryptoJS.enc.Base64)
    },

    // http://www.imperialviolet.org/2013/06/15/suddendeathentropy.html
    generateNonce: function (m) {
      var priv = BigInt.bigInt2bits(BigInt.trim(this.x, 0))
      var rand = BigInt.bigInt2bits(BigInt.randBigInt(256))

      var sha256 = CryptoJS.algo.SHA256.create()
      sha256.update(CryptoJS.enc.Latin1.parse(priv))
      sha256.update(m)
      sha256.update(CryptoJS.enc.Latin1.parse(rand))

      var hash = sha256.finalize()
      hash = HLP.bits2bigInt(hash.toString(CryptoJS.enc.Latin1))
      BigInt.rightShift_(hash, 256 - BigInt.bitSize(this.q))

      return HLP.between(hash, ZERO, this.q) ? hash : this.generateNonce(m)
    },

    sign: function (m) {
      m = CryptoJS.enc.Latin1.parse(m)
      var b = BigInt.str2bigInt(m.toString(CryptoJS.enc.Hex), 16)
      var k, r = ZERO, s = ZERO
      while (BigInt.isZero(s) || BigInt.isZero(r)) {
        k = this.generateNonce(m)
        r = BigInt.mod(BigInt.powMod(this.g, k, this.p), this.q)
        if (BigInt.isZero(r)) continue
        s = BigInt.inverseMod(k, this.q)
        s = BigInt.mult(s, BigInt.add(b, BigInt.mult(this.x, r)))
        s = BigInt.mod(s, this.q)
      }
      return [r, s]
    },

    fingerprint: function () {
      var pk = this.packPublic()
      if (this.type === KEY_TYPE) pk = pk.substring(2)
      pk = CryptoJS.enc.Latin1.parse(pk)
      return CryptoJS.SHA1(pk).toString(CryptoJS.enc.Hex)
    }

  }

  DSA.parsePublic = function (str, priv) {
    var fields = ['SHORT', 'MPI', 'MPI', 'MPI', 'MPI']
    if (priv) fields.push('MPI')
    str = HLP.splitype(fields, str)
    var obj = {
        type: str[0]
      , p: HLP.readMPI(str[1])
      , q: HLP.readMPI(str[2])
      , g: HLP.readMPI(str[3])
      , y: HLP.readMPI(str[4])
    }
    if (priv) obj.x = HLP.readMPI(str[5])
    return new DSA(obj)
  }

  function tokenizeStr(str) {
    var start, end

    start = str.indexOf("(")
    end = str.lastIndexOf(")")

    if (start < 0 || end < 0)
      throw new Error("Malformed S-Expression")

    str = str.substring(start + 1, end)

    var splt = str.search(/\s/)
    var obj = {
        type: str.substring(0, splt)
      , val: []
    }

    str = str.substring(splt + 1, end)
    start = str.indexOf("(")

    if (start < 0) obj.val.push(str)
    else {

      var i, len, ss, es
      while (start > -1) {
        i = start + 1
        len = str.length
        for (ss = 1, es = 0; i < len && es < ss; i++) {
          if (str[i] === "(") ss++
          if (str[i] === ")") es++
        }
        obj.val.push(tokenizeStr(str.substring(start, ++i)))
        str = str.substring(++i)
        start = str.indexOf("(")
      }

    }
    return obj
  }

  function parseLibotr(obj) {
    if (!obj.type) throw new Error("Parse error.")

    var o, val
    if (obj.type === "privkeys") {
      o = []
      obj.val.forEach(function (i) {
        o.push(parseLibotr(i))
      })
      return o
    }

    o = {}
    obj.val.forEach(function (i) {

      val = i.val[0]
      if (typeof val === "string") {

        if (val.indexOf("#") === 0) {
          val = val.substring(1, val.lastIndexOf("#"))
          val = BigInt.str2bigInt(val, 16)
        }

      } else {
        val = parseLibotr(i)
      }

      o[i.type] = val
    })

    return o
  }

  DSA.parsePrivate = function (str, libotr) {
    if (!libotr) {
      str = CryptoJS.enc.Base64.parse(str)
      str = str.toString(CryptoJS.enc.Latin1)
      return DSA.parsePublic(str, true)
    }
    // only returning the first key found
    return parseLibotr(tokenizeStr(str))[0]["private-key"].dsa
  }

  DSA.verify = function (key, m, r, s) {
    if (!HLP.between(r, ZERO, key.q) || !HLP.between(s, ZERO, key.q))
      return false

    var hm = CryptoJS.enc.Latin1.parse(m)  // CryptoJS.SHA1(m)
    hm = BigInt.str2bigInt(hm.toString(CryptoJS.enc.Hex), 16)

    var w = BigInt.inverseMod(s, key.q)
    var u1 = BigInt.multMod(hm, w, key.q)
    var u2 = BigInt.multMod(r, w, key.q)

    u1 = BigInt.powMod(key.g, u1, key.p)
    u2 = BigInt.powMod(key.y, u2, key.p)

    var v = BigInt.mod(BigInt.multMod(u1, u2, key.p), key.q)

    return BigInt.equals(v, r)
  }

  DSA.createInWebWorker = function (path, cb) {
    var worker = new Worker(path ? path : WWPath)
    worker.onmessage = function (e) {
      var data = e.data
      switch (data.type) {
        case "debug":
          if (!DEBUG || typeof console === 'undefined') return
          console.log(data.val)
          break;
        case "data":
          worker.terminate()
          cb(DSA.parsePrivate(data.val))
          break;
        default:
          throw new Error("Unrecognized type.")
      }
    }
    worker.postMessage({ seed: BigInt.getSeed(), debug: DEBUG })
  }

}).call(this)<commit_msg>Allow custom paths for DSA ww imports.<commit_after>;(function () {
  "use strict";

  var root = this

  var CryptoJS, BigInt, Worker, WWPath, HLP
  if (typeof module !== 'undefined' && module.exports) {
    module.exports = DSA
    CryptoJS = require('../vendor/crypto.js')
    BigInt = require('../vendor/bigint.js')
    Worker = require('webworker-threads').Worker
    WWPath = require('path').join(__dirname, '/dsa-webworker.js')
    HLP = require('./helpers.js')
  } else {
    // copy over and expose internals
    Object.keys(root.DSA).forEach(function (k) {
      DSA[k] = root.DSA[k]
    })
    root.DSA = DSA
    CryptoJS = root.CryptoJS
    BigInt = root.BigInt
    Worker = root.Worker
    WWPath = 'dsa-webworker.js'
    HLP = DSA.HLP
  }

  var ZERO = BigInt.str2bigInt('0', 10)
    , ONE = BigInt.str2bigInt('1', 10)
    , TWO = BigInt.str2bigInt('2', 10)
    , KEY_TYPE = '\x00\x00'

  var DEBUG = false
  function timer() {
    var start = (new Date()).getTime()
    return function (s) {
      if (!DEBUG || typeof console === 'undefined') return
      var t = (new Date()).getTime()
      console.log(s + ': ' + (t - start))
      start = t
    }
  }

  function makeRandom(min, max) {
    var c = BigInt.randBigInt(BigInt.bitSize(max))
    if (!HLP.between(c, min, max)) return makeRandom(min, max)
    return c
  }

  // altered BigInt.randProbPrime()
  // n rounds of Miller Rabin (after trial division with small primes)
  var rpprb = []
  function isProbPrime(k, n) {
    var i, B = 30000, l = BigInt.bitSize(k)
    var primes = BigInt.primes

    if (primes.length === 0)
      primes = BigInt.findPrimes(B)

    if (rpprb.length != k.length)
      rpprb = BigInt.dup(k)

    // check ans for divisibility by small primes up to B
    for (i = 0; (i < primes.length) && (primes[i] <= B); i++)
      if (BigInt.modInt(k, primes[i]) === 0 && !BigInt.equalsInt(k, primes[i]))
        return 0

    // do n rounds of Miller Rabin, with random bases less than k
    for (i = 0; i < n; i++) {
      BigInt.randBigInt_(rpprb, l, 0)
      while(!BigInt.greater(k, rpprb))  // pick a random rpprb that's < k
        BigInt.randBigInt_(rpprb, l, 0)
      if (!BigInt.millerRabin(k, rpprb))
        return 0
    }

    return 1
  }

  var bit_lengths = {
      '1024': { N: 160, repeat: 40 }  // 40x should give 2^-80 confidence
    , '2048': { N: 224, repeat: 56 }
  }

  var primes = {}

  // follows go lang http://golang.org/src/pkg/crypto/dsa/dsa.go
  // fips version was removed in 0c99af0df3e7
  function generatePrimes(bit_length) {

    var t = timer()  // for debugging

    // number of MR tests to perform
    var repeat = bit_lengths[bit_length].repeat

    var N = bit_lengths[bit_length].N

    var LM1 = BigInt.twoToThe(bit_length - 1)
    var bl4 = 4 * bit_length
    var brk = false

    var q, p, rem, counter
    for (;;) {

      q = BigInt.randBigInt(N, 1)
      q[0] |= 1

      if (!isProbPrime(q, repeat)) continue
      t('q')

      for (counter = 0; counter < bl4; counter++) {
        p = BigInt.randBigInt(bit_length, 1)
        p[0] |= 1

        rem = BigInt.mod(p, q)
        rem = BigInt.sub(rem, ONE)
        p = BigInt.sub(p, rem)

        if (BigInt.greater(LM1, p)) continue
        if (!isProbPrime(p, repeat)) continue

        t('p')
        primes[bit_length] = { p: p, q: q }
        brk = true
        break
      }

      if (brk) break
    }

    var h = BigInt.dup(TWO)
    var pm1 = BigInt.sub(p, ONE)
    var e = BigInt.multMod(pm1, BigInt.inverseMod(q, p), p)

    var g
    for (;;) {
      g = BigInt.powMod(h, e, p)
      if (BigInt.equals(g, ONE)) {
        h = BigInt.add(h, ONE)
        continue
      }
      primes[bit_length].g = g
      t('g')
      return
    }

    throw new Error('Unreachable!')
  }

  function DSA(obj, opts) {
    if (!(this instanceof DSA)) return new DSA(obj, opts)

    // options
    opts = opts || {}

    // inherit
    if (obj) {
      var self = this
      ;['p', 'q', 'g', 'y', 'x'].forEach(function (prop) {
        self[prop] = obj[prop]
      })
      this.type = obj.type || KEY_TYPE
      return
    }

    // default to 1024
    var bit_length = parseInt(opts.bit_length ? opts.bit_length : 1024, 10)

    if (!bit_lengths[bit_length])
      throw new Error('Unsupported bit length.')

    // set primes
    if (!primes[bit_length])
      generatePrimes(bit_length)

    this.p = primes[bit_length].p
    this.q = primes[bit_length].q
    this.g = primes[bit_length].g

    // key type
    this.type = KEY_TYPE

    // private key
    this.x = makeRandom(ZERO, this.q)

    // public keys (p, q, g, y)
    this.y = BigInt.powMod(this.g, this.x, this.p)

    // nocache?
    if (opts.nocache) primes[bit_length] = null
  }

  DSA.prototype = {

    constructor: DSA,

    packPublic: function () {
      var str = this.type
      str += HLP.packMPI(this.p)
      str += HLP.packMPI(this.q)
      str += HLP.packMPI(this.g)
      str += HLP.packMPI(this.y)
      return str
    },

    packPrivate: function () {
      var str = this.packPublic() + HLP.packMPI(this.x)
      str = CryptoJS.enc.Latin1.parse(str)
      return str.toString(CryptoJS.enc.Base64)
    },

    // http://www.imperialviolet.org/2013/06/15/suddendeathentropy.html
    generateNonce: function (m) {
      var priv = BigInt.bigInt2bits(BigInt.trim(this.x, 0))
      var rand = BigInt.bigInt2bits(BigInt.randBigInt(256))

      var sha256 = CryptoJS.algo.SHA256.create()
      sha256.update(CryptoJS.enc.Latin1.parse(priv))
      sha256.update(m)
      sha256.update(CryptoJS.enc.Latin1.parse(rand))

      var hash = sha256.finalize()
      hash = HLP.bits2bigInt(hash.toString(CryptoJS.enc.Latin1))
      BigInt.rightShift_(hash, 256 - BigInt.bitSize(this.q))

      return HLP.between(hash, ZERO, this.q) ? hash : this.generateNonce(m)
    },

    sign: function (m) {
      m = CryptoJS.enc.Latin1.parse(m)
      var b = BigInt.str2bigInt(m.toString(CryptoJS.enc.Hex), 16)
      var k, r = ZERO, s = ZERO
      while (BigInt.isZero(s) || BigInt.isZero(r)) {
        k = this.generateNonce(m)
        r = BigInt.mod(BigInt.powMod(this.g, k, this.p), this.q)
        if (BigInt.isZero(r)) continue
        s = BigInt.inverseMod(k, this.q)
        s = BigInt.mult(s, BigInt.add(b, BigInt.mult(this.x, r)))
        s = BigInt.mod(s, this.q)
      }
      return [r, s]
    },

    fingerprint: function () {
      var pk = this.packPublic()
      if (this.type === KEY_TYPE) pk = pk.substring(2)
      pk = CryptoJS.enc.Latin1.parse(pk)
      return CryptoJS.SHA1(pk).toString(CryptoJS.enc.Hex)
    }

  }

  DSA.parsePublic = function (str, priv) {
    var fields = ['SHORT', 'MPI', 'MPI', 'MPI', 'MPI']
    if (priv) fields.push('MPI')
    str = HLP.splitype(fields, str)
    var obj = {
        type: str[0]
      , p: HLP.readMPI(str[1])
      , q: HLP.readMPI(str[2])
      , g: HLP.readMPI(str[3])
      , y: HLP.readMPI(str[4])
    }
    if (priv) obj.x = HLP.readMPI(str[5])
    return new DSA(obj)
  }

  function tokenizeStr(str) {
    var start, end

    start = str.indexOf("(")
    end = str.lastIndexOf(")")

    if (start < 0 || end < 0)
      throw new Error("Malformed S-Expression")

    str = str.substring(start + 1, end)

    var splt = str.search(/\s/)
    var obj = {
        type: str.substring(0, splt)
      , val: []
    }

    str = str.substring(splt + 1, end)
    start = str.indexOf("(")

    if (start < 0) obj.val.push(str)
    else {

      var i, len, ss, es
      while (start > -1) {
        i = start + 1
        len = str.length
        for (ss = 1, es = 0; i < len && es < ss; i++) {
          if (str[i] === "(") ss++
          if (str[i] === ")") es++
        }
        obj.val.push(tokenizeStr(str.substring(start, ++i)))
        str = str.substring(++i)
        start = str.indexOf("(")
      }

    }
    return obj
  }

  function parseLibotr(obj) {
    if (!obj.type) throw new Error("Parse error.")

    var o, val
    if (obj.type === "privkeys") {
      o = []
      obj.val.forEach(function (i) {
        o.push(parseLibotr(i))
      })
      return o
    }

    o = {}
    obj.val.forEach(function (i) {

      val = i.val[0]
      if (typeof val === "string") {

        if (val.indexOf("#") === 0) {
          val = val.substring(1, val.lastIndexOf("#"))
          val = BigInt.str2bigInt(val, 16)
        }

      } else {
        val = parseLibotr(i)
      }

      o[i.type] = val
    })

    return o
  }

  DSA.parsePrivate = function (str, libotr) {
    if (!libotr) {
      str = CryptoJS.enc.Base64.parse(str)
      str = str.toString(CryptoJS.enc.Latin1)
      return DSA.parsePublic(str, true)
    }
    // only returning the first key found
    return parseLibotr(tokenizeStr(str))[0]["private-key"].dsa
  }

  DSA.verify = function (key, m, r, s) {
    if (!HLP.between(r, ZERO, key.q) || !HLP.between(s, ZERO, key.q))
      return false

    var hm = CryptoJS.enc.Latin1.parse(m)  // CryptoJS.SHA1(m)
    hm = BigInt.str2bigInt(hm.toString(CryptoJS.enc.Hex), 16)

    var w = BigInt.inverseMod(s, key.q)
    var u1 = BigInt.multMod(hm, w, key.q)
    var u2 = BigInt.multMod(r, w, key.q)

    u1 = BigInt.powMod(key.g, u1, key.p)
    u2 = BigInt.powMod(key.y, u2, key.p)

    var v = BigInt.mod(BigInt.multMod(u1, u2, key.p), key.q)

    return BigInt.equals(v, r)
  }

  DSA.createInWebWorker = function (options, cb) {
    var opts = {
        path: WWPath
      , seed: BigInt.getSeed
    }
    if (options && typeof options === 'object')
      Object.keys(options).forEach(function (k) {
        opts[k] = options[k]
      })
    var worker = new Worker(opts.path)
    worker.onmessage = function (e) {
      var data = e.data
      switch (data.type) {
        case "debug":
          if (!DEBUG || typeof console === 'undefined') return
          console.log(data.val)
          break;
        case "data":
          worker.terminate()
          cb(DSA.parsePrivate(data.val))
          break;
        default:
          throw new Error("Unrecognized type.")
      }
    }
    worker.postMessage({
        seed: opts.seed()
      , imports: opts.imports
      , debug: DEBUG
    })
  }

}).call(this)<|endoftext|>


<commit_before>
/*!
 * EJS
 * Copyright(c) 2012 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var utils = require('./utils')
  , path = require('path')
  , dirname = path.dirname
  , extname = path.extname
  , join = path.join
  , fs = require('fs')
  , read = fs.readFileSync;

/**
 * Filters.
 *
 * @type Object
 */

var filters = exports.filters = require('./filters');

/**
 * Intermediate js cache.
 *
 * @type Object
 */

var cache = {};

/**
 * Clear intermediate js cache.
 *
 * @api public
 */

exports.clearCache = function(){
  cache = {};
};

/**
 * Translate filtered code into function calls.
 *
 * @param {String} js
 * @return {String}
 * @api private
 */

function filtered(js) {
  return js.substr(1).split('|').reduce(function(js, filter){
    var parts = filter.split(':')
      , name = parts.shift()
      , args = parts.join(':') || '';
    if (args) args = ', ' + args;
    return 'filters.' + name + '(' + js + args + ')';
  });
};

/**
 * Re-throw the given `err` in context to the
 * `str` of ejs, `filename`, and `lineno`.
 *
 * @param {Error} err
 * @param {String} str
 * @param {String} filename
 * @param {String} lineno
 * @api private
 */

function rethrow(err, str, filename, lineno){
  var lines = str.split('\n')
    , start = Math.max(lineno - 3, 0)
    , end = Math.min(lines.length, lineno + 3);

  // Error context
  var context = lines.slice(start, end).map(function(line, i){
    var curr = i + start + 1;
    return (curr == lineno ? ' >> ' : '    ')
      + curr
      + '| '
      + line;
  }).join('\n');

  // Alter exception message
  err.path = filename;
  err.message = (filename || 'ejs') + ':'
    + lineno + '\n'
    + context + '\n\n'
    + err.message;

  throw err;
}

/**
 * Parse the given `str` of ejs, returning the function body.
 *
 * @param {String} str
 * @return {String}
 * @api public
 */

var parse = exports.parse = function(str, options){
  var options = options || {}
    , open = options.open || exports.open || '<%'
    , close = options.close || exports.close || '%>'
    , filename = options.filename
    , compileDebug = options.compileDebug !== false
    , buf = "";

  buf += 'var buf = [];';
  if (false !== options._with) buf += '\nwith (locals || {}) { (function(){ ';
  buf += '\n buf.push(\'';

  var lineno = 1;

  var consumeEOL = false;
  for (var i = 0, len = str.length; i < len; ++i) {
    var stri = str[i];
    if (str.slice(i, open.length + i) == open) {
      i += open.length

      var prefix, postfix, line = (compileDebug ? '__stack.lineno=' : '') + lineno;
      switch (str[i]) {
        case '=':
          prefix = "', escape((" + line + ', ';
          postfix = ")), '";
          ++i;
          break;
        case '-':
          prefix = "', (" + line + ', ';
          postfix = "), '";
          ++i;
          break;
        default:
          prefix = "');" + line + ';';
          postfix = "; buf.push('";
      }

      var end = str.indexOf(close, i)
        , js = str.substring(i, end)
        , start = i
        , include = null
        , n = 0;

      if ('-' == js[js.length-1]){
        js = js.substring(0, js.length - 2);
        consumeEOL = true;
      }

      if (0 == js.trim().indexOf('include')) {
        var name = js.trim().slice(7).trim();
        if (!filename) throw new Error('filename option is required for includes');
        var path = resolveInclude(name, filename);
        include = read(path, 'utf8');
        include = exports.parse(include, { filename: path, _with: false, open: open, close: close, compileDebug: compileDebug });
        buf += "' + (function(){" + include + "})() + '";
        js = '';
      }

      while (~(n = js.indexOf("\n", n))) n++, lineno++;
      if (js.substr(0, 1) == ':') js = filtered(js);
      if (js) {
        if (js.lastIndexOf('//') > js.lastIndexOf('\n')) js += '\n';
        buf += prefix;
        buf += js;
        buf += postfix;
      }
      i += end - start + close.length - 1;

    } else if (stri == "\\") {
      buf += "\\\\";
    } else if (stri == "'") {
      buf += "\\'";
    } else if (stri == "\r") {
      // ignore
    } else if (stri == "\n") {
      if (consumeEOL) {
        consumeEOL = false;
      } else {
        buf += "\\n";
        lineno++;
      }
    } else {
      buf += stri;
    }
  }

  if (false !== options._with) buf += "'); })();\n} \nreturn buf.join('');";
  else buf += "');\nreturn buf.join('');";
  return buf;
};

/**
 * Compile the given `str` of ejs into a `Function`.
 *
 * @param {String} str
 * @param {Object} options
 * @return {Function}
 * @api public
 */

var compile = exports.compile = function(str, options){
  options = options || {};
  var escape = options.escape || utils.escape;

  var input = JSON.stringify(str)
    , compileDebug = options.compileDebug !== false
    , client = options.client
    , filename = options.filename
        ? JSON.stringify(options.filename)
        : 'undefined';

  if (compileDebug) {
    // Adds the fancy stack trace meta info
    str = [
      'var __stack = { lineno: 1, input: ' + input + ', filename: ' + filename + ' };',
      rethrow.toString(),
      'try {',
      exports.parse(str, options),
      '} catch (err) {',
      '  rethrow(err, __stack.input, __stack.filename, __stack.lineno);',
      '}'
    ].join("\n");
  } else {
    str = exports.parse(str, options);
  }

  if (options.debug) console.log(str);
  if (client) str = 'escape = escape || ' + escape.toString() + ';\n' + str;

  try {
    var fn = new Function('locals, filters, escape, rethrow', str);
  } catch (err) {
    if ('SyntaxError' == err.name) {
      err.message += options.filename
        ? ' in ' + filename
        : ' while compiling ejs';
    }
    throw err;
  }

  if (client) return fn;

  return function(locals){
    return fn.call(this, locals, filters, escape, rethrow);
  }
};

/**
 * Render the given `str` of ejs.
 *
 * Options:
 *
 *   - `locals`          Local variables object
 *   - `cache`           Compiled functions are cached, requires `filename`
 *   - `filename`        Used by `cache` to key caches
 *   - `scope`           Function execution context
 *   - `debug`           Output generated function body
 *   - `open`            Open tag, defaulting to "<%"
 *   - `close`           Closing tag, defaulting to "%>"
 *
 * @param {String} str
 * @param {Object} options
 * @return {String}
 * @api public
 */

exports.render = function(str, options){
  var fn
    , options = options || {};

  if (options.cache) {
    if (options.filename) {
      fn = cache[options.filename] || (cache[options.filename] = compile(str, options));
    } else {
      throw new Error('"cache" option requires "filename".');
    }
  } else {
    fn = compile(str, options);
  }

  options.__proto__ = options.locals;
  return fn.call(options.scope, options);
};

/**
 * Render an EJS file at the given `path` and callback `fn(err, str)`.
 *
 * @param {String} path
 * @param {Object|Function} options or callback
 * @param {Function} fn
 * @api public
 */

exports.renderFile = function(path, options, fn){
  var key = path + ':string';

  if ('function' == typeof options) {
    fn = options, options = {};
  }

  options.filename = path;

  var str;
  try {
    str = options.cache
      ? cache[key] || (cache[key] = read(path, 'utf8'))
      : read(path, 'utf8');
  } catch (err) {
    fn(err);
    return;
  }
  fn(null, exports.render(str, options));
};

/**
 * Resolve include `name` relative to `filename`.
 *
 * @param {String} name
 * @param {String} filename
 * @return {String}
 * @api private
 */

function resolveInclude(name, filename) {
  var path = join(dirname(filename), name);
  var ext = extname(name);
  if (!ext) path += '.ejs';
  return path;
}

// express support

exports.__express = exports.renderFile;

/**
 * Expose to require().
 */

if (require.extensions) {
  require.extensions['.ejs'] = function (module, filename) {
    filename = filename || module.filename;
    var options = { filename: filename, client: true }
      , template = fs.readFileSync(filename).toString()
      , fn = compile(template, options);
    module._compile('module.exports = ' + fn.toString() + ';', filename);
  };
} else if (require.registerExtension) {
  require.registerExtension('.ejs', function(src) {
    return compile(src, {});
  });
}
<commit_msg>Fixes #135<commit_after>/*!
 * EJS
 * Copyright(c) 2012 TJ Holowaychuk <tj@vision-media.ca>
 * MIT Licensed
 */

/**
 * Module dependencies.
 */

var utils = require('./utils')
  , path = require('path')
  , dirname = path.dirname
  , extname = path.extname
  , join = path.join
  , fs = require('fs')
  , read = fs.readFileSync;

/**
 * Filters.
 *
 * @type Object
 */

var filters = exports.filters = require('./filters');

/**
 * Intermediate js cache.
 *
 * @type Object
 */

var cache = {};

/**
 * Clear intermediate js cache.
 *
 * @api public
 */

exports.clearCache = function(){
  cache = {};
};

/**
 * Translate filtered code into function calls.
 *
 * @param {String} js
 * @return {String}
 * @api private
 */

function filtered(js) {
  return js.substr(1).split('|').reduce(function(js, filter){
    var parts = filter.split(':')
      , name = parts.shift()
      , args = parts.join(':') || '';
    if (args) args = ', ' + args;
    return 'filters.' + name + '(' + js + args + ')';
  });
};

/**
 * Re-throw the given `err` in context to the
 * `str` of ejs, `filename`, and `lineno`.
 *
 * @param {Error} err
 * @param {String} str
 * @param {String} filename
 * @param {String} lineno
 * @api private
 */

function rethrow(err, str, filename, lineno){
  var lines = str.split('\n')
    , start = Math.max(lineno - 3, 0)
    , end = Math.min(lines.length, lineno + 3);

  // Error context
  var context = lines.slice(start, end).map(function(line, i){
    var curr = i + start + 1;
    return (curr == lineno ? ' >> ' : '    ')
      + curr
      + '| '
      + line;
  }).join('\n');

  // Alter exception message
  err.path = filename;
  err.message = (filename || 'ejs') + ':'
    + lineno + '\n'
    + context + '\n\n'
    + err.message;

  throw err;
}

/**
 * Parse the given `str` of ejs, returning the function body.
 *
 * @param {String} str
 * @return {String}
 * @api public
 */

var parse = exports.parse = function(str, options){
  var options = options || {}
    , open = options.open || exports.open || '<%'
    , close = options.close || exports.close || '%>'
    , filename = options.filename
    , compileDebug = options.compileDebug !== false
    , buf = "";

  buf += 'var buf = [];';
  if (false !== options._with) buf += '\nwith (locals || {}) { (function(){ ';
  buf += '\n buf.push(\'';

  var lineno = 1;

  var consumeEOL = false;
  for (var i = 0, len = str.length; i < len; ++i) {
    var stri = str[i];
    if (str.slice(i, open.length + i) == open) {
      i += open.length

      var prefix, postfix, line = (compileDebug ? '__stack.lineno=' : '') + lineno;
      switch (str[i]) {
        case '=':
          prefix = "', escape((" + line + ', ';
          postfix = ")), '";
          ++i;
          break;
        case '-':
          prefix = "', (" + line + ', ';
          postfix = "), '";
          ++i;
          break;
        default:
          prefix = "');" + line + ';';
          postfix = "; buf.push('";
      }

      var end = str.indexOf(close, i)
        , js = str.substring(i, end)
        , start = i
        , include = null
        , n = 0;

      if ('-' == js[js.length-1]){
        js = js.substring(0, js.length - 2);
        consumeEOL = true;
      }

      if (0 == js.trim().indexOf('include')) {
        var name = js.trim().slice(7).trim();
        if (!filename) throw new Error('filename option is required for includes');
        var path = resolveInclude(name, filename);
        include = read(path, 'utf8');
        include = exports.parse(include, { filename: path, _with: false, open: open, close: close, compileDebug: compileDebug });
        buf += "' + (function(){" + include + "})() + '";
        js = '';
      }

      while (~(n = js.indexOf("\n", n))) n++, lineno++;

      switch(js.substr(0, 1)) {
        case ':':
          js = filtered(js);
          break;
        case '%':
          js = " buf.push('<%" + js.substring(1).replace(/'/g, "\\'") + "%>');";
          break;
      }

      if (js) {
        if (js.lastIndexOf('//') > js.lastIndexOf('\n')) js += '\n';
        buf += prefix;
        buf += js;
        buf += postfix;
      }
      i += end - start + close.length - 1;

    } else if (stri == "\\") {
      buf += "\\\\";
    } else if (stri == "'") {
      buf += "\\'";
    } else if (stri == "\r") {
      // ignore
    } else if (stri == "\n") {
      if (consumeEOL) {
        consumeEOL = false;
      } else {
        buf += "\\n";
        lineno++;
      }
    } else {
      buf += stri;
    }
  }

  if (false !== options._with) buf += "'); })();\n} \nreturn buf.join('');";
  else buf += "');\nreturn buf.join('');";
  return buf;
};

/**
 * Compile the given `str` of ejs into a `Function`.
 *
 * @param {String} str
 * @param {Object} options
 * @return {Function}
 * @api public
 */

var compile = exports.compile = function(str, options){
  options = options || {};
  var escape = options.escape || utils.escape;

  var input = JSON.stringify(str)
    , compileDebug = options.compileDebug !== false
    , client = options.client
    , filename = options.filename
        ? JSON.stringify(options.filename)
        : 'undefined';

  if (compileDebug) {
    // Adds the fancy stack trace meta info
    str = [
      'var __stack = { lineno: 1, input: ' + input + ', filename: ' + filename + ' };',
      rethrow.toString(),
      'try {',
      exports.parse(str, options),
      '} catch (err) {',
      '  rethrow(err, __stack.input, __stack.filename, __stack.lineno);',
      '}'
    ].join("\n");
  } else {
    str = exports.parse(str, options);
  }

  if (options.debug) console.log(str);
  if (client) str = 'escape = escape || ' + escape.toString() + ';\n' + str;

  try {
    var fn = new Function('locals, filters, escape, rethrow', str);
  } catch (err) {
    if ('SyntaxError' == err.name) {
      err.message += options.filename
        ? ' in ' + filename
        : ' while compiling ejs';
    }
    throw err;
  }

  if (client) return fn;

  return function(locals){
    return fn.call(this, locals, filters, escape, rethrow);
  }
};

/**
 * Render the given `str` of ejs.
 *
 * Options:
 *
 *   - `locals`          Local variables object
 *   - `cache`           Compiled functions are cached, requires `filename`
 *   - `filename`        Used by `cache` to key caches
 *   - `scope`           Function execution context
 *   - `debug`           Output generated function body
 *   - `open`            Open tag, defaulting to "<%"
 *   - `close`           Closing tag, defaulting to "%>"
 *
 * @param {String} str
 * @param {Object} options
 * @return {String}
 * @api public
 */

exports.render = function(str, options){
  var fn
    , options = options || {};

  if (options.cache) {
    if (options.filename) {
      fn = cache[options.filename] || (cache[options.filename] = compile(str, options));
    } else {
      throw new Error('"cache" option requires "filename".');
    }
  } else {
    fn = compile(str, options);
  }

  options.__proto__ = options.locals;
  return fn.call(options.scope, options);
};

/**
 * Render an EJS file at the given `path` and callback `fn(err, str)`.
 *
 * @param {String} path
 * @param {Object|Function} options or callback
 * @param {Function} fn
 * @api public
 */

exports.renderFile = function(path, options, fn){
  var key = path + ':string';

  if ('function' == typeof options) {
    fn = options, options = {};
  }

  options.filename = path;

  var str;
  try {
    str = options.cache
      ? cache[key] || (cache[key] = read(path, 'utf8'))
      : read(path, 'utf8');
  } catch (err) {
    fn(err);
    return;
  }
  fn(null, exports.render(str, options));
};

/**
 * Resolve include `name` relative to `filename`.
 *
 * @param {String} name
 * @param {String} filename
 * @return {String}
 * @api private
 */

function resolveInclude(name, filename) {
  var path = join(dirname(filename), name);
  var ext = extname(name);
  if (!ext) path += '.ejs';
  return path;
}

// express support

exports.__express = exports.renderFile;

/**
 * Expose to require().
 */

if (require.extensions) {
  require.extensions['.ejs'] = function (module, filename) {
    filename = filename || module.filename;
    var options = { filename: filename, client: true }
      , template = fs.readFileSync(filename).toString()
      , fn = compile(template, options);
    module._compile('module.exports = ' + fn.toString() + ';', filename);
  };
} else if (require.registerExtension) {
  require.registerExtension('.ejs', function(src) {
    return compile(src, {});
  });
}
<|endoftext|>


<commit_before>var Perl   = require('perl').Perl
,   _      = require('underscore')
,   moment = require('moment')
,   fs     = require('fs')
;

function epg(conf) {
    var instance = this;

    epg = function() {
      return instance;
    };

    epg.prototype = this;
    this.constructor = epg;

    this.dbName = (conf.epg_db || 'eit.db');
    this.carouselDbName = (conf.carousel_db || 'carousel.db');
    var scheduledHours = (conf.scheduled_hours || 24);
    this.maxSegments = parseInt(scheduledHours / 3);
    this.interval = conf.eit_interval;
    this.pid    = (conf.pid || 18);

    this.perl = new Perl();

    this.perl.use('DVB::Epg');
    this.perl.use('DVB::Carousel');
    this.perl.use('Time::Local');

    if (!fs.existsSync(this.dbName)) {
        this._initDb();
    }
}

epg.prototype._initDb = function() {
    this._epg().initdb();
};

/*
 * SERVICE ATTRS:
 * signalId (int),
 * originalNetworkId (int),
 * transportStreamId (int),
 * serviceId (int),
 * comment (string)
 */
epg.prototype.addService = function(service) {
    service.pid = this.pid;
    if (!service.pid)
        throw new Error("pid must be defined");
    else if (!service.signalId)
        throw new Error("signalId must be defined");
    else if (!service.originalNetworkId)
        throw new Error("originalNetworkId must be defined");
    else if (!service.transportStreamId)
        throw new Error("transportStreamId must be defined");
    else if (!service.serviceId)
        throw new Error("serviceId must be defined");
    else if (!service.comment)
        throw new Error("comment must be defined");

    var services = _.filter(this.listServices(), function(serv) {
        return (serv.signalId === service.signalId) && (serv.serviceId !== service.serviceId);
    });
    if (services.length !== 0)
        throw new Error("can't add a service with an existent signalId");

    var result = this._epg().addEit(service.pid, service.signalId, service.originalNetworkId, service.transportStreamId, service.serviceId, this.maxSegments, 1, service.comment);
    if (result !== 1)
        throw new Error("unknown error adding service");
};

epg.prototype.listServices = function() {
    var services = this._epg().listEit();
    return _.map(services, this._getServiceFromPerl);
};

epg.prototype.listPids = function() {
    return this._epg().listPid.callList();
};

epg.prototype._getServiceFromPerl = function(service) {
    var _service = {};
    _service.pid = service[0];
    _service.signalId = service[1];
    _service.originalNetworkId = service[2];
    _service.transportStreamId = service[3];
    _service.serviceId = service[4];
    _service.comment = service[7];
    return _service;
};

/*
 * FILTER ATTRS (none required):
 * pid (int)
 * signalId (int)
 * originalNetworkId (int)
 * transportStreamId (int)
 */
epg.prototype.deleteServices = function(filter) {
    if (!filter)
        filter = {};

    if (!filter.pid)
        filter.pid = false;
    if (!filter.signalId)
        filter.signalId = false;
    if (!filter.originalNetworkId)
        filter.originalNetworkId = false;
    if (!filter.transportStreamId)
        filter.transportStreamId = false;

    var services = _.filter(this.listServices(), function(service) {
        return ((filter.pid && (filter.pid === service.pid)) || (!filter.pid))
            && ((filter.signalId && (filter.signalId === service.signalId)) || (!filter.signalId))
            && ((filter.originalNetworkId && (filter.originalNetworkId === service.originalNetworkId)) || (!filter.originalNetworkId))
            && ((filter.transportStreamId && (filter.transportStreamId === service.transportStreamId)) || (!filter.transportStreamId));
    });

    var self = this;
    var serv = _.filter(services, function(service) {
        return self.listEvents({serviceId: service.serviceId}).length !== 0;
    });

    if (serv.length !== 0)
        throw new Error("can't delete services which have events");

    return this._epg().deleteEit(filter.pid, filter.signalId, filter.originalNetworkId, filter.transportStreamId);
};

/*
 * EVENT ATTRS:
 * id (int) -> IF NOT PROVIDED, ASSINGED INSIDE FUNCTION,
 * start (moment),
 * duration (moment.duration),
 * serviceId (int),
 * title (string),
 * synopsis (string),
 */
epg.prototype.addEvent = function(event) {
    if (!event.serviceId)
        throw new Error("serviceId must be defined");
    else if (!event.start)
        throw new Error("start must be defined");
    else if (!moment.isMoment(event.start))
        throw new Error("start must be a moment");
    else if (!event.duration)
        throw new Error("duration must be defined");
    else if (!moment.isDuration(event.duration))
        throw new Error("duration must be a moment.duration");
    else if (event.duration.asMilliseconds() === 0)
        throw new Error("duration must be greater than 0");

    var services = this.listServices();
    if (!_.find(services, function(service) {return service.serviceId === event.serviceId;}))
        throw new Error("can't add an event which service does not exists");

    var _event = {};
    if (event.id) {
        _event.id = event.id;
    } else {
        var data = [];
        var self = this;
        _.each(services, function(service) {
            var events = self.listEvents({serviceId: service.serviceId});
            data = _.union(events, data);
        });
        if (!_.isEmpty(data))
            _event.id = _.max(data, function(evt) {return evt.id;}).id + 1;
        else
            _event.id = 1;
    }
    _event.uid = event.serviceId;
    _event.start = this._getPerlDate(event.start);
    _event.stop = this._getPerlDate(moment(event.start).add(event.duration));
    var descriptors = [];
    var shortDescriptor = {};
    shortDescriptor.descriptor_tag = 0x4d;
    shortDescriptor.language_code = "spa";
    shortDescriptor.codepage_prefix = "";
    shortDescriptor.event_name = event.title;
    shortDescriptor.text = event.synopsis;
    descriptors.push(shortDescriptor);
    _event.descriptors = descriptors;
    event.id = this._epg().addEvent(_event);
    if (event.id)
        return this.listEvents({serviceId: event.serviceId, eventId: event.id})[0];
    else
        throw new Error("unknown error adding event");
};

/*
 * FILTER ATTRS (only serviceId is required):
 * serviceId (int)
 * eventId (int)
 * start (moment)
 * stop (moment)
 * lastUpdated (moment)
 */
epg.prototype.listEvents = function(filter) {
    if (!filter)
        throw new Error("must provide a filter");
    else if (!filter.serviceId)
        throw new Error("serviceId must be defined");

    if (!filter.eventId)
        filter.eventId = false;
    if (filter.start && moment.isMoment(filter.start))
        filter.start = this._getPerlDate(filter.start);
    else
        filter.start = false;

    if (filter.stop && moment.isMoment(filter.stop))
        filter.stop = this._getPerlDate(filter.stop);
    else
        filter.stop= false;

    if (filter.lastUpdated && moment.isMoment(filter.lastUpdated))
        filter.lastUpdated = this._getPerlDate(filter.lastUpdated);
    else
        filter.lastUpdated = false;

    var events = this._epg().listEvents.callList(filter.serviceId, filter.eventId, filter.start, filter.stop, filter.lastUpdated);
    return _.map(events, this._getEventFromPerl);
};

/*
 * FILTER ATTRS (none required):
 * serviceId (int)
 * eventId (int)
 * startMin (moment)
 * startMax (moment)
 * stopMin (moment)
 * stopMax (moment)
 */
epg.prototype.deleteEvents = function(filter) {
    if (!filter)
        filter = {};

    if (!filter.eventId)
        filter.eventId = false;
    if (!filter.serviceId)
        filter.serviceId = false;
    if (!filter.eventId)
        filter.eventId = false;
    if (filter.startMin  && moment.isMoment(filter.startMin))
        filter.startMin = this._getPerlDate(filter.startMin);
    else
        filter.startMin = false;
    if (filter.startMax  && moment.isMoment(filter.startMax))
        filter.startMax = this._getPerlDate(filter.startMax);
    else
        filter.startMax = false;
    if (filter.stopMin  && moment.isMoment(filter.stopMin))
        filter.stopMin = this._getPerlDate(filter.stopMin);
    else
        filter.stopMin = false;
    if (filter.stopMax  && moment.isMoment(filter.stopMax))
        filter.stopMax = this._getPerlDate(filter.stopMax);
    else
        filter.stopMax = false;

    return this._epg().deleteEvent(filter.serviceId, filter.eventId, filter.startMin, filter.startMax, filter.stopMin, filter.stopMax);
};

epg.prototype._getEventFromPerl = function(evt) {
    var event = {};
    event.id = evt.event_id;
    event.start = moment.unix(parseInt(evt.start));
    event.duration = moment.duration(parseInt(evt.stop) - parseInt(evt.start), 'seconds');
    event.serviceId = evt.uid;
    if (evt.descriptors && evt.descriptors[0]) {
        event.title = evt.descriptors[0].event_name;
        event.synopsis = evt.descriptors[0].text;
    }
    return event;
};

epg.prototype._getPerlDate = function(date) {
    return this.perl.evaluate("timelocal (" + date.seconds() + "," + date.minutes() + "," + date.hours() + "," + date.date() + "," + date.month() + "," + date.year() + ")");
};

epg.prototype.updateEit = function(pid) {
    pid = this.pid;
    if (!pid)
        throw new Error("pid must be defined");
    var ret = this._epg().updateEit(pid);

    if (ret === 1)
        return true;
    else if (ret === 0)
        return false;
    else
        throw new Error("unknown error updating eit");
};

epg.prototype.getEit = function(pid) {
    pid = this.pid;
    if (!pid)
        throw new Error("pid must be defined");
    var ret = this._epg().getEit(pid, this.interval);
    if (ret)
        return ret;
    else
        throw new Error("unknown error geting eit");
};

epg.prototype.updateCarousel = function(pid) {
    pid = this.pid;
    if (!pid)
        throw new Error("pid must be defined");
    var ret = this._epg().updateCarousel(pid, this.interval);

    if (ret)
        return true;
    else
        throw new Error("unknown error updating carousel");
};

epg.prototype.getMaxEventId = function() {
    return this._epg().getMaxEventId();
};

epg.prototype._epg = function() {
    var now = moment();
    var returnNew = false;
    if (!this.__epg) {
        returnNew = true;
    } else {
        if (this.lastCall)
            returnNew = now.diff(this.lastCall, 'minutes') > 1;
        else
            returnNew = true;
    }
    if (returnNew) {
        this.perl.evaluate("package Epg; "
            + "my $epg = DVB::Epg->new('" + this.dbName + "'); "
            + "sub initdb { "
                + "return $epg->initdb(); "
            + "} "
            + "sub addEvent { "
                + "my $self = shift; "
                + "my ($event) = @_; "
                + "return $epg->addEvent($event); "
            + "}"
            + "sub listEvents { "
                + "my $self = shift; "
                + "my ( $uid, $event_id, $start, $stop, $touch ) = @_; "
                + "return $epg->listEvent($uid, ( ! $event_id ? undef : $event_id ), ( ! $start ? undef : $start ), ( ! $stop ? undef : $stop ), ( ! $touch ? undef : $touch )); "
            + "}"
            + "sub addEit { "
                + "my $self = shift; "
                + "my ( $pid, $service_id, $original_network_id, $transport_stream_id, $uid, $maxsegments, $actual, $comment ) = @_; "
                + "return $epg->addEit($pid, $service_id, $original_network_id, $transport_stream_id, $uid, $maxsegments, $actual, $comment); "
            + "}"
            + "sub listEit { "
                + "return $epg->listEit(); "
            + "} "
            + "sub listPid { "
                + "return $epg->listPid(); "
            + "} "
            + "sub deleteEit { "
                + "my $self = shift; "
                + "my ( $pid, $service_id, $original_network_id, $transport_stream_id) = @_; "
                + "return $epg->deleteEit(( ! $pid ? undef : $pid ), ( ! $service_id ? undef : $service_id ), ( ! $original_network_id ? undef : $original_network_id ), ( ! $transport_stream_id ? undef : $transport_stream_id )); "
            + "} "
            + "sub deleteEvent { "
                + "my $self = shift; "
                + "my ( $uid, $event_id, $start_min, $start_max, $stop_min, $stop_max) = @_; "
                + "return $epg->deleteEvent(( ! $uid ? undef : $uid ), ( ! $event_id ? undef : $event_id ), ( ! $start_min ? undef : $start_min ), ( ! $start_max ? undef : $start_max ), ( ! $stop_min ? undef : $stop_min ), ( ! $stop_max ? undef : $stop_max )); "
            + "} "
            + "sub updateEit { "
                + "my $self = shift; "
                + "my $pid = shift; "
                + "return $epg->updateEit($pid); "
            + "}"
            + "sub getEit { "
                + "my $self = shift; "
                + "my $pid = shift; "
                + "my $timeFrame = shift; "
                + "return $epg->getEit($pid, $timeFrame); "
            + "}"
            + "sub updateCarousel { "
                + "my $self = shift; "
                + "my $pid = shift; "
                + "my $interval = shift; "
                + "my $carousel = DVB::Carousel->new('" + this.carouselDbName + "'); "
                + "my $pes = $epg->getEit( $pid, $interval); "
                + "return $carousel->addMts( $pid, \\$pes, $interval*1000); "
            + "}"
            + "sub getMaxEventId { "
                + "my $self = shift; "
                + "my $dbh  = $epg->{dbh}; "
                + "my @row = $dbh->selectrow_array( 'SELECT event_id FROM event ORDER BY event_id DESC LIMIT 1' ); "
                + "if ( $#row == 0 ) { "
                    + "return $row[0]; "
                + "} else { "
                    + "return 0 "
                + "} "
            + "}"
        );
        this.__epg = this.perl.getClass("Epg");
    }
    this.lastCall = now;
    return this.__epg;
};

exports = module.exports = function(conf) {
    var e = new epg(conf);
    return e;
};<commit_msg>Setting codepage<commit_after>var Perl   = require('perl').Perl
,   _      = require('underscore')
,   moment = require('moment')
,   fs     = require('fs')
;

function epg(conf) {
    var instance = this;

    epg = function() {
      return instance;
    };

    epg.prototype = this;
    this.constructor = epg;

    this.dbName = (conf.epg_db || 'eit.db');
    this.carouselDbName = (conf.carousel_db || 'carousel.db');
    var scheduledHours = (conf.scheduled_hours || 24);
    this.maxSegments = parseInt(scheduledHours / 3);
    this.interval = conf.eit_interval;
    this.pid    = (conf.pid || 18);

    this.perl = new Perl();

    this.perl.use('DVB::Epg');
    this.perl.use('DVB::Carousel');
    this.perl.use('Time::Local');

    if (!fs.existsSync(this.dbName)) {
        this._initDb();
    }
}

epg.prototype._initDb = function() {
    this._epg().initdb();
};

/*
 * SERVICE ATTRS:
 * signalId (int),
 * originalNetworkId (int),
 * transportStreamId (int),
 * serviceId (int),
 * comment (string)
 */
epg.prototype.addService = function(service) {
    service.pid = this.pid;
    if (!service.pid)
        throw new Error("pid must be defined");
    else if (!service.signalId)
        throw new Error("signalId must be defined");
    else if (!service.originalNetworkId)
        throw new Error("originalNetworkId must be defined");
    else if (!service.transportStreamId)
        throw new Error("transportStreamId must be defined");
    else if (!service.serviceId)
        throw new Error("serviceId must be defined");
    else if (!service.comment)
        throw new Error("comment must be defined");

    var services = _.filter(this.listServices(), function(serv) {
        return (serv.signalId === service.signalId) && (serv.serviceId !== service.serviceId);
    });
    if (services.length !== 0)
        throw new Error("can't add a service with an existent signalId");

    var result = this._epg().addEit(service.pid, service.signalId, service.originalNetworkId, service.transportStreamId, service.serviceId, this.maxSegments, 1, service.comment);
    if (result !== 1)
        throw new Error("unknown error adding service");
};

epg.prototype.listServices = function() {
    var services = this._epg().listEit();
    return _.map(services, this._getServiceFromPerl);
};

epg.prototype.listPids = function() {
    return this._epg().listPid.callList();
};

epg.prototype._getServiceFromPerl = function(service) {
    var _service = {};
    _service.pid = service[0];
    _service.signalId = service[1];
    _service.originalNetworkId = service[2];
    _service.transportStreamId = service[3];
    _service.serviceId = service[4];
    _service.comment = service[7];
    return _service;
};

/*
 * FILTER ATTRS (none required):
 * pid (int)
 * signalId (int)
 * originalNetworkId (int)
 * transportStreamId (int)
 */
epg.prototype.deleteServices = function(filter) {
    if (!filter)
        filter = {};

    if (!filter.pid)
        filter.pid = false;
    if (!filter.signalId)
        filter.signalId = false;
    if (!filter.originalNetworkId)
        filter.originalNetworkId = false;
    if (!filter.transportStreamId)
        filter.transportStreamId = false;

    var services = _.filter(this.listServices(), function(service) {
        return ((filter.pid && (filter.pid === service.pid)) || (!filter.pid))
            && ((filter.signalId && (filter.signalId === service.signalId)) || (!filter.signalId))
            && ((filter.originalNetworkId && (filter.originalNetworkId === service.originalNetworkId)) || (!filter.originalNetworkId))
            && ((filter.transportStreamId && (filter.transportStreamId === service.transportStreamId)) || (!filter.transportStreamId));
    });

    var self = this;
    var serv = _.filter(services, function(service) {
        return self.listEvents({serviceId: service.serviceId}).length !== 0;
    });

    if (serv.length !== 0)
        throw new Error("can't delete services which have events");

    return this._epg().deleteEit(filter.pid, filter.signalId, filter.originalNetworkId, filter.transportStreamId);
};

/*
 * EVENT ATTRS:
 * id (int) -> IF NOT PROVIDED, ASSINGED INSIDE FUNCTION,
 * start (moment),
 * duration (moment.duration),
 * serviceId (int),
 * title (string),
 * synopsis (string),
 */
epg.prototype.addEvent = function(event) {
    if (!event.serviceId)
        throw new Error("serviceId must be defined");
    else if (!event.start)
        throw new Error("start must be defined");
    else if (!moment.isMoment(event.start))
        throw new Error("start must be a moment");
    else if (!event.duration)
        throw new Error("duration must be defined");
    else if (!moment.isDuration(event.duration))
        throw new Error("duration must be a moment.duration");
    else if (event.duration.asMilliseconds() === 0)
        throw new Error("duration must be greater than 0");

    var services = this.listServices();
    if (!_.find(services, function(service) {return service.serviceId === event.serviceId;}))
        throw new Error("can't add an event which service does not exists");

    var _event = {};
    if (event.id) {
        _event.id = event.id;
    } else {
        var data = [];
        var self = this;
        _.each(services, function(service) {
            var events = self.listEvents({serviceId: service.serviceId});
            data = _.union(events, data);
        });
        if (!_.isEmpty(data))
            _event.id = _.max(data, function(evt) {return evt.id;}).id + 1;
        else
            _event.id = 1;
    }
    _event.uid = event.serviceId;
    _event.start = this._getPerlDate(event.start);
    _event.stop = this._getPerlDate(moment(event.start).add(event.duration));
    var descriptors = [];
    var shortDescriptor = {};
    shortDescriptor.descriptor_tag = 0x4d;
    shortDescriptor.language_code = "spa";
    shortDescriptor.codepage_prefix = "\\x05";
    shortDescriptor.event_name = event.title;
    shortDescriptor.text = event.synopsis;
    descriptors.push(shortDescriptor);
    _event.descriptors = descriptors;
    event.id = this._epg().addEvent(_event);
    if (event.id)
        return this.listEvents({serviceId: event.serviceId, eventId: event.id})[0];
    else
        throw new Error("unknown error adding event");
};

/*
 * FILTER ATTRS (only serviceId is required):
 * serviceId (int)
 * eventId (int)
 * start (moment)
 * stop (moment)
 * lastUpdated (moment)
 */
epg.prototype.listEvents = function(filter) {
    if (!filter)
        throw new Error("must provide a filter");
    else if (!filter.serviceId)
        throw new Error("serviceId must be defined");

    if (!filter.eventId)
        filter.eventId = false;
    if (filter.start && moment.isMoment(filter.start))
        filter.start = this._getPerlDate(filter.start);
    else
        filter.start = false;

    if (filter.stop && moment.isMoment(filter.stop))
        filter.stop = this._getPerlDate(filter.stop);
    else
        filter.stop= false;

    if (filter.lastUpdated && moment.isMoment(filter.lastUpdated))
        filter.lastUpdated = this._getPerlDate(filter.lastUpdated);
    else
        filter.lastUpdated = false;

    var events = this._epg().listEvents.callList(filter.serviceId, filter.eventId, filter.start, filter.stop, filter.lastUpdated);
    return _.map(events, this._getEventFromPerl);
};

/*
 * FILTER ATTRS (none required):
 * serviceId (int)
 * eventId (int)
 * startMin (moment)
 * startMax (moment)
 * stopMin (moment)
 * stopMax (moment)
 */
epg.prototype.deleteEvents = function(filter) {
    if (!filter)
        filter = {};

    if (!filter.eventId)
        filter.eventId = false;
    if (!filter.serviceId)
        filter.serviceId = false;
    if (!filter.eventId)
        filter.eventId = false;
    if (filter.startMin  && moment.isMoment(filter.startMin))
        filter.startMin = this._getPerlDate(filter.startMin);
    else
        filter.startMin = false;
    if (filter.startMax  && moment.isMoment(filter.startMax))
        filter.startMax = this._getPerlDate(filter.startMax);
    else
        filter.startMax = false;
    if (filter.stopMin  && moment.isMoment(filter.stopMin))
        filter.stopMin = this._getPerlDate(filter.stopMin);
    else
        filter.stopMin = false;
    if (filter.stopMax  && moment.isMoment(filter.stopMax))
        filter.stopMax = this._getPerlDate(filter.stopMax);
    else
        filter.stopMax = false;

    return this._epg().deleteEvent(filter.serviceId, filter.eventId, filter.startMin, filter.startMax, filter.stopMin, filter.stopMax);
};

epg.prototype._getEventFromPerl = function(evt) {
    var event = {};
    event.id = evt.event_id;
    event.start = moment.unix(parseInt(evt.start));
    event.duration = moment.duration(parseInt(evt.stop) - parseInt(evt.start), 'seconds');
    event.serviceId = evt.uid;
    if (evt.descriptors && evt.descriptors[0]) {
        event.title = evt.descriptors[0].event_name;
        event.synopsis = evt.descriptors[0].text;
    }
    return event;
};

epg.prototype._getPerlDate = function(date) {
    return this.perl.evaluate("timelocal (" + date.seconds() + "," + date.minutes() + "," + date.hours() + "," + date.date() + "," + date.month() + "," + date.year() + ")");
};

epg.prototype.updateEit = function(pid) {
    pid = this.pid;
    if (!pid)
        throw new Error("pid must be defined");
    var ret = this._epg().updateEit(pid);

    if (ret === 1)
        return true;
    else if (ret === 0)
        return false;
    else
        throw new Error("unknown error updating eit");
};

epg.prototype.getEit = function(pid) {
    pid = this.pid;
    if (!pid)
        throw new Error("pid must be defined");
    var ret = this._epg().getEit(pid, this.interval);
    if (ret)
        return ret;
    else
        throw new Error("unknown error geting eit");
};

epg.prototype.updateCarousel = function(pid) {
    pid = this.pid;
    if (!pid)
        throw new Error("pid must be defined");
    var ret = this._epg().updateCarousel(pid, this.interval);

    if (ret)
        return true;
    else
        throw new Error("unknown error updating carousel");
};

epg.prototype.getMaxEventId = function() {
    return this._epg().getMaxEventId();
};

epg.prototype._epg = function() {
    var now = moment();
    var returnNew = false;
    if (!this.__epg) {
        returnNew = true;
    } else {
        if (this.lastCall)
            returnNew = now.diff(this.lastCall, 'minutes') > 1;
        else
            returnNew = true;
    }
    if (returnNew) {
        this.perl.evaluate("package Epg; "
            + "my $epg = DVB::Epg->new('" + this.dbName + "'); "
            + "sub initdb { "
                + "return $epg->initdb(); "
            + "} "
            + "sub addEvent { "
                + "my $self = shift; "
                + "my ($event) = @_; "
                + "return $epg->addEvent($event); "
            + "}"
            + "sub listEvents { "
                + "my $self = shift; "
                + "my ( $uid, $event_id, $start, $stop, $touch ) = @_; "
                + "return $epg->listEvent($uid, ( ! $event_id ? undef : $event_id ), ( ! $start ? undef : $start ), ( ! $stop ? undef : $stop ), ( ! $touch ? undef : $touch )); "
            + "}"
            + "sub addEit { "
                + "my $self = shift; "
                + "my ( $pid, $service_id, $original_network_id, $transport_stream_id, $uid, $maxsegments, $actual, $comment ) = @_; "
                + "return $epg->addEit($pid, $service_id, $original_network_id, $transport_stream_id, $uid, $maxsegments, $actual, $comment); "
            + "}"
            + "sub listEit { "
                + "return $epg->listEit(); "
            + "} "
            + "sub listPid { "
                + "return $epg->listPid(); "
            + "} "
            + "sub deleteEit { "
                + "my $self = shift; "
                + "my ( $pid, $service_id, $original_network_id, $transport_stream_id) = @_; "
                + "return $epg->deleteEit(( ! $pid ? undef : $pid ), ( ! $service_id ? undef : $service_id ), ( ! $original_network_id ? undef : $original_network_id ), ( ! $transport_stream_id ? undef : $transport_stream_id )); "
            + "} "
            + "sub deleteEvent { "
                + "my $self = shift; "
                + "my ( $uid, $event_id, $start_min, $start_max, $stop_min, $stop_max) = @_; "
                + "return $epg->deleteEvent(( ! $uid ? undef : $uid ), ( ! $event_id ? undef : $event_id ), ( ! $start_min ? undef : $start_min ), ( ! $start_max ? undef : $start_max ), ( ! $stop_min ? undef : $stop_min ), ( ! $stop_max ? undef : $stop_max )); "
            + "} "
            + "sub updateEit { "
                + "my $self = shift; "
                + "my $pid = shift; "
                + "return $epg->updateEit($pid); "
            + "}"
            + "sub getEit { "
                + "my $self = shift; "
                + "my $pid = shift; "
                + "my $timeFrame = shift; "
                + "return $epg->getEit($pid, $timeFrame); "
            + "}"
            + "sub updateCarousel { "
                + "my $self = shift; "
                + "my $pid = shift; "
                + "my $interval = shift; "
                + "my $carousel = DVB::Carousel->new('" + this.carouselDbName + "'); "
                + "my $pes = $epg->getEit( $pid, $interval); "
                + "return $carousel->addMts( $pid, \\$pes, $interval*1000); "
            + "}"
            + "sub getMaxEventId { "
                + "my $self = shift; "
                + "my $dbh  = $epg->{dbh}; "
                + "my @row = $dbh->selectrow_array( 'SELECT event_id FROM event ORDER BY event_id DESC LIMIT 1' ); "
                + "if ( $#row == 0 ) { "
                    + "return $row[0]; "
                + "} else { "
                    + "return 0 "
                + "} "
            + "}"
        );
        this.__epg = this.perl.getClass("Epg");
    }
    this.lastCall = now;
    return this.__epg;
};

exports = module.exports = function(conf) {
    var e = new epg(conf);
    return e;
};<|endoftext|>