<commit_before>var AmpersandModel = require('ampersand-model');
var AmpersandCollection = require('ampersand-rest-collection');
var AmpersandState = require('ampersand-state');
var DatabaseCollection = require('mongodb-database-model').Collection;
var CollectionCollection = require('mongodb-collection-model').Collection;
var hostname = require('os').hostname();

var HostInfo = AmpersandState.extend({
  props: {
    system_time: 'date',
    hostname: 'string',
    os: 'string',
    os_family: 'string',
    kernel_version: 'string',
    kernel_version_string: 'string',
    memory_bits: 'number',
    memory_page_size: 'number',
    arch: 'string',
    cpu_cores: 'number',
    cpu_cores_physical: 'number',
    cpu_scheduler: 'string',
    cpu_frequency: 'number',
    cpu_string: 'string',
    cpu_bits: 'number',
    machine_model: 'string',
    feature_numa: 'boolean',
    feature_always_full_sync: 'number',
    feature_nfs_async: 'number'
  }
});

var BuildInfo = AmpersandState.extend({
  props: {
    version: 'string',
    commit: 'string',
    commit_url: 'string',
    flags_loader: 'string',
    flags_compiler: 'string',
    allocator: 'string',
    javascript_engine: 'string',
    debug: 'boolean',
    for_bits: 'number',
    max_bson_object_size: 'number',
    enterprise_module: 'boolean'
  }
});

var Instance = AmpersandModel.extend({
  modelType: 'Instance',
  idAttribute: '_id',
  props: {
    _id: {
      type: 'string',
      required: true
    },
    name: {
      type: 'string'
    }
  },
  derived: {
    hostname: {
      deps: ['_id'],
      fn: function() {
        if (!this._id) {
          return undefined;
        }
        return this._id.split(':')[0];
      }
    },
    port: {
      deps: ['_id'],
      fn: function() {
        if (!this._id) {
          return undefined;
        }
        return parseInt(this._id.split(':')[1], 10);
      }
    }
  },
  collections: {
    databases: DatabaseCollection,
    collections: CollectionCollection
  },
  children: {
    host: HostInfo,
    build: BuildInfo
  }
});

var InstanceCollection = AmpersandCollection.extend({
  comparator: '_id',
  model: Instance,
  modelType: 'InstanceCollection'
});

module.exports = Instance;
module.exports.Collection = InstanceCollection;
module.exports.BuildInfo = BuildInfo;
module.exports.HostInfo = HostInfo;

module.exports.getId = function(id) {
  if (typeof id === 'number') {
    id = 'localhost:' + id;
  }
  return id.toLowerCase().replace(hostname.toLowerCase(), 'localhost').replace('mongodb://', '');
};
<commit_msg>:zap: improvements + todos from mongodb-scope-server<commit_after>var _ = require('lodash');
var AmpersandModel = require('ampersand-model');
var AmpersandCollection = require('ampersand-rest-collection');
var AmpersandState = require('ampersand-state');
var DatabaseCollection = require('mongodb-database-model').Collection;
var CollectionCollection = require('mongodb-collection-model').Collection;
var hostname = require('os').hostname();

var HostInfo = AmpersandState.extend({
  props: {
    system_time: 'date',
    hostname: 'string',
    os: 'string',
    os_family: 'string',
    kernel_version: 'string',
    kernel_version_string: 'string',
    memory_bits: 'number',
    memory_page_size: 'number',
    arch: 'string',
    cpu_cores: 'number',
    cpu_cores_physical: 'number',
    cpu_scheduler: 'string',
    cpu_frequency: 'number',
    cpu_string: 'string',
    cpu_bits: 'number',
    machine_model: 'string',
    feature_numa: 'boolean',
    feature_always_full_sync: 'number',
    feature_nfs_async: 'number'
  }
});

var BuildInfo = AmpersandState.extend({
  props: {
    version: 'string',
    commit: 'string',
    commit_url: 'string',
    flags_loader: 'string',
    flags_compiler: 'string',
    allocator: 'string',
    javascript_engine: 'string',
    debug: 'boolean',
    for_bits: 'number',
    max_bson_object_size: 'number',
    enterprise_module: 'boolean'
  }
});

var Instance = AmpersandModel.extend({
  modelType: 'Instance',
  idAttribute: '_id',
  props: {
    _id: {
      type: 'string',
      required: true
    },
    name: {
      type: 'string'
    },
    /**
     * @todo (imlucas) Stabilize and cleanup
     * `replicaset`, `state`, and `aliases`.
     *
     * See: http://npm.im/mongodb-replicaset
     */
    replicaset: 'string',
    state: 'string',
    aliases: {
      type: 'array',
      default: function() {
        return [];
      }
    }
  },
  derived: {
    hostname: {
      deps: ['_id'],
      fn: function() {
        if (!this._id) {
          return undefined;
        }
        return this._id.split(':')[0];
      }
    },
    port: {
      deps: ['_id'],
      fn: function() {
        if (!this._id) {
          return undefined;
        }
        return parseInt(this._id.split(':')[1], 10);
      }
    }
  },
  collections: {
    databases: DatabaseCollection,
    collections: CollectionCollection
  },
  children: {
    host: HostInfo,
    build: BuildInfo
  },
  /**
   * Override `AmpersandModel.serialize()` to
   * make logging less chatty and problems easier
   * to debug.
   *
   * @return {Object}
   */
  serialize: function() {
    var res = this.getAttributes({
      props: true,
      derived: true
    }, true);
    if (this.databases.length > 0) {
      _.each(this._children, function(value, key) {
        res[key] = this[key].serialize();
      }, this);
      _.each(this._collections, function(value, key) {
        res[key] = this[key].serialize();
      }, this);
    }
    return res;
  }
});

var InstanceCollection = AmpersandCollection.extend({
  comparator: '_id',
  model: Instance,
  modelType: 'InstanceCollection'
});

module.exports = Instance;
module.exports.Collection = InstanceCollection;
module.exports.BuildInfo = BuildInfo;
module.exports.HostInfo = HostInfo;

module.exports.getId = function(id) {
  if (typeof id === 'number') {
    id = 'localhost:' + id;
  }
  return id.toLowerCase().replace(hostname.toLowerCase(), 'localhost').replace('mongodb://', '');
};
<|endoftext|>


<commit_before>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 * @module deep
 */
