<commit_before>'use strict';

const _ = require('lodash');
const path = require('path');
const EventEmitter = require('events');
const Sqlite = require('sqlite-pool');
const iterwait = require('./iterwait');
const { decompose, recombine } = require('./parsecfg');
// const Filters = require('./filters');

const sqlite_opts = {
  acquireTimeout: 5000,
  busyTimeout: 5000,
  min: 2,
  max: 4,
};

class BaseDB extends EventEmitter {
  constructor (filename, { create_file = false, single_conn = false } = {}) {
    super();

    const db_opts = Object.assign({}, sqlite_opts);

    // Change default open mode if not creating
    db_opts.mode = create_file
      ? (Sqlite.OPEN_READWRITE | Sqlite.OPEN_CREATE)
      : Sqlite.OPEN_READWRITE;

    // Clamp pool min/max in single mode
    if (single_conn) {
      db_opts.min = 1;
      db_opts.max = 1;
    }

    // Open database
    // Don't allow anonymous file-based, too finicky
    this.path = (!filename || filename === ':memory:')
      ? ':memory:'
      : path.resolve(filename);
    this.db = new Sqlite(this.path, db_opts);

    // Set up one-time events for file open
    // Only triggers one or the other of success/error events
    let cb_open, cb_err;
    cb_open = () => {
      // Remove error listener
      this.db.removeListener('error', cb_err);
      cb_err = null;
      // Emit open events
      this.emit('open:success', this.path, this);
      this.emit('open', this.path, this);
    };
    cb_err = (err) => {
      // Remove open listener
      this.db.removeListener('open', cb_open);
      cb_open = null;
      // Emit error events
      this.emit('open:error', err, this);
      if (this.listenerCount('error') > 0) {
        // Only emit 'error' with active listeners, so
        // the process doesn't crash before cleanup
        this.emit('error', err, this);
      }
    };
    this.db.once('open', cb_open);
    this.db.once('error', cb_err);

    // Config cache
    this.config = {};
  }

  close () {
    return this.db.close().then(() => {
      this.emit('close', this);
      return this;
    });
  }

  run_async (gen, {use_conn = null, start_trx = false, delay} = {}) {
    // Just using run_async() to simplify iterwait()
    if (use_conn === false) {
      return iterwait(get.call(this, this.db), delay);
    }
    // Already have a connection or transaction
    else if (use_conn) {
      return iterwait(gen.call(this, use_conn), delay);
    }
    // Start a transaction
    else if (start_trx) {
      return this.db.transaction(trx => iterwait(gen.call(this, trx), delay));
    }
    // Acquire a connection
    else {
      return this.db.use(conn => iterwait(gen.call(this, conn), delay));
    }
  }

  /**
   *  Fetches config key(s)/value(s), returns as single entry/object
   */
  get_config (key, times = false, trx = null) {
    // Don't want to start a transaction, but do use single connection
    return trx
      ? _getcfg.call(this, trx)
      : this.db.use(conn => _getcfg.call(this, conn));

    function _getcfg (conn) {
      const columns = times ? 'value, time_ms AS time' : 'value';
      let queries;

      if (key) {
        queries = [
          conn.get(
            `SELECT ${columns} FROM config WHERE key = ?`,
            [key]
          ),
          conn.all(
            `SELECT key, ${columns} FROM config WHERE key LIKE (? || '.%')`,
            [key]
          ),
        ];
      }
      else {
        queries = [
          Promise.resolve(),
          conn.all(`SELECT key, ${columns} FROM config`),
        ];
      }

      return Promise.all(queries).then(([exact, prefixed]) => {
        // Check to make sure we don't have both exact
        // and prefixed under the same key
        if (exact && prefixed.length) {
          throw new Error(`Found both exact and prefixed values for key ${key}`);
        }

        const extract = (entry) => {
          const key = entry.key;
          const value = JSON.parse(entry.value);

          // Update config cache
          _.set(this.config, key, value);

          return times
            ? [key, [value, entry.time]]
            : [key, value];
        };

        if (exact) {
          // Will only be exact if key supplied
          exact.key = key;
          // Update cache, return value only
          return extract(exact)[1];
        }

        // Otherwise (either key not found or key is prefix) we
        // have to recombine the entries into a composite value
        return prefixed.length
          ? recombine(prefixed.map(extract), key)
          : undefined;
      });
    }
  }

  set_config (value, key = '', times = false, trx = null) {
    // Start a transaction if one not provided
    return trx
      ? _setcfg.call(this, trx)
      : this.db.transaction(_trx => _setcfg.call(this, _trx));

    function _setcfg (conn) {
      // Decompose value into [k, v] or [k, [v, t]] arrays
      // Array w/o prefix is assumed to already be decomposed
      const entries = (!key && Array.isArray(value)) ? value : decompose(value, key);

      let valfn;
      // Use per-value timestamps
      if (times === true) {
        valfn = ([k, [v, t]]) => [k, JSON.stringify(v), t];
      }
      // Use given timestamp or now
      else {
        let set_time = times ? new Date(times).getTime() : Date.now();
        valfn = ([k, v]) => [k, JSON.stringify(v), set_time];
      }

      // Prepare statements ahead of time to speed things up
      return Promise.all([
        conn.prepare(
          `UPDATE config SET value = ?2, time_ms = ?3
WHERE key = ?1 AND (time_ms IS NULL OR time_ms < ?3)`
        ),
        conn.prepare(
          `SELECT EXISTS(SELECT 1 FROM config WHERE key LIKE (? || '.%')) AS prefix`
        ),
        conn.prepare(
          `INSERT OR IGNORE INTO config(key, value, time_ms) VALUES (?, ?, ?)`
        ),
      ])
      .then((statements) => {
        let [ upd, chk, ins ] = statements;

        return Promise.all(entries.map((val) => {
          // Get [k, v, t] array
          const kvt = valfn(val);
          const [k, v, t] = kvt;

          // Do not update if value is undefined
          if (v === undefined) {
            return [k, false];
          }

          // Now the tricky bit: have to try updating each key if
          // new timestamp is higher; then, if it doesn't update,
          // only insert if key isn't already a prefix of others.
          return upd.run(kvt).then(({ changes }) => {
            if (changes) {
              return [k, true];
            }
            return chk.get(k).then(({ prefix }) => {
              if (prefix) {
                throw new Error(`Key ${k} is already a prefix of other keys`);
              }
              return ins.run(kvt).then(({ changes }) => [k, !!changes]);
            });
          })
          .then((changed) => {
            // Update cache if key updated/inserted
            if (changed[1]) {
              _.set(this.config, k, times ? val[1][0] : val[1]);
            }
            return changed;
          });
        }))
        .then(result =>
          Promise.all(statements.map(x => x.finalize()))
          .then(() => result)
        )
        .catch(err =>
          Promise.all(statements.map(x => x.finalize()))
          .then(() => Promise.reject(err))
        );
      });
    }
  }

  del_config (key, times = false, trx = null) {
    if (!key) {
      return Promise.reject(new Error(`Must specify key to be deleted`));
    }

    return trx ? _delcfg(trx) : this.db.transaction(_trx => _delcfg(_trx));

    function _delcfg (conn) {
      return Promise.all([
        conn.get(
          `SELECT key, time_ms AS time FROM config WHERE key = ?`,
          [key]
        ),
        conn.all(
          `SELECT key, time_ms AS time FROM config WHERE key LIKE (? || '.%')`,
          [key]
        ),
      ])
      .then(([ exact, prefixed ]) => {
        // Check to make sure we don't have both exact
        // and prefixed under the same key
        if (exact && prefixed.length) {
          throw new Error(`Found both exact and prefixed values for key ${key}`);
        }

        let postfn = ({ changes }) => !!changes;
        let del_time = times
          ? times === true
            ? Date.now()
            : new Date(times).getTime()
          : false;

        if (exact) {
          return (!del_time || exact.time <= del_time)
            ? conn.run(`DELETE FROM config WHERE key = ?`, [key]).then(postfn)
            : false;
        }

        // Ensure all timestamps are at or below given time
        if (del_time) {
          let eligible = 0;
          for (let p of prefixed) {
            if (p.time <= del_time) {
              eligible++;
            }
          }

          // Delete all or none
          if (eligible !== prefixed.length) {
            return false;
          }
        }

        return conn.run(
          `DELETE FROM config WHERE key LIKE (? || '.%')`,
          [key]
        )
        .then(postfn);

      });
    }
  }

}

module.exports = BaseDB;
<commit_msg>Added config cache updates to BaseDB.del_config() (from MasterDB)<commit_after>'use strict';

const _ = require('lodash');
const path = require('path');
const EventEmitter = require('events');
const Sqlite = require('sqlite-pool');
const iterwait = require('./iterwait');
const { decompose, recombine } = require('./parsecfg');
// const Filters = require('./filters');

const sqlite_opts = {
  acquireTimeout: 5000,
  busyTimeout: 5000,
  min: 2,
  max: 4,
};

class BaseDB extends EventEmitter {
  constructor (filename, { create_file = false, single_conn = false } = {}) {
    super();

    const db_opts = Object.assign({}, sqlite_opts);

    // Change default open mode if not creating
    db_opts.mode = create_file
      ? (Sqlite.OPEN_READWRITE | Sqlite.OPEN_CREATE)
      : Sqlite.OPEN_READWRITE;

    // Clamp pool min/max in single mode
    if (single_conn) {
      db_opts.min = 1;
      db_opts.max = 1;
    }

    // Open database
    // Don't allow anonymous file-based, too finicky
    this.path = (!filename || filename === ':memory:')
      ? ':memory:'
      : path.resolve(filename);
    this.db = new Sqlite(this.path, db_opts);

    // Set up one-time events for file open
    // Only triggers one or the other of success/error events
    let cb_open, cb_err;
    cb_open = () => {
      // Remove error listener
      this.db.removeListener('error', cb_err);
      cb_err = null;
      // Emit open events
      this.emit('open:success', this.path, this);
      this.emit('open', this.path, this);
    };
    cb_err = (err) => {
      // Remove open listener
      this.db.removeListener('open', cb_open);
      cb_open = null;
      // Emit error events
      this.emit('open:error', err, this);
      if (this.listenerCount('error') > 0) {
        // Only emit 'error' with active listeners, so
        // the process doesn't crash before cleanup
        this.emit('error', err, this);
      }
    };
    this.db.once('open', cb_open);
    this.db.once('error', cb_err);

    // Config cache
    this.config = {};
  }

  close () {
    return this.db.close().then(() => {
      this.emit('close', this);
      return this;
    });
  }

  run_async (gen, {use_conn = null, start_trx = false, delay} = {}) {
    // Just using run_async() to simplify iterwait()
    if (use_conn === false) {
      return iterwait(get.call(this, this.db), delay);
    }
    // Already have a connection or transaction
    else if (use_conn) {
      return iterwait(gen.call(this, use_conn), delay);
    }
    // Start a transaction
    else if (start_trx) {
      return this.db.transaction(trx => iterwait(gen.call(this, trx), delay));
    }
    // Acquire a connection
    else {
      return this.db.use(conn => iterwait(gen.call(this, conn), delay));
    }
  }

