<commit_before>/*

browserclient.js
================

Model objects (via Backbone) for a browser client (a connection to a browser + the test run session)
and the returned test results for a run of tests.

*/

var log = require('winston')
  , Backbone = require('backbone')
  , tap = require('tap')

var TestResults = Backbone.Model.extend({
    initialize: function(){
        this.reset()
    }
    , reset: function(){
        this.set({
            topLevelError: null
            , failed: 0
            , passed: 0
            , total: 0
            , tests: new Backbone.Collection
            , all: false
        })
    }
    , addResult: function(result){
        var total = this.get('total')
          , passed = this.get('passed')
          , failed = this.get('failed')
        total++
        if (result.failed == 0)
            passed++
        else
            failed++
        this.set({
            total: total
            , passed: passed
            , failed: failed
            , items: result.items
        })
        this.get('tests').push(result)
    }
})

exports.TestResults = TestResults

var BrowserRunner = Backbone.Model.extend({
    defaults: {
        type: 'browser'
    }
    , initialize: function(){
        this.set({
            messages: new Backbone.Collection
            , results: new TestResults
        }, {silent: true})
        this.registerSocketEvents()
        this.on('change:socket', function(){
            this.previous('socket').removeAllListeners()
            this.registerSocketEvents()
        }, this)
    }
    , registerSocketEvents: function(){
        var self = this
        var results = this.get('results')
        var messages = this.get('messages')
        var socket = this.get('socket')
        var app = this.get('app')
        var server = app.server
        socket.on('top-level-error', function(msg, url, line){
            var message = msg + ' at ' + url + ', line ' + line + '\n'
            messages.push({
                type: 'error'
                , text: message
            })
        })
        socket.on('error', function(message){
            messages.push({
                type: 'error'
                , text: message + '\n'
            })
        })
        socket.on('warn', function(message){
            messages.push({
                type: 'warn'
                , text: message + '\n'
            })
        })
        socket.on('log', function(message){
            messages.push({
                type: 'log'
                , text: message + '\n'
            })
        })
        socket.on('tests-start', function(){
            self.trigger('tests-start')
        })
        socket.on('test-result', function(result){
            results.addResult(result)
            server.emit('test-result', result)
        })
        socket.on('all-test-results', function(){
            results.set('all', true)
            self.trigger('tests-end')
            app.emit('all-test-results', results, self)
        })
        socket.on('disconnect', function(){
            log.info('Client disconnected ' + self.get('name'))
            self.get('results').reset()
            self.get('messages').reset()
            self.pending = setTimeout(function(){
                app.removeBrowser(self)
            }, 1000)
        })
    }
    , startTests: function(){
        this.get('results').reset()
        this.get('socket').emit('start-tests')
    }
    , hasResults: function(){
        var results = this.get('results')
        var total = results.get('total')
        return total > 0
    }
    , hasMessages: function(){
        return this.get('messages').length > 0
    }
})

exports.BrowserRunner = BrowserRunner

var ProcessRunner = Backbone.Model.extend({
    defaults: {
        type: 'process'
    }
    , initialize: function(attrs){
        this.launcher = attrs.launcher
        this.app = attrs.app
        // Assume launcher has already launched
        this.set({
            name: this.launcher.name
            , messages: new Backbone.Collection
            , results: this.isTap() ? new TestResults : null
        })

        this.startTests()
    }
    , isTap: function(){
        return this.launcher.settings.protocol === 'tap'
    }
    , hasResults: function(){
        return this.isTap()
    }
    , hasMessages: function(){
        return this.get('messages').length > 0
    }
    , registerProcess: function(process){
        var settings = this.launcher.settings
        var stdout = process.stdout
        var stderr = process.stderr
        var self = this
        if (!settings.hide_stdout){
            stdout.on('data', function(data){
                self.get('messages').push({
                    type: 'log'
                    , text: '' + data
                })
            })
        }
        if (!settings.hide_stderr){
            stderr.on('data', function(data){
                self.get('messages').push({
                    type: 'error'
                    , text: '' + data
                })
            })
        }
        process.on('exit', function(code){
            self.set('allPassed', code === 0)
            self.trigger('tests-end')
        })
        if (this.isTap()){
            this.setupTapConsumer(process)
        }
    }
    , setupTapConsumer: function(process){
        var stdout = process.stdout
        this.message = null
        this.stacktrace = []
        this.tapConsumer = new tap.Consumer
        this.tapConsumer.on('data', this.onTapData.bind(this))
        this.tapConsumer.on('end', this.onTapEnd.bind(this))
        this.tapConsumer.on('bailout', this.onTapError.bind(this))
        stdout.pipe(this.tapConsumer)
    }
    , onTapData: function(data){
        if (typeof data === 'string'){
            if (this.message === null){
                this.message = data
            }else{
                this.stacktrace.push(data)
            }
            return
        }
        if (data.skip){
            return
        }
        var results = this.get('results')

        if (data.id === undefined) {
            return
        }

        var test = {
            passed: 0
            , failed: 0
            , total: 1
            , id: data.id
            , name: data.name.trim()
            , items: []
        }

        if (!data.ok) {
            var stack = data.stack

            if (!stack) {
                stack = this.stacktrace.join('\n')
            } else if (Array.isArray(stack)) {
                stack = stack.join("\n")
            } else {
                var keys = Object.keys(stack)
                stack = keys.map(function (key) {
                    var values = Object.keys(stack[key])
                        .map(function (key2) {
                            return key2 + " " + stack[key][key2]
                        })

                    return [key].concat(values).join("\n")
                }).join("\n")
            }

            test.items.push({
                passed: false
                , message: this.message
                , stacktrace: stack
            })
            test.failed++
        } else {
            test.passed++
        }
        results.addResult(test)
        this.message = null
        this.stacktrace = []
    }
    , onTapError: function(){
        this.set('results', null)
    }
    , onTapEnd: function(err, testCount){
        var results = this.get('results')
        results.set('all', true)
        this.tapConsumer.removeAllListeners()
        this.tapConsumer = null
        this.app.emit('all-test-results', results)
        this.launcher.kill()
    }
    , startTests: function(){
        var self = this
        if (this.get('results')){
            this.get('results').reset()
        }else{
            this.set('results', this.isTap() ? new TestResults : null)
        }
        this.get('messages').reset([])
        this.set('allPassed', undefined)

        this.launcher.launch(function(process){
            self.registerProcess(process)
            setTimeout(function(){
                self.trigger('tests-start')
            }, 1)
        })
    }
})

exports.ProcessRunner = ProcessRunner

<commit_msg>JSON.stringify instead. More generic<commit_after>/*

browserclient.js
================

Model objects (via Backbone) for a browser client (a connection to a browser + the test run session)
and the returned test results for a run of tests.

*/

var log = require('winston')
  , Backbone = require('backbone')
  , tap = require('tap')

var TestResults = Backbone.Model.extend({
    initialize: function(){
        this.reset()
    }
    , reset: function(){
        this.set({
            topLevelError: null
            , failed: 0
            , passed: 0
            , total: 0
            , tests: new Backbone.Collection
            , all: false
        })
    }
    , addResult: function(result){
        var total = this.get('total')
          , passed = this.get('passed')
          , failed = this.get('failed')
        total++
        if (result.failed == 0)
            passed++
        else
            failed++
        this.set({
            total: total
            , passed: passed
            , failed: failed
            , items: result.items
        })
        this.get('tests').push(result)
    }
})

exports.TestResults = TestResults

var BrowserRunner = Backbone.Model.extend({
    defaults: {
        type: 'browser'
    }
    , initialize: function(){
        this.set({
            messages: new Backbone.Collection
            , results: new TestResults
        }, {silent: true})
        this.registerSocketEvents()
        this.on('change:socket', function(){
            this.previous('socket').removeAllListeners()
            this.registerSocketEvents()
        }, this)
    }
    , registerSocketEvents: function(){
        var self = this
        var results = this.get('results')
        var messages = this.get('messages')
        var socket = this.get('socket')
        var app = this.get('app')
        var server = app.server
        socket.on('top-level-error', function(msg, url, line){
            var message = msg + ' at ' + url + ', line ' + line + '\n'
            messages.push({
                type: 'error'
                , text: message
            })
        })
        socket.on('error', function(message){
            messages.push({
                type: 'error'
                , text: message + '\n'
            })
        })
        socket.on('warn', function(message){
            messages.push({
                type: 'warn'
                , text: message + '\n'
            })
        })
        socket.on('log', function(message){
            messages.push({
                type: 'log'
                , text: message + '\n'
            })
        })
        socket.on('tests-start', function(){
            self.trigger('tests-start')
        })
        socket.on('test-result', function(result){
            results.addResult(result)
            server.emit('test-result', result)
        })
        socket.on('all-test-results', function(){
            results.set('all', true)
            self.trigger('tests-end')
            app.emit('all-test-results', results, self)
        })
        socket.on('disconnect', function(){
            log.info('Client disconnected ' + self.get('name'))
            self.get('results').reset()
            self.get('messages').reset()
            self.pending = setTimeout(function(){
                app.removeBrowser(self)
            }, 1000)
        })
    }
    , startTests: function(){
        this.get('results').reset()
        this.get('socket').emit('start-tests')
    }
    , hasResults: function(){
        var results = this.get('results')
        var total = results.get('total')
        return total > 0
    }
    , hasMessages: function(){
        return this.get('messages').length > 0
    }
})

exports.BrowserRunner = BrowserRunner