if (typeof define !== 'function') {
	var define = require('amdefine')(module);
}
define(["require", "./promise", "./utils", "./protocol", "./query"], function(require, prom, utils, protoc, querier) {

	var nodes = {
		slog:function(args, s, e)
		{
			if(args.length === 0)
                args.push("dp:success : ");
            var v = s;
            if(s && s._deep_query_node_)
                v = s.value;
            else if(v && v._deep_array_)
            {
                args.push("("+v.length+" node(s)) : \n\n");
                var res = [], count = 0;
                v.forEach(function(e) {
                    res.push(count++, ": ", e.value);
                    if(count<v.length)
                    	res.push(",\n\n");
                });
                if(res.length)
                	args = args.concat(res);
            }
            if(!v || !v._deep_array_)
            	args.push(v);
            if(deep.context.logger)
            {
                var logger = deep.context.logger;
                if(logger._deep_ocm_)
                    logger = logger();
                logger.log.apply(logger, args);
            }
            else if(console.log.apply)
                console.log.apply(console, args);
            else
                console.log(deep.utils.argToArr(args));
            return s;
		},
		elog:function(args, s, e)
		{
            if(args.length === 0)
                args.unshift("dp:error");
            if (deep.debug)
            {
                utils.dumpError(e, args);
                return e;
            }
            else if (e.report)
                args.push("(" + e.status + "): ", e.message, e.report);
            else
                args.push("(" + e.status + "): ", e.message);
            if(deep.context.logger)
            {
                var logger = deep.context.logger;
                if(logger._deep_ocm_)
                    logger = logger();
                logger.error.apply(logger, arguments);
            }
            else if(console.error.apply)
                console.error.apply(console, arguments);
            else
                console.error(deep.utils.argToArr(arguments));
            return e;
		},
		stringify: function(nodes, options) {
			options = options || {};
			var res = "";
			handler._state.nodes.forEach(function(e) {
				if (options.pretty)
					res += JSON.stringify(e.value, null, ' ') + "\n";
				else
					res += JSON.stringify(e.value) + "\n";
			});
			return res;
		},
		val: function(s) {
			if(!s)
				return s;
			if(s._deep_array_)
				return s.map(function(e) {
					return e.value;
				});
			if(s._deep_query_node_)
				return s.value;
			return s;
		},
		values: function(nodes) {
			return nodes.map(function(e) {
				return e.value;
			});
		},
		paths: function(nodes) {
			return nodes.map(function(e) {
				return e.path;
			});
		},
		schemas: function(nodes) {
			return nodes.map(function(e) {
				e.schema;
			});
		},
		transform: function(node, transformer) {
			var r = transformer(node);
			if (r && (r.then || r.promise))
				return deep.when(r)
					.done(function(r) {
						node.value = r;
						if (node.ancestor)
							node.ancestor.value[node.key] = node.value;
					})
			node.value = r;
			if (node.ancestor)
				node.ancestor.value[node.key] = node.value;
			return r;
		},
		applyTransformers: function(object, schema) {
			if (!object._deep_query_node_)
				object = nodes.root(object, schema);
			//console.log("apply Transfor on : ", object);
			var query = ".//?_schema.transformers";
			deep.query(object, query, {
				fullOutput:true,
				schema: schema
			})
				.forEach(function(node) {
					for (var i = 0, len = node.schema.transformers.length; i < len; ++i)
						deep.nodes.transform(node, node.schema.transformers[i]);
				});
		},
		transformNodes: function(nodes, transformer) {
			var results = [];
			nodes.forEach(function(e) {
				results.push(transformer(e));
			});
			return prom.all(results)
				.done(function(res) {
					var count = 0;
					nodes.forEach(function(n) {
						n.value = res[count++];
						if (n.ancestor)
							n.ancestor.value[n.key] = n.value;
					});
				});
		},
		print:function(node){
			/*for(var i in node)
			{
				if(i == "root" || i == "ancestor")
					node[i] = "[_deep_query_node_]";
			}*/

			var oldRoot = node.root, oldAncestor = node.ancestor;
			if(node.root)
				node.root = "[_deep_query_node_]";
			if (node.ancestor)
				node.ancestor = "[_deep_query_node_]";
			console.log("print node : ", node.path)
			var r = JSON.stringify(node);
			if(oldRoot)
				node.root = oldRoot;
			if(oldAncestor)
				node.ancestor = oldAncestor;
			return r;
		}
		/*,print:function(node){
			var res = "_deep_query_node_ :";
			if(node.root)
				res += " root:{...},";
			res += " path:'"+node.path+"',";
			res += " depth:"+node.depth+",";
			if(node.schema)
				res += " schema:"+node.schema+",";
			if(node.ancestor)
				res += " ancestor:{...},";
			res += " value:"+node.value;//.replace(/\"/g,"'"));
			return res;
		}*/
	};

	/**
	 * create a DeepQuery entry that hold info of objet node (path, value, ancestor, etc)
	 * @method create
	 * @param  {[type]} key
	 * @param  {[type]} ancestor
	 * @return {[type]}
	 */
	nodes.create = function (key, ancestor) {
		var path = ancestor.path;
		if (!ancestor.key)
			path += key;
		else
			path += "/" + key;

		var schema = null;
		if (ancestor.schema)
			schema = utils.retrieveFullSchemaByPath(ancestor.schema, key, "/");
		//console.log("deep.nodes.create : "+path+" : schema : ",schema)
		return {
			_deep_query_node_: true,
			root: ancestor.root || ancestor,
			value: ancestor.value[key],
			path: path,
			paths: ancestor.paths.concat(key),
			key: key,
			ancestor: ancestor,
			schema: schema,
			depth: ancestor.depth + 1
			/*,
			toString:function(){
				return nodes.print(this);
			}*/
		};
	};

	nodes.clone = function(node) {
		//console.log("deep.nodes.create : "+path+" : schema : ",schema)
		var clone = utils.shallowCopy(node);
		if (clone.paths)
			clone.paths = clone.paths.slice();
		return clone;
	};

	/**
	 * create a root DeepQuery node
	 *
	 * @static
	 * @method root
	 * @param  {Object} obj
	 * @param  {Object} schema
	 * @return {Object} a DeepQuery root node
	 */
	nodes.root = function(obj, schema, options) {
		options = options || {};
		if (obj && obj._deep_undefined_)
			obj = undefined;
		var node = {
			_deep_query_node_: true,
			value: obj,
			path: "/",
			paths: [],
			uri: options.uri || null,
			key: null,
			ancestor: null,
			schema: schema,
			depth: 0/*,
			toString:function(){
				return nodes.print(this);
			}*/
		};
		//node.root = node;
		return node;
	};

	nodes.deepLoad = function(entries, context, destructive, excludeFunctions) {
		var doDeepLoad = function(toLoad) {
			var value = toLoad.value;
			if (value && value._deep_ocm_)
				value = value();
			var type = typeof value;
			if (type === 'string') {
				if (context)
					value = utils.interpret(value, context);
				var s = protoc.get(value, {
					entry: toLoad
				});
				if (s && s.then)
					return prom.when(s)
						.then(function(s) {
							if (s && s._deep_ocm_)
								return s();
						});
				if (s && s._deep_ocm_)
					return s();
				return s;
			} else if (type === 'function') {
				var d = null;
				if (toLoad.ancestor)
					d = value.call(toLoad.ancestor.value, context);
				else
					d = value(context);
				if (d && d.then)
					return prom.when(d)
						.done(function(s) {
							if (s && s._deep_ocm_)
								return s();
						});
				return d;
			} else
				return value;
		};
		var toloads = [],
			res = [];
		for (var i = 0, len = entries.length; i < len; ++i) {
			var entry = entries[i];
			var value = entry.value,
				toLoads;
			if (value._deep_ocm_)
				value = value();
			if(value && value._deep_loadable_ === false)
				continue;
			if (!destructive) {
				entry = utils.simpleCopy(entry);
				entry.value = utils.copy(value);
			} else
				entry.value = value;
			res.push(entry);
			if (typeof value === 'object') {
				var query = (excludeFunctions) ? ".//!?_type=string" : ".//!?or(_type=string,_type=function)";
				toloads.push.apply(toloads, querier.query(entry, query, {
					fullOutput:true
				}));
			} else
			{
				if(!destructive)
					entry.ancestor = null;
				toloads.push(entry);
			}
		}
		return nodes.transformNodes(toloads, doDeepLoad)
			.done(function() {
				return nodes.values(res);
			});
	};

	return nodes;
})<commit_msg>clean nodes<commit_after>/**
 * @author Gilles Coomans <gilles.coomans@gmail.com>
 * @module deep
 */
if (typeof define !== 'function') {
	var define = require('amdefine')(module);
}
define(["require", "./promise", "./utils", "./protocol", "./query"], function(require, prom, utils, protoc, querier) {

	var nodes = {
		slog:function(args, s, e)
		{
			if(args.length === 0)
                args.push("dp:success : ");
            var v = s;
            if(s && s._deep_query_node_)
                v = s.value;
            else if(v && v._deep_array_)
            {
                args.push("("+v.length+" node(s)) : \n\n");
                var res = [], count = 0;
                v.forEach(function(e) {
                    res.push(count++, ": ", e.value);
                    if(count<v.length)
                    	res.push(",\n\n");
                });
                if(res.length)
                	args = args.concat(res);
            }
            if(!v || !v._deep_array_)
            	args.push(v);
            if(deep.context.logger)
            {
                var logger = deep.context.logger;
                if(logger._deep_ocm_)
                    logger = logger();
                logger.log.apply(logger, args);
            }
            else if(console.log.apply)
                console.log.apply(console, args);
            else
                console.log(deep.utils.argToArr(args));
            return s;
		},
		elog:function(args, s, e)
		{
            if(args.length === 0)
                args.unshift("dp:error");
            if (deep.debug)
            {
                utils.dumpError(e, args);
                return e;
            }
            else if (e.report)
                args.push("(" + e.status + "): ", e.message, e.report);
            else
                args.push("(" + e.status + "): ", e.message);
            if(deep.context.logger)
            {
                var logger = deep.context.logger;
                if(logger._deep_ocm_)
                    logger = logger();
                logger.error.apply(logger, arguments);
            }
            else if(console.error.apply)
                console.error.apply(console, arguments);
            else
                console.error(deep.utils.argToArr(arguments));
            return e;
		},
		val: function(s) {
			if(!s)
				return s;
			if(s._deep_array_)
				return s.map(function(e) {
					return e.value;
				});
			if(s._deep_query_node_)
				return s.value;
			return s;
		},
		paths: function(nodes) {
			if(nodes._deep_query_node_)
				return [nodes.path];
			return nodes.map(function(e) {
				return e.path;
			});
		},
		schemas: function(nodes) {
			if(nodes._deep_query_node_)
				return [nodes.schema];
			return nodes.map(function(e) {
				return e.schema;
			});
		},
		transform: function(node, transformer) {
			if(node._deep_query_node_)
			{
				var r = transformer(node);
				if (r && (r.then || r.promise))
					return deep.when(r)
						.done(function(r) {
							node.value = r;
							if (node.ancestor)
								node.ancestor.value[node.key] = node.value;
						})
				node.value = r;
				if (node.ancestor)
					node.ancestor.value[node.key] = node.value;
				return r;
			}
			return prom.all(node.map(function(e) {
				return transformer(e);
			}))
			.done(function(res) {
				var count = 0;
				node.forEach(function(n) {
					n.value = res[count++];
					if (n.ancestor)
						n.ancestor.value[n.key] = n.value;
				});
			});
		},
		/**
		 * applyshcema constraints transformers (see constraints docs).
		 * For internal use normally.
		 * @param  {Object} object any object where applying transformers
		 * @param  {Object} schema the schema containing transformers references to apply.
		 * @return nothing
		 */
		applyTransformers: function(object, schema) {
			if (!object._deep_query_node_)
				object = nodes.root(object, schema);
			//console.log("apply Transfor on : ", object);
			var query = ".//?_schema.transformers";
			deep.query(object, query, {
				fullOutput:true,
				schema: schema
			})
			.forEach(function(node) {
				for (var i = 0, len = node.schema.transformers.length; i < len; ++i)
					deep.nodes.transform(node, node.schema.transformers[i]);
			});
		},
		print:function(node){
			/*for(var i in node)
			{
				if(i == "root" || i == "ancestor")
					node[i] = "[_deep_query_node_]";
			}*/

			var oldRoot = node.root, oldAncestor = node.ancestor;
			if(node.root)
				node.root = "[_deep_query_node_]";
			if (node.ancestor)
				node.ancestor = "[_deep_query_node_]";
			console.log("print node : ", node.path)
			var r = JSON.stringify(node);
			if(oldRoot)
				node.root = oldRoot;
			if(oldAncestor)
				node.ancestor = oldAncestor;
			return r;
		}
		/*,print:function(node){
			var res = "_deep_query_node_ :";
			if(node.root)
				res += " root:{...},";
			res += " path:'"+node.path+"',";
			res += " depth:"+node.depth+",";
			if(node.schema)
				res += " schema:"+node.schema+",";
			if(node.ancestor)
				res += " ancestor:{...},";
			res += " value:"+node.value;//.replace(/\"/g,"'"));
			return res;
		}*/
	};

	/**
	 * create a DeepQuery entry that hold info of objet node (path, value, ancestor, etc)
	 * @method create
	 * @param  {[type]} key
	 * @param  {[type]} ancestor
	 * @return {[type]}
	 */
	nodes.create = function (key, ancestor) {
		var path = ancestor.path;
		if (!ancestor.key)
			path += key;
		else
			path += "/" + key;

		var schema = null;
		if (ancestor.schema)
			schema = utils.retrieveFullSchemaByPath(ancestor.schema, key, "/");
		//console.log("deep.nodes.create : "+path+" : schema : ",schema)
		return {
			_deep_query_node_: true,
			root: ancestor.root || ancestor,
			value: ancestor.value[key],
			path: path,
			paths: ancestor.paths.concat(key),
			key: key,
			ancestor: ancestor,
			schema: schema,
			depth: ancestor.depth + 1
			/*,
			toString:function(){
				return nodes.print(this);
			}*/
		};
	};

	nodes.clone = function(node) {
		//console.log("deep.nodes.create : "+path+" : schema : ",schema)
		var clone = utils.shallowCopy(node);
		if (clone.paths)
			clone.paths = clone.paths.slice();
		return clone;
	};

	/**
	 * create a root DeepQuery node
	 *
	 * @static
	 * @method root
	 * @param  {Object} obj
	 * @param  {Object} schema
	 * @return {Object} a DeepQuery root node
	 */
	nodes.root = function(obj, schema, options) {
		options = options || {};
		if (obj && obj._deep_undefined_)
			obj = undefined;
		var node = {
			_deep_query_node_: true,
			value: obj,
			path: "/",
			paths: [],
			uri: options.uri || null,
			key: null,
			ancestor: null,
			schema: schema,
			depth: 0/*,
			toString:function(){
				return nodes.print(this);
			}*/
		};
		//node.root = node;
		return node;
	};

	nodes.deepLoad = function(entries, context, destructive, excludeFunctions) {
		var doDeepLoad = function(toLoad) {
			var value = toLoad.value;
			if (value && value._deep_ocm_)
				value = value();
			var type = typeof value;
			if (type === 'string') {
				if (context)
					value = utils.interpret(value, context);
				var s = protoc.get(value, {
					entry: toLoad
				});
				if (s && s.then)
					return prom.when(s)
						.then(function(s) {
							if (s && s._deep_ocm_)
								return s();
						});
				if (s && s._deep_ocm_)
					return s();
				return s;
			} else if (type === 'function') {
				var d = null;
				if (toLoad.ancestor)
					d = value.call(toLoad.ancestor.value, context);
				else
					d = value(context);
				if (d && d.then)
					return prom.when(d)
						.done(function(s) {
							if (s && s._deep_ocm_)
								return s();
						});
				return d;
			} else
				return value;
		};
		var toloads = [],
			res = [];
		for (var i = 0, len = entries.length; i < len; ++i) {
			var entry = entries[i];
			var value = entry.value,
				toLoads;
			if (value._deep_ocm_)
				value = value();
			if(value && value._deep_loadable_ === false)
				continue;
			if (!destructive) {
				entry = utils.simpleCopy(entry);
				entry.value = utils.copy(value);
			} else
				entry.value = value;
			res.push(entry);
			if (typeof value === 'object') {
				var query = (excludeFunctions) ? ".//!?_type=string" : ".//!?or(_type=string,_type=function)";
				toloads.push.apply(toloads, querier.query(entry, query, {
					fullOutput:true
				}));
			} else
			{
				if(!destructive)
					entry.ancestor = null;
				toloads.push(entry);
			}
		}
		return nodes.transform(toloads, doDeepLoad)
			.done(function() {
				return res.map(function(e) {
					return e.value;
				});
			});
	};

	return nodes;
})<|endoftext|>