  /**
   *  Fetches config key(s)/value(s), returns as single entry/object
   */
  get_config (key, times = false, trx = null) {
    // Don't want to start a transaction, but do use single connection
    return trx
      ? _getcfg.call(this, trx)
      : this.db.use(conn => _getcfg.call(this, conn));

    function _getcfg (conn) {
      const columns = times ? 'value, time_ms AS time' : 'value';
      let queries;

      if (key) {
        queries = [
          conn.get(
            `SELECT ${columns} FROM config WHERE key = ?`,
            [key]
          ),
          conn.all(
            `SELECT key, ${columns} FROM config WHERE key LIKE (? || '.%')`,
            [key]
          ),
        ];
      }
      else {
        queries = [
          Promise.resolve(),
          conn.all(`SELECT key, ${columns} FROM config`),
        ];
      }

      return Promise.all(queries).then(([exact, prefixed]) => {
        // Check to make sure we don't have both exact
        // and prefixed under the same key
        if (exact && prefixed.length) {
          throw new Error(`Found both exact and prefixed values for key ${key}`);
        }

        const extract = (entry) => {
          const key = entry.key;
          const value = JSON.parse(entry.value);

          // Update config cache
          _.set(this.config, key, value);

          return times
            ? [key, [value, entry.time]]
            : [key, value];
        };

        if (exact) {
          // Will only be exact if key supplied
          exact.key = key;
          // Update cache, return value only
          return extract(exact)[1];
        }

        // Otherwise (either key not found or key is prefix) we
        // have to recombine the entries into a composite value
        return prefixed.length
          ? recombine(prefixed.map(extract), key)
          : undefined;
      });
    }
  }

  set_config (value, key = '', times = false, trx = null) {
    // Start a transaction if one not provided
    return trx
      ? _setcfg.call(this, trx)
      : this.db.transaction(_trx => _setcfg.call(this, _trx));

    function _setcfg (conn) {
      // Decompose value into [k, v] or [k, [v, t]] arrays
      // Array w/o prefix is assumed to already be decomposed
      const entries = (!key && Array.isArray(value)) ? value : decompose(value, key);

      let valfn;
      // Use per-value timestamps
      if (times === true) {
        valfn = ([k, [v, t]]) => [k, JSON.stringify(v), t];
      }
      // Use given timestamp or now
      else {
        const set_time = times ? new Date(times).getTime() : Date.now();
        valfn = ([k, v]) => [k, JSON.stringify(v), set_time];
      }

      // Prepare statements ahead of time to speed things up
      return Promise.all([
        conn.prepare(
          `UPDATE config SET value = ?2, time_ms = ?3
WHERE key = ?1 AND (time_ms IS NULL OR time_ms < ?3)`
        ),
        conn.prepare(
          `SELECT EXISTS(SELECT 1 FROM config WHERE key LIKE (? || '.%')) AS prefix`
        ),
        conn.prepare(
          `INSERT OR IGNORE INTO config(key, value, time_ms) VALUES (?, ?, ?)`
        ),
      ])
      .then((statements) => {
        const [ upd, chk, ins ] = statements;

        return Promise.all(entries.map((val) => {
          // Get [k, v, t] array
          const kvt = valfn(val);
          const [k, v, t] = kvt;

          // Do not update if value is undefined
          if (v === undefined) {
            return [k, false];
          }

          // Now the tricky bit: have to try updating each key if
          // new timestamp is higher; then, if it doesn't update,
          // only insert if key isn't already a prefix of others.
          return upd.run(kvt).then(({ changes }) => {
            if (changes) {
              return [k, true];
            }
            return chk.get(k).then(({ prefix }) => {
              if (prefix) {
                throw new Error(`Key ${k} is already a prefix of other keys`);
              }
              return ins.run(kvt).then(({ changes }) => [k, !!changes]);
            });
          })
          .then((changed) => {
            // Update cache if key updated/inserted
            if (changed[1]) {
              _.set(this.config, k, times ? val[1][0] : val[1]);
            }
            return changed;
          });
        }))
        .then(result =>
          Promise.all(statements.map(x => x.finalize()))
          .then(() => result)
        )
        .catch(err =>
          Promise.all(statements.map(x => x.finalize()))
          .then(() => Promise.reject(err))
        );
      });
    }
  }

  /**
   *  Deletes config key(s)/value(s), returns whether delete successful
   */
  del_config (key, times = false, trx = null) {
    if (!key) {
      return Promise.reject(new Error(`Must specify key to be deleted`));
    }

    return trx
      ? _delcfg.call(this, trx)
      : this.db.transaction(_trx => _delcfg.call(this, _trx));

    function _delcfg (conn) {
      return Promise.all([
        conn.get(
          `SELECT key, time_ms AS time FROM config WHERE key = ?`,
          [key]
        ),
        conn.all(
          `SELECT key, time_ms AS time FROM config WHERE key LIKE (? || '.%')`,
          [key]
        ),
      ])
      .then(([ exact, prefixed ]) => {
        // Check to make sure we don't have both exact
        // and prefixed under the same key
        if (exact && prefixed.length) {
          throw new Error(`Found both exact and prefixed values for key ${key}`);
        }

        const postfn = ({ changes }) => {
          const changed = (changes > 0);
          // Delete config cache
          if (changed) {
            _.unset(this.config, key);
          }
          return changed;
        };
        const del_time = times
          ? times === true
            ? Date.now()
            : new Date(times).getTime()
          : false;

        if (exact) {
          return (!del_time || exact.time <= del_time)
            ? conn.run(`DELETE FROM config WHERE key = ?`, [key]).then(postfn)
            : false;
        }

        // Ensure all timestamps are at or below given time
        if (del_time) {
          let eligible = 0;
          for (let p of prefixed) {
            if (p.time <= del_time) {
              eligible++;
            }
          }

          // Delete all or none
          if (eligible !== prefixed.length) {
            return false;
          }
        }

        return conn.run(
          `DELETE FROM config WHERE key LIKE (? || '.%')`,
          [key]
        )
        .then(postfn);

      });
    }
  }

}

module.exports = BaseDB;
<|endoftext|>


<commit_before>/*jshint laxcomma:true, smarttabs: true */
'use strict';
/**
 * DESCRIPTION
 * @module tastypie/lib/api
 * @author Eric Satterwhite
 * @requires tastypie/lib/class
 * @requires tastypie/lib/class/options
 * @requires mout
 **/
var  Class           = require( './class' ) // this is Class
   , util            = require('util')
   , path            = require('path')
   , Boom            = require('hapi/node_modules/boom')
   , array           = require( 'mout/array' ) // custom mout
   , clone           = require( 'mout/lang/clone' ) // custom mout
   , toArray         = require( 'mout/lang/toArray' ) // custom mout
   , isObject        = require('mout/lang/isObject')
   , makePath        = require( 'mout/string/makePath' )
   , debug           = require('debug')('tastypie:api')
   , Options         = require( './class/options' )
   , pkg         	 = require('../package.json')
   , mime            = require('./mime')
   , startsWithSlash = /^\//
   , endsWithSlash   = /\/$/
   , trailingSlashes = /^\/|\/$/g
   , Api
   ;

/**
 * Provides namespaces for collections of resource ( api/v1, api/v2, etc )
 * @constructor
 * @alias module:tastypie/lib/api
 * @param {Object} options api configuration options
 * @example var x = new Api({});
 */
Api = Class( /** @lends module:NAME.Api.prototype */{
	mixin: Options

	,options:{
		DEFAULT_LIMIT:20
		,MAX_LIMIT:500
		, serializer: null
	}

	,constructor: function( path,  options ){
		// this.setOptions( app.settings.api )
		this.basepath = this.normalizePath( path );
		this.baseexp = new RegExp( "^" + this.basepath );
		this.setOptions( options );
		this.api_cache = {};
		this.url_cache = [];
		this.pending = []

		this.register = function( plugin, options, next ){
			this.plg = plugin;
			var resource;
			var that = this;

			for( var key in this.api_cache ){
				debug('registering plugin %s', key );
				this._register(key, this.api_cache[ key ] );
			}

			plugin.expose('name', pkg.name);
			plugin.expose('version', pkg.version);

			this.registered = true
			//////
			debug("serializer? %s", !!this.options.serializer)
			if( this.options.serializer ){
				debug("Loading serialization plugin")
				debug('adding server serializer for %s', this.basepath)
				plugin.ext('onPostAuth', function( request, reply ){

					var Serializer = this.options.serializer;
					// if it is a format hapi deals with, it has already parsed it
					//
					if( !request.payload || isObject( request.payload ) ){
						return reply.continue();
					}

					// if it is a string, lets try to deserialize it
					Serializer.deserialize( request.payload, request.headers['content-type'], function( err, data ){
					  if( data ){
						request.payload = data;
					  }
					  reply( err );
					});
				}.bind( this ));

				plugin.ext('onPreResponse', function(request, reply){

					debug('makign data %s', format)
					var response = request.response
					  , query    = request.query
					  , Serializer = this.options.serializer
					  , format
					  ;

					var fmt  = request.query && request.query.format
					var ct = this.options.serializer.convertFormat( fmt )

					if(fmt && !ct ){
						reply(new Boom.UnsupportedFormat( 'Unsupported format: ' + fmt ) )
					} else if( fmt && ct ){
						format = ct;
					} else{
						format = mime.determine( request, Serializer.types )
					}

					if( query.hasOwnProperty('callback') && !( query.hasOwnProperty('format') ) ){
					  return reply.continue();
					}

					if(!format){
					  return reply.continue();
					}

					if( response.isBoom ){
						return reply.continue();
					}
					Serializer.serialize( response.source, format, request.query, function(err, content ){

					  reply(content)
						.type(format);
					});
				}.bind( this ));
			}

			/////
			plugin.route({
				path: this.basepath
				,method:'GET'
				,handler: function( request, reply ){
					var data = {}, current;
					for( var name in that.api_cache ){
						current = that.api_cache[ name ]

						data[ name ] = {}
						current.routes.forEach( function( r ){
							options
							var plg = r.config.plugins.tastypie
							if( !plg.name ){
								return;
							}
							data[name][plg.name] = r.path
						})
					}
					reply( data )
				}
			})
			return next();
		}.bind( this )

		this.register.attributes = {
			name:'tastypie'
			,version:pkg.version
			,multiple: true
		};

	}
	, _register: function( name, resource ){
		this.plg.route( resource.routes );
	}
	/**
	 * This does someApi
	 * @param {TYPE} name DESCRPTION
	 * @param {TYPE} name DESCRIPTION
	 * @returns {TYPE} DESCRIPTION
	 */
	 ,add: function(/*prefix, resource*/){
	 	var prefix, router, resource
	 	if( arguments.length == 1){
	 		resource = arguments[0]
	 		prefix   = arguments[0].options.name || ''
		 	prefix   = prefix.replace(endsWithSlash,'');
	 	} else {
	 		resource = arguments[1];
	 		prefix   = arguments[0];
		 	prefix   = prefix.replace(endsWithSlash,'');
		 	resource.setOptions({
		 		name:prefix
		 		,apiname: this.basepath
		 	} );
	 	}
	 	this.api_cache[ prefix ] = resource
	 	resource.setOptions({
	 		apiname:this.basepath
	 		,name: resource.options.name || prefix
	 	})

	 	if( this.plg ){
			this._register( prefix, resource );

	 	}
		this.url_cache = [];
	 	return this;

	 }

	/**
	 * Normalizes a uri path by stripping trailing or adding leading slashed acordingly
	 * @param {String} path The path to normalize
	 * @param {TYPE} name DESCRIPTION
	 * @returns {String} The newly formated path
	 */
	 ,normalizePath: function( path ){
		return util.format(
			'/%s'
			, path.replace(trailingSlashes,'')
		)
	 }

});