var ProcessRunner = Backbone.Model.extend({
    defaults: {
        type: 'process'
    }
    , initialize: function(attrs){
        this.launcher = attrs.launcher
        this.app = attrs.app
        // Assume launcher has already launched
        this.set({
            name: this.launcher.name
            , messages: new Backbone.Collection
            , results: this.isTap() ? new TestResults : null
        })

        this.startTests()
    }
    , isTap: function(){
        return this.launcher.settings.protocol === 'tap'
    }
    , hasResults: function(){
        return this.isTap()
    }
    , hasMessages: function(){
        return this.get('messages').length > 0
    }
    , registerProcess: function(process){
        var settings = this.launcher.settings
        var stdout = process.stdout
        var stderr = process.stderr
        var self = this
        if (!settings.hide_stdout){
            stdout.on('data', function(data){
                self.get('messages').push({
                    type: 'log'
                    , text: '' + data
                })
            })
        }
        if (!settings.hide_stderr){
            stderr.on('data', function(data){
                self.get('messages').push({
                    type: 'error'
                    , text: '' + data
                })
            })
        }
        process.on('exit', function(code){
            self.set('allPassed', code === 0)
            self.trigger('tests-end')
        })
        if (this.isTap()){
            this.setupTapConsumer(process)
        }
    }
    , setupTapConsumer: function(process){
        var stdout = process.stdout
        this.message = null
        this.stacktrace = []
        this.tapConsumer = new tap.Consumer
        this.tapConsumer.on('data', this.onTapData.bind(this))
        this.tapConsumer.on('end', this.onTapEnd.bind(this))
        this.tapConsumer.on('bailout', this.onTapError.bind(this))
        stdout.pipe(this.tapConsumer)
    }
    , onTapData: function(data){
        if (typeof data === 'string'){
            if (this.message === null){
                this.message = data
            }else{
                this.stacktrace.push(data)
            }
            return
        }
        if (data.skip){
            return
        }
        var results = this.get('results')

        if (data.id === undefined) {
            return
        }

        var test = {
            passed: 0
            , failed: 0
            , total: 1
            , id: data.id
            , name: data.name.trim()
            , items: []
        }

        if (!data.ok) {
            var stack = data.stack

            if (!stack) {
                stack = this.stacktrace.join('\n')
            } else if (Array.isArray(stack)) {
                stack = stack.join("\n")
            } else {
                stack = JSON.stringify(stack, null, "\t")
            }

            test.items.push({
                passed: false
                , message: this.message
                , stacktrace: stack
            })
            test.failed++
        } else {
            test.passed++
        }
        results.addResult(test)
        this.message = null
        this.stacktrace = []
    }
    , onTapError: function(){
        this.set('results', null)
    }
    , onTapEnd: function(err, testCount){
        var results = this.get('results')
        results.set('all', true)
        this.tapConsumer.removeAllListeners()
        this.tapConsumer = null
        this.app.emit('all-test-results', results)
        this.launcher.kill()
    }
    , startTests: function(){
        var self = this
        if (this.get('results')){
            this.get('results').reset()
        }else{
            this.set('results', this.isTap() ? new TestResults : null)
        }
        this.get('messages').reset([])
        this.set('allPassed', undefined)

        this.launcher.launch(function(process){
            self.registerProcess(process)
            setTimeout(function(){
                self.trigger('tests-start')
            }, 1)
        })
    }
})

exports.ProcessRunner = ProcessRunner

<|endoftext|>


<commit_before>if ( typeof Promise == 'undefined' ) { var _P = require('../lib/promise.min.js').Promise; } else { var _P = Promise; }
var Excp = require('excp.js');
var Session = require('session.js');
var Table = require('table.js');

function User( option )  {

	option = option || {};

	this.ss = new Session( option );
	this.host = option['https'] || option['host'];

	this.api = 'https://' +  this.host + '/_a/baas/user';

	// 1.5.1 + 废弃
	this.prefix= option['table.prefix'] || '';
	// 1.5.1 + 废弃
	this.table_name = option['user.table'] || 'user';
	// 1.5.1 + 废弃
	this.tab = new Table( option, this.table_name );
	// 1.5.1 + 废弃
	this.cid =  option.app || '';

	// 1.5.1 新增: 用户数据处理云端应用
	this.handler = option['user'] || '';
	this.appid = option['appid']; // 小程序ID，可不填写
	this.secret = option['secret']; // 云端鉴权 secret 格式为 appid|secret

	// 用户退出
	this.logout = function() {

		var that = this;
		return new _P(function (resolve, reject) {

			var reqData = {
				_sid:that.ss.id(),
				_cid:that.cid,
				_table:that.table_name,
				_prefix:that.prefix,
			};

			wx.request({
				url: that.api + '/logout',
				data: reqData, // 使用 Code 换取 Session ID
				header: {'content-type': 'application/json'},
				method: 'POST',
				success: function (res){

					if ( res.statusCode != 200 ) {
						reject(new Excp('用户退出失败 API错误',500, {
							'res':res,
						}));
						return;
					}

					if ( typeof res['data'] != 'object') {
						reject(new Excp('用户退出失败 API错误',500, {
							'res':res,
						}));
						return;
					}

					if ( res['data']['code'] != 0) {
						reject(new Excp('用户退出失败, Session 校验失败',500, {
							'res':res,
						}));
						return;
					}
					that.ss.destory(); // 清空会话数据
					resolve( res['data'] );
				},

				fail: function (res) {

					reject(new Excp('用户退出失败',500, {
						'res':res,
					}));
				}
			});
		});
	}

	// 用户登录
	this.login = function() {

		var that = this;
		return new _P(function (resolve, reject) {

			wx.login({
				success: function( coderes ) {

					that.get()

					.catch(function(e){
						reject(e);
					})

					.then( function( res ) {

						res = res || {};
						var userinfo = res.userInfo;

						if ( that.ss.isVerified() ) {
							userinfo['_id'] = that.ss.get('_login_id') ||  null;
							userinfo['_user']= that.ss.get('_login_user') ||  null;
							if ( userinfo['_id']  != null || userinfo['_user'] !=null ) {
								resolve( userinfo );
								return;
							}
						}

						var reqData = {
							_sid:that.ss.id(),
							_handler:that.handler, // 用户处理程序 (转交给云端应用处理)
							_secret:that.secret,  // 后端校验 Secret
							_appid:that.appid,    // 小程序的 APPID
							_cid:that.cid,  // 1.5.1 + 废弃
							_table:that.table_name,  // 1.5.1 + 废弃
							_prefix:that.prefix, // 1.5.1 + 废弃
							code: coderes.code,
							rawData:res.rawData,
							signature:res.signature
						};

						wx.request({
							url: that.api + '/login',
							data: reqData, // 使用 Code 换取 Session ID
							header: {'content-type': 'application/json'},
							method: 'POST',
							success: function (res){

								if ( res.statusCode != 200 ) {
									reject(new Excp('用户登录失败 API错误',500, {
										'res':res,
									}));
									return;
								}

								if ( typeof res['data'] != 'object') {
									reject(new Excp('用户登录失败 API错误',500, {
										'res':res,
									}));
									return;
								}

								if ( res['data']['result'] !== true) {
									reject(new Excp('用户登录失败, Session 校验失败',500, {
										'res':res,
									}));
									return;
								}

								that.ss.id( res['data']['id'] ); // 设定服务端分配的ID
								that.ss.set('_login_id', res['data']['_id']);
								userinfo['_id'] = res['data']['_id'];
								if (typeof res['data']['_user'] != 'undefined' ) {
									that.ss.set('_login_user', res['data']['_user']);
									userinfo['_user'] = res['data']['_user'];
								}

								resolve( userinfo );
							},

							fail: function (res) {
								reject(new Excp('用户登录失败',500, {
									'res':res,
								}));
							}
						});

					})

      			},

      			fail: function( res ) {
      				reject(new Excp('用户登录失败',500, {
						'res':res,
					}));
      			}
			});

		});
	}

	this.get = function() {

		return new _P(function (resolve, reject) {

			wx.getUserInfo({
      			success: function(res) {
      				resolve( res );
      			},

      			fail: function( res ) {
      				reject(new Excp('读取用户信息错误',500, {
						'res':res,
					}));
      			}
      		});

		});
	}
}

module.exports = User;<commit_msg>+ user.refresh 方法<commit_after>if ( typeof Promise == 'undefined' ) { var _P = require('../lib/promise.min.js').Promise; } else { var _P = Promise; }
var Excp = require('excp.js');
var Session = require('session.js');
var Table = require('table.js');

function User( option )  {

	option = option || {};

	this.ss = new Session( option );
	this.host = option['https'] || option['host'];

	this.api = 'https://' +  this.host + '/_a/baas/user';

	// 1.5.1 + 废弃
	this.prefix= option['table.prefix'] || '';
	// 1.5.1 + 废弃
	this.table_name = option['user.table'] || 'user';
	// 1.5.1 + 废弃
	this.tab = new Table( option, this.table_name );
	// 1.5.1 + 废弃
	this.cid =  option.app || '';

	// 1.5.1 新增: 用户数据处理云端应用
	this.handler = option['user'] || '';
	this.appid = option['appid']; // 小程序ID，可不填写
	this.secret = option['secret']; // 云端鉴权 secret 格式为 appid|secret

	// 用户退出
	this.logout = function() {

		var that = this;
		return new _P(function (resolve, reject) {

			var reqData = {
				_sid:that.ss.id(),
				_cid:that.cid,
				_table:that.table_name,
				_prefix:that.prefix,
			};

			wx.request({
				url: that.api + '/logout',
				data: reqData, // 使用 Code 换取 Session ID
				header: {'content-type': 'application/json'},
				method: 'POST',
				success: function (res){

					if ( res.statusCode != 200 ) {
						reject(new Excp('用户退出失败 API错误',500, {
							'res':res,
						}));
						return;
					}

					if ( typeof res['data'] != 'object') {
						reject(new Excp('用户退出失败 API错误',500, {
							'res':res,
						}));
						return;
					}

					if ( res['data']['code'] != 0) {
						reject(new Excp('用户退出失败, Session 校验失败',500, {
							'res':res,
						}));
						return;
					}
					that.ss.destory(); // 清空会话数据
					resolve( res['data'] );
				},

				fail: function (res) {

					reject(new Excp('用户退出失败',500, {
						'res':res,
					}));
				}
			});
		});
	}

	// 清空缓存
	this.refresh = function(){
		this.ss.set('_login_user', null);
		this.ss.set('_login_id', null);
		return this;
	}

	// 用户登录
	this.login = function() {

		var that = this;
		return new _P(function (resolve, reject) {

			wx.login({
				success: function( coderes ) {

					that.get()

					.catch(function(e){
						reject(e);
					})

					.then( function( res ) {

						res = res || {};
						var userinfo = res.userInfo;

						if ( that.ss.isVerified() ) {
							userinfo['_id'] = that.ss.get('_login_id') ||  null;
							userinfo['_user']= that.ss.get('_login_user') ||  null;
							if ( userinfo['_id']  != null || userinfo['_user'] !=null ) {
								resolve( userinfo );
								return;
							}
						}

						var reqData = {
							_sid:that.ss.id(),
							_handler:that.handler, // 用户处理程序 (转交给云端应用处理)
							_secret:that.secret,  // 后端校验 Secret
							_appid:that.appid,    // 小程序的 APPID
							_cid:that.cid,  // 1.5.1 + 废弃
							_table:that.table_name,  // 1.5.1 + 废弃
							_prefix:that.prefix, // 1.5.1 + 废弃
							code: coderes.code,
							rawData:res.rawData,
							signature:res.signature
						};

						wx.request({
							url: that.api + '/login',
							data: reqData, // 使用 Code 换取 Session ID
							header: {'content-type': 'application/json'},
							method: 'POST',
							success: function (res){

								if ( res.statusCode != 200 ) {
									reject(new Excp('用户登录失败 API错误',500, {
										'res':res,
									}));
									return;
								}

								if ( typeof res['data'] != 'object') {
									reject(new Excp('用户登录失败 API错误',500, {
										'res':res,
									}));
									return;
								}

								if ( res['data']['result'] !== true) {
									reject(new Excp('用户登录失败, Session 校验失败',500, {
										'res':res,
									}));
									return;
								}

								that.ss.id( res['data']['id'] ); // 设定服务端分配的ID
								that.ss.set('_login_id', res['data']['_id']);
								userinfo['_id'] = res['data']['_id'];
								if (typeof res['data']['_user'] != 'undefined' ) {
									that.ss.set('_login_user', res['data']['_user']);
									userinfo['_user'] = res['data']['_user'];
								}

								resolve( userinfo );
							},

							fail: function (res) {
								reject(new Excp('用户登录失败',500, {
									'res':res,
								}));
							}
						});

					})

      			},

      			fail: function( res ) {
      				reject(new Excp('用户登录失败',500, {
						'res':res,
					}));
      			}
			});

		});
	}

	this.get = function() {

		return new _P(function (resolve, reject) {

			wx.getUserInfo({
      			success: function(res) {
      				resolve( res );
      			},

      			fail: function( res ) {
      				reject(new Excp('读取用户信息错误',500, {
						'res':res,
					}));
      			}
      		});

		});
	}
}