<commit_before>#!/usr/bin/env node
var express = require('express'),
    color = require('colors'),
    pros = require('./promises'),
    httpServer = require('http');

var VERSION = '0.1.12';

var sys = {__version: VERSION};
var server = express();
var main = express();

var allowCrossDomain = function (req, res, next) {
    'use strict';
    res.header('Access-Control-Allow-Origin', sys.allowOrigins);
    res.header('Access-Control-Allow-Credentials', sys.credentials);
    res.header('Access-Control-Allow-Methods',
               'GET, PUT, POST, DELETE, OPTIONS');
    res.header('Access-Control-Allow_Headers', 'Content-Type');
    next();
};

// check if the requested URL contains file extensions
var checkUrl = function (req, res, next) {
    'use strict';
    // only if contains sub-levels
    if (req.method.toUpperCase() === 'GET' && req.url.indexOf('.') === -1 &&
        req.url.lastIndexOf('/') !== req.url.length - 1) {
        res.redirect(req.url + '/');
    } else {
        next();
    }
};

var configMiddleware = function (json) {
    'use strict';
    sys.allowOrigins = json.origins || '*';
    sys.credentials = json.credentials || 'false';

    main.use(express.compress());
    main.use(express.bodyParser());
    main.use(express.methodOverride());
    main.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
    main.use(allowCrossDomain);
    main.use(checkUrl);

    console.log('Allow Origins: '.green + sys.allowOrigins);
    console.log('Allow Credentials: '.green + sys.credentials);
};

var prepareFilename = function (filename) {
    'use strict';
    filename = filename.replace(/\$ROOT/g, sys.root);
    filename = filename.replace(/\$NOVER/g, __dirname);
    filename = filename.replace(/\$PWD/g, process.cwd());
    return filename;
};

var prepareDomain = function (url) {
    'use strict';
    url = url.replace(/\$DOMAIN/g, sys.top);
    return url;
};

var interpretSites = function (sites, app) {
    'use strict';
    if (sites) {
        sites.forEach(function (site) {
            var domain = site.url || undefined;
            var local = site.local || undefined;
            var directory;

            if (domain && local) {
                directory = prepareFilename(local);
                app.use(domain, express.static(directory));
                console.log('Site:'.blue, domain,
                           '('.blue, directory, ')'.blue);
            }
        });
    }
    app.use(app.router);
};

var interpretSockets = function (sockets, app) {
    'use strict';
    if (sockets) {
        sockets.forEach(function (socket) {
            var port = socket.port || process.env.PORT;
            var local = socket.local || undefined;
            var filename;
            if (local) {
                var httpd;
                // port = port.replace(/\$PORT/g, sys.httpPort);
                if (port === process.env.PORT || port === '$PORT') {
                    httpd = httpServer.createServer(app);
                } else {
                    httpd = httpServer.createServer(main);
                }
                filename = prepareFilename(local);
                require(filename).__socket(httpd);
                console.log('WebSocket:'.cyan, local,
                            '('.cyan, port, ')'.cyan);
                httpd.listen(port);
            }
        });
    }
};

var interpretApis = function (apis, app) {
    'use strict';
    if (apis) {
        apis.forEach(function (api) {
            var uri = api.uri || undefined;
            var method = api.method || undefined;
            var local = api.local || undefined;
            var filename;

            if(uri && method && local) {
                filename = prepareFilename(local);
                switch (method.toUpperCase()) {
                case 'GET':
                    app.get(uri, function (req, res) {
                        require(filename).__get(req, res);
                    });
                    break;
                case 'POST':
                    app.post(uri, function (req, res) {
                        require(filename).__post(req, res);
                    });
                    break;
                case 'PUT':
                    app.put(uri, function (req, res) {
                        require(filename).__put(req, res);
                    });
                    break;
                case 'DELETE':
                    app.delete(uri, function (req, res) {
                        require(filename).__delete(req, res);
                    });
                    break;
                case 'ALL':
                    app.all(uri, function (req, res) {
                        require(filename).__all(req, res);
                    });
                    break;
                }
                console.log('API:'.yellow, uri, '('.yellow,
                            method.toUpperCase().yellow, '|'.yellow,
                           local, ')'.yellow);
            }
        });
    }
};

var loadFromJson = function (json) {
    'use strict';

    // config middleware
    configMiddleware(json);

    // set main domain
    sys.top = json.domain || '127.0.0.1';
    main.use(express.vhost(sys.top, server));
    console.log('Main domain: '.green + sys.top);

    // set httpPort
    sys.httpPort = json.port || process.env.PORT;
    console.log('HTTP Port: '.green + sys.httpPort);

    sys.root = json.root || '';
    console.log('Root: '.green + sys.root);

    sys.log = json.log || 0;
    if (sys.log === 1 || sys.log === 'true') {
        server.use(express.logger());
    }

    sys.favicon = json.favicon || undefined;
    if (sys.favicon) {
        server.use(express.favicon(prepareFilename(sys.favicon)));
        console.log('Favicon: '.green + sys.favicon);
    }

    // now bring in static sites
    sys.sites = json.sites || undefined;
    interpretSites(sys.sites, server);

    // now bring in RESTful APIs
    sys.apis = json.apis || undefined;
    interpretApis(sys.apis, server);

     // now bring in Sockets
    sys.sockets = json.sockets || undefined;
    interpretSockets(sys.sockets, server);
    console.log();

    // now bring in vhosts
    sys.vhosts = json.vhosts || undefined;
    if (sys.vhosts) {
        sys.vhostApp = [];
        sys.vhosts.forEach(function (vhost, index, vhosts) {
            if (vhost.subdomain) {
                console.log('-------------------'.green);
                console.log('Adding virtual host: '.green + vhost.subdomain);
                var vApp = express();
                sys.vhostApp.push(vApp);
                var sites = vhost.sites || undefined;
                var apis = vhost.apis || undefined;
                var sockets = vhost.sockets || undefined;
                var favicon = vhost.favicon || undefined;
                // applying system logger config
                if (sys.log) {
                    vApp.use(express.logger());
                }
                if (favicon) {
                    vApp.use(express.favicon(prepareFilename(favicon)));
                    console.log('Vhost Favicon: '.green + favicon);
                }
                if (sites) {
                    interpretSites(sites, vApp);
                }
                if (apis) {
                    interpretApis(apis, vApp);
                }
                if (sockets) {
                    interpretSockets(sockets, vApp);
                }
                main.use(express.vhost(prepareDomain(vhost.subdomain), vApp));
                console.log('Registered vitual host "'.green + vhost.subdomain + '".'.green);
                console.log('-------------------\n'.green);
            }
        });
    }
};

module.exports = function (argv) {
    'use strict';

    if (argv.length < 3) {
        console.log('Nover version'.green, sys.__version.green);
        console.log('Fatal: Missing config file.'.red);
        console.log('Usage: nover <config file location>'.yellow);
        process.exit(0);
    }

    // check if config file is given
    sys.config = process.argv[2];
    console.log('Config: '.green + sys.config);

    // read in config file first
    console.log('Now reading config file...'.green);
    console.log('==========================\n'.green);
    pros.readfile(sys.config)
    .then(function (data) {
        var json = JSON.parse(data);
        loadFromJson(json);

        main.listen(sys.httpPort);
        console.log('================'.green);
        console.log('Now listening...'.green);
    }, function (err) {
        console.error(err.red);
    });
};

<commit_msg>Trying to fix bug in socket.io on sys.httpPort<commit_after>#!/usr/bin/env node
var express = require('express'),
    color = require('colors'),
    pros = require('./promises'),
    httpServer = require('http');

var VERSION = '0.1.12';

var sys = {__version: VERSION};
var server = express();
var main = express();

var allowCrossDomain = function (req, res, next) {
    'use strict';
    res.header('Access-Control-Allow-Origin', sys.allowOrigins);
    res.header('Access-Control-Allow-Credentials', sys.credentials);
    res.header('Access-Control-Allow-Methods',
               'GET, PUT, POST, DELETE, OPTIONS');
    res.header('Access-Control-Allow_Headers', 'Content-Type');
    next();
};