// defines a quick url look up per API object
// it is cached on first call until another
// resource is registered
Object.defineProperties(Api.prototype,{
	urls:{
		get: function(){
			var routes
			if( !this.url_cache.length ){
				routes = this.plg && this.plg.table()[0].table || [];
				var baseexp = this.baseexp;

				this.url_cache = routes
					.map(function( r ){
						return r.path
					})
					.filter(function( r ){
						return baseexp.test( r )
					});
			}
			return this.url_cache;
		}
	}
})

module.exports = Api;
<commit_msg>[PIE-4] Make sure to carry the original status code after serializing<commit_after>/*jshint laxcomma:true, smarttabs: true */
'use strict';
/**
 * DESCRIPTION
 * @module tastypie/lib/api
 * @author Eric Satterwhite
 * @requires tastypie/lib/class
 * @requires tastypie/lib/class/options
 * @requires mout
 **/
var  Class           = require( './class' ) // this is Class
   , util            = require('util')
   , path            = require('path')
   , Boom            = require('hapi/node_modules/boom')
   , array           = require( 'mout/array' ) // custom mout
   , clone           = require( 'mout/lang/clone' ) // custom mout
   , toArray         = require( 'mout/lang/toArray' ) // custom mout
   , isObject        = require('mout/lang/isObject')
   , makePath        = require( 'mout/string/makePath' )
   , debug           = require('debug')('tastypie:api')
   , Options         = require( './class/options' )
   , pkg         	 = require('../package.json')
   , mime            = require('./mime')
   , startsWithSlash = /^\//
   , endsWithSlash   = /\/$/
   , trailingSlashes = /^\/|\/$/g
   , Api
   ;

/**
 * Provides namespaces for collections of resource ( api/v1, api/v2, etc )
 * @constructor
 * @alias module:tastypie/lib/api
 * @param {Object} options api configuration options
 * @example var x = new Api({});
 */
Api = Class( /** @lends module:NAME.Api.prototype */{
	mixin: Options

	,options:{
		DEFAULT_LIMIT:20
		,MAX_LIMIT:500
		, serializer: null
	}

	,constructor: function( path,  options ){
		// this.setOptions( app.settings.api )
		this.basepath = this.normalizePath( path );
		this.baseexp = new RegExp( "^" + this.basepath );
		this.setOptions( options );
		this.api_cache = {};
		this.url_cache = [];
		this.pending = []

		this.register = function( plugin, options, next ){
			this.plg = plugin;
			var resource;
			var that = this;

			for( var key in this.api_cache ){
				debug('registering plugin %s', key );
				this._register(key, this.api_cache[ key ] );
			}

			plugin.expose('name', pkg.name);
			plugin.expose('version', pkg.version);

			this.registered = true
			//////
			debug("serializer? %s", !!this.options.serializer)
			if( this.options.serializer ){
				debug("Loading serialization plugin")
				debug('adding server serializer for %s', this.basepath)
				plugin.ext('onPostAuth', function( request, reply ){

					var Serializer = this.options.serializer;
					// if it is a format hapi deals with, it has already parsed it
					//
					if( !request.payload || isObject( request.payload ) ){
						return reply.continue();
					}

					// if it is a string, lets try to deserialize it
					Serializer.deserialize( request.payload, request.headers['content-type'], function( err, data ){
					  if( data ){
						request.payload = data;
					  }
					  reply( err );
					});
				}.bind( this ));

				plugin.ext('onPreResponse', function(request, reply){

					debug('makign data %s', format)
					var response = request.response
					  , query    = request.query
					  , Serializer = this.options.serializer
					  , format
					  ;

					var fmt  = request.query && request.query.format
					var ct = this.options.serializer.convertFormat( fmt )

					if(fmt && !ct ){
						reply(new Boom.UnsupportedFormat( 'Unsupported format: ' + fmt ) )
					} else if( fmt && ct ){
						format = ct;
					} else{
						format = mime.determine( request, Serializer.types )
					}

					if( query.hasOwnProperty('callback') && !( query.hasOwnProperty('format') ) ){
					  return reply.continue();
					}

					if(!format){
					  return reply.continue();
					}

					if( response.isBoom ){
						return reply.continue();
					}
					Serializer.serialize( response.source, format, request.query, function( err, content ){

					  reply(content)
					  	.code( response.statusCode )
						.type(format);
					});
				}.bind( this ));
			}

			/////
			plugin.route({
				path: this.basepath
				,method:'GET'
				,handler: function( request, reply ){
					var data = {}, current;
					for( var name in that.api_cache ){
						current = that.api_cache[ name ]

						data[ name ] = {}
						current.routes.forEach( function( r ){
							options
							var plg = r.config.plugins.tastypie
							if( !plg.name ){
								return;
							}
							data[name][plg.name] = r.path
						})
					}
					reply( data )
				}
			})
			return next();
		}.bind( this )

		this.register.attributes = {
			name:'tastypie'
			,version:pkg.version
			,multiple: true
		};

	}
	, _register: function( name, resource ){
		this.plg.route( resource.routes );
	}
	/**
	 * This does someApi
	 * @param {TYPE} name DESCRPTION
	 * @param {TYPE} name DESCRIPTION
	 * @returns {TYPE} DESCRIPTION
	 */
	 ,add: function(/*prefix, resource*/){
	 	var prefix, router, resource
	 	if( arguments.length == 1){
	 		resource = arguments[0]
	 		prefix   = arguments[0].options.name || ''
		 	prefix   = prefix.replace(endsWithSlash,'');
	 	} else {
	 		resource = arguments[1];
	 		prefix   = arguments[0];
		 	prefix   = prefix.replace(endsWithSlash,'');
		 	resource.setOptions({
		 		name:prefix
		 		,apiname: this.basepath
		 	} );
	 	}
	 	this.api_cache[ prefix ] = resource
	 	resource.setOptions({
	 		apiname:this.basepath
	 		,name: resource.options.name || prefix
	 	})

	 	if( this.plg ){
			this._register( prefix, resource );

	 	}
		this.url_cache = [];
	 	return this;

	 }

	/**
	 * Normalizes a uri path by stripping trailing or adding leading slashed acordingly
	 * @param {String} path The path to normalize
	 * @param {TYPE} name DESCRIPTION
	 * @returns {String} The newly formated path
	 */
	 ,normalizePath: function( path ){
		return util.format(
			'/%s'
			, path.replace(trailingSlashes,'')
		)
	 }

});

// defines a quick url look up per API object
// it is cached on first call until another
// resource is registered
Object.defineProperties(Api.prototype,{
	urls:{
		get: function(){
			var routes
			if( !this.url_cache.length ){
				routes = this.plg && this.plg.table()[0].table || [];
				var baseexp = this.baseexp;

				this.url_cache = routes
					.map(function( r ){
						return r.path
					})
					.filter(function( r ){
						return baseexp.test( r )
					});
			}
			return this.url_cache;
		}
	}
})

module.exports = Api;
<|endoftext|>


<commit_before>/*
 * benc2json
 * https://github.com/pwmckenna/benc2json
 *
 * Copyright (c) 2013 Patrick Williams
 * Licensed under the MIT license.
 */
var express = require('express');
var Firebase = require('./firebase-node');
var http = require('http');
var URL = require('url');
var q = require('q');
var request = require('request');
var URL = require('url');

var MAGNET_LINK_IDENTIFIER = 'magnet:?xt=urn:btih:';
var firebase = new Firebase('https://todium.firebaseio.com/');

var addFirebaseTracker = function(firebase, id, info_hash, src) {
    console.log('addFirebaseTracker', id, info_hash, src);

    var campaignTracker = firebase.child('campaigns').child(id).child('trackers').push();
    var globalTracker = firebase.child('trackers').push();
    globalTracker.set({
        campaign: id,
        info_hash: info_hash,
        stats: {
            completed: 0,
            transferred: 0,
        },
        src: src,
        time: new Date().getTime()
    });
    campaignTracker.set(globalTracker.name());
    return globalTracker;
}

var addFirebaseTorrent = function(firebase, id, tracker, torrent) {
    var file = URL.parse(torrent).pathname;
    file = file.substring(file.lastIndexOf('/'));
    var shortened = firebase.child('torrent').push({
        tracker: tracker,
        torrent: torrent,
        campaign: id
    });
    return 'http://torrent.todium.com/' + shortened.name() + '.torrent';
}

var addLinkRequest = function(firebase, id, src) {
    console.log('addLinkRequest', id, src);

    var ret = new q.defer();
    if(src.indexOf(MAGNET_LINK_IDENTIFIER) === 0) {
        console.log('replacing magnet link');
        var info_hash = src.substr(MAGNET_LINK_IDENTIFIER.length, 40);
        var tracker = addFirebaseTracker(firebase, id, info_hash, src);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = src + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        ret.resolve(trackable);
    } else {
        console.log('replacing torrent file', src);
        request.get({
            url: 'http://hasher.todium.com',
            qs: {
                torrent: src
            },
            json: true
        }, function (error, response, info_hash) {
            console.log(error, response.statusCode, info_hash);
            if(error || typeof info_hash !== 'string' || info_hash.length !== 40) {
                ret.reject(error);
                return;
            } else {
                var tracker = addFirebaseTracker(firebase, id, info_hash, src);
                var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
                var trackable = addFirebaseTorrent(firebase, id, url, src);
                tracker.child('trackable').set(trackable);
                ret.resolve(trackable);
            }
        });
    }
    return ret.promise;
};

var authenticationRequest = function(id, secret) {
    console.log('authenticationRequest', id, secret);
    var ret = new q.defer();
    firebase.child('campaigns').child(id).child('secret').once('value', function(secretSnapshot) {
        if(secretSnapshot.val() !== secret) {
            var error = 'invalid campaign secret';
            console.error(error);
            return ret.reject(error);
        } else {
            return ret.resolve();
        }
    });
    return ret.promise;
};