module.exports = User;<|endoftext|>


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

/**
 * @class
 * All executions in the system happen within a Sandbox. Every map and reduce function is executed here. All environment
 * initializations also happen within the sandbox. It holds the custom scope that every function is supposed to run on.
 */
function Sandbox(streamlogger) {
	// this.cleanSandbox();
	this.initializeUtilities(streamlogger);
}
/**
 * Function to clean and reset the Sandbox
 */
Sandbox.prototype.cleanSandbox = function() {
	this.mapFunction = "";
	this.reduceFunction = "";
	this.array = [];
	this.options = {};
	this.scope = null;
};

/**
 * Function to initialize Sandbox for executing remote procedures
 * @param  {Function} map    The map function that has to be executed
 * @param  {Function} reduce The reduce function that has to be executed on the server
 * @param  {JSON} scope  The custom scope that each function needs to be run on
 */
Sandbox.prototype.initializeSandbox = function(array, map, reduce, scope) {
	if(map && typeof map == 'string') {
		this.mapFunction = eval(map);
	}
	else this.mapFunction = map;
	if(reduce && typeof reduce == 'string') {
		this.reduceFunction = eval(reduce);
	}
	else this.reduceFunction = reduce;
	this.array = array;
	this.scope = scope;
};
Sandbox.prototype.initializeParallelSandbox = function(map, reduce, scope) {
	if(map && typeof map == 'string') {
		this.mapFunction = eval(map);
	}
	else this.mapFunction = map;
	if(reduce && typeof reduce == 'string') {
		this.reduceFunction = eval(reduce);
	}
	else this.reduceFunction = reduce;
	this.scope = scope;
}

/**
 * Setter for the array variable
 * @param {Array} array The array to be set
 */
Sandbox.prototype.setArray = function(array) {
	this.array = array;
};
/**
 * Getter to get the data which will be emitted to other sockets
 * @return {JSON} The data that needs to be emitted. The Map, Reduce and the Scope
 */
Sandbox.prototype.getSandboxEmitData = function() {
	var data = {
		mapFunction: this.mapFunction.toString(),
		reduceFunction: this.reduceFunction.toString(),
		scope: this.scope
	};
	return data;
};
Sandbox.prototype.initializeEnvironment = function(data, finalCallback) {
	map = data.mapFunction;
  reduce = data.reduceFunction;
	var i = 0;
	var func_arr = []
	if (data.array) {
		for (i = 0; i < data.array.length; i++) {
			func_arr.push(eval('('+data.arrn[i]+')'));
		}
	}
	scope = data.scope;
	if (map && typeof map == 'string') {
		this.mapFunction=eval('('+map+')');
	}
	else this.mapFunction = map;
	if (reduce && typeof reduce == 'string') {
		this.reduceFunction=eval('('+reduce+')');
	}
	else this.reduceFunction = reduce;
	if (data.array) {
		this.array = data.array;
	}
	this.arrn = func_arr;
	finalCallback();
};
/**
 * Trigger to execute the Map Function
 * @param  {Function} finalCallback Callback called at the end of the Map Function execution. Returns err and results
 */
Sandbox.prototype.executeMapFunction = function(finalCallback) {
	if (this.array.length == 0) {
		async.series([this.mapFunction.bind(this.utilities), finalCallback]);
	}
	else {
		var i = 0;
		if (this.array[0] != null) {
			async.map.call({}, this.array, this.mapFunction.bind(this.utilities), finalCallback);
		}
		else{
			var i = 0;
		  for (i = 0; i < this.arrn.length; i++) {
				this.arrn[i] = this.arrn[i].bind(this.utilities)
			}
			async.series(this.arrn, function() {
				finalCallback();
			});
		}
	}
};
/**
 * Trigger to execute the Reduce Function
 * @param  {Function} finalCallback Callback called at the end of the Reduce Function execution.
 */
Sandbox.prototype.executeReduceFunction = function(err, results, finalCallback) {
	this.reduceFunction.call(this.scope, err, results);
	finalCallback();
};

Sandbox.prototype.initializeUtilities = function(streamlogger) {
	this.utilities = {
		logger: streamlogger
	}
}
module.exports = Sandbox;
<commit_msg>Logging functionality<commit_after>var async=require('async');

/**
 * @class
 * All executions in the system happen within a Sandbox. Every map and reduce function is executed here. All environment
 * initializations also happen within the sandbox. It holds the custom scope that every function is supposed to run on.
 */

function Sandbox(streamlogger) {
	this.cleanSandbox();
	this.initializeUtilities(streamlogger);
}
/**
 * Function to clean and reset the Sandbox
 */
Sandbox.prototype.cleanSandbox = function() {
	this.mapFunction = "";
	this.reduceFunction = "";
	this.array = [];
	this.options = {};
	this.scope = null;
};

/**
 * Function to initialize Sandbox for executing remote procedures
 * @param  {Function} map    The map function that has to be executed
 * @param  {Function} reduce The reduce function that has to be executed on the server
 * @param  {JSON} scope  The custom scope that each function needs to be run on
 */

Sandbox.prototype.initializeSandbox = function(array, map, reduce, scope) {
	if(map && typeof map == 'string') {
		this.mapFunction = eval(map);
	}
	else this.mapFunction = map;
	if(reduce && typeof reduce == 'string') {
		this.reduceFunction = eval(reduce);
	}
	else this.reduceFunction = reduce;
	this.array = array;
	this.scope = scope;
};
Sandbox.prototype.initializeParallelSandbox = function(map, reduce, scope) {
	if(map && typeof map == 'string') {
		this.mapFunction = eval(map);
	}
	else this.mapFunction = map;
	if(reduce && typeof reduce == 'string') {
		this.reduceFunction = eval(reduce);
	}
	else this.reduceFunction = reduce;
	this.scope = scope;
}

/**
 * Setter for the array variable
 * @param {Array} array The array to be set
 */
Sandbox.prototype.setArray = function(array) {
	this.array = array;
};
/**
 * Getter to get the data which will be emitted to other sockets
 * @return {JSON} The data that needs to be emitted. The Map, Reduce and the Scope
 */
Sandbox.prototype.getSandboxEmitData = function() {
	var data = {
		mapFunction: this.mapFunction.toString(),
		reduceFunction: this.reduceFunction.toString(),
		scope: this.scope
	};
	return data;
};
Sandbox.prototype.initializeEnvironment = function(data, finalCallback) {
	map = data.mapFunction;
  reduce = data.reduceFunction;
	var i = 0;
	var func_arr = []
	if (data.array) {
		for (i = 0; i < data.array.length; i++) {
			func_arr.push(eval('('+data.arrn[i]+')'));
		}
	}
	scope = data.scope;
	if (map && typeof map == 'string') {
		this.mapFunction=eval('('+map+')');
	}
	else this.mapFunction = map;
	if (reduce && typeof reduce == 'string') {
		this.reduceFunction=eval('('+reduce+')');
	}
	else this.reduceFunction = reduce;
	if (data.array) {
		this.array = data.array;
	}
	this.arrn = func_arr;
	finalCallback();
};
/**
 * Trigger to execute the Map Function
 * @param  {Function} finalCallback Callback called at the end of the Map Function execution. Returns err and results
 */
Sandbox.prototype.executeMapFunction = function(finalCallback) {
	if (this.array.length == 0) {
		async.series([this.mapFunction.bind(this.utilities), finalCallback]);
	}
	else {
		var i = 0;
		if (this.array[0] != null) {
			async.map.call({}, this.array, this.mapFunction.bind(this.utilities), finalCallback);
		}
		else{
			var i = 0;
		  for (i = 0; i < this.arrn.length; i++) {
				this.arrn[i] = this.arrn[i].bind(this.utilities)
			}
			async.series(this.arrn, function() {
				finalCallback();
			});
		}
	}
};
/**
 * Trigger to execute the Reduce Function
 * @param  {Function} finalCallback Callback called at the end of the Reduce Function execution.
 */
Sandbox.prototype.executeReduceFunction = function(err, results, finalCallback) {
	this.reduceFunction.call(this.scope, err, results);
	finalCallback();
};

Sandbox.prototype.initializeUtilities = function(streamlogger) {
	this.utilities = {
		logger: streamlogger
	}
}
module.exports = Sandbox;
<|endoftext|>