// check if the requested URL contains file extensions
var checkUrl = function (req, res, next) {
    'use strict';
    // only if contains sub-levels
    if (req.method.toUpperCase() === 'GET' && req.url.indexOf('.') === -1 &&
        req.url.lastIndexOf('/') !== req.url.length - 1) {
        res.redirect(req.url + '/');
    } else {
        next();
    }
};

var configMiddleware = function (json) {
    'use strict';
    sys.allowOrigins = json.origins || '*';
    sys.credentials = json.credentials || 'false';

    main.use(express.compress());
    main.use(express.bodyParser());
    main.use(express.methodOverride());
    main.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
    main.use(allowCrossDomain);
    main.use(checkUrl);

    console.log('Allow Origins: '.green + sys.allowOrigins);
    console.log('Allow Credentials: '.green + sys.credentials);
};

var prepareFilename = function (filename) {
    'use strict';
    filename = filename.replace(/\$ROOT/g, sys.root);
    filename = filename.replace(/\$NOVER/g, __dirname);
    filename = filename.replace(/\$PWD/g, process.cwd());
    return filename;
};

var prepareDomain = function (url) {
    'use strict';
    url = url.replace(/\$DOMAIN/g, sys.top);
    return url;
};

var interpretSites = function (sites, app) {
    'use strict';
    if (sites) {
        sites.forEach(function (site) {
            var domain = site.url || undefined;
            var local = site.local || undefined;
            var directory;

            if (domain && local) {
                directory = prepareFilename(local);
                app.use(domain, express.static(directory));
                console.log('Site:'.blue, domain,
                           '('.blue, directory, ')'.blue);
            }
        });
    }
    app.use(app.router);
};

var interpretSockets = function (sockets, app) {
    'use strict';
    if (sockets) {
        sockets.forEach(function (socket) {
            var port = socket.port || process.env.PORT;
            var local = socket.local || undefined;
            var filename;
            if (local) {
                var httpd;
                if (typeof port === 'string') {
                    port = port.replace(/\$PORT/, sys.httpPort);
                }
                if (port === process.env.PORT || port === '$PORT') {
                    httpd = httpServer.createServer(app);
                } else {
                    httpd = httpServer.createServer(main);
                }
                filename = prepareFilename(local);
                require(filename).__socket(httpd);
                console.log('WebSocket:'.cyan, local,
                            '('.cyan, port, ')'.cyan);
                httpd.listen(port);
            }
        });
    }
};

var interpretApis = function (apis, app) {
    'use strict';
    if (apis) {
        apis.forEach(function (api) {
            var uri = api.uri || undefined;
            var method = api.method || undefined;
            var local = api.local || undefined;
            var filename;

            if(uri && method && local) {
                filename = prepareFilename(local);
                switch (method.toUpperCase()) {
                case 'GET':
                    app.get(uri, function (req, res) {
                        require(filename).__get(req, res);
                    });
                    break;
                case 'POST':
                    app.post(uri, function (req, res) {
                        require(filename).__post(req, res);
                    });
                    break;
                case 'PUT':
                    app.put(uri, function (req, res) {
                        require(filename).__put(req, res);
                    });
                    break;
                case 'DELETE':
                    app.delete(uri, function (req, res) {
                        require(filename).__delete(req, res);
                    });
                    break;
                case 'ALL':
                    app.all(uri, function (req, res) {
                        require(filename).__all(req, res);
                    });
                    break;
                }
                console.log('API:'.yellow, uri, '('.yellow,
                            method.toUpperCase().yellow, '|'.yellow,
                           local, ')'.yellow);
            }
        });
    }
};

var loadFromJson = function (json) {
    'use strict';

    // config middleware
    configMiddleware(json);

    // set main domain
    sys.top = json.domain || '127.0.0.1';
    main.use(express.vhost(sys.top, server));
    console.log('Main domain: '.green + sys.top);

    // set httpPort
    sys.httpPort = json.port || process.env.PORT;
    console.log('HTTP Port: '.green + sys.httpPort);

    sys.root = json.root || '';
    console.log('Root: '.green + sys.root);

    sys.log = json.log || 0;
    if (sys.log === 1 || sys.log === 'true') {
        server.use(express.logger());
    }

    sys.favicon = json.favicon || undefined;
    if (sys.favicon) {
        server.use(express.favicon(prepareFilename(sys.favicon)));
        console.log('Favicon: '.green + sys.favicon);
    }

    // now bring in static sites
    sys.sites = json.sites || undefined;
    interpretSites(sys.sites, server);

    // now bring in RESTful APIs
    sys.apis = json.apis || undefined;
    interpretApis(sys.apis, server);

     // now bring in Sockets
    sys.sockets = json.sockets || undefined;
    interpretSockets(sys.sockets, server);
    console.log();

    // now bring in vhosts
    sys.vhosts = json.vhosts || undefined;
    if (sys.vhosts) {
        sys.vhostApp = [];
        sys.vhosts.forEach(function (vhost, index, vhosts) {
            if (vhost.subdomain) {
                console.log('-------------------'.green);
                console.log('Adding virtual host: '.green + vhost.subdomain);
                var vApp = express();
                sys.vhostApp.push(vApp);
                var sites = vhost.sites || undefined;
                var apis = vhost.apis || undefined;
                var sockets = vhost.sockets || undefined;
                var favicon = vhost.favicon || undefined;
                // applying system logger config
                if (sys.log) {
                    vApp.use(express.logger());
                }
                if (favicon) {
                    vApp.use(express.favicon(prepareFilename(favicon)));
                    console.log('Vhost Favicon: '.green + favicon);
                }
                if (sites) {
                    interpretSites(sites, vApp);
                }
                if (apis) {
                    interpretApis(apis, vApp);
                }
                if (sockets) {
                    interpretSockets(sockets, vApp);
                }
                main.use(express.vhost(prepareDomain(vhost.subdomain), vApp));
                console.log('Registered vitual host "'.green + vhost.subdomain + '".'.green);
                console.log('-------------------\n'.green);
            }
        });
    }
};

module.exports = function (argv) {
    'use strict';

    if (argv.length < 3) {
        console.log('Nover version'.green, sys.__version.green);
        console.log('Fatal: Missing config file.'.red);
        console.log('Usage: nover <config file location>'.yellow);
        process.exit(0);
    }

    // check if config file is given
    sys.config = process.argv[2];
    console.log('Config: '.green + sys.config);

    // read in config file first
    console.log('Now reading config file...'.green);
    console.log('==========================\n'.green);
    pros.readfile(sys.config)
    .then(function (data) {
        var json = JSON.parse(data);
        loadFromJson(json);

        main.listen(sys.httpPort);
        console.log('================'.green);
        console.log('Now listening...'.green);
    }, function (err) {
        console.error(err.red);
    });
};

<|endoftext|>


<commit_before>var JSONAPIClient = require('json-api-client');
var Model = JSONAPIClient.Model;
var makeHTTPRequest = JSONAPIClient.makeHTTPRequest;
var ls = require('local-storage');

var config = require('./config');
var apiClient = require('./api-client');

// Use this to override the default API-specific headers.
var JSON_HEADERS = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
};

// We don't want to wait until the token is already expired before refreshing it.
var TOKEN_EXPIRATION_ALLOWANCE = 60 * 1000;

// Save local storage stuff as something totally obvious
var LOCAL_STORAGE_PREFIX = 'panoptesClientOAuth_';

// Specify whether to use local or session storage for session data.
var SESSION_STORAGE = window.sessionStorage;