var port = process.env.PORT || 5050;
console.log('port', port);
var app = express();
app.use(express.bodyParser());
app.post('/', function(req, res) {
    var abort = function(err) {
        res.send(400, err);
    };

    // if we don't have a url argument, then lets bail
    if(!req.body.hasOwnProperty('id') ||
        !req.body.hasOwnProperty('secret') ||
        !req.body.hasOwnProperty('src')
    ) {
        abort('src/id/secret mandatory');
        return;
    }
    var id = req.body['id'];
    var secret = req.body['secret'];
    var src = req.body['src'];

    console.log('id', id);
    console.log('secret', secret);
    console.log('src', src);

    authenticationRequest(id, secret).then(function() {
        addLinkRequest(firebase, id, src).then(function(link) {
            res.send(200, link);
        }, abort);
    }, abort);
}).listen(port, function() {
    console.log('Listening on ' + port);
});<commit_msg>adding support for info hash to tracker conversions via the api<commit_after>/*
 * benc2json
 * https://github.com/pwmckenna/benc2json
 *
 * Copyright (c) 2013 Patrick Williams
 * Licensed under the MIT license.
 */
var express = require('express');
var Firebase = require('./firebase-node');
var http = require('http');
var URL = require('url');
var q = require('q');
var request = require('request');
var URL = require('url');

var MAGNET_LINK_IDENTIFIER = 'magnet:?xt=urn:btih:';
var firebase = new Firebase('https://todium.firebaseio.com/');

var addFirebaseTracker = function(firebase, id, info_hash, src) {
    console.log('addFirebaseTracker', id, info_hash, src);

    var campaignTracker = firebase.child('campaigns').child(id).child('trackers').push();
    var globalTracker = firebase.child('trackers').push();
    globalTracker.set({
        campaign: id,
        info_hash: info_hash,
        stats: {
            completed: 0,
            transferred: 0,
        },
        src: src,
        time: new Date().getTime()
    });
    campaignTracker.set(globalTracker.name());
    return globalTracker;
}

var addFirebaseTorrent = function(firebase, id, tracker, torrent) {
    var file = URL.parse(torrent).pathname;
    file = file.substring(file.lastIndexOf('/'));
    var shortened = firebase.child('torrent').push({
        tracker: tracker,
        torrent: torrent,
        campaign: id
    });
    return 'http://torrent.todium.com/' + shortened.name() + '.torrent';
}

var addLinkRequest = function(firebase, id, src) {
    console.log('addLinkRequest', id, src);

    var ret = new q.defer();
    if(src.match(/[0-9A-Fa-f]{40}/)) {
        var info_hash = src;
        var tracker = addFirebaseTracker(firebase, id, info_hash, MAGNET_LINK_IDENTIFIER + info_hash);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = MAGNET_LINK_IDENTIFIER + info_hash + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        ret.resolve(url);
    } else if(src.indexOf(MAGNET_LINK_IDENTIFIER) === 0) {
        console.log('replacing magnet link');
        var info_hash = src.substr(MAGNET_LINK_IDENTIFIER.length, 40);
        var tracker = addFirebaseTracker(firebase, id, info_hash, src);
        var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
        var trackable = src + '&tr=' + url;
        tracker.child('trackable').set(trackable);
        ret.resolve(trackable);
    } else {
        console.log('replacing torrent file', src);
        request.get({
            url: 'http://hasher.todium.com',
            qs: {
                torrent: src
            },
            json: true
        }, function (error, response, info_hash) {
            console.log(error, response.statusCode, info_hash);
            if(error || typeof info_hash !== 'string' || info_hash.length !== 40) {
                ret.reject(error);
                return;
            } else {
                var tracker = addFirebaseTracker(firebase, id, info_hash, src);
                var url = 'http://tracker.todium.com/' + tracker.name() + '/announce';
                var trackable = addFirebaseTorrent(firebase, id, url, src);
                tracker.child('trackable').set(trackable);
                ret.resolve(trackable);
            }
        });
    }
    return ret.promise;
};

var authenticationRequest = function(id, secret) {
    console.log('authenticationRequest', id, secret);
    var ret = new q.defer();
    firebase.child('campaigns').child(id).child('secret').once('value', function(secretSnapshot) {
        if(secretSnapshot.val() !== secret) {
            var error = 'invalid campaign secret';
            console.error(error);
            return ret.reject(error);
        } else {
            return ret.resolve();
        }
    });
    return ret.promise;
};

var port = process.env.PORT || 5051;
console.log('port', port);
var app = express();
app.use(express.bodyParser());
app.post('/', function(req, res) {
    var abort = function(err) {
        res.send(400, err);
    };

    // if we don't have a url argument, then lets bail
    if(!req.body.hasOwnProperty('id') ||
        !req.body.hasOwnProperty('secret') ||
        !req.body.hasOwnProperty('src')
    ) {
        abort('src/id/secret mandatory');
        return;
    }
    var id = req.body['id'];
    var secret = req.body['secret'];
    var src = req.body['src'];

    console.log('id', id);
    console.log('secret', secret);
    console.log('src', src);

    authenticationRequest(id, secret).then(function() {
        addLinkRequest(firebase, id, src).then(function(link) {
            res.send(200, link);
        }, abort);
    }, abort);
}).listen(5051, function() {
    console.log('Listening on ' + port);
});<|endoftext|>


<commit_before>// /api endpoint and authentication.

var log = require('./log');
var fs = require('./fs');
var pg = require('pg');
var configuration = require('./conf');
var pgConfig = configuration.pg;
pgConfig.pg = pg;
var EmailLogin = require('email-login');
var emailLogin = new EmailLogin({
  db: new EmailLogin.PgDb(pgConfig),
  mailer: configuration.mailer,
});
var website = configuration.website;

exports.main = function (camp) {
  camp.post('/api/1/signup', signup);
  camp.post('/api/1/signin', signin);
  camp.path('/api/1/login', login);
  camp.path('/api/1/logback', logback);
  camp.post('/api/1/logout', logout);
  camp.get('/~', home);
  camp.handle(authenticate);
};

function error(code, msg, err, res) {
  if (err) { log.error(err); }
  res.statusCode = code || 500;
  res.json({errors: [msg || 'Internal Server Error']});
}

// The following should be considered a valid name: 0ééλ統𝌆
var allowedUsernames = /^[\w\xa0-\u1fff\u2c00-\u2dff\u2e80-\ud7ff\uf900-\uffef\u{10000}-\u{2fa1f}]{1,20}$/u;
var reservedNames = /^(file|block|snap|root|lib|api|test|app|about|demo|doc|\w|\w\w)$/;

function allowedUsername(name) {
  if (!allowedUsernames.test(name) || reservedNames.test(name)) {
    return Promise.resolve(false);
  }
  return fs.meta('/' + name).then(function(meta) {
    return meta === undefined;
  });
}

function signup(req, res) {
  var email = req.data.email;
  var name = req.data.name;
  allowedUsername(name).then(function(allowed) {
    if (!allowed) {
      error(400, "Disallowed name", null, res);
      return;
    }
    emailConfirmation(req, res, {
      email: email,
      name: name,
      subject: 'TheFileTree account creation: confirm your email address',
      confirmUrl: function(tok) {
        return website + "/api/1/login?token=" + encodeURIComponent(tok) +
          '&name=' + encodeURIComponent(name);
      },
    }, function(err) {
      if (err != null) { error(500, "Failed to send email", err, res); return; }
      res.end();
    });
  });
}

// Prepare logging back in.
function signin(req, res) {
  var email = req.data.email;
  emailConfirmation(req, res, {
    email: email,
    subject: 'TheFileTree account log in: confirm your email address',
    confirmUrl: function(tok) {
      return website + "/api/1/logback?token=" + encodeURIComponent(tok);
    },
  }, function(err) {
    if (err != null) { error(500, "Failed to send email", err, res); return; }
    res.end();
  });
}

// options:
// - email: address to send the email to.
// - name: (optional) name of the user to create.
// - subject: content of the email's subject line.
// - confirmUrl: function(token: String) returns the confirmation link URL.
// callback: run when the email is sent.
function emailConfirmation(req, res, options = {}, callback) {
  var email = options.email;
  var name = options.name;
  if (!email) { error(400, "Empty email", null, res); }
  emailLogin.login(function(err, token, session) {
    if (err != null) { error(500, "Sign up failed", err, res); return; }
    req.cookies.set('token', token);
    emailLogin.proveEmail({
      token: token,
      email: email,
      name: options.subject || 'TheFileTree',
      confirmUrl: options.confirmUrl,
    }, function(err) {
      if (err != null) {
        error(500, "Sending the email confirmation failed", err, res);
        return;
      }
      callback();
    });
  });
}

function login(req, res) {
  var name = req.data.name;
  emailLogin.confirmEmail(req.cookies.get('token'), req.data.token,
  function(err, token, session) {
    if (err != null) {
      log.error(err);
      res.redirect('/app/account/email-not-confirmed.html');
      return;
    }
    if (token) {
      var home = '/' + name;
      fs.create(home, { type: 'folder' })
      .then(function() { return fs.meta(home); })
      .then(function(meta) {
        meta.acl = {};
        meta.acl[name] = 'x';
        meta.acl['*'] = '-';
        return fs.updateMeta(home, meta);
      }).then(function() {
        emailLogin.setAccountData(session.email, {name: name}, function(err) {
          if (err != null) { error(500, "Login failed", err, res); return; }
          req.cookies.set('token', token);
          res.redirect('/app/account/logged-in.html');
        });
      }).catch(function(err) {
        error(400, "Failed to create your home folder, " + name, err, res);
      });
    } else {
      res.redirect('/app/account/email-not-confirmed.html');
    }
  });
}

function logback(req, res) {
  emailLogin.confirmEmail(req.cookies.get('token'), req.data.token,
  function(err, token, session) {
    if (err != null) {
      log.error(err);
      res.redirect('/app/account/email-not-confirmed.html');
      return;
    }
    if (token) {
      req.cookies.set('token', token);
      res.redirect('/app/account/logged-back.html');
    } else {
      res.redirect('/app/account/email-not-confirmed.html');
    }
  });
}

function logout(req, res) {
  var token = req.cookies.get('token');
  req.cookies.set('token');
  res.redirect('/app/account/');
  // Clearing database information is not safety-critical.
  emailLogin.logout(token, function(err) {
    if (err) { log.error(err); }
  });
}

function home(req, res) {
  if (req.user && (typeof req.user.name === 'string')) {
    res.redirect('/' + req.user.name);
  } else { res.redirect('/'); }
}

function authenticate(req, res, next) {
  emailLogin.authenticate(req.cookies.get('token'),
  function(err, authenticated, session, token) {
    if (token) { req.cookies.set('token', token); }
    if (authenticated && session.emailVerified()) {
      req.user = {
        email: session.email,
        name: session.account.data.name,
      };
    }
    next();
  });
}
exports.authenticate = authenticate;
<commit_msg>Normalize account names to Unicode NFC<commit_after>// /api endpoint and authentication.

var log = require('./log');
var fs = require('./fs');
var pg = require('pg');
var configuration = require('./conf');
var pgConfig = configuration.pg;
pgConfig.pg = pg;
var EmailLogin = require('email-login');
var emailLogin = new EmailLogin({
  db: new EmailLogin.PgDb(pgConfig),
  mailer: configuration.mailer,
});
var website = configuration.website;