<commit_before>var _ = require('underscore');
var logger = require('./loggingservice.js');
var Bitstamp = require('bitstamp');
var Kraken = require('kraken-api');
var async = require('async');

//------------------------------Config
var config = require('../config.js');
//------------------------------Config

var api = function() {

  this.exchange = config.exchangeSettings.exchange;
  this.currencyPair = config.exchangeSettings.currencyPair;

  if(this.exchange === 'bitstamp') {

    var key = config.apiSettings.bitstamp.apiKey;
    var secret = config.apiSettings.bitstamp.secret;
    var client_id = config.apiSettings.bitstamp.clientId;

    this.bitstamp = new Bitstamp(key, secret, client_id);

  } else if(this.exchange === 'kraken') {

    this.kraken = new Kraken(config.apiSettings.kraken.apiKey, config.apiSettings.kraken.secret);

  } else {

    logger.error('Invalid exchange, exiting!');
    return process.exit();

  }

  this.q = async.queue(function (task, callback) {
    task();
    setTimeout(callback,1000);
  }, 1);

  _.bindAll(this, 'retry', 'errorHandler', 'getTrades', 'getBalance', 'getOrderBook', 'placeOrder', 'orderFilled' ,'cancelOrder');

};

api.prototype.retry = function(method, args) {

  var self = this;

  // make sure the callback (and any other fn)
  // is bound to api
  _.each(args, function(arg, i) {
    if(_.isFunction(arg))
      args[i] = _.bind(arg, self);
  });

  // run the failed method again with the same
  // arguments after wait
  setTimeout(function() { method.apply(self, args); }, 1000*15);

};

api.prototype.errorHandler = function(method, receivedArgs, retryAllowed, caller, cb) {

  var args = _.toArray(receivedArgs);

  return function(err, result) {

    if(err) {

      if(this.exchange === 'kraken' && err[0] === 'EQuery:Unknown asset pair') {

        logger.error('Kraken returned Unknown asset pair error, exiting!');
        return process.exit();

      } else if(retryAllowed) {

        logger.error(caller + ' Couldn\'t connect to the API, retrying in 15 seconds!');
        logger.error(JSON.stringify(err).substring(0,99));
        return this.retry(method, args);

      } else {

        logger.error(caller + ' Couldn\'t connect to the API.');
        cb(err, null);
        return logger.error(JSON.stringify(err).substring(0,99));

      }

    }

    if(this.exchange === 'bitstamp' && result.error === 'Invalid nonce') {
      logger.error('Bitstamp returned invalid nonce error, retrying in 15 seconds!');
      return this.retry(method, args);
    }

    logger.debug('API Call Result (Substring)!');
    logger.debug(JSON.stringify(result).substring(0,99));

    //_.last(args)(null, result);
    cb(null, result);

  }.bind(this);

};

api.prototype.getTrades = function(cb) {

  var args = arguments;

  var wrapper = function() {

    var handler;

    var pair = this.currencyPair.pair;

    if(this.exchange === 'bitstamp') {

      handler = function(err, response) {

        var trades = _.map(response, function(t) {

          return {date: parseInt(t.date), price: parseFloat(t.price), amount: parseFloat(t.amount)};

        });

        var result = _.sortBy(trades, function(trade){ return trade.date; });

        cb(null, result);

      };

      this.bitstamp.transactions({time: 'hour'}, this.errorHandler(this.getTrades, args, false, 'getTrades', handler));

    } else if(this.exchange === 'kraken') {

      handler = function(err, data) {

        var values = _.find(data.result, function(value, key) {

          return key === pair;

        });

        var trades = _.map(values, function(t) {

          return {date: parseInt(t[2]), price: parseFloat(t[0]), amount: parseFloat(t[1])};

        });

        cb(null, trades);

      };

      this.kraken.api('Trades', {"pair": pair}, this.errorHandler(this.getTrades, args, false, 'getTrades', handler));

    }

  };

  this.q.push(_.bind(wrapper,this));

};

api.prototype.getBalance = function(cb) {

  var args = arguments;

  var wrapper = function() {

    var handler;

    var asset = this.currencyPair.asset;
    var currency = this.currencyPair.currency;

    var pair = this.currencyPair.pair;

    if(this.exchange === 'bitstamp') {

      handler = function(err, result) {

        cb(null, {currencyAvailable:result.usd_available, assetAvailable:result.btc_available, fee:result.fee});

      };

      this.bitstamp.balance(this.errorHandler(this.getBalance, args, true, 'getBalance', handler));

    } else if(this.exchange === 'kraken') {

      handler = function(err, data) {

        var assetValue = _.find(data.result, function(value, key) {
          return key === asset;
        });

        var currencyValue = _.find(data.result, function(value, key) {
          return key === currency;
        });

        if(!assetValue) {
          assetValue = 0;
        }

        if(!currencyValue) {
          currencyValue = 0;
        }

        this.kraken.api('TradeVolume', {"pair": pair}, this.errorHandler(this.getBalance, args, true, 'getBalance', function(err, data) {

          var fee = parseFloat(_.find(data.result.fees, function(value, key) {
            return key === pair;
          }).fee);

          cb(null, {currencyAvailable:currencyValue, assetAvailable:assetValue, fee:fee});

        }));

      }.bind(this);

      this.kraken.api('Balance', {}, this.errorHandler(this.getBalance, args, true, 'getBalance', handler));

    }

  };

  this.q.push(_.bind(wrapper,this));

};

api.prototype.getOrderBook = function(cb) {

  var args = arguments;

  var wrapper = function () {

    var handler;

    var pair = this.currencyPair.pair;

    if(this.exchange === 'bitstamp') {

      handler = function(err, result) {

        var bids = _.map(result.bids, function(bid) {
          return {assetAmount: bid[1], currencyPrice: bid[0]};
        });

        var asks = _.map(result.asks, function(ask) {
          return {assetAmount: ask[1], currencyPrice: ask[0]};
        });

        cb(null, {bids: bids, asks: asks});

      };

      this.bitstamp.order_book(1, this.errorHandler(this.getOrderBook, args, true, 'getOrderBook', handler));

    } else if(this.exchange === 'kraken') {

      handler = function(err, data) {

        var orderbook = _.find(data.result, function(value, key) {

          return key === pair;

        });

        var bids = _.map(orderbook.bids, function(bid) {
          return {assetAmount: bid[1], currencyPrice: bid[0]};
        });

        var asks = _.map(orderbook.asks, function(ask) {
          return {assetAmount: ask[1], currencyPrice: ask[0]};
        });

        cb(null, {bids: bids, asks: asks});

      };

      this.kraken.api('Depth', {"pair": pair}, this.errorHandler(this.getOrderBook, args, true, 'getOrderBook', handler));

    }

  };

  this.q.push(_.bind(wrapper,this));

};

api.prototype.placeOrder = function(type, amount, price, cb) {

  var args = arguments;

  var wrapper = function() {

    var handler;

    var pair = this.currencyPair.pair;

    if(this.exchange === 'bitstamp') {

      handler = function(err, result) {

        cb(null, {txid: result.id});

      };

      if(type === 'buy') {

        this.bitstamp.buy(amount, price, this.errorHandler(this.placeOrder, args, true, 'placeOrder', handler));

      } else if (type === 'sell') {

        this.bitstamp.sell(amount, price, this.errorHandler(this.placeOrder, args, true, 'placeOrder', handler));

      } else {

        logger.log('Invalid order type!');
      }

    } else if(this.exchange === 'kraken') {

      handler = function(err, data) {

        cb(null, {txid: data.result.txid[0]});

      };

      if(type === 'buy') {

        this.kraken.api('AddOrder', {"pair": pair, "type": 'buy', "ordertype": 'limit', "price": price, "volume": amount}, this.errorHandler(this.placeOrder, args, true, 'placeOrder', handler));

      } else if (type === 'sell') {

        this.kraken.api('AddOrder', {"pair": pair, "type": 'sell', "ordertype": 'limit', "price": price, "volume": amount}, this.errorHandler(this.placeOrder, args, true, 'placeOrder', handler));

      } else {

        logger.log('Invalid order type!');

      }

    }

  };

  this.q.push(_.bind(wrapper,this));

};

api.prototype.orderFilled = function(order, cb) {

  var args = arguments;

  var wrapper = function() {

    var handler;

    if(this.exchange === 'bitstamp') {

      handler = function(err, result) {

        var open = _.find(result, function(o) {

          return o.id === order;

        }, this);

        if(open) {

          cb(null, false);

        } else {

          cb(null, true);

        }

      };

      this.bitstamp.open_orders(this.errorHandler(this.orderFilled, args, false, 'orderFilled', handler));

    } else if(this.exchange === 'kraken') {

      handler = function(err, data) {

        var open = _.find(data.result.open, function(value, key) {

          return key === order;

        });

        if(open) {

          cb(null, false);

        } else {

          cb(null, true);

        }

      };

      this.kraken.api('OpenOrders', {}, this.errorHandler(this.orderFilled, args, false, 'orderFilled', handler));

    }

  };

  this.q.push(_.bind(wrapper,this));

};

api.prototype.cancelOrder = function(order, cb) {

  var args = arguments;

  var wrapper = function() {

    var handler;

    if(this.exchange === 'bitstamp') {

      handler = function(err, result) {

        if(!result.error) {
          cb(null, true);
        } else {
          cb(null, false);
        }

      };

      this.bitstamp.cancel_order(order,this.errorHandler(this.cancelOrder, args, true, 'cancelOrder', handler));

    } else if(this.exchange === 'kraken') {

      this.orderFilled(order, function(err, filled) {

        if(!filled) {

          handler = function(err, result) {

            if(result.count > 0) {
              cb(null, true);
            } else {
              cb(null, false);
            }

          };

          this.kraken.api('CancelOrder', {"txid": order}, this.errorHandler(this.cancelOrder, args, true, 'cancelOrder', handler));

        } else {

          cb(null, false);

        }

      }.bind(this));

    }

  };

  this.q.push(_.bind(wrapper,this));

};

var apiservice = new api();

module.exports = apiservice;
<commit_msg>Fixed another bug in api.js<commit_after>var _ = require('underscore');
var logger = require('./loggingservice.js');
var Bitstamp = require('bitstamp');
var Kraken = require('kraken-api');
var async = require('async');

//------------------------------Config
var config = require('../config.js');
//------------------------------Config