// Create our model, then do stuff with it later
module.exports = new Model({
  _bearerRefreshTimeout: NaN,
  _clientAppId: ls.get(LOCAL_STORAGE_PREFIX + 'clientAppId'),
  _currentSessionCheckPromise: null,
  _currentUserPromise: null,
  _tokenDetails: null,

  checkCurrent: function() {
    console.log('Checking current user');

    // If we're checking for an existing session already, defer this until
    // it's finished
    var initialCheck = this._currentSessionCheckPromise || Promise.resolve();

    if (!this._currentUserPromise) {
      this.update({
        _currentUserPromise: initialCheck
          .then(this._getSession)
          .catch(function() {
            // Nobody's signed in. This isn't an error.
            console.info('No current user');
            return null;
          }),
      });
    }

    return this._currentUserPromise;
  },

  init: function (appID) {
    return new Promise(function(resolve, reject) {

      // Don't init if we're in an iFrame, as we could be the token refresh process
      if (window.frameElement) {
        return false;
      }

      console.log('Using OAuth (implicit grant) for login');
      console.info('Setting app ID to', appID);
      ls.get(LOCAL_STORAGE_PREFIX + 'clientAppId', appID);
      this._clientAppId = appID;

      // Handle new token details if we've completed a sign in
      if (checkUrlForToken(window.location.hash)) {
        console.log('Token found in URL');
        var tokenDetails = parseUrl(window.location.hash);
        this._handleBearerToken(tokenDetails);
        SESSION_STORAGE.setItem(LOCAL_STORAGE_PREFIX + 'tokenDetails', JSON.stringify(tokenDetails));

        // And redirect to the desired page
        var url = ls.get(LOCAL_STORAGE_PREFIX + 'redirectUri');
        location.assign(url);
      }

      // If not, let's try and pick up an existing Panoptes session anyway
      this._checkForPanoptesSession()
        .then(function(tokenDetails) {
          this._handleBearerToken(tokenDetails);
          resolve(tokenDetails)
        }.bind(this))
        .catch(function (error) {
          // We probably haven't signed in before
          console.info(error);
          resolve(null);
        });

    }.bind(this));

  },

  signIn: function(redirectUri) {
    console.log('Signing in with OAuth');
    var originalArguments = arguments;
    return this.checkCurrent()
      .then(function (token) {
        if (token) {
          return this.signOut().then(function() {
            return this.signIn.apply(this, originalArguments);
          }.bind(this));
        } else {
          this._saveRedirectUri(redirectUri);
          location.assign(this._createOAuthUrl(redirectUri));
        }
      }.bind(this));
  },

  signOut: function() {
    console.log('Signing out');
    return this.checkCurrent().then(function(user) {
      if (user) {
        return this._getAuthToken().then(function(token) {
          var url = config.oauthHost + '/users/sign_out';

          var deleteHeaders = Object.create(JSON_HEADERS);
          deleteHeaders['X-CSRF-Token'] = token;

          return makeHTTPRequest('DELETE', url, null, deleteHeaders)
            .then(function() {
              this._deleteBearerToken();
              this.update({
                _currentUserPromise: Promise.resolve(null),
              });
              console.info('Signed out');
              return null;
            }.bind(this))
            .catch(function(request) {
              console.error('Failed to sign out');
              return apiClient.handleError(request);
            }.bind(this));
        }.bind(this));
      } else {
        throw new Error('Failed to sign out; not signed in');
      }
    }.bind(this));
  },

  _createOAuthUrl: function(redirectUri) {
    if (!this._clientAppId) {
      throw new Error('Client app ID not set');
    }

    return [
      config.oauthHost,
      '/oauth/authorize',
      '?response_type=token',
      '&client_id=',
      this._clientAppId,
      '&redirect_uri=',
      redirectUri
    ].join('');
  },

  _checkForPanoptesSession: function() {
    var sessionTokenDetails = JSON.parse(SESSION_STORAGE.getItem(LOCAL_STORAGE_PREFIX + 'tokenDetails'));
    var redirectUri = ls.get(LOCAL_STORAGE_PREFIX + 'redirectUri');
    this.update({
      _currentSessionCheckPromise: new Promise(function(resolve, reject) {
        if (sessionTokenDetails) {
          resolve(sessionTokenDetails);
        }

        if (!redirectUri) {
          reject(Error('No redirect URI found'));
        }

        // Create a new iFrame
        var url = this._createOAuthUrl(redirectUri);
        this._iframe = createIFrame(url);

        // Try and get the token details from our iFrame. If it throws an error,
        // it's because we're being redirected to the signin page (and therefore
        // there is no session) - so we should also replace the security error
        // with a more relevant one.
        this._iframe.onload = function() {
          try {
            var newUrl = this._iframe.contentWindow.location.href;
            if (checkUrlForToken(newUrl)) {
              console.info('Found existing Panoptes session');
              var newTokenDetails = parseUrl(newUrl);
              resolve(newTokenDetails);
            } else {
              throw new TypeError('Valid OAuth details not found in URL');
            }
          } catch (error) {
            if (error.name === 'SecurityError') {
              console.warn('No existing Panoptes session found');
            }
            reject(error);
          } finally {
            this._iframe = destroyIFrame(this._iframe);
          }
        }.bind(this);
      }.bind(this))
    });

    return this._currentSessionCheckPromise;
  },

  _deleteBearerToken: function() {
    console.log('Deleting bearer token');
    this._tokenDetails = null;
    delete apiClient.headers.Authorization;
    SESSION_STORAGE.removeItem(LOCAL_STORAGE_PREFIX + 'tokenDetails');
  },

  _getAuthToken: function() {
    console.log('Getting auth token');
    var url = config.oauthHost + '/users/sign_in/?now=' + Date.now();
    return makeHTTPRequest('GET', url, null, JSON_HEADERS)
      .then(function(response) {
        var authToken = response.header['x-csrf-token'];
        console.info('Got auth token', authToken.slice(-6));
        return authToken;
      })
      .catch(function(response) {
        console.error('Failed to get auth token');
        apiClient.handleError(response);
      });
  },

  _getSession: function() {
    console.log('Getting session');
    return apiClient.get('/me')
      .then(function(users) {
        var user = users[0];
        console.info('Got session', user.login, user.id);
        return user;
      })
      .catch(function(error) {
        console.error('Failed to get session');
        throw error;
      });
  },

  _handleBearerToken: function(tokenDetails) {
    console.log('Got new bearer token', tokenDetails.access_token.slice(-6));
    this._tokenDetails = tokenDetails;
    apiClient.headers.Authorization = 'Bearer ' + tokenDetails.access_token;

    var refresh = this._refreshBearerToken.bind(this);
    var timeToRefresh = (tokenDetails.expires_in * 1000) - TOKEN_EXPIRATION_ALLOWANCE;
    this._bearerRefreshTimeout = setTimeout(refresh, timeToRefresh);
    return tokenDetails;
  },

  _refreshBearerToken: function() {
    this._deleteBearerToken();
    return this._checkForPanoptesSession()
      .then(function(tokenDetails) {
        SESSION_STORAGE.setItem(LOCAL_STORAGE_PREFIX + 'tokenDetails', JSON.stringify(tokenDetails));
        return this._handleBearerToken(tokenDetails);
      }.bind(this))
      .catch(function (error) {
        console.info('Panoptes session has expired');
      });
  },

  _saveRedirectUri: function(redirectUri) {
    console.info('Saving redirectUri:', redirectUri);
    ls.set(LOCAL_STORAGE_PREFIX + 'redirectUri', redirectUri);
  }

});

// Utility functions
function checkUrlForToken(string) {
  return string.indexOf('access_token') !== -1 &&
    string.indexOf('token_type=bearer') !== -1;
}

function parseUrl(string) {
  var params = string.slice(1).split('&');
  var tokenDetails = {};
  params.forEach(function(param) {
    var [key, value] = param.split('=');
    tokenDetails[key] = value;
  });
  return tokenDetails;
}

function isTokenStillValid(tokenDetails) {
  return (tokenDetails.started_at + tokenDetails.expires_in) > Date.now();
}

function createIFrame(url) {
  var iframe = document.createElement('iframe');
  iframe.setAttribute('src', url);
  iframe.setAttribute('style', 'display: none;');
  document.body.appendChild(iframe);
  return iframe;
}

function destroyIFrame(iframe) {
  iframe.parentNode.removeChild(iframe);
  return null;
}
<commit_msg>Log refresh token errors<commit_after>var JSONAPIClient = require('json-api-client');
var Model = JSONAPIClient.Model;
var makeHTTPRequest = JSONAPIClient.makeHTTPRequest;
var ls = require('local-storage');

var config = require('./config');
var apiClient = require('./api-client');

// Use this to override the default API-specific headers.
var JSON_HEADERS = {
  'Content-Type': 'application/json',
  'Accept': 'application/json',
};

// We don't want to wait until the token is already expired before refreshing it.
var TOKEN_EXPIRATION_ALLOWANCE = 60 * 1000;

// Save local storage stuff as something totally obvious
var LOCAL_STORAGE_PREFIX = 'panoptesClientOAuth_';

// Specify whether to use local or session storage for session data.
var SESSION_STORAGE = window.sessionStorage;

// Create our model, then do stuff with it later
module.exports = new Model({
  _bearerRefreshTimeout: NaN,
  _clientAppId: ls.get(LOCAL_STORAGE_PREFIX + 'clientAppId'),
  _currentSessionCheckPromise: null,
  _currentUserPromise: null,
  _tokenDetails: null,

  checkCurrent: function() {
    console.log('Checking current user');

    // If we're checking for an existing session already, defer this until
    // it's finished
    var initialCheck = this._currentSessionCheckPromise || Promise.resolve();

    if (!this._currentUserPromise) {
      this.update({
        _currentUserPromise: initialCheck
          .then(this._getSession)
          .catch(function() {
            // Nobody's signed in. This isn't an error.
            console.info('No current user');
            return null;
          }),
      });
    }

    return this._currentUserPromise;
  },

  init: function (appID) {
    return new Promise(function(resolve, reject) {

      // Don't init if we're in an iFrame, as we could be the token refresh process
      if (window.frameElement) {
        return false;
      }

      console.log('Using OAuth (implicit grant) for login');
      console.info('Setting app ID to', appID);
      ls.get(LOCAL_STORAGE_PREFIX + 'clientAppId', appID);
      this._clientAppId = appID;

      // Handle new token details if we've completed a sign in
      if (checkUrlForToken(window.location.hash)) {
        console.log('Token found in URL');
        var tokenDetails = parseUrl(window.location.hash);
        this._handleBearerToken(tokenDetails);
        SESSION_STORAGE.setItem(LOCAL_STORAGE_PREFIX + 'tokenDetails', JSON.stringify(tokenDetails));

        // And redirect to the desired page
        var url = ls.get(LOCAL_STORAGE_PREFIX + 'redirectUri');
        location.assign(url);
      }

      // If not, let's try and pick up an existing Panoptes session anyway
      this._checkForPanoptesSession()
        .then(function(tokenDetails) {
          this._handleBearerToken(tokenDetails);
          resolve(tokenDetails)
        }.bind(this))
        .catch(function (error) {
          // We probably haven't signed in before
          console.info(error);
          resolve(null);
        });

    }.bind(this));

  },

  signIn: function(redirectUri) {
    console.log('Signing in with OAuth');
    var originalArguments = arguments;
    return this.checkCurrent()
      .then(function (token) {
        if (token) {
          return this.signOut().then(function() {
            return this.signIn.apply(this, originalArguments);
          }.bind(this));
        } else {
          this._saveRedirectUri(redirectUri);
          location.assign(this._createOAuthUrl(redirectUri));
        }
      }.bind(this));
  },

  signOut: function() {
    console.log('Signing out');
    return this.checkCurrent().then(function(user) {
      if (user) {
        return this._getAuthToken().then(function(token) {
          var url = config.oauthHost + '/users/sign_out';

          var deleteHeaders = Object.create(JSON_HEADERS);
          deleteHeaders['X-CSRF-Token'] = token;

          return makeHTTPRequest('DELETE', url, null, deleteHeaders)
            .then(function() {
              this._deleteBearerToken();
              this.update({
                _currentUserPromise: Promise.resolve(null),
              });
              console.info('Signed out');
              return null;
            }.bind(this))
            .catch(function(request) {
              console.error('Failed to sign out');
              return apiClient.handleError(request);
            }.bind(this));
        }.bind(this));
      } else {
        throw new Error('Failed to sign out; not signed in');
      }
    }.bind(this));
  },

  _createOAuthUrl: function(redirectUri) {
    if (!this._clientAppId) {
      throw new Error('Client app ID not set');
    }

    return [
      config.oauthHost,
      '/oauth/authorize',
      '?response_type=token',
      '&client_id=',
      this._clientAppId,
      '&redirect_uri=',
      redirectUri
    ].join('');
  },

  _checkForPanoptesSession: function() {
    var sessionTokenDetails = JSON.parse(SESSION_STORAGE.getItem(LOCAL_STORAGE_PREFIX + 'tokenDetails'));
    var redirectUri = ls.get(LOCAL_STORAGE_PREFIX + 'redirectUri');
    this.update({
      _currentSessionCheckPromise: new Promise(function(resolve, reject) {
        if (sessionTokenDetails) {
          resolve(sessionTokenDetails);
        }

        if (!redirectUri) {
          reject(Error('No redirect URI found'));
        }

        // Create a new iFrame
        var url = this._createOAuthUrl(redirectUri);
        this._iframe = createIFrame(url);

        // Try and get the token details from our iFrame. If it throws an error,
        // it's because we're being redirected to the signin page (and therefore
        // there is no session) - so we should also replace the security error
        // with a more relevant one.
        this._iframe.onload = function() {
          try {
            var newUrl = this._iframe.contentWindow.location.href;
            if (checkUrlForToken(newUrl)) {
              console.info('Found existing Panoptes session');
              var newTokenDetails = parseUrl(newUrl);
              resolve(newTokenDetails);
            } else {
              throw new TypeError('Valid OAuth details not found in URL');
            }
          } catch (error) {
            if (error.name === 'SecurityError') {
              console.warn('No existing Panoptes session found');
            }
            reject(error);
          } finally {
            this._iframe = destroyIFrame(this._iframe);
          }
        }.bind(this);
      }.bind(this))
    });

    return this._currentSessionCheckPromise;
  },

  _deleteBearerToken: function() {
    console.log('Deleting bearer token');
    this._tokenDetails = null;
    delete apiClient.headers.Authorization;
    SESSION_STORAGE.removeItem(LOCAL_STORAGE_PREFIX + 'tokenDetails');
  },

  _getAuthToken: function() {
    console.log('Getting auth token');
    var url = config.oauthHost + '/users/sign_in/?now=' + Date.now();
    return makeHTTPRequest('GET', url, null, JSON_HEADERS)
      .then(function(response) {
        var authToken = response.header['x-csrf-token'];
        console.info('Got auth token', authToken.slice(-6));
        return authToken;
      })
      .catch(function(response) {
        console.error('Failed to get auth token');
        apiClient.handleError(response);
      });
  },

  _getSession: function() {
    console.log('Getting session');
    return apiClient.get('/me')
      .then(function(users) {
        var user = users[0];
        console.info('Got session', user.login, user.id);
        return user;
      })
      .catch(function(error) {
        console.error('Failed to get session');
        throw error;
      });
  },

  _handleBearerToken: function(tokenDetails) {
    console.log('Got new bearer token', tokenDetails.access_token.slice(-6));
    this._tokenDetails = tokenDetails;
    apiClient.headers.Authorization = 'Bearer ' + tokenDetails.access_token;

    var refresh = this._refreshBearerToken.bind(this);
    var timeToRefresh = (tokenDetails.expires_in * 1000) - TOKEN_EXPIRATION_ALLOWANCE;
    this._bearerRefreshTimeout = setTimeout(refresh, timeToRefresh);
    return tokenDetails;
  },

  _refreshBearerToken: function() {
    this._deleteBearerToken();
    return this._checkForPanoptesSession()
      .then(function(tokenDetails) {
        SESSION_STORAGE.setItem(LOCAL_STORAGE_PREFIX + 'tokenDetails', JSON.stringify(tokenDetails));
        return this._handleBearerToken(tokenDetails);
      }.bind(this))
      .catch(function (error) {
        console.info('Panoptes session has expired');
        console.log(error);
      });
  },

  _saveRedirectUri: function(redirectUri) {
    console.info('Saving redirectUri:', redirectUri);
    ls.set(LOCAL_STORAGE_PREFIX + 'redirectUri', redirectUri);
  }

});