exports.main = function (camp) {
  camp.post('/api/1/signup', signup);
  camp.post('/api/1/signin', signin);
  camp.path('/api/1/login', login);
  camp.path('/api/1/logback', logback);
  camp.post('/api/1/logout', logout);
  camp.get('/~', home);
  camp.handle(authenticate);
};

function error(code, msg, err, res) {
  if (err) { log.error(err); }
  res.statusCode = code || 500;
  res.json({errors: [msg || 'Internal Server Error']});
}

// The following should be considered a valid name: 0ééλ統𝌆
// Names are normalized in NFC.
var allowedUsernames = /^[\w\xa0-\u1fff\u2c00-\u2dff\u2e80-\ud7ff\uf900-\uffef\u{10000}-\u{2fa1f}]{1,20}$/u;
var reservedNames = /^(file|block|snap|root|lib|api|test|app|about|demo|doc|\w|\w\w)$/;

function allowedUsername(name) {
  if (!allowedUsernames.test(name) || reservedNames.test(name)) {
    return Promise.resolve(false);
  }
  return fs.meta('/' + name).then(function(meta) {
    return meta === undefined;
  });
}

function normalizeNFC(name) {
  if (typeof name === 'string') { return name.normalize(); }
}

function signup(req, res) {
  var email = req.data.email;
  var name = normalizeNFC(req.data.name;
  allowedUsername(name).then(function(allowed) {
    if (!allowed) {
      error(400, "Disallowed name", null, res);
      return;
    }
    emailConfirmation(req, res, {
      email: email,
      name: name,
      subject: 'TheFileTree account creation: confirm your email address',
      confirmUrl: function(tok) {
        return website + "/api/1/login?token=" + encodeURIComponent(tok) +
          '&name=' + encodeURIComponent(name);
      },
    }, function(err) {
      if (err != null) { error(500, "Failed to send email", err, res); return; }
      res.end();
    });
  });
}

// Prepare logging back in.
function signin(req, res) {
  var email = req.data.email;
  emailConfirmation(req, res, {
    email: email,
    subject: 'TheFileTree account log in: confirm your email address',
    confirmUrl: function(tok) {
      return website + "/api/1/logback?token=" + encodeURIComponent(tok);
    },
  }, function(err) {
    if (err != null) { error(500, "Failed to send email", err, res); return; }
    res.end();
  });
}

// options:
// - email: address to send the email to.
// - name: (optional) name of the user to create.
// - subject: content of the email's subject line.
// - confirmUrl: function(token: String) returns the confirmation link URL.
// callback: run when the email is sent.
function emailConfirmation(req, res, options = {}, callback) {
  var email = options.email;
  var name = normalizeNFC(options.name);
  if (!email) { error(400, "Empty email", null, res); }
  emailLogin.login(function(err, token, session) {
    if (err != null) { error(500, "Sign up failed", err, res); return; }
    req.cookies.set('token', token);
    emailLogin.proveEmail({
      token: token,
      email: email,
      name: options.subject || 'TheFileTree',
      confirmUrl: options.confirmUrl,
    }, function(err) {
      if (err != null) {
        error(500, "Sending the email confirmation failed", err, res);
        return;
      }
      callback();
    });
  });
}

function login(req, res) {
  var name = normalizeNFC(req.data.name);
  emailLogin.confirmEmail(req.cookies.get('token'), req.data.token,
  function(err, token, session) {
    if (err != null) {
      log.error(err);
      res.redirect('/app/account/email-not-confirmed.html');
      return;
    }
    if (token) {
      var home = '/' + name;
      fs.create(home, { type: 'folder' })
      .then(function() { return fs.meta(home); })
      .then(function(meta) {
        meta.acl = {};
        meta.acl[name] = 'x';
        meta.acl['*'] = '-';
        return fs.updateMeta(home, meta);
      }).then(function() {
        emailLogin.setAccountData(session.email, {name: name}, function(err) {
          if (err != null) { error(500, "Login failed", err, res); return; }
          req.cookies.set('token', token);
          res.redirect('/app/account/logged-in.html');
        });
      }).catch(function(err) {
        error(400, "Failed to create your home folder, " + name, err, res);
      });
    } else {
      res.redirect('/app/account/email-not-confirmed.html');
    }
  });
}

function logback(req, res) {
  emailLogin.confirmEmail(req.cookies.get('token'), req.data.token,
  function(err, token, session) {
    if (err != null) {
      log.error(err);
      res.redirect('/app/account/email-not-confirmed.html');
      return;
    }
    if (token) {
      req.cookies.set('token', token);
      res.redirect('/app/account/logged-back.html');
    } else {
      res.redirect('/app/account/email-not-confirmed.html');
    }
  });
}

function logout(req, res) {
  var token = req.cookies.get('token');
  req.cookies.set('token');
  res.redirect('/app/account/');
  // Clearing database information is not safety-critical.
  emailLogin.logout(token, function(err) {
    if (err) { log.error(err); }
  });
}

function home(req, res) {
  if (req.user && (typeof req.user.name === 'string')) {
    res.redirect('/' + req.user.name.normalize());
  } else { res.redirect('/'); }
}

function authenticate(req, res, next) {
  emailLogin.authenticate(req.cookies.get('token'),
  function(err, authenticated, session, token) {
    if (token) { req.cookies.set('token', token); }
    if (authenticated && session.emailVerified()) {
      req.user = {
        email: session.email,
        name: normalizeNFC(session.account.data.name),
      };
    }
    next();
  });
}
exports.authenticate = authenticate;
<|endoftext|>


<commit_before>// @require Auger.Event
window.Auger || (window.Auger = {});
Auger.Dispatcher = {_actions: []};
Auger.Dispatcher._onhashchange = function(e){ 	// gets detached (avoid unnecessary closure)
	if(!e)e=window.event;
	var a=Auger.Dispatcher._actions;
	var h=Auger.Dispatcher.getHash();
	for(var i=0;i<a.length;++i){
		var v = null, t = a[i][1];
		switch(a[i][0]){
			case 0: v=true; break;
			case 1: v=(h.indexOf(t)==0?h.split(t.charAt(t.length-1)):null); break;
			case 2: v=h.match(t); break;
			case 3: try{v=t(h);}catch(e){v=null;}; break;
		}
		if(v) try{ 	// don't let the errors of one dispatched method keep the others from executing
			a[i][2].call(this, e, h, v); 	// call as a method on the element and send event (for consistency)
		}catch(e){}
	}
};
// prefix string, regex, or boolean function (allows multiple "apps" on the same page)
Auger.Dispatcher._dispatch_types = [null, 'string', RegExp, Function];
Auger.Dispatcher.dispatch = function(c,f){
	var a = Auger.Dispatcher; 	// this
	var t = a._dispatch_types, d = null;
	if(!f)
		d = [0,null,c];
	else if(typeof(c) == 'string')
		d = [1,c,f];
	else
		for(var i=2; i<t.length; ++i)
			if(c instanceof t[i])
				d = [i,c,f];
	if(d)
		a._actions.push(d);
	else
		throw("Unknown argument: " + c);
	a._lastHash = a.getHash(); 	// requires the page to load the initial hash
	if(a._actions.length == 1) 	// only add it the first time since we roll through all the possibilities
		a.listen(a._onhashchange);
};
Auger.Dispatcher.getHash = function(){
	//var h = location.hash; return h ? h.replace(/^#+/,'') : location.href.replace(/^[^#]*#/,'');
	return location.hash.replace(/^#+/,'');
};
Auger.Dispatcher.setHash = function(h){
	if( h.charAt(0) != '#' ) h = '#' + h;
	var f;
	if((f = this._iframe)) f.src = f.src.replace(/#.*$/, h);
	location.hash = h;
};
Auger.Dispatcher.listen = function(f){ 			// override with desired event library
	var e = 'hashchange';
	var oe = 'on'+e;
	if(Auger.Event) Auger.Event.add(window, e, f);
	else if(oe in window) window[oe] = f;
	else throw('Event library required if window does not support '+oe);
}
<commit_msg>Remove reference to iframe (that's the job of the Event library, not the Dispatcher)<commit_after>// @require Auger.Event
window.Auger || (window.Auger = {});
Auger.Dispatcher = {_actions: []};
Auger.Dispatcher._onhashchange = function(e){ 	// gets detached (avoid unnecessary closure)
	if(!e)e=window.event;
	var a=Auger.Dispatcher._actions;
	var h=Auger.Dispatcher.getHash();
	for(var i=0;i<a.length;++i){
		var v = null, t = a[i][1];
		switch(a[i][0]){
			case 0: v=true; break;
			case 1: v=(h.indexOf(t)==0?h.split(t.charAt(t.length-1)):null); break;
			case 2: v=h.match(t); break;
			case 3: try{v=t(h);}catch(e){v=null;}; break;
		}
		if(v) try{ 	// don't let the errors of one dispatched method keep the others from executing
			a[i][2].call(this, e, h, v); 	// call as a method on the element and send event (for consistency)
		}catch(e){}
	}
};
// prefix string, regex, or boolean function (allows multiple "apps" on the same page)
Auger.Dispatcher._dispatch_types = [null, 'string', RegExp, Function];
Auger.Dispatcher.dispatch = function(c,f){
	var a = Auger.Dispatcher; 	// this
	var t = a._dispatch_types, d = null;
	if(!f)
		d = [0,null,c];
	else if(typeof(c) == 'string')
		d = [1,c,f];
	else
		for(var i=2; i<t.length; ++i)
			if(c instanceof t[i])
				d = [i,c,f];
	if(d)
		a._actions.push(d);
	else
		throw("Unknown argument: " + c);
	a._lastHash = a.getHash(); 	// requires the page to load the initial hash
	if(a._actions.length == 1) 	// only add it the first time since we roll through all the possibilities
		a.listen(a._onhashchange);
};
Auger.Dispatcher.getHash = function(){
	//var h = location.hash; return h ? h.replace(/^#+/,'') : location.href.replace(/^[^#]*#/,'');
	return location.hash.replace(/^#+/,'');
};
Auger.Dispatcher.setHash = function(h){
	if(h.charAt(0) != '#') h = '#'+h;
	location.hash = h;
};
Auger.Dispatcher.listen = function(f){ 			// override with desired event library
	var e = 'hashchange';
	var oe = 'on'+e;
	if(Auger.Event) Auger.Event.add(window, e, f);
	else if(oe in window) window[oe] = f;
	else throw('Event library required if window does not support '+oe);
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

const path = require('path');
const packageInfo = require('../package.json'),
    config = require('./config'),
    utils = require('./utils'),
    commands = require('./commands');

const debug = require('debug')('sockbot');

/**
 * Construct a stringified message to log
 *
 * @param {Array<*>} args Item to stringify and log
 * @returns {string} stringified message
 */
exports._buildMessage = function _buildMessage(args) {
    if (!args || args.length < 1) {
        return '';
    }
    if (!Array.isArray(args)) {
        args = Array.prototype.slice.apply(args);
    }
    args.unshift(`[${new Date().toISOString()}]`);
    return args
        .map((part) => typeof part === 'string' ? part : JSON.stringify(part, null, '\t'))
        .join(' ');
};

/**
 * Log a message to stdout
 *
 * @param {...*} message Message to log to stdout
 */
exports.log = function log() {
    console.log(exports._buildMessage(arguments)); // eslint-disable-line no-console
};

/**
 * Log a message to stderr
 *
 * @param {...*} message Message to log to stderr
 */
exports.error = function error() {
    console.error(exports._buildMessage(arguments)); // eslint-disable-line no-console
};

/**
 * Load a module relative to a local path, or relative to loaded config file
 *
 * @param {string} relativePath Local path to use
 * @param {string} module Module to load
 * @param {function} requireIt Function to use to load module
 * @returns {object | function} Loaded module
 */
exports.relativeRequire = function relativeRequire(relativePath, module, requireIt) {
    try {
        let resolved = `${__dirname}/../${relativePath}/${module}`;
        if (module.startsWith('/') || module.startsWith('./') || module.startsWith('../')) {
            resolved = path.posix.resolve(config.basePath, module);
        }
        debug(`requiring ${relativePath} ${module} as ${resolved}`);
        // Look in plugins first
        return requireIt(resolved);
    } catch (err) {
        // Error! check if it's ENOENT and try raw module
        if (/^Cannot find module/.test(err.message)) {
            debug(`retrying requiring ${relativePath} ${module} as raw`);
            return requireIt(module);
        }
        // Rethrow error if it wasn't ENOENT
        throw err;
    }
};

/**
 * Load plugins for forum instance
 *
 * @param {Provider} forumInstance Provider instance to load plugins into
 * @param {object} botConfig Bot configuration to load plugins with
 */
exports.loadPlugins = function loadPlugins(forumInstance, botConfig) {
    return Promise.all(Object.keys(botConfig.plugins).map((name) => {
        exports.log(`Loading plugin ${name} for ${botConfig.core.username}`);
        const plugin = exports.relativeRequire('plugins', name, require);
        const pluginConfig = botConfig.plugins[name];
        return forumInstance.addPlugin(plugin, pluginConfig).catch((err) => {
            exports.error(`Plugin ${name} failed to load with error: ${err}`);
            throw err;
        });
    }));
};

/**
 * Activate a loaded configuration.
 *
 * @param {object} botConfig Configuration to activate
 * @returns {Promise} Resolves when configuration is fully activated
 */
exports.activateConfig = function activateConfig(botConfig) {
    const Provider = exports.relativeRequire('providers', botConfig.core.provider, require);
    exports.log(`Using provider ${botConfig.core.provider} for ${botConfig.core.username}`);
    const instance = new Provider(botConfig);
    instance.on('log', exports.log);
    instance.on('error', exports.error);
    instance.on('logExtended', utils.logExtended);
    instance.Commands = commands.bindCommands(instance);
    return exports.loadPlugins(instance, botConfig)
        .then(() => {
            exports.log(`${botConfig.core.username} ready for login`);
        })
        .then(() => instance.login())
        .then(() => {
            exports.log(`${botConfig.core.username} login successful`);
            return instance.activate();
        })
        .then(() => exports.log(`${botConfig.core.username} activated`));
};

exports.require = require;

/* istanbul ignore if */
if (require.main === module) {
    const yargs = require('yargs');
    const argv = yargs
        .usage('Usage: $0 <cfgFile> [options]')
        .demand(1, 1, 'A valid configuration file must be provided')
        .describe({
            checkCfg: 'Check the validity of the configuration file without starting the bot'
        })
        .argv;
    exports.log(`Starting Sockbot ${packageInfo.version} - ${packageInfo.releaseName}`);
    config.load(argv._[0])
        .then((cfg) => {
            exports.log(`Loaded configuration file: ${argv._[0]}`);
            return cfg;
        })
        .then((cfg) => {
            const promises = cfg.map((botConfig) => {
                exports.log(`Activating logon: ${botConfig.core.username}`);
                return exports.activateConfig(botConfig)
                    .catch((err) => exports.error(err, err.stack));
            });
            return Promise.all(promises);
        })
        .catch((err) => exports.error('Fatal Startup Error:\n', err.message || err));
}
<commit_msg> oops.... fix jsdocs<commit_after>#!/usr/bin/env node

'use strict';

const path = require('path');
const packageInfo = require('../package.json'),
    config = require('./config'),
    utils = require('./utils'),
    commands = require('./commands');

const debug = require('debug')('sockbot');

/**
 * Construct a stringified message to log
 *
 * @param {Array<*>} args Item to stringify and log
 * @returns {string} stringified message
 */
exports._buildMessage = function _buildMessage(args) {
    if (!args || args.length < 1) {
        return '';
    }
    if (!Array.isArray(args)) {
        args = Array.prototype.slice.apply(args);
    }
    args.unshift(`[${new Date().toISOString()}]`);
    return args
        .map((part) => typeof part === 'string' ? part : JSON.stringify(part, null, '\t'))
        .join(' ');
};

/**
 * Log a message to stdout
 *
 * @param {...*} message Message to log to stdout
 */
exports.log = function log() {
    console.log(exports._buildMessage(arguments)); // eslint-disable-line no-console
};

/**
 * Log a message to stderr
 *
 * @param {...*} message Message to log to stderr
 */
exports.error = function error() {
    console.error(exports._buildMessage(arguments)); // eslint-disable-line no-console
};

/**
 * Load a module relative to a local path, or relative to loaded config file
 *
 * @param {string} relativePath Local path to use
 * @param {string} module Module to load
 * @param {function} requireIt Function to use to load module
 * @returns {object | function} Loaded module
 */
exports.relativeRequire = function relativeRequire(relativePath, module, requireIt) {
    try {
        let resolved = `${__dirname}/../${relativePath}/${module}`;
        if (module.startsWith('/') || module.startsWith('./') || module.startsWith('../')) {
            resolved = path.posix.resolve(config.basePath, module);
        }
        debug(`requiring ${relativePath} ${module} as ${resolved}`);
        // Look in plugins first
        return requireIt(resolved);
    } catch (err) {
        // Error! check if it's ENOENT and try raw module
        if (/^Cannot find module/.test(err.message)) {
            debug(`retrying requiring ${relativePath} ${module} as raw`);
            return requireIt(module);
        }
        // Rethrow error if it wasn't ENOENT
        throw err;
    }
};

/**
 * Load plugins for forum instance
 *
 * @param {Provider} forumInstance Provider instance to load plugins into
 * @param {object} botConfig Bot configuration to load plugins with
 * @returns {Promise} Resolves when plugins have been loaded
 */
exports.loadPlugins = function loadPlugins(forumInstance, botConfig) {
    return Promise.all(Object.keys(botConfig.plugins).map((name) => {
        exports.log(`Loading plugin ${name} for ${botConfig.core.username}`);
        const plugin = exports.relativeRequire('plugins', name, require);
        const pluginConfig = botConfig.plugins[name];
        return forumInstance.addPlugin(plugin, pluginConfig).catch((err) => {
            exports.error(`Plugin ${name} failed to load with error: ${err}`);
            throw err;
        });
    }));
};

/**
 * Activate a loaded configuration.
 *
 * @param {object} botConfig Configuration to activate
 * @returns {Promise} Resolves when configuration is fully activated
 */
exports.activateConfig = function activateConfig(botConfig) {
    const Provider = exports.relativeRequire('providers', botConfig.core.provider, require);
    exports.log(`Using provider ${botConfig.core.provider} for ${botConfig.core.username}`);
    const instance = new Provider(botConfig);
    instance.on('log', exports.log);
    instance.on('error', exports.error);
    instance.on('logExtended', utils.logExtended);
    instance.Commands = commands.bindCommands(instance);
    return exports.loadPlugins(instance, botConfig)
        .then(() => {
            exports.log(`${botConfig.core.username} ready for login`);
        })
        .then(() => instance.login())
        .then(() => {
            exports.log(`${botConfig.core.username} login successful`);
            return instance.activate();
        })
        .then(() => exports.log(`${botConfig.core.username} activated`));
};

exports.require = require;

/* istanbul ignore if */
if (require.main === module) {
    const yargs = require('yargs');
    const argv = yargs
        .usage('Usage: $0 <cfgFile> [options]')
        .demand(1, 1, 'A valid configuration file must be provided')
        .describe({
            checkCfg: 'Check the validity of the configuration file without starting the bot'
        })
        .argv;
    exports.log(`Starting Sockbot ${packageInfo.version} - ${packageInfo.releaseName}`);
    config.load(argv._[0])
        .then((cfg) => {
            exports.log(`Loaded configuration file: ${argv._[0]}`);
            return cfg;
        })
        .then((cfg) => {
            const promises = cfg.map((botConfig) => {
                exports.log(`Activating logon: ${botConfig.core.username}`);
                return exports.activateConfig(botConfig)
                    .catch((err) => exports.error(err, err.stack));
            });
            return Promise.all(promises);
        })
        .catch((err) => exports.error('Fatal Startup Error:\n', err.message || err));
}
<|endoftext|>


<commit_before>var url = require('url');
var crc = require('crc').crc16xmodem;
var util = require('util');
var debug = require('debug')('smart-bus');
var dgram = require('dgram');
var Device = require('./device');
var Command = require('./command');
var EventEmitter = require('events');

module.exports = Bus;

var Constants = new Buffer(12);

Constants.write('HDLMIRACLE');
Constants.writeUInt16BE(0xAAAA, 10);

function Bus(resource) {
  var config = parse(resource);
  var device = config.device ? config.device.split('.') : [];

  var id = config.id || device[1];
  var port = config.port;
  var subnet = config.subnet || device[0];

  this.id = parseInt(id);
  this.type = 0xFFFE;
  this.subnet = parseInt(subnet);
  this.address = new Buffer([0, 0, 0, 0]);

  this.port = port ? parseInt(port) : 6000;
  this.gateway = config.gateway;

  this.socket = createSocket(this);

  this.devices = {};

  EventEmitter.call(this);
}

util.inherits(Bus, EventEmitter);

/**
 * Return device object by its address
 *
 * @param  {Number|String} subnet or address Subnet or address, e.g. '1.4'
 * @param  {Number} [id]                     Device id
 * @return {Device}
 */
Bus.prototype.device = function(address, id) {
  var subnet = address;

  if (typeof address === 'number') address = address + '.' + id;
  else subnet = subnet.split('.'), id = subnet[1], subnet = subnet[0];

  var devices = this.devices;

  if (!devices[address]) devices[address] = new Device(this, subnet, id);

  return devices[address];
};

/**
 * Send command to device via HDL SmartBus gateway
 *
 * @param  {String|Device}  target   Device address or object
 * @param  {Number}         code     Command code
 * @param  {Object|Buffer}  [data]     Additional data
 * @param  {Function}       callback
 */
Bus.prototype.send = function(target, code, data, callback) {
  if (typeof target === 'string') target = this.device(target);
  if (typeof data === 'function') callback = data, data = undefined;

  var content;
  var command = new Command(code, {
    sender: this,
    target: target,

    data: data
  });

  debug(command.toString());

  try {
    content = command.message;
  } catch (err) {
    return callback(err);
  }

  var checksum = new Buffer(2);

  checksum.writeUInt16BE(crc(content), 0);

  var message = Buffer.concat([this.address, Constants, content, checksum]);

  this.socket.send(message, 0, message.length,
    this.port, this.gateway, callback);
};

/**
 * Parse SmartBus message
 *
 * @param  {Buffer} message
 * @return {Command}
 */
Bus.prototype.parse = function(message) {
  if (!this.validate(message)) return;

  message = message.slice(16);

  var code = message.readUIntBE(5, 2);
  var sender = this.device(message.readUInt8(1), message.readUInt8(2));

  sender.type = message.readUIntBE(3, 2);

  return new Command(code, {
    target: this.device(message.readUInt8(7), message.readUInt8(8)),
    sender: sender,

    data: message.slice(9, message.readUInt8(0) - 2)
  });
};

/**
 * Close underlying UDP socket and stop listening on it
 *
 * @param {Function} [callback] - `close` event handler
 */
Bus.prototype.close = function(callback) {
  this.socket.close(callback);
};

/**
 * Set socket broadcast flag
 *
 * @param {Boolean} flag - broadcast flag
 */
Bus.prototype.setBroadcast = function(flag) {
  this.socket.setBroadcast(flag);

  // Check source gateway only when broadcasting is off
  this.validate = flag ? isValid : validateAndCheckGateway;
};

/**
 * Check if message is allowed for Bus instance
 *
 * This function could be changed on instance during
 * runtime with `setBroadcast` method.
 *
 * @param {Buffer} message - HDL message contents
 */
Bus.prototype.validate = validateAndCheckGateway;

/**
 * Return bus listener address as string representation
 *
 * @return {String}
 */
Bus.prototype.toString = Object
  .getOwnPropertyDescriptor(Device.prototype, 'address').get;

function handler(message) {
  var command = this.parse(message);

  if (!command) return;

  var data = command.data;
  var code = command.code;
  var sender = command.sender;
  var target = command.target;

  debug(command.toString());

  this.emit('command', command);

  if (target.subnet === 0xFF && target.id === 0xFF)
    this.emit('broadcast', command);

  this.emit(code, command);
  sender.emit(code, data, target);
}

function createSocket(bus) {
  var socket = dgram.createSocket({ type: 'udp4', reuseAddr: true });

  socket.on('message', handler.bind(bus));

  socket.on('error', function(err) {
    console.error('Error on socket: %s', err.message);

    socket.close();

    bus.emit('error', err);
  });

  socket.on('close', function() {
    bus.emit('close');
  });

  socket.on('listening', function() {
    var address = socket.address();

    bus.address = new Buffer(address.address.split('.'));

    debug('UDP Server is listening on ' + address.address + ':' + address.port);

    bus.emit('listening');
  });

  socket.bind(bus.port);

  return socket;
}

/**
 * Check if buffer is valid Smart Bus command
 *
 * @param  {Buffer} message
 * @return {Boolean}
 */
function isValid(message) {
  if (!Constants.equals(message.slice(4, 16))) return false;

  var checksum = message.readUInt16BE(message.length - 2);

  return checksum === crc(message.slice(16, -2));
}

/**
 * Check if HDL message is valid and source gateway is the
 * same as defined on Bus instance.
 *
 * @param {Buffer} message - HDL message contents
 * @return {Boolean}
 */
function validateAndCheckGateway(message) {
  return isValid(message) && source(message) === this.gateway;
}

/**
 * Return message sender IP address
 *
 * @param  {Buffer} message
 * @return {String}
 */
function source(message) {
  var ip = [];

  for (var i = 0; i < 4; i++) ip.push(message[i]);

  return ip.join('.');
}

/**
 * Parse bus configuration from connection string with following format
 *
 *   hdl://device@gateway[:port]
 *
 * @param  {String|Object} resource - connection string or configutaion
 * @return {Object} - bus connection configuration
 */
function parse(resource) {
  if (typeof resource === 'object') return resource;

  var config = url.parse(resource);

  return {
    port: config.port,
    device: config.auth,
    gateway: config.hostname
  };
}
<commit_msg>Fix socket closing handling on older version of node<commit_after>var url = require('url');
var crc = require('crc').crc16xmodem;
var util = require('util');
var debug = require('debug')('smart-bus');
var dgram = require('dgram');
var Device = require('./device');
var Command = require('./command');
var EventEmitter = require('events');

module.exports = Bus;

var Constants = new Buffer(12);

Constants.write('HDLMIRACLE');
Constants.writeUInt16BE(0xAAAA, 10);

function Bus(resource) {
  var config = parse(resource);
  var device = config.device ? config.device.split('.') : [];

  var id = config.id || device[1];
  var port = config.port;
  var subnet = config.subnet || device[0];

  this.id = parseInt(id);
  this.type = 0xFFFE;
  this.subnet = parseInt(subnet);
  this.address = new Buffer([0, 0, 0, 0]);

  this.port = port ? parseInt(port) : 6000;
  this.gateway = config.gateway;

  this.socket = createSocket(this);

  this.devices = {};

  EventEmitter.call(this);
}

util.inherits(Bus, EventEmitter);

/**
 * Return device object by its address
 *
 * @param  {Number|String} subnet or address Subnet or address, e.g. '1.4'
 * @param  {Number} [id]                     Device id
 * @return {Device}
 */
Bus.prototype.device = function(address, id) {
  var subnet = address;

  if (typeof address === 'number') address = address + '.' + id;
  else subnet = subnet.split('.'), id = subnet[1], subnet = subnet[0];

  var devices = this.devices;

  if (!devices[address]) devices[address] = new Device(this, subnet, id);

  return devices[address];
};

/**
 * Send command to device via HDL SmartBus gateway
 *
 * @param  {String|Device}  target   Device address or object
 * @param  {Number}         code     Command code
 * @param  {Object|Buffer}  [data]     Additional data
 * @param  {Function}       callback
 */
Bus.prototype.send = function(target, code, data, callback) {
  if (typeof target === 'string') target = this.device(target);
  if (typeof data === 'function') callback = data, data = undefined;

  var content;
  var command = new Command(code, {
    sender: this,
    target: target,

    data: data
  });

  debug(command.toString());

  try {
    content = command.message;
  } catch (err) {
    return callback(err);
  }

  var checksum = new Buffer(2);

  checksum.writeUInt16BE(crc(content), 0);

  var message = Buffer.concat([this.address, Constants, content, checksum]);

  this.socket.send(message, 0, message.length,
    this.port, this.gateway, callback);
};

/**
 * Parse SmartBus message
 *
 * @param  {Buffer} message
 * @return {Command}
 */
Bus.prototype.parse = function(message) {
  if (!this.validate(message)) return;

  message = message.slice(16);

  var code = message.readUIntBE(5, 2);
  var sender = this.device(message.readUInt8(1), message.readUInt8(2));

  sender.type = message.readUIntBE(3, 2);

  return new Command(code, {
    target: this.device(message.readUInt8(7), message.readUInt8(8)),
    sender: sender,

    data: message.slice(9, message.readUInt8(0) - 2)
  });
};

/**
 * Close underlying UDP socket and stop listening on it
 *
 * @param {Function} [callback] - `close` event handler
 */
Bus.prototype.close = function(callback) {
  this.socket.close(callback);
};

/**
 * Set socket broadcast flag
 *
 * @param {Boolean} flag - broadcast flag
 */
Bus.prototype.setBroadcast = function(flag) {
  this.socket.setBroadcast(flag);

  // Check source gateway only when broadcasting is off
  this.validate = flag ? isValid : validateAndCheckGateway;
};

/**
 * Check if message is allowed for Bus instance
 *
 * This function could be changed on instance during
 * runtime with `setBroadcast` method.
 *
 * @param {Buffer} message - HDL message contents
 */
Bus.prototype.validate = validateAndCheckGateway;

/**
 * Return bus listener address as string representation
 *
 * @return {String}
 */
Bus.prototype.toString = Object
  .getOwnPropertyDescriptor(Device.prototype, 'address').get;

function handler(message) {
  var command = this.parse(message);

  if (!command) return;

  var data = command.data;
  var code = command.code;
  var sender = command.sender;
  var target = command.target;

  debug(command.toString());

  this.emit('command', command);

  if (target.subnet === 0xFF && target.id === 0xFF)
    this.emit('broadcast', command);

  this.emit(code, command);
  sender.emit(code, data, target);
}

function createSocket(bus) {
  var socket = dgram.createSocket({ type: 'udp4', reuseAddr: true });

  socket.on('message', handler.bind(bus));

  socket.on('error', function(err) {
    console.error('Error on socket: %s', err.message);

    bus.emit('error', err);

    socket.close();
  });

  socket.on('close', function() {
    bus.emit('close');
  });

  socket.on('listening', function() {
    var address = socket.address();

    bus.address = new Buffer(address.address.split('.'));

    debug('UDP Server is listening on ' + address.address + ':' + address.port);

    bus.emit('listening');
  });

  socket.bind(bus.port);

  return socket;
}

/**
 * Check if buffer is valid Smart Bus command
 *
 * @param  {Buffer} message
 * @return {Boolean}
 */
function isValid(message) {
  if (!Constants.equals(message.slice(4, 16))) return false;

  var checksum = message.readUInt16BE(message.length - 2);

  return checksum === crc(message.slice(16, -2));
}

/**
 * Check if HDL message is valid and source gateway is the
 * same as defined on Bus instance.
 *
 * @param {Buffer} message - HDL message contents
 * @return {Boolean}
 */
function validateAndCheckGateway(message) {
  return isValid(message) && source(message) === this.gateway;
}

/**
 * Return message sender IP address
 *
 * @param  {Buffer} message
 * @return {String}
 */
function source(message) {
  var ip = [];

  for (var i = 0; i < 4; i++) ip.push(message[i]);

  return ip.join('.');
}

/**
 * Parse bus configuration from connection string with following format
 *
 *   hdl://device@gateway[:port]
 *
 * @param  {String|Object} resource - connection string or configutaion
 * @return {Object} - bus connection configuration
 */
function parse(resource) {
  if (typeof resource === 'object') return resource;

  var config = url.parse(resource);

  return {
    port: config.port,
    device: config.auth,
    gateway: config.hostname
  };
}
<|endoftext|>


<commit_before>// diffbot - node.js api wrapper for diffbot
// http://github.com/markbao/node-diffbot
//
// Copyright 2011 Mark Bao
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var http = require('http'),
    url = require('url'),
    request = require('request'),
    urlregex = /^https?:/;

var Diffbot = exports.Diffbot =
  function(token) {
    this.token = token || undefined;
  }

Diffbot.prototype.article = function (options, callback) {
  for (var i in options) {
    this[i] = options[i];
  }

  var options = this;

  // support 'url'
  if (options.url) {
    options.uri = options.url;
    delete options.url;
  }

  if (!options.uri) {
    throw new Error("the URI is required.");
  }

  var diffbot_url = "http://api.diffbot.com/v3/article?token=" + this.token + "&url=" + encodeURIComponent(options.uri);

  // process extras
  if (options.callback) {
    diffbot_url += "&callback=" + callback;
  }

  if (options.html) {
    diffbot_url += "&html=1";
  }

  if (options.dontStripAds) {
    diffbot_url += "&dontStripAds=1";
  }

  if (options.tags) {
    diffbot_url += "&tags=1";
  }

  if (options.comments) {
    // experimental
    diffbot_url += "&comments=1";
  }

  if (options.stats) {
    diffbot_url += "&stats=1";
  }

  request({uri: diffbot_url}, function(error, response, body) {
    if (error) {
      callback(error, undefined);
    } else {
      callback(false, JSON.parse(body));
    }
  });
}

Diffbot.prototype.frontpage = function (options, callback) {
  for (var i in options) {
    this[i] = options[i];
  }

  var options = this;

  // support 'url'
  if (options.url) {
    options.uri = options.url;
    delete options.url;
  }

  if (!options.uri) {
    throw new Error("the URI is required.");
  }

  var diffbot_url = "http://api.diffbot.com/v3/frontpage?token=" + this.token + "&url=" + encodeURIComponent(options.uri) + "&format=json";

  request({uri: diffbot_url}, function(error, response, body) {
    if (error) {
      callback(error, undefined);
    } else {
      callback(false, JSON.parse(body));
    }
  });
}
<commit_msg>Fixing JSON.parse()<commit_after>// diffbot - node.js api wrapper for diffbot
// http://github.com/markbao/node-diffbot
//
// Copyright 2011 Mark Bao
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

var http = require('http'),
    url = require('url'),
    request = require('request'),
    urlregex = /^https?:/;

var Diffbot = exports.Diffbot =
  function(token) {
    this.token = token || undefined;
  }

Diffbot.prototype.article = function (options, callback) {
  for (var i in options) {
    this[i] = options[i];
  }

  var options = this;

  // support 'url'
  if (options.url) {
    options.uri = options.url;
    delete options.url;
  }

  if (!options.uri) {
    throw new Error("the URI is required.");
  }

  var diffbot_url = "http://api.diffbot.com/v3/article?token=" + this.token + "&url=" + encodeURIComponent(options.uri);

  // process extras
  if (options.callback) {
    diffbot_url += "&callback=" + callback;
  }

  if (options.html) {
    diffbot_url += "&html=1";
  }

  if (options.dontStripAds) {
    diffbot_url += "&dontStripAds=1";
  }

  if (options.tags) {
    diffbot_url += "&tags=1";
  }

  if (options.comments) {
    // experimental
    diffbot_url += "&comments=1";
  }

  if (options.stats) {
    diffbot_url += "&stats=1";
  }

  request({uri: diffbot_url}, function(error, response, body) {
    if (error) {
      return callback(error, undefined);
    } else {
      try {
        var parsedJSON = JSON.parse(body);
      } catch (e) {
        return callback(e, undefined);
      }
      callback(false, parsedJSON);
    }
  });
}

Diffbot.prototype.frontpage = function (options, callback) {
  for (var i in options) {
    this[i] = options[i];
  }

  var options = this;

  // support 'url'
  if (options.url) {
    options.uri = options.url;
    delete options.url;
  }

  if (!options.uri) {
    throw new Error("the URI is required.");
  }

  var diffbot_url = "http://api.diffbot.com/v3/frontpage?token=" + this.token + "&url=" + encodeURIComponent(options.uri) + "&format=json";

  request({uri: diffbot_url}, function(error, response, body) {
    if (error) {
      return callback(error, undefined);
    } else {
      try {
        var parsedJSON = JSON.parse(body);
      } catch (e) {
        return callback(e, undefined);
      }
      callback(false, parsedJSON);
    }
  });
}
<|endoftext|>


<commit_before>'use strict';
<commit_msg>testing<commit_after>'use strict';

require('dotenv').config({ path: `${__dirname}/../.test.env` });
require('./lib/mock-aws.js');
const superagent = require('superagent');
const expect = require('expect');
const server = require('../lib/server.js');
const cleanDB = require('./lib/clean-db.js');
const mockUser = require('./lib/mock-user.js');
const mockComment = require('./lib/mock-comment.js');

const API_URL = process.env.API_URL;

describe('Testing Profile Model', () => {
  before(server.start);
  after(server.stop);
  afterEach(cleanDB);

  describe('Testing POST', () => {
    it('should return 200', () => {
      return mockUser.createOne().then(userData => {
        return superagent
          .post(`${API_URL}/api/comments/${userData._id}`)
          .set('Authorization', `Bearer ${userData.token}`)
          .send({
            content:'Jannet\'s dog was in my yard, digging in garden',
            date: Date.now()})
          .then(res => {
            expect(res.status).toEqual(200);
            expect(res._id).toEqual(userData._id);
            expect(res.body.content).toEqual('Jannet\'s dog was in my yard, digging in garden');
            expect(res.body.date).toExist();
          });
      });
    });
    it('should return 400 bad request', () => {
      return mockUser.createOne().then(userData => {
        return superagent
          .post(`${API_URL}/api/comments/${userData._id}`)
          .set('Authorization', `Bearer ${userData.token}`)
          .send({
            nope: 'non existent'
          })
          .then(res => {
            throw res;
          })
          .catch(res => {
            expect(res.status).toEqual(400);
          });
      });
    });
    it('should return 401 unauthorized', () => {
      return mockUser.createOne().then(userData => {
        return superagent
          .post(`${API_URL}/api/comments/${userData._id}`)
          .set('Authorization', `Bearer skdfhskjdfhakdjf`)
          .send({
            content:
              'Neighbors are outside again, there\'s at least 20 cars at their house',
            date: Date.now()
          })
          .then(res => {
            throw res;
          })
          .catch(res => {
            expect(res.status).toEqual(401);
          });
      });
    });
    it('Should return with 404 not found', () => {
      return superagent
        .post(`${API_URL}/api/comments/asdasdasdasd`)
        .send({
          nope: 'non existent'
        })
        .then(res => {
          throw res;
        })
        .catch(res => {
          expect(res.status).toEqual(404);
        });
    });
  });

  describe('Testing GET - Incident Array', () => {
    it('should return 200', () => {
      return mockComment.createOne().then(comment => {
        return mockUser.createOne().then(userData => {
          return superagent
            .post(`${API_URL}/api/comments/${userData._id}`)
            .set('Authorization', `Bearer ${userData.token}`)
            .send({
              content:
                'Neighbors are outside again, there\'s at least 20 cars at their house',
              date: Date.now()
            })
            .then(() => {
              return superagent
                .get(`${API_URL}/api/comments/${userData._id}`)
                .then(res => {
                  expect(res.status).toEqual(200);
                  expect(res._id).toEqual(userData._id);
                  expect(res.body.content).toEqual('Neighbors are outside again, there\'s at least 20 cars at their house');
                  expect(res.body.date).toExist();
                });
            });
        });
      });
    });
    it('Should return with 404 not found', () => {
      return superagent
        .get(`${API_URL}/api/comments/asdasdasdasd`)
        .then(res => {
          throw res;
        })
        .catch(res => {
          expect(res.status).toEqual(404);
        });
    });
  });

  describe('Testing PUT', () => {
    it('should return 200', () => {
      return mockComment.createOne().then(comment => {
        return mockUser.createOne().then(userData => {
          return superagent
            .post(`${API_URL}/api/comments/${userData._id}`)
            .set('Authorization', `Bearer ${userData.token}`)
            .send({
              content:
                'Neighbors are outside again, there\'s at least 20 cars at their house',
              date: Date.now()
            })
            .then(() => {
              return superagent
                .put(`${API_URL}/api/comments/${userData._id}`)
                .set('Authorization', `Bearer ${userData.token}`)
                .send({
                  content:
                    'Neighbors are outside again, there\'s like 100 cars at their house',
                  date: Date.now()
                })
                .then(res => {
                  expect(res.status).toEqual(200);
                  expect(res._id).toEqual(userData._id);
                  expect(res.body.content).toEqual('Neighbors are outside again, there\'s like 100 cars at their house');
                  expect(res.body.date).toExist();
                });
            });
        });
      });
    });
    it('should return 400 bad request', () => {
      return mockComment.createOne().then(comment => {
        return mockUser.createOne().then(userData => {
          return superagent
            .post(`${API_URL}/api/comments/${userData._id}`)
            .set('Authorization', `Bearer ${userData.token}`)
            .send({
              content:
                'Neighbors are outside again, there\'s at least 20 cars at their house',
              date: Date.now()
            })
            .then(() => {
              return superagent
                .put(`${API_URL}/api/comments/${userData._id}`)
                .set('Authorization', `Bearer ${userData.token}`)
                .send({
                  content:
                    'Neighbors are outside again, there\'s like 100 cars at their house',
                  date: Date.now()
                })
                .then(res => {
                  expect(res.status).toEqual(400);
                });
            });
        });
      });
    });
    it('should return 401 unauthorized', () => {
      return mockUser.createOne().then(userData => {
        return superagent
          .put(`${API_URL}/api/comments/${userData._id}`)
          .set('Authorization', `Bearer skdfhskjdfhakdjf`)
          .send({
            content:
              'Neighbors are outside again, there\'s at least 20 cars at their house',
            date: Date.now()
          })
          .then(res => {
            throw res;
          })
          .catch(res => {
            expect(res.status).toEqual(401);
          });
      });
    });
    it('Should return with 404 not found', () => {
      return superagent
        .put(`${API_URL}/api/comments/asdasdasdasd`)
        .send({
          nope: 'non existent'
        })
        .then(res => {
          throw res;
        })
        .catch(res => {
          expect(res.status).toEqual(404);
        });
    });
  });
});
<|endoftext|>


<commit_before>//Import dependencies
var colors = require('colors');

//Display class
var Display =
{
  //Display a done message
  done: function(text){ console.log('  DONE   '.black.bgGreen.bold + ' ' + text.green.bold); },

  //Display a warning message
  warning: function(text){ console.log(' WARNING '.black.bgYellow.bold + ' ' + text.yellow.bold); },

  //Display an error message
  error: function(text){ console.log('  ERROR  '.black.bgRed.bold + ' ' + text.red.bold); },

  //Display an info message
  info: function(text){ console.log('  INFO   '.black.bgBlue.bold + ' ' + text.blue.bold); }
};

//Exports to node
module.exports = Display;
<commit_msg>lib/display.js: minor changes<commit_after>//Import dependencies
var colors = require('colors');

//Display object
var display = {};

//Display a done message
display.done = function(text){ console.log('  DONE   '.black.bgGreen.bold + ' ' + text.green.bold); },

//Display a warning message
display.warning = function(text){ console.log(' WARNING '.black.bgYellow.bold + ' ' + text.yellow.bold); },

//Display an error message
display.error = function(text){ console.log('  ERROR  '.black.bgRed.bold + ' ' + text.red.bold); },

//Display an info message
display.info = function(text){ console.log('  INFO   '.black.bgBlue.bold + ' ' + text.blue.bold); };

//Exports to node
module.exports = display;
<|endoftext|>