var api = function() {

  this.exchange = config.exchangeSettings.exchange;
  this.currencyPair = config.exchangeSettings.currencyPair;

  if(this.exchange === 'bitstamp') {

    var key = config.apiSettings.bitstamp.apiKey;
    var secret = config.apiSettings.bitstamp.secret;
    var client_id = config.apiSettings.bitstamp.clientId;

    this.bitstamp = new Bitstamp(key, secret, client_id);

  } else if(this.exchange === 'kraken') {

    this.kraken = new Kraken(config.apiSettings.kraken.apiKey, config.apiSettings.kraken.secret);

  } else {

    logger.error('Invalid exchange, exiting!');
    return process.exit();

  }

  this.q = async.queue(function (task, callback) {
    task();
    setTimeout(callback,1000);
  }, 1);

  _.bindAll(this, 'retry', 'errorHandler', 'getTrades', 'getBalance', 'getOrderBook', 'placeOrder', 'orderFilled' ,'cancelOrder');

};

api.prototype.retry = function(method, args) {

  var self = this;

  // make sure the callback (and any other fn)
  // is bound to api
  _.each(args, function(arg, i) {
    if(_.isFunction(arg))
      args[i] = _.bind(arg, self);
  });

  // run the failed method again with the same
  // arguments after wait
  setTimeout(function() { method.apply(self, args); }, 1000*15);

};

api.prototype.errorHandler = function(method, receivedArgs, retryAllowed, caller, cb) {

  var args = _.toArray(receivedArgs);

  return function(err, result) {

    if(err) {

      if(this.exchange === 'kraken' && err[0] === 'EQuery:Unknown asset pair') {

        logger.error('Kraken returned Unknown asset pair error, exiting!');
        return process.exit();

      } else if(retryAllowed) {

        logger.error(caller + ' Couldn\'t connect to the API, retrying in 15 seconds!');
        logger.error(JSON.stringify(err).substring(0,99));
        return this.retry(method, args);

      } else {

        logger.error(caller + ' Couldn\'t connect to the API.');
        cb(err, null);
        return logger.error(JSON.stringify(err).substring(0,99));

      }

    }

    if(this.exchange === 'bitstamp' && result.error === 'Invalid nonce') {
      logger.error('Bitstamp returned invalid nonce error, retrying in 15 seconds!');
      return this.retry(method, args);
    }

    logger.debug('API Call Result (Substring)!');
    logger.debug(JSON.stringify(result).substring(0,99));

    //_.last(args)(null, result);
    cb(null, result);

  }.bind(this);

};

api.prototype.getTrades = function(cb) {

  var args = arguments;

  var wrapper = function() {

    var handler;

    var pair = this.currencyPair.pair;

    if(this.exchange === 'bitstamp') {

      handler = function(err, response) {

        if(!err) {

          var trades = _.map(response, function(t) {

            return {date: parseInt(t.date), price: parseFloat(t.price), amount: parseFloat(t.amount)};

          });

          var result = _.sortBy(trades, function(trade){ return trade.date; });

          cb(null, result);

        } else {

          cb(err, null);

        }

      };

      this.bitstamp.transactions({time: 'hour'}, this.errorHandler(this.getTrades, args, false, 'getTrades', handler));

    } else if(this.exchange === 'kraken') {

      handler = function(err, data) {

        if(!err) {

          var values = _.find(data.result, function(value, key) {

            return key === pair;

          });

          var trades = _.map(values, function(t) {

            return {date: parseInt(t[2]), price: parseFloat(t[0]), amount: parseFloat(t[1])};

          });

          cb(null, trades);

        } else {

          cb(err, null);

        }

      };

      this.kraken.api('Trades', {"pair": pair}, this.errorHandler(this.getTrades, args, false, 'getTrades', handler));

    }

  };

  this.q.push(_.bind(wrapper,this));

};

api.prototype.getBalance = function(cb) {

  var args = arguments;

  var wrapper = function() {

    var handler;

    var asset = this.currencyPair.asset;
    var currency = this.currencyPair.currency;

    var pair = this.currencyPair.pair;

    if(this.exchange === 'bitstamp') {

      handler = function(err, result) {

        cb(null, {currencyAvailable:result.usd_available, assetAvailable:result.btc_available, fee:result.fee});

      };

      this.bitstamp.balance(this.errorHandler(this.getBalance, args, true, 'getBalance', handler));

    } else if(this.exchange === 'kraken') {

      handler = function(err, data) {

        var assetValue = _.find(data.result, function(value, key) {
          return key === asset;
        });

        var currencyValue = _.find(data.result, function(value, key) {
          return key === currency;
        });

        if(!assetValue) {
          assetValue = 0;
        }

        if(!currencyValue) {
          currencyValue = 0;
        }

        this.kraken.api('TradeVolume', {"pair": pair}, this.errorHandler(this.getBalance, args, true, 'getBalance', function(err, data) {

          var fee = parseFloat(_.find(data.result.fees, function(value, key) {
            return key === pair;
          }).fee);

          cb(null, {currencyAvailable:currencyValue, assetAvailable:assetValue, fee:fee});

        }));

      }.bind(this);

      this.kraken.api('Balance', {}, this.errorHandler(this.getBalance, args, true, 'getBalance', handler));

    }

  };

  this.q.push(_.bind(wrapper,this));

};

api.prototype.getOrderBook = function(cb) {

  var args = arguments;

  var wrapper = function () {

    var handler;

    var pair = this.currencyPair.pair;

    if(this.exchange === 'bitstamp') {

      handler = function(err, result) {

        var bids = _.map(result.bids, function(bid) {
          return {assetAmount: bid[1], currencyPrice: bid[0]};
        });

        var asks = _.map(result.asks, function(ask) {
          return {assetAmount: ask[1], currencyPrice: ask[0]};
        });

        cb(null, {bids: bids, asks: asks});

      };

      this.bitstamp.order_book(1, this.errorHandler(this.getOrderBook, args, true, 'getOrderBook', handler));

    } else if(this.exchange === 'kraken') {

      handler = function(err, data) {

        var orderbook = _.find(data.result, function(value, key) {

          return key === pair;

        });

        var bids = _.map(orderbook.bids, function(bid) {
          return {assetAmount: bid[1], currencyPrice: bid[0]};
        });

        var asks = _.map(orderbook.asks, function(ask) {
          return {assetAmount: ask[1], currencyPrice: ask[0]};
        });

        cb(null, {bids: bids, asks: asks});

      };

      this.kraken.api('Depth', {"pair": pair}, this.errorHandler(this.getOrderBook, args, true, 'getOrderBook', handler));

    }

  };

  this.q.push(_.bind(wrapper,this));

};

api.prototype.placeOrder = function(type, amount, price, cb) {

  var args = arguments;

  var wrapper = function() {

    var handler;

    var pair = this.currencyPair.pair;

    if(this.exchange === 'bitstamp') {

      handler = function(err, result) {

        cb(null, {txid: result.id});

      };

      if(type === 'buy') {

        this.bitstamp.buy(amount, price, this.errorHandler(this.placeOrder, args, true, 'placeOrder', handler));

      } else if (type === 'sell') {

        this.bitstamp.sell(amount, price, this.errorHandler(this.placeOrder, args, true, 'placeOrder', handler));

      } else {

        logger.log('Invalid order type!');
      }

    } else if(this.exchange === 'kraken') {

      handler = function(err, data) {

        cb(null, {txid: data.result.txid[0]});

      };

      if(type === 'buy') {

        this.kraken.api('AddOrder', {"pair": pair, "type": 'buy', "ordertype": 'limit', "price": price, "volume": amount}, this.errorHandler(this.placeOrder, args, true, 'placeOrder', handler));

      } else if (type === 'sell') {

        this.kraken.api('AddOrder', {"pair": pair, "type": 'sell', "ordertype": 'limit', "price": price, "volume": amount}, this.errorHandler(this.placeOrder, args, true, 'placeOrder', handler));

      } else {

        logger.log('Invalid order type!');

      }

    }

  };

  this.q.push(_.bind(wrapper,this));

};

api.prototype.orderFilled = function(order, cb) {

  var args = arguments;

  var wrapper = function() {

    var handler;

    if(this.exchange === 'bitstamp') {

      handler = function(err, result) {

        if(!err) {

          var open = _.find(result, function(o) {

            return o.id === order;

          }, this);

          if(open) {

            cb(null, false);

          } else {

            cb(null, true);

          }

        } else {

          cb(err, null);

        }

      };

      this.bitstamp.open_orders(this.errorHandler(this.orderFilled, args, false, 'orderFilled', handler));

    } else if(this.exchange === 'kraken') {

      handler = function(err, data) {

        if(!err) {

          var open = _.find(data.result.open, function(value, key) {

            return key === order;

          });

          if(open) {

            cb(null, false);

          } else {

            cb(null, true);

          }

        } else {

          cb(err, null);

        }

      };

      this.kraken.api('OpenOrders', {}, this.errorHandler(this.orderFilled, args, false, 'orderFilled', handler));

    }

  };

  this.q.push(_.bind(wrapper,this));

};

api.prototype.cancelOrder = function(order, cb) {

  var args = arguments;

  var wrapper = function() {

    var handler;

    if(this.exchange === 'bitstamp') {

      handler = function(err, result) {

        if(!result.error) {
          cb(null, true);
        } else {
          cb(null, false);
        }

      };

      this.bitstamp.cancel_order(order,this.errorHandler(this.cancelOrder, args, true, 'cancelOrder', handler));

    } else if(this.exchange === 'kraken') {

      this.orderFilled(order, function(err, filled) {

        if(!filled) {

          handler = function(err, result) {

            if(result.count > 0) {
              cb(null, true);
            } else {
              cb(null, false);
            }

          };

          this.kraken.api('CancelOrder', {"txid": order}, this.errorHandler(this.cancelOrder, args, true, 'cancelOrder', handler));

        } else {

          cb(null, false);

        }

      }.bind(this));

    }

  };

  this.q.push(_.bind(wrapper,this));

};

var apiservice = new api();

module.exports = apiservice;
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
'use strict';

var Stream = require('stream');

function Scaper(mojito) {
    this.mojito = mojito;
    this.server = null;
}

Scaper.prototype = Object.create(Stream.prototype);

Scaper.prototype.start = function start(opts) {
    this.server = this.mojito.createServer(opts);
    return this;
};