// Utility functions
function checkUrlForToken(string) {
  return string.indexOf('access_token') !== -1 &&
    string.indexOf('token_type=bearer') !== -1;
}

function parseUrl(string) {
  var params = string.slice(1).split('&');
  var tokenDetails = {};
  params.forEach(function(param) {
    var [key, value] = param.split('=');
    tokenDetails[key] = value;
  });
  return tokenDetails;
}

function isTokenStillValid(tokenDetails) {
  return (tokenDetails.started_at + tokenDetails.expires_in) > Date.now();
}

function createIFrame(url) {
  var iframe = document.createElement('iframe');
  iframe.setAttribute('src', url);
  iframe.setAttribute('style', 'display: none;');
  document.body.appendChild(iframe);
  return iframe;
}

function destroyIFrame(iframe) {
  iframe.parentNode.removeChild(iframe);
  return null;
}
<|endoftext|>


<commit_before>var crypto= require('crypto'),
    sha1= require('./sha1'),
    http= require('http'),
    URL= require('url'),
    querystring= require('querystring');

exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, version, authorize_callback, signatureMethod, nonceSize, customHeaders) {
  this._requestUrl= requestUrl;
  this._accessUrl= accessUrl;
  this._consumerKey= consumerKey;
  this._consumerSecret= this._encodeData( consumerSecret );
  this._version= version;
  if( authorize_callback === undefined ) {
    this._authorize_callback= "oob";
  }
  else {
    this._authorize_callback= authorize_callback;
  }

  if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1")
    throw new Error("Un-supported signature method: " + signatureMethod )
  this._signatureMethod= signatureMethod;
  this._nonceSize= nonceSize || 32;
  this._headers= customHeaders || {"Accept" : "*/*",
                                   "Connection" : "close",
                                   "User-Agent" : "Node authentication"}
};

exports.OAuth.prototype._getTimestamp= function() {
  return Math.floor( (new Date()).getTime() / 1000 );
}

exports.OAuth.prototype._encodeData= function(toEncode){
 if( toEncode == null || toEncode == "" ) return ""
 else {
    var result= encodeURIComponent(toEncode);
    // Fix the mismatch between OAuth's  RFC3986's and Javascript's beliefs in what is right and wrong ;)
    return result.replace(/\!/g, "%21")
                 .replace(/\'/g, "%27")
                 .replace(/\(/g, "%28")
                 .replace(/\)/g, "%29")
                 .replace(/\*/g, "%2A");
 }
}

exports.OAuth.prototype._decodeData= function(toDecode) {
  if( toDecode != null ) {
    toDecode = toDecode.replace(/\+/g, " ");
  }
  return decodeURIComponent( toDecode);
}

exports.OAuth.prototype._getSignature= function(method, url, parameters, tokenSecret) {
  var signatureBase= this._createSignatureBase(method, url, parameters);
  return this._createSignature( signatureBase, tokenSecret );
}

exports.OAuth.prototype._normalizeUrl= function(url) {
  var parsedUrl= URL.parse(url, true)
   var port ="";
   if( parsedUrl.port ) {
     if( (parsedUrl.protocol == "http:" && parsedUrl.port != "80" ) ||
         (parsedUrl.protocol == "https:" && parsedUrl.port != "443") ) {
           port= ":" + parsedUrl.port;
         }
   }

  if( !parsedUrl.pathname  || parsedUrl.pathname == "" ) parsedUrl.pathname ="/";

  return parsedUrl.protocol + "//" + parsedUrl.hostname + port + parsedUrl.pathname;
}

// build the OAuth request authorization header
exports.OAuth.prototype._buildAuthorizationHeaders= function(orderedParameters) {
  var authHeader="OAuth ";
  for( var i= 0 ; i < orderedParameters.length; i++) {
     // Whilst the all the parameters should be included within the signature, only the oauth_ arguments
     // should appear within the authorization header.
     if( orderedParameters[i][0].match('^oauth_') == "oauth_") {
      authHeader+= this._encodeData(orderedParameters[i][0])+"=\""+ this._encodeData(orderedParameters[i][1])+"\",";
     }
  }
  authHeader= authHeader.substring(0, authHeader.length-1);
  return authHeader;
}

// Takes a literal in, then returns a sorted array
exports.OAuth.prototype._sortRequestParams= function(argumentsHash) {
  var argument_pairs= [];
  for(var key in argumentsHash ) {
      argument_pairs[argument_pairs.length]= [key, argumentsHash[key]];
  }
  // Sort by name, then value.
  argument_pairs.sort(function(a,b) {
      if ( a[0]== b[0] )  {
        return a[1] < b[1] ? -1 : 1;
      }
      else return a[0] < b[0] ? -1 : 1;
  });

  return argument_pairs;
}

exports.OAuth.prototype._normaliseRequestParams= function(arguments) {
  var argument_pairs= this._sortRequestParams( arguments );
  var args= "";
  for(var i=0;i<argument_pairs.length;i++) {
      args+= this._encodeData( argument_pairs[i][0] );
      args+= "="
      args+= this._encodeData( argument_pairs[i][1] );
      if( i < argument_pairs.length-1 ) args+= "&";
  }
  return args;
}

exports.OAuth.prototype._createSignatureBase= function(method, url, parameters) {
  url= this._encodeData( this._normalizeUrl(url) );
  parameters= this._encodeData( parameters );
  return method.toUpperCase() + "&" + url + "&" + parameters;
}

exports.OAuth.prototype._createSignature= function(signatureBase, tokenSecret) {
   if( tokenSecret === undefined ) var tokenSecret= "";
   else tokenSecret= this._encodeData( tokenSecret );
   // consumerSecret is already encoded
   var key= this._consumerSecret + "&" + tokenSecret;

   var hash= ""
   if( this._signatureMethod == "PLAINTEXT" ) {
     hash= this._encodeData(key);
   }
   else {
     hash= sha1.HMACSHA1(key, signatureBase);
   }

   return hash;
}
exports.OAuth.prototype.NONCE_CHARS= ['a','b','c','d','e','f','g','h','i','j','k','l','m','n',
              'o','p','q','r','s','t','u','v','w','x','y','z','A','B',
              'C','D','E','F','G','H','I','J','K','L','M','N','O','P',
              'Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3',
              '4','5','6','7','8','9'];

exports.OAuth.prototype._getNonce= function(nonceSize) {
   var result = [];
   var chars= this.NONCE_CHARS;
   var char_pos;
   var nonce_chars_length= chars.length;

   for (var i = 0; i < nonceSize; i++) {
       char_pos= Math.floor(Math.random() * nonce_chars_length);
       result[i]=  chars[char_pos];
   }
   return result.join('');
}

exports.OAuth.prototype._createClient= function( port, hostname, sshEnabled, credentials ) {
  return http.createClient(port, hostname, sshEnabled, credentials);
}

exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type,  callback ) {
  var oauthParameters= {
      "oauth_timestamp":        this._getTimestamp(),
      "oauth_nonce":            this._getNonce(this._nonceSize),
      "oauth_version":          this._version,
      "oauth_signature_method": this._signatureMethod,
      "oauth_consumer_key":     this._consumerKey
  };

  if( oauth_token ) {
    oauthParameters["oauth_token"]= oauth_token;
  }
  if( extra_params ) {
    for( var key in extra_params ) {
         oauthParameters[key]= extra_params[key];
    }
  }
  if( !post_content_type ) {
    post_content_type= "application/x-www-form-urlencoded";
  }

  var parsedUrl= URL.parse( url, false );
  if( parsedUrl.protocol == "http:" && !parsedUrl.port ) parsedUrl.port= 80;
  if( parsedUrl.protocol == "https:" && !parsedUrl.port ) parsedUrl.port= 443;

  if( parsedUrl.query ) {
   var extraParameters= querystring.parse(parsedUrl.query);
   for(var key in extraParameters ) {
     oauthParameters[key]= extraParameters[key];
   }
  }

  var sig= this._getSignature( method,  url,  this._normaliseRequestParams(oauthParameters), oauth_token_secret);
  var orderedParameters= this._sortRequestParams( oauthParameters );
  orderedParameters[orderedParameters.length]= ["oauth_signature", sig];

  var query="";
  for( var i= 0 ; i < orderedParameters.length; i++) {
    query+= this._encodeData(orderedParameters[i][0])+"="+ this._encodeData(orderedParameters[i][1]) + "&";
  }
  query= query.substring(0, query.length-1);

  var oauthProvider;
  if( parsedUrl.protocol == "https:" ) {
    oauthProvider= this._createClient(parsedUrl.port, parsedUrl.hostname, true, crypto.createCredentials({}));
  }
  else {
    oauthProvider= this._createClient(parsedUrl.port, parsedUrl.hostname);
  }

  var headers= {};
  headers["Authorization"]= this._buildAuthorizationHeaders(orderedParameters);
  headers["Host"] = parsedUrl.host

  for( var key in this._headers ) {
    if (this._headers.hasOwnProperty(key)) {
      headers[key]= this._headers[key];
    }
  }

  headers["Content-length"]= post_body ? post_body.length : 0; //Probably going to fail if not posting ascii
  headers["Content-Type"]= post_content_type;

  var path;
  if( !parsedUrl.pathname  || parsedUrl.pathname == "" ) parsedUrl.pathname ="/";
  if( parsedUrl.query ) path= parsedUrl.pathname + "?"+ parsedUrl.query ;
  else path= parsedUrl.pathname;

  var request = oauthProvider.request(method,  path , headers);
  if( callback ) {
    var data="";
    var self= this;
    request.addListener('response', function (response) {
      response.setEncoding('utf8');
      response.addListener('data', function (chunk) {
        data+=chunk;
      });
      response.addListener('end', function () {
        if( response.statusCode != 200 ) {
          callback({ statusCode: response.statusCode, data: data });
        } else {
          callback(null, data, response);
        }
      });
    });

    request.socket.addListener("error",callback);
    if( method == "POST" && post_body != null && post_body != "" ) {
      request.write(post_body);
    }
    request.end();
  }
  else {
    if( method == "POST" && post_body != null && post_body != "" ) {
      request.write(post_body);
    }
    return request;
  }

  return;
}

exports.OAuth.prototype.getOAuthAccessToken= function(oauth_token, oauth_token_secret, oauth_verifier,  callback) {
  var extraParams= {};
  if( typeof oauth_verifier == "function" ) {
    callback= oauth_verifier;
  } else {
    extraParams.oauth_verifier= oauth_verifier;
  }

   this._performSecureRequest( oauth_token, oauth_token_secret, "GET", this._accessUrl, extraParams, "", null, function(error, data, response) {
         if( error ) callback(error);
         else {
           var results= querystring.parse( data );
           var oauth_access_token= results["oauth_token"];
           delete results["oauth_token"];
           var oauth_access_token_secret= results["oauth_token_secret"];
           delete results["oauth_token_secret"];
           callback(null, oauth_access_token, oauth_access_token_secret, results );
         }
   })
}

// Deprecated
exports.OAuth.prototype.getProtectedResource= function(url, method, oauth_token, oauth_token_secret, callback) {
  this._performSecureRequest( oauth_token, oauth_token_secret, method, url, null, "", null, callback );
}

exports.OAuth.prototype.get= function(url, oauth_token, oauth_token_secret, callback) {
  return this._performSecureRequest( oauth_token, oauth_token_secret, "GET", url, null, "", null, callback );
}

exports.OAuth.prototype.post= function(url, oauth_token, oauth_token_secret, post_body, post_content_type, callback) {
  var extra_params= null;
  if( typeof post_content_type == "function" ) {
    callback= post_content_type;
    post_content_type= null;
  }
  if( typeof post_body != "string" ) {
    post_content_type= "application/x-www-form-urlencoded"
    extra_params= post_body;
    post_body= querystring.stringify(post_body);
  }
  return this._performSecureRequest( oauth_token, oauth_token_secret, "POST", url, extra_params, post_body, post_content_type, callback );
}

exports.OAuth.prototype.getOAuthRequestToken= function(extraParams, callback) {
  if( typeof extraParams == "function" ){
    callback = extraParams;
    extraParams = {};
  }

  // Callbacks are 1.0A related
  if( this._authorize_callback ) {
    extraParams["oauth_callback"]= this._authorize_callback;
  }
  this._performSecureRequest( null, null, "POST", this._requestUrl, extraParams, "", null, function(error, data, response) {
    if( error ) callback(error);
    else {
      var results= querystring.parse(data);

      var oauth_token= results["oauth_token"];
      var oauth_token_secret= results["oauth_token_secret"];
      delete results["oauth_token"];
      delete results["oauth_token_secret"];
      callback(null, oauth_token, oauth_token_secret,  results );
    }
  });
}

exports.OAuth.prototype.signUrl= function(url, oauth_token, oauth_token_secret, method) {
  var collectedParameters= {
      "oauth_timestamp":        this._getTimestamp(),
      "oauth_nonce":            this._getNonce(this._nonceSize),
      "oauth_version":          this._version,
      "oauth_signature_method": this._signatureMethod,
      "oauth_consumer_key":     this._consumerKey
  };

  if( oauth_token ) {
    collectedParameters["oauth_token"]= oauth_token;
  }
  if( method === undefined ) {
    var method= "GET";
  }

  var parsedUrl= URL.parse( url, false );
  if( parsedUrl.protocol == "http:" && !parsedUrl.port ) parsedUrl.port= 80;
  if( parsedUrl.protocol == "https:" && !parsedUrl.port ) parsedUrl.port= 443;

  if( parsedUrl.query ) {
   var queryParams= querystring.parse(parsedUrl.query);
   for(var key in queryParams ) {
     collectedParameters[key]= queryParams[key];
   }
  }

  var sig= this._getSignature( method,  url,  this._normaliseRequestParams(collectedParameters), oauth_token_secret);
  var orderedParameters= this._sortRequestParams( collectedParameters );
  orderedParameters[orderedParameters.length]= ["oauth_signature", sig];

  var query="";
  for( var i= 0 ; i < orderedParameters.length; i++) {
    query+= orderedParameters[i][0]+"="+ this._encodeData(orderedParameters[i][1]) + "&";
  }
  query= query.substring(0, query.length-1);

  return parsedUrl.protocol + "//"+ parsedUrl.host + parsedUrl.pathname + "?" + query;
};

<commit_msg>more strict comparison.<commit_after>var crypto= require('crypto'),
    sha1= require('./sha1'),
    http= require('http'),
    URL= require('url'),
    querystring= require('querystring');

exports.OAuth= function(requestUrl, accessUrl, consumerKey, consumerSecret, version, authorize_callback, signatureMethod, nonceSize, customHeaders) {
  this._requestUrl= requestUrl;
  this._accessUrl= accessUrl;
  this._consumerKey= consumerKey;
  this._consumerSecret= this._encodeData( consumerSecret );
  this._version= version;
  if( authorize_callback === undefined ) {
    this._authorize_callback= "oob";
  }
  else {
    this._authorize_callback= authorize_callback;
  }

  if( signatureMethod != "PLAINTEXT" && signatureMethod != "HMAC-SHA1")
    throw new Error("Un-supported signature method: " + signatureMethod )
  this._signatureMethod= signatureMethod;
  this._nonceSize= nonceSize || 32;
  this._headers= customHeaders || {"Accept" : "*/*",
                                   "Connection" : "close",
                                   "User-Agent" : "Node authentication"}
};

exports.OAuth.prototype._getTimestamp= function() {
  return Math.floor( (new Date()).getTime() / 1000 );
}