Scaper.prototype.fetch = function fetch(buildmap, cb) {
    var me = this;

    this.server.listen(null, null, function (err) {
        var keys = Object.keys(buildmap),
            have = 0,
            failed = 0,
            need = keys.length,
            opts = {headers: {'x-mojito-build': 'html5app'}};

        if (err) {
            me.emit('error', err);
            return cb('Failed to start server.');
        }

        keys.forEach(function (key) {
            me.server.getWebPage(key, opts, function (err, uri, content) {
                if (err) {
                    failed += 1;
                    me.emit('warn', 'FAILED to get ' + uri);
                } else {
                    me.emit('scraped-one', buildmap[uri], content);
                }

                have += 1;
                if (have === need) {
                    me.server.close();
                    me.emit('scraping-done', null, have, failed);
                }
            });
        });
    });
};

module.exports = Scaper;
<commit_msg>spelling: s/Scaper/Scraper/<commit_after>/*
 * Copyright (c) 2011-2013, Yahoo! Inc.  All rights reserved.
 * Copyrights licensed under the New BSD License.
 * See the accompanying LICENSE file for terms.
 */
'use strict';

var Stream = require('stream');

function Scraper(mojito) {
    this.mojito = mojito;
    this.server = null;
}

Scraper.prototype = Object.create(Stream.prototype);

Scraper.prototype.start = function start(opts) {
    this.server = this.mojito.createServer(opts);
    return this;
};

Scraper.prototype.fetch = function fetch(buildmap, cb) {
    var me = this;

    this.server.listen(null, null, function (err) {
        var keys = Object.keys(buildmap),
            have = 0,
            failed = 0,
            need = keys.length,
            opts = {headers: {'x-mojito-build': 'html5app'}};

        if (err) {
            me.emit('error', err);
            return cb('Failed to start server.');
        }

        keys.forEach(function (key) {
            me.server.getWebPage(key, opts, function (err, uri, content) {
                if (err) {
                    failed += 1;
                    me.emit('warn', 'FAILED to get ' + uri);
                } else {
                    me.emit('scraped-one', buildmap[uri], content);
                }

                have += 1;
                if (have === need) {
                    me.server.close();
                    me.emit('scraping-done', null, have, failed);
                }
            });
        });
    });
};

module.exports = Scraper;
<|endoftext|>


<commit_before>'use strict';

/**
 * Set the scripts for a template.
 * @param {Object} req HTTP response object.
 * @param {Object} res HTTP request object.
 * @param {Array} templateScripts An array of script objects.
 * @return {Promise} Resolves to an array or scripts.
 */
const setTemplateScripts = (req, res, templateScripts) => new Promise((resolve, reject) => {

    const linz = require('../');

    const defaults = [
        {
            crossorigin: 'anonymous',
            integrity: 'sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=',
            src: '//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js',
        },
        {
            crossorigin: 'anonymous',
            integrity: 'sha256-JklDYODbg0X+8sPiKkcFURb5z7RvlNMIaE3RA2z97vw=',
            src: '//cdnjs.cloudflare.com/ajax/libs/jquery-migrate/3.0.0/jquery-migrate.min.js',
        },
        {
            crossorigin: 'anonymous',
            integrity: 'sha256-KM512VNnjElC30ehFwehXjx1YCHPiQkOPmqnrWtpccM=',
            src: '//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js',
        },
        {
            crossorigin: 'anonymous',
            integrity: 'sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa',
            src: '//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js',
        },
        {
            crossorigin: 'anonymous',
            integrity: 'sha256-CutOzxCRucUsn6C6TcEYsauvvYilEniTXldPa6/wu0k=',
            src: '//cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js',
        },
        {
            crossorigin: 'anonymous',
            integrity: 'sha256-5YmaxAwMjIpMrVlK84Y/+NjCpKnFYa8bWWBbUHSBGfU=',
            src: '//cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js'
        },
        {
            src: `${linz.get('admin path')}/public/js/utils.js`,
            inHead: true,
        },
        {
            src: `${linz.get('admin path')}/public/ckeditor/ckeditor.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/modernizr.custom.min.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/multiselect.min.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/noty.min.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/template.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/ui.js?v4`,
        },
        {
            src: `${linz.get('admin path')}/public/js/validator.min.js?v1`,
        },
        {
            src: `${linz.get('admin path')}/public/js/views/model-export.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/tz.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/csrf-submit.js`
        },
    ];

    if (linz.app.locals.adminJSFile) {
        defaults.push({ src: linz.app.locals.adminJSFile });
    }

    res.locals.scripts = [].concat(defaults, templateScripts)
        .filter(script => script !== undefined);

    const customScripts = linz.get('scripts');

    if (!customScripts) {
        return resolve(res.locals.scripts);
    }

    customScripts(req, res)
        .then(resolve)
        .catch(reject);

});

module.exports = setTemplateScripts;
<commit_msg>update script version<commit_after>'use strict';

/**
 * Set the scripts for a template.
 * @param {Object} req HTTP response object.
 * @param {Object} res HTTP request object.
 * @param {Array} templateScripts An array of script objects.
 * @return {Promise} Resolves to an array or scripts.
 */
const setTemplateScripts = (req, res, templateScripts) => new Promise((resolve, reject) => {

    const linz = require('../');

    const defaults = [
        {
            crossorigin: 'anonymous',
            integrity: 'sha256-hwg4gsxgFZhOsEEamdOYGBf13FyQuiTwlAQgxVSNgt4=',
            src: '//cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js',
        },
        {
            crossorigin: 'anonymous',
            integrity: 'sha256-JklDYODbg0X+8sPiKkcFURb5z7RvlNMIaE3RA2z97vw=',
            src: '//cdnjs.cloudflare.com/ajax/libs/jquery-migrate/3.0.0/jquery-migrate.min.js',
        },
        {
            crossorigin: 'anonymous',
            integrity: 'sha256-KM512VNnjElC30ehFwehXjx1YCHPiQkOPmqnrWtpccM=',
            src: '//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js',
        },
        {
            crossorigin: 'anonymous',
            integrity: 'sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa',
            src: '//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js',
        },
        {
            crossorigin: 'anonymous',
            integrity: 'sha256-CutOzxCRucUsn6C6TcEYsauvvYilEniTXldPa6/wu0k=',
            src: '//cdnjs.cloudflare.com/ajax/libs/moment.js/2.22.2/moment.min.js',
        },
        {
            crossorigin: 'anonymous',
            integrity: 'sha256-5YmaxAwMjIpMrVlK84Y/+NjCpKnFYa8bWWBbUHSBGfU=',
            src: '//cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/js/bootstrap-datetimepicker.min.js'
        },
        {
            src: `${linz.get('admin path')}/public/js/utils.js`,
            inHead: true,
        },
        {
            src: `${linz.get('admin path')}/public/ckeditor/ckeditor.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/modernizr.custom.min.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/multiselect.min.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/noty.min.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/template.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/ui.js?v4`,
        },
        {
            src: `${linz.get('admin path')}/public/js/validator.min.js?v1`,
        },
        {
            src: `${linz.get('admin path')}/public/js/views/model-export.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/tz.js`,
        },
        {
            src: `${linz.get('admin path')}/public/js/csrf-submit.js?v1`
        },
    ];

    if (linz.app.locals.adminJSFile) {
        defaults.push({ src: linz.app.locals.adminJSFile });
    }

    res.locals.scripts = [].concat(defaults, templateScripts)
        .filter(script => script !== undefined);

    const customScripts = linz.get('scripts');

    if (!customScripts) {
        return resolve(res.locals.scripts);
    }

    customScripts(req, res)
        .then(resolve)
        .catch(reject);

});

module.exports = setTemplateScripts;
<|endoftext|>


<commit_before>var crypto = require('crypto');
var async  = require('async');
var utils  = require('./utils');
var pki = require('node-forge').pki;
var deasync = require('deasync');

function encryptKeyInfoWithScheme(symmetricKey, options, scheme, callback) {
  // convert a PEM-formatted public key to a Forge public key
  var rsa_pub = pki.publicKeyFromPem(options.rsa_pub);
  // encrypt the forge public key with the scheme algorithm
  var encrypted = rsa_pub.encrypt(symmetricKey.toString('binary'), scheme);
  // encode the encrypt key to base 64
  var base64EncodedEncryptedKey = new Buffer(encrypted, 'binary').toString('base64');

  var params = {
    encryptedKey:  base64EncodedEncryptedKey,
    //encryptionPublicCert: '<X509Data><X509Certificate>' + utils.pemToCert(options.pem.toString()) + '</X509Certificate></X509Data>',
    encryptionPublicCert: utils.pemToCert(options.pem.toString()),
    keyEncryptionMethod: options.keyEncryptionAlgorighm
  };

  var result = utils.renderTemplate('keyinfo', params);

  return callback(null, result);
}

function encryptKeyInfo(symmetricKey, options, callback) {
  if (!options)
    return callback(new Error('must provide options'));
  if (!options.rsa_pub)
    return callback(new Error('must provide options.rsa_pub with public key RSA'));
  if (!options.pem)
    return callback(new Error('must provide options.pem with certificate'));

  if (!options.keyEncryptionAlgorighm)
    return callback(new Error('encryption without encrypted key is not supported yet'));

  switch (options.keyEncryptionAlgorighm) {
    case 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p':
      return encryptKeyInfoWithScheme(symmetricKey, options, 'RSA-OAEP', callback)

    case 'http://www.w3.org/2001/04/xmlenc#rsa-1_5':
      return encryptKeyInfoWithScheme(symmetricKey, options, 'RSAES-PKCS1-V1_5', callback)

    default:
      return callback(new Error('encryption key algorithm not supported'));
  }
}

function jsonEncrypt(json, jsonKey, options, callback) {
  if (!json)
    return callback(new Error('must provide json object'));
  if (!jsonKey)
    return callback(new Error('must provide jsonKey path to encrypt'));

  var key = jsonKey.split(/[\[\].,]/).filter(String);

  callback(null, findKeyEncryptValue(json,key,options));
}

function findKeyEncryptValue(json, keyPath, options){
  if(keyPath.length == 1) {
    json[keyPath[0]] = jsonSyncEncrypt(JSON.stringify(json[keyPath[0]]), options);
    return json;
  }

  json[keyPath[0]] = findKeyEncryptValue(json[keyPath[0]], keyPath.slice(1, keyPath.length), options);
  return json;
}

function jsonSyncEncrypt(value, options){
  var json;
  encrypt(value, options, function(err, result) {
    json = JSON.parse(result);
    });
    while(json === undefined) {
      deasync.runLoopOnce();
    }
    return json;
}

function jsonDecrypt(json, jsonKey, options, callback) {
  if (!json)
    return callback(new Error('must provide json object'));
  if (!jsonKey)
    return callback(new Error('must provide jsonKey to encrypt'));

  var key = jsonKey.split(/[\[\].,]/).filter(String);

  callback(null, findKeyDecryptValue(json,key,options));
}

function findKeyDecryptValue(json, keyPath, options){
  if(keyPath.length == 1) {
    json[keyPath[0]] = jsonSyncDecrypt(JSON.stringify(json[keyPath[0]]), options);
    return json;
  }

  json[keyPath[0]] = findKeyDecryptValue(json[keyPath[0]], keyPath.slice(1, keyPath.length), options);
  return json;
}

function jsonSyncDecrypt(value, options){
    var json;
    decrypt(value, options, function(err, result) {
      json = JSON.parse(result);
    });
    while(json === undefined) {
      deasync.runLoopOnce();
    }
    return json;
}

function encrypt(content, options, callback) {
  if (!options)
    return callback(new Error('must provide options'));
  if (!content)
    return callback(new Error('must provide content to encrypt'));
  if (!options.rsa_pub)
    return callback(new Error('rsa_pub option is mandatory and you should provide a valid RSA public key'));
  if (!options.pem)
    return callback(new Error('pem option is mandatory and you should provide a valid x509 certificate encoded as PEM'));

  options.input_encoding = options.input_encoding || 'utf8';

  async.waterfall([
    function generate_symmetric_key(cb) {
      switch (options.encryptionAlgorithm) {
        case 'http://www.w3.org/2001/04/xmlenc#aes128-cbc':
          crypto.randomBytes(16, cb); // generate a symmetric random key 16 bytes length
          break;
        case 'http://www.w3.org/2001/04/xmlenc#aes256-cbc':
          crypto.randomBytes(32, cb); // generate a symmetric random key 32 bytes length
          break;
        case 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc':
          crypto.randomBytes(24, cb); // generate a symmetric random key 24 bytes (192 bits) length
          break;
        default:
          crypto.randomBytes(32, cb); // generate a symmetric random key 32 bytes length
      }
    },
    function encrypt_content(symmetricKey, cb) {
      switch (options.encryptionAlgorithm) {
        case 'http://www.w3.org/2001/04/xmlenc#aes128-cbc':
          encryptWithAlgorithm('aes-128-cbc', symmetricKey, 16, content, options.input_encoding, function (err, encryptedContent) {
            if (err) return cb(err);
            cb(null, symmetricKey, encryptedContent);
          });
          break;
        case 'http://www.w3.org/2001/04/xmlenc#aes256-cbc':
          encryptWithAlgorithm('aes-256-cbc', symmetricKey, 16, content, options.input_encoding, function (err, encryptedContent) {
            if (err) return cb(err);
            cb(null, symmetricKey, encryptedContent);
          });
          break;
        case 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc':
          encryptWithAlgorithm('des-ede3-cbc', symmetricKey, 8, content, options.input_encoding, function (err, encryptedContent) {
            if (err) return cb(err);
            cb(null, symmetricKey, encryptedContent);
          });
          break;
        default:
          cb(new Error('encryption algorithm not supported'));
      }
    },
    function encrypt_key(symmetricKey, encryptedContent, cb) {
      encryptKeyInfo(symmetricKey, options, function(err, keyInfo) {
        if (err) return cb(err);

        var result = utils.renderTemplate('encrypted-key', {
          encryptedContent: encryptedContent.toString('base64'),
          keyInfo: keyInfo,
          contentEncryptionMethod: options.encryptionAlgorithm
        });

        cb(null, result);
      });
    }
  ], callback);
}

function decryptKeyInfoWithScheme(encryptedKey, options, scheme) {
  var key = new Buffer(encryptedKey.textContent, 'base64').toString('binary');
  var private_key = pki.privateKeyFromPem(options.key);
  var decrypted = private_key.decrypt(key, scheme);
  return new Buffer(decrypted, 'binary');
}

function encryptWithAlgorithm(algorithm, symmetricKey, ivLength, content, encoding, callback) {
  // create a random iv for algorithm
  crypto.randomBytes(ivLength, function(err, iv) {
    if (err) return callback(err);

    var cipher = crypto.createCipheriv(algorithm, symmetricKey, iv);
    // encrypted content
    var encrypted = cipher.update(content, encoding, 'binary') + cipher.final('binary');
    return callback(null, Buffer.concat([iv, new Buffer(encrypted, 'binary')]));
  });
}

function decrypt(json, options, callback) {
  if (!options)
    return callback(new Error('must provide options'));
  if (!json)
    return callback(new Error('must provide JSON to decrypt'));
  if (!options.key)
    return callback(new Error('key option is mandatory and you should provide a valid RSA private key'));

  var decrypted;

  try {
    var doc = JSON.parse(json);

    var symmetricKey = decryptKeyInfo(doc, options);
    var encryptionAlgorithm = doc.EncryptedData.EncryptionMethod;

    var decipher;
    var padding;
    var encryptedContent = doc.EncryptedData.CipherData.CipherValue;

    var encrypted = new Buffer(encryptedContent, 'base64');

    switch (encryptionAlgorithm) {
      case 'http://www.w3.org/2001/04/xmlenc#aes128-cbc':
        decipher = crypto.createDecipheriv('aes-128-cbc', symmetricKey, encrypted.slice(0, 16));

        decipher.setAutoPadding(false);
        decrypted = decipher.update(encrypted.slice(16), null, 'binary') + decipher.final('binary');

        // Remove padding bytes equal to the value of the last byte of the returned data.
        padding = decrypted.charCodeAt(decrypted.length - 1);
        if (1 <= padding && padding <= 16) {
          decrypted = decrypted.substr(0, decrypted.length - padding);
        } else {
          callback(new Error('padding length invalid'));
          return;
        }

        decrypted = new Buffer(decrypted, 'binary').toString('utf8');
        break;
      case 'http://www.w3.org/2001/04/xmlenc#aes256-cbc':
        decipher = crypto.createDecipheriv('aes-256-cbc', symmetricKey, encrypted.slice(0, 16));

        decipher.setAutoPadding(false);
        decrypted = decipher.update(encrypted.slice(16), null, 'binary') + decipher.final('binary');

        // Remove padding bytes equal to the value of the last byte of the returned data.
        padding = decrypted.charCodeAt(decrypted.length - 1);
        if (1 <= padding && padding <= 16) {
          decrypted = decrypted.substr(0, decrypted.length - padding);
        } else {
          callback(new Error('padding length invalid'));
          return;
        }
        decrypted = new Buffer(decrypted, 'binary').toString('utf8');
        break;
      case 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc':
        decipher = crypto.createDecipheriv('des-ede3-cbc', symmetricKey, encrypted.slice(0,8));
        decrypted = decipher.update(encrypted.slice(8), null, 'binary') + decipher.final('binary');
        decrypted = new Buffer(decrypted, 'binary').toString('utf8');
        break;
      default:
        return callback(new Error('encryption algorithm ' + encryptionAlgorithm + ' not supported'));
    }
  } catch (e) {
    return callback(e);
  }

  callback(null, decrypted);
}

function decryptKeyInfo(doc, options) {

  var keyInfo = doc.EncryptedData.KeyInfo.EncryptedKey.KeyInfo;
  var keyEncryptionAlgorighm = doc.EncryptedData.KeyInfo.EncryptedKey.EncryptionMethod.Algorithm;
  var encryptedKey = doc.EncryptedData.KeyInfo.EncryptedKey.CipherData.CipherValue;

  switch (keyEncryptionAlgorighm) {
    case 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p':
      return decryptKeyInfoWithScheme(encryptedKey, options, 'RSA-OAEP')
    case 'http://www.w3.org/2001/04/xmlenc#rsa-1_5':
      return decryptKeyInfoWithScheme(encryptedKey, options, 'RSAES-PKCS1-V1_5')
    default:
      throw new Error('key encryption algorithm ' + keyEncryptionAlgorighm + ' not supported');
  }
}

function decryptKeyInfoWithScheme(encryptedKey, options, scheme) {
  var key = new Buffer(encryptedKey, 'base64').toString('binary');
  var private_key = pki.privateKeyFromPem(options.key);
  var decrypted = private_key.decrypt(key, scheme);
  return new Buffer(decrypted, 'binary');
}

exports = module.exports = {
  encrypt: encrypt,
  decrypt: decrypt,
  jsonEncrypt: jsonEncrypt,
  jsonDecrypt: jsonDecrypt
};
<commit_msg>fix url's<commit_after>var crypto = require('crypto');
var async  = require('async');
var utils  = require('./utils');
var pki = require('node-forge').pki;
var deasync = require('deasync');

function encryptKeyInfoWithScheme(symmetricKey, options, scheme, callback) {
  // convert a PEM-formatted public key to a Forge public key
  var rsa_pub = pki.publicKeyFromPem(options.rsa_pub);
  // encrypt the forge public key with the scheme algorithm
  var encrypted = rsa_pub.encrypt(symmetricKey.toString('binary'), scheme);
  // encode the encrypt key to base 64
  var base64EncodedEncryptedKey = new Buffer(encrypted, 'binary').toString('base64');

  var params = {
    encryptedKey:  base64EncodedEncryptedKey,
    //encryptionPublicCert: '<X509Data><X509Certificate>' + utils.pemToCert(options.pem.toString()) + '</X509Certificate></X509Data>',
    encryptionPublicCert: utils.pemToCert(options.pem.toString()),
    keyEncryptionMethod: options.keyEncryptionAlgorighm
  };

  var result = utils.renderTemplate('keyinfo', params);

  return callback(null, result);
}