exports.OAuth.prototype._encodeData= function(toEncode){
 if( toEncode == null || toEncode == "" ) return ""
 else {
    var result= encodeURIComponent(toEncode);
    // Fix the mismatch between OAuth's  RFC3986's and Javascript's beliefs in what is right and wrong ;)
    return result.replace(/\!/g, "%21")
                 .replace(/\'/g, "%27")
                 .replace(/\(/g, "%28")
                 .replace(/\)/g, "%29")
                 .replace(/\*/g, "%2A");
 }
}

exports.OAuth.prototype._decodeData= function(toDecode) {
  if( toDecode != null ) {
    toDecode = toDecode.replace(/\+/g, " ");
  }
  return decodeURIComponent( toDecode);
}

exports.OAuth.prototype._getSignature= function(method, url, parameters, tokenSecret) {
  var signatureBase= this._createSignatureBase(method, url, parameters);
  return this._createSignature( signatureBase, tokenSecret );
}

exports.OAuth.prototype._normalizeUrl= function(url) {
  var parsedUrl= URL.parse(url, true)
   var port ="";
   if( parsedUrl.port ) {
     if( (parsedUrl.protocol == "http:" && parsedUrl.port != "80" ) ||
         (parsedUrl.protocol == "https:" && parsedUrl.port != "443") ) {
           port= ":" + parsedUrl.port;
         }
   }

  if( !parsedUrl.pathname  || parsedUrl.pathname == "" ) parsedUrl.pathname ="/";

  return parsedUrl.protocol + "//" + parsedUrl.hostname + port + parsedUrl.pathname;
}

// build the OAuth request authorization header
exports.OAuth.prototype._buildAuthorizationHeaders= function(orderedParameters) {
  var authHeader="OAuth ";
  for( var i= 0 ; i < orderedParameters.length; i++) {
     // Whilst the all the parameters should be included within the signature, only the oauth_ arguments
     // should appear within the authorization header.
     var m = orderedParameters[i][0].match('^oauth_');
     if( m && m[0] === "oauth_") {
      authHeader+= this._encodeData(orderedParameters[i][0])+"=\""+ this._encodeData(orderedParameters[i][1])+"\",";
     }
  }
  authHeader= authHeader.substring(0, authHeader.length-1);
  return authHeader;
}

// Takes a literal in, then returns a sorted array
exports.OAuth.prototype._sortRequestParams= function(argumentsHash) {
  var argument_pairs= [];
  for(var key in argumentsHash ) {
      argument_pairs[argument_pairs.length]= [key, argumentsHash[key]];
  }
  // Sort by name, then value.
  argument_pairs.sort(function(a,b) {
      if ( a[0]== b[0] )  {
        return a[1] < b[1] ? -1 : 1;
      }
      else return a[0] < b[0] ? -1 : 1;
  });

  return argument_pairs;
}

exports.OAuth.prototype._normaliseRequestParams= function(arguments) {
  var argument_pairs= this._sortRequestParams( arguments );
  var args= "";
  for(var i=0;i<argument_pairs.length;i++) {
      args+= this._encodeData( argument_pairs[i][0] );
      args+= "="
      args+= this._encodeData( argument_pairs[i][1] );
      if( i < argument_pairs.length-1 ) args+= "&";
  }
  return args;
}

exports.OAuth.prototype._createSignatureBase= function(method, url, parameters) {
  url= this._encodeData( this._normalizeUrl(url) );
  parameters= this._encodeData( parameters );
  return method.toUpperCase() + "&" + url + "&" + parameters;
}

exports.OAuth.prototype._createSignature= function(signatureBase, tokenSecret) {
   if( tokenSecret === undefined ) var tokenSecret= "";
   else tokenSecret= this._encodeData( tokenSecret );
   // consumerSecret is already encoded
   var key= this._consumerSecret + "&" + tokenSecret;

   var hash= ""
   if( this._signatureMethod == "PLAINTEXT" ) {
     hash= this._encodeData(key);
   }
   else {
     hash= sha1.HMACSHA1(key, signatureBase);
   }

   return hash;
}
exports.OAuth.prototype.NONCE_CHARS= ['a','b','c','d','e','f','g','h','i','j','k','l','m','n',
              'o','p','q','r','s','t','u','v','w','x','y','z','A','B',
              'C','D','E','F','G','H','I','J','K','L','M','N','O','P',
              'Q','R','S','T','U','V','W','X','Y','Z','0','1','2','3',
              '4','5','6','7','8','9'];

exports.OAuth.prototype._getNonce= function(nonceSize) {
   var result = [];
   var chars= this.NONCE_CHARS;
   var char_pos;
   var nonce_chars_length= chars.length;

   for (var i = 0; i < nonceSize; i++) {
       char_pos= Math.floor(Math.random() * nonce_chars_length);
       result[i]=  chars[char_pos];
   }
   return result.join('');
}

exports.OAuth.prototype._createClient= function( port, hostname, sshEnabled, credentials ) {
  return http.createClient(port, hostname, sshEnabled, credentials);
}

exports.OAuth.prototype._performSecureRequest= function( oauth_token, oauth_token_secret, method, url, extra_params, post_body, post_content_type,  callback ) {
  var oauthParameters= {
      "oauth_timestamp":        this._getTimestamp(),
      "oauth_nonce":            this._getNonce(this._nonceSize),
      "oauth_version":          this._version,
      "oauth_signature_method": this._signatureMethod,
      "oauth_consumer_key":     this._consumerKey
  };

  if( oauth_token ) {
    oauthParameters["oauth_token"]= oauth_token;
  }
  if( extra_params ) {
    for( var key in extra_params ) {
         oauthParameters[key]= extra_params[key];
    }
  }
  if( !post_content_type ) {
    post_content_type= "application/x-www-form-urlencoded";
  }

  var parsedUrl= URL.parse( url, false );
  if( parsedUrl.protocol == "http:" && !parsedUrl.port ) parsedUrl.port= 80;
  if( parsedUrl.protocol == "https:" && !parsedUrl.port ) parsedUrl.port= 443;

  if( parsedUrl.query ) {
   var extraParameters= querystring.parse(parsedUrl.query);
   for(var key in extraParameters ) {
     oauthParameters[key]= extraParameters[key];
   }
  }

  var sig= this._getSignature( method,  url,  this._normaliseRequestParams(oauthParameters), oauth_token_secret);
  var orderedParameters= this._sortRequestParams( oauthParameters );
  orderedParameters[orderedParameters.length]= ["oauth_signature", sig];

  var query="";
  for( var i= 0 ; i < orderedParameters.length; i++) {
    query+= this._encodeData(orderedParameters[i][0])+"="+ this._encodeData(orderedParameters[i][1]) + "&";
  }
  query= query.substring(0, query.length-1);

  var oauthProvider;
  if( parsedUrl.protocol == "https:" ) {
    oauthProvider= this._createClient(parsedUrl.port, parsedUrl.hostname, true, crypto.createCredentials({}));
  }
  else {
    oauthProvider= this._createClient(parsedUrl.port, parsedUrl.hostname);
  }

  var headers= {};
  headers["Authorization"]= this._buildAuthorizationHeaders(orderedParameters);
  headers["Host"] = parsedUrl.host

  for( var key in this._headers ) {
    if (this._headers.hasOwnProperty(key)) {
      headers[key]= this._headers[key];
    }
  }

  headers["Content-length"]= post_body ? post_body.length : 0; //Probably going to fail if not posting ascii
  headers["Content-Type"]= post_content_type;

  var path;
  if( !parsedUrl.pathname  || parsedUrl.pathname == "" ) parsedUrl.pathname ="/";
  if( parsedUrl.query ) path= parsedUrl.pathname + "?"+ parsedUrl.query ;
  else path= parsedUrl.pathname;

  var request = oauthProvider.request(method,  path , headers);
  if( callback ) {
    var data="";
    var self= this;
    request.addListener('response', function (response) {
      response.setEncoding('utf8');
      response.addListener('data', function (chunk) {
        data+=chunk;
      });
      response.addListener('end', function () {
        if( response.statusCode != 200 ) {
          callback({ statusCode: response.statusCode, data: data });
        } else {
          callback(null, data, response);
        }
      });
    });

    request.socket.addListener("error",callback);
    if( method == "POST" && post_body != null && post_body != "" ) {
      request.write(post_body);
    }
    request.end();
  }
  else {
    if( method == "POST" && post_body != null && post_body != "" ) {
      request.write(post_body);
    }
    return request;
  }

  return;
}

exports.OAuth.prototype.getOAuthAccessToken= function(oauth_token, oauth_token_secret, oauth_verifier,  callback) {
  var extraParams= {};
  if( typeof oauth_verifier == "function" ) {
    callback= oauth_verifier;
  } else {
    extraParams.oauth_verifier= oauth_verifier;
  }

   this._performSecureRequest( oauth_token, oauth_token_secret, "GET", this._accessUrl, extraParams, "", null, function(error, data, response) {
         if( error ) callback(error);
         else {
           var results= querystring.parse( data );
           var oauth_access_token= results["oauth_token"];
           delete results["oauth_token"];
           var oauth_access_token_secret= results["oauth_token_secret"];
           delete results["oauth_token_secret"];
           callback(null, oauth_access_token, oauth_access_token_secret, results );
         }
   })
}

// Deprecated
exports.OAuth.prototype.getProtectedResource= function(url, method, oauth_token, oauth_token_secret, callback) {
  this._performSecureRequest( oauth_token, oauth_token_secret, method, url, null, "", null, callback );
}

exports.OAuth.prototype.get= function(url, oauth_token, oauth_token_secret, callback) {
  return this._performSecureRequest( oauth_token, oauth_token_secret, "GET", url, null, "", null, callback );
}

exports.OAuth.prototype.post= function(url, oauth_token, oauth_token_secret, post_body, post_content_type, callback) {
  var extra_params= null;
  if( typeof post_content_type == "function" ) {
    callback= post_content_type;
    post_content_type= null;
  }
  if( typeof post_body != "string" ) {
    post_content_type= "application/x-www-form-urlencoded"
    extra_params= post_body;
    post_body= querystring.stringify(post_body);
  }
  return this._performSecureRequest( oauth_token, oauth_token_secret, "POST", url, extra_params, post_body, post_content_type, callback );
}

exports.OAuth.prototype.getOAuthRequestToken= function(extraParams, callback) {
  if( typeof extraParams == "function" ){
    callback = extraParams;
    extraParams = {};
  }

  // Callbacks are 1.0A related
  if( this._authorize_callback ) {
    extraParams["oauth_callback"]= this._authorize_callback;
  }
  this._performSecureRequest( null, null, "POST", this._requestUrl, extraParams, "", null, function(error, data, response) {
    if( error ) callback(error);
    else {
      var results= querystring.parse(data);

      var oauth_token= results["oauth_token"];
      var oauth_token_secret= results["oauth_token_secret"];
      delete results["oauth_token"];
      delete results["oauth_token_secret"];
      callback(null, oauth_token, oauth_token_secret,  results );
    }
  });
}

exports.OAuth.prototype.signUrl= function(url, oauth_token, oauth_token_secret, method) {
  var collectedParameters= {
      "oauth_timestamp":        this._getTimestamp(),
      "oauth_nonce":            this._getNonce(this._nonceSize),
      "oauth_version":          this._version,
      "oauth_signature_method": this._signatureMethod,
      "oauth_consumer_key":     this._consumerKey
  };

  if( oauth_token ) {
    collectedParameters["oauth_token"]= oauth_token;
  }
  if( method === undefined ) {
    var method= "GET";
  }

  var parsedUrl= URL.parse( url, false );
  if( parsedUrl.protocol == "http:" && !parsedUrl.port ) parsedUrl.port= 80;
  if( parsedUrl.protocol == "https:" && !parsedUrl.port ) parsedUrl.port= 443;

  if( parsedUrl.query ) {
   var queryParams= querystring.parse(parsedUrl.query);
   for(var key in queryParams ) {
     collectedParameters[key]= queryParams[key];
   }
  }

  var sig= this._getSignature( method,  url,  this._normaliseRequestParams(collectedParameters), oauth_token_secret);
  var orderedParameters= this._sortRequestParams( collectedParameters );
  orderedParameters[orderedParameters.length]= ["oauth_signature", sig];

  var query="";
  for( var i= 0 ; i < orderedParameters.length; i++) {
    query+= orderedParameters[i][0]+"="+ this._encodeData(orderedParameters[i][1]) + "&";
  }
  query= query.substring(0, query.length-1);

  return parsedUrl.protocol + "//"+ parsedUrl.host + parsedUrl.pathname + "?" + query;
};

<|endoftext|>