function encryptKeyInfo(symmetricKey, options, callback) {
  if (!options)
    return callback(new Error('must provide options'));
  if (!options.rsa_pub)
    return callback(new Error('must provide options.rsa_pub with public key RSA'));
  if (!options.pem)
    return callback(new Error('must provide options.pem with certificate'));

  if (!options.keyEncryptionAlgorighm)
    return callback(new Error('encryption without encrypted key is not supported yet'));

  switch (options.keyEncryptionAlgorighm) {
    case 'http://tiagomistral.github.io/SecJSON#rsa-oaep-mgf1p':
      return encryptKeyInfoWithScheme(symmetricKey, options, 'RSA-OAEP', callback)

    case 'http://tiagomistral.github.io/SecJSON#rsa-1_5':
      return encryptKeyInfoWithScheme(symmetricKey, options, 'RSAES-PKCS1-V1_5', callback)

    default:
      return callback(new Error('encryption key algorithm not supported'));
  }
}

function jsonEncrypt(json, jsonKey, options, callback) {
  if (!json)
    return callback(new Error('must provide json object'));
  if (!jsonKey)
    return callback(new Error('must provide jsonKey path to encrypt'));

  var key = jsonKey.split(/[\[\].,]/).filter(String);

  callback(null, findKeyEncryptValue(json,key,options));
}

function findKeyEncryptValue(json, keyPath, options){
  if(keyPath.length == 1) {
    json[keyPath[0]] = jsonSyncEncrypt(JSON.stringify(json[keyPath[0]]), options);
    return json;
  }

  json[keyPath[0]] = findKeyEncryptValue(json[keyPath[0]], keyPath.slice(1, keyPath.length), options);
  return json;
}

function jsonSyncEncrypt(value, options){
  var json;
  encrypt(value, options, function(err, result) {
    json = JSON.parse(result);
    });
    while(json === undefined) {
      deasync.runLoopOnce();
    }
    return json;
}

function jsonDecrypt(json, jsonKey, options, callback) {
  if (!json)
    return callback(new Error('must provide json object'));
  if (!jsonKey)
    return callback(new Error('must provide jsonKey to encrypt'));

  var key = jsonKey.split(/[\[\].,]/).filter(String);

  callback(null, findKeyDecryptValue(json,key,options));
}

function findKeyDecryptValue(json, keyPath, options){
  if(keyPath.length == 1) {
    json[keyPath[0]] = jsonSyncDecrypt(JSON.stringify(json[keyPath[0]]), options);
    return json;
  }

  json[keyPath[0]] = findKeyDecryptValue(json[keyPath[0]], keyPath.slice(1, keyPath.length), options);
  return json;
}

function jsonSyncDecrypt(value, options){
    var json;
    decrypt(value, options, function(err, result) {
      json = JSON.parse(result);
    });
    while(json === undefined) {
      deasync.runLoopOnce();
    }
    return json;
}

function encrypt(content, options, callback) {
  if (!options)
    return callback(new Error('must provide options'));
  if (!content)
    return callback(new Error('must provide content to encrypt'));
  if (!options.rsa_pub)
    return callback(new Error('rsa_pub option is mandatory and you should provide a valid RSA public key'));
  if (!options.pem)
    return callback(new Error('pem option is mandatory and you should provide a valid x509 certificate encoded as PEM'));

  options.input_encoding = options.input_encoding || 'utf8';

  async.waterfall([
    function generate_symmetric_key(cb) {
      switch (options.encryptionAlgorithm) {
        case 'http://tiagomistral.github.io/SecJSON#aes128-cbc':
          crypto.randomBytes(16, cb); // generate a symmetric random key 16 bytes length
          break;
        case 'http://tiagomistral.github.io/SecJSON#aes256-cbc':
          crypto.randomBytes(32, cb); // generate a symmetric random key 32 bytes length
          break;
        case 'http://tiagomistral.github.io/SecJSON#tripledes-cbc':
          crypto.randomBytes(24, cb); // generate a symmetric random key 24 bytes (192 bits) length
          break;
        default:
          crypto.randomBytes(32, cb); // generate a symmetric random key 32 bytes length
      }
    },
    function encrypt_content(symmetricKey, cb) {
      switch (options.encryptionAlgorithm) {
        case 'http://tiagomistral.github.io/SecJSON#aes128-cbc':
          encryptWithAlgorithm('aes-128-cbc', symmetricKey, 16, content, options.input_encoding, function (err, encryptedContent) {
            if (err) return cb(err);
            cb(null, symmetricKey, encryptedContent);
          });
          break;
        case 'http://tiagomistral.github.io/SecJSON#aes256-cbc':
          encryptWithAlgorithm('aes-256-cbc', symmetricKey, 16, content, options.input_encoding, function (err, encryptedContent) {
            if (err) return cb(err);
            cb(null, symmetricKey, encryptedContent);
          });
          break;
        case 'http://tiagomistral.github.io/SecJSON#tripledes-cbc':
          encryptWithAlgorithm('des-ede3-cbc', symmetricKey, 8, content, options.input_encoding, function (err, encryptedContent) {
            if (err) return cb(err);
            cb(null, symmetricKey, encryptedContent);
          });
          break;
        default:
          cb(new Error('encryption algorithm not supported'));
      }
    },
    function encrypt_key(symmetricKey, encryptedContent, cb) {
      encryptKeyInfo(symmetricKey, options, function(err, keyInfo) {
        if (err) return cb(err);

        var result = utils.renderTemplate('encrypted-key', {
          encryptedContent: encryptedContent.toString('base64'),
          keyInfo: keyInfo,
          contentEncryptionMethod: options.encryptionAlgorithm
        });

        cb(null, result);
      });
    }
  ], callback);
}

function decryptKeyInfoWithScheme(encryptedKey, options, scheme) {
  var key = new Buffer(encryptedKey.textContent, 'base64').toString('binary');
  var private_key = pki.privateKeyFromPem(options.key);
  var decrypted = private_key.decrypt(key, scheme);
  return new Buffer(decrypted, 'binary');
}

function encryptWithAlgorithm(algorithm, symmetricKey, ivLength, content, encoding, callback) {
  // create a random iv for algorithm
  crypto.randomBytes(ivLength, function(err, iv) {
    if (err) return callback(err);

    var cipher = crypto.createCipheriv(algorithm, symmetricKey, iv);
    // encrypted content
    var encrypted = cipher.update(content, encoding, 'binary') + cipher.final('binary');
    return callback(null, Buffer.concat([iv, new Buffer(encrypted, 'binary')]));
  });
}

function decrypt(json, options, callback) {
  if (!options)
    return callback(new Error('must provide options'));
  if (!json)
    return callback(new Error('must provide JSON to decrypt'));
  if (!options.key)
    return callback(new Error('key option is mandatory and you should provide a valid RSA private key'));

  var decrypted;

  try {
    var doc = JSON.parse(json);

    var symmetricKey = decryptKeyInfo(doc, options);
    var encryptionAlgorithm = doc.EncryptedData.EncryptionMethod;

    var decipher;
    var padding;
    var encryptedContent = doc.EncryptedData.CipherData.CipherValue;

    var encrypted = new Buffer(encryptedContent, 'base64');

    switch (encryptionAlgorithm) {
      case 'http://tiagomistral.github.io/SecJSON#aes128-cbc':
        decipher = crypto.createDecipheriv('aes-128-cbc', symmetricKey, encrypted.slice(0, 16));

        decipher.setAutoPadding(false);
        decrypted = decipher.update(encrypted.slice(16), null, 'binary') + decipher.final('binary');

        // Remove padding bytes equal to the value of the last byte of the returned data.
        padding = decrypted.charCodeAt(decrypted.length - 1);
        if (1 <= padding && padding <= 16) {
          decrypted = decrypted.substr(0, decrypted.length - padding);
        } else {
          callback(new Error('padding length invalid'));
          return;
        }

        decrypted = new Buffer(decrypted, 'binary').toString('utf8');
        break;
      case 'http://tiagomistral.github.io/SecJSON#aes256-cbc':
        decipher = crypto.createDecipheriv('aes-256-cbc', symmetricKey, encrypted.slice(0, 16));

        decipher.setAutoPadding(false);
        decrypted = decipher.update(encrypted.slice(16), null, 'binary') + decipher.final('binary');

        // Remove padding bytes equal to the value of the last byte of the returned data.
        padding = decrypted.charCodeAt(decrypted.length - 1);
        if (1 <= padding && padding <= 16) {
          decrypted = decrypted.substr(0, decrypted.length - padding);
        } else {
          callback(new Error('padding length invalid'));
          return;
        }
        decrypted = new Buffer(decrypted, 'binary').toString('utf8');
        break;
      case 'http://tiagomistral.github.io/SecJSON#tripledes-cbc':
        decipher = crypto.createDecipheriv('des-ede3-cbc', symmetricKey, encrypted.slice(0,8));
        decrypted = decipher.update(encrypted.slice(8), null, 'binary') + decipher.final('binary');
        decrypted = new Buffer(decrypted, 'binary').toString('utf8');
        break;
      default:
        return callback(new Error('encryption algorithm ' + encryptionAlgorithm + ' not supported'));
    }
  } catch (e) {
    return callback(e);
  }

  callback(null, decrypted);
}

function decryptKeyInfo(doc, options) {

  var keyInfo = doc.EncryptedData.KeyInfo.EncryptedKey.KeyInfo;
  var keyEncryptionAlgorighm = doc.EncryptedData.KeyInfo.EncryptedKey.EncryptionMethod.Algorithm;
  var encryptedKey = doc.EncryptedData.KeyInfo.EncryptedKey.CipherData.CipherValue;

  switch (keyEncryptionAlgorighm) {
    case 'http://tiagomistral.github.io/SecJSON#rsa-oaep-mgf1p':
      return decryptKeyInfoWithScheme(encryptedKey, options, 'RSA-OAEP')
    case 'http://tiagomistral.github.io/SecJSON#rsa-1_5':
      return decryptKeyInfoWithScheme(encryptedKey, options, 'RSAES-PKCS1-V1_5')
    default:
      throw new Error('key encryption algorithm ' + keyEncryptionAlgorighm + ' not supported');
  }
}

function decryptKeyInfoWithScheme(encryptedKey, options, scheme) {
  var key = new Buffer(encryptedKey, 'base64').toString('binary');
  var private_key = pki.privateKeyFromPem(options.key);
  var decrypted = private_key.decrypt(key, scheme);
  return new Buffer(decrypted, 'binary');
}

exports = module.exports = {
  encrypt: encrypt,
  decrypt: decrypt,
  jsonEncrypt: jsonEncrypt,
  jsonDecrypt: jsonDecrypt
};
<|endoftext|>