/* 	@preserve Copyright 2013-2014 Peersm (Nas)

Modified MIT license for now (which removes the rights to modify, merge, sublicense, and sell):

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, publish, and/or distribute copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

	With adaptations of:

		http://code.google.com/p/stringencoding/
		Copyright Joshua Bell (chromium)
		license Apache 2.0
		Implementing TextEncoder/TextDecoder WHATWG specifications

		https://github.com/digitalbazaar/forge
		Dave Longley
		Copyright (c) 2010-2012 Digital Bazaar, Inc.
		BSD License
		You may use the Forge project under the terms of either the BSD License or the
		GNU General Public License (GPL) Version 2.

		The BSD License is recommended for most projects. It is simple and easy to
		understand and it places almost no restrictions on what you can do with the
		Forge project.

		If the GPL suits your project better you are also free to use Forge under
		that license.

		You don't have to do anything special to choose one license or the other and
		you don't have to notify anyone which license you are using. You are free to
		use this project in commercial projects as long as the copyright header is
		left intact.

		If you are a commercial entity and use this set of libraries in your
		commercial software then reasonable payment to Digital Bazaar, if you can
		afford it, is not required but is expected and would be appreciated. If this
		library saves you time, then it's saving you money. The cost of developing
		the Forge software was on the order of several hundred hours and tens of
		thousands of dollars. We are attempting to strike a balance between helping
		the development community while not being taken advantage of by lucrative
		commercial entities for our efforts.

		Stanford Javascript Crypto Library
		http://bitwiseshiftleft.github.io/sjcl/
		Copyright 2009-2010 Emily Stark, Mike Hamburg, Dan Boneh, Stanford University.

		This is for liability reasons. (Speaking of which, SJCL comes with NO
		WARRANTY WHATSOEVER, express or implied, to the limit of applicable
		law.)

		SJCL is dual-licensed under the GNU GPL version 2.0 or higher, and a
		2-clause BSD license. You may use SJCL under the terms of either of
		these licenses. For your convenience, the GPL versions 2.0 and 3.0
		and the 2-clause BSD license are included here. Additionally, you may
		serve "crunched" copies of sjcl (i.e. those with comments removed,
		and other transformations to reduce code size) without any copyright
		notice.

		RSA and ECC in JavaScript
		http://www-cs-students.stanford.edu/~tjw/jsbn/
		 * Copyright (c) 2003-2005  Tom Wu
		 * All Rights Reserved.
		 *
		 * Permission is hereby granted, free of charge, to any person obtaining
		 * a copy of this software and associated documentation files (the
		 * "Software"), to deal in the Software without restriction, including
		 * without limitation the rights to use, copy, modify, merge, publish,
		 * distribute, sublicense, and/or sell copies of the Software, and to
		 * permit persons to whom the Software is furnished to do so, subject to
		 * the following conditions:
		 *
		 * The above copyright notice and this permission notice shall be
		 * included in all copies or substantial portions of the Software.
		 *
		 * THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND,
		 * EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY
		 * WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
		 *
		 * IN NO EVENT SHALL TOM WU BE LIABLE FOR ANY SPECIAL, INCIDENTAL,
		 * INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, OR ANY DAMAGES WHATSOEVER
		 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER OR NOT ADVISED OF
		 * THE POSSIBILITY OF DAMAGE, AND ON ANY THEORY OF LIABILITY, ARISING OUT
		 * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
		 *
		 * In addition, the following condition applies:
		 *
		 * All redistributions must retain an intact copy of this copyright notice
		 * and disclaimer.
		 *

		http://webrsa.cvs.sourceforge.net/viewvc/webrsa/Client/RSAES-OAEP.js?content-type=text%2Fplain:
		RSAES-OAEP.js
		Copyright (C) Ellis Pritchard, Guardian Unlimited 2003.
		BSD License.

		https://github.com/mafintosh/torrent-stream
		Copyright (C) 2014 Mathias Buus Madsen <mathiasbuus@gmail.com>
		Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

		The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

		THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

		https://github.com/gpac/mp4box.js
		Copyright (c) 2012. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato
		All rights reserved.
		Redistribution and use in source and binary forms, with or without
		modification, are permitted provided that the following conditions are met:
		* Redistributions of source code must retain the above copyright
		notice, this list of conditions and the following disclaimer.
		* Redistributions in binary form must reproduce the above copyright
		notice, this list of conditions and the following disclaimer in the
		documentation and/or other materials provided with the distribution.
		* Neither the name of the copyright holder nor the
		names of its contributors may be used to endorse or promote products
		derived from this software without specific prior written permission.
		THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
		ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
		WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
		DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
		DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
		(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
		LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
		ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
		(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
		SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

	And:

	https://github.com/mishoo/UglifyJS2
	Mihai Bazon
	UglifyJS is released under the BSD license:

	Copyright 2012-2013 (c) Mihai Bazon <mihai.bazon@gmail.com>

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions
	are met:

		* Redistributions of source code must retain the above
		  copyright notice, this list of conditions and the following
		  disclaimer.

		* Redistributions in binary form must reproduce the above
		  copyright notice, this list of conditions and the following
		  disclaimer in the documentation and/or other materials
		  provided with the distribution.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AS IS AND ANY
	EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
	PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
	PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
	THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
	TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
	THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
	SUCH DAMAGE.

*/
/*
Server IP change: modify bridge.js and fake_cid_server.js
*/
(function() {
	//Copyright 2013 jCore - Aymeric Vitte
	//minification:
	//reserved eval_,Guards(Guards),Exit(Exit),FindProxyForURL
	//Buffer= --> var Buffer=

	//TODO : eradication of binary format (watch node.js's deprecating the binary format (0.10 ?))
	//TODO : replace bind by fat arrow (=>) (ES6)
	//TODO : replace do_not_wait and timeout by promises (tasks.js,q)
	//TODO : optimize buffers (remove instantiation readUInt and writeUint)
	/* TODO subclass Array :
	Uint8Array.prototype.__proto__=Array.prototype;
	Array.prototype.concatBuffers=function() {
		var a=[];
		this.forEach(function(b) {a=a.concat(b)});
		return new Uint8Array(a);
	};
	Comparer performances par rapport fonction actuelle
	*/
	//rtorrent -n -o import=/home/user/.rtorrent1.rc

	//REDIRECT ERRORS

	//process.on('uncaughtException', function (err) {
	//	writefile('error2.txt',err.arguments[1].constructor.toString()+'\n');
	//});

	//DEBUG

	//var eval_=function() {};

	/*modifs node.js buffer.cc
	replace in HasInstance
	ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType();
	if (type != kExternalUnsignedByteArray)
			return false;
	};
	by
	ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType();
	if (type == kExternalUnsignedByteArray)
			return true;
	};
	Watch util.js util.isBuffer
	*/

	var TEST_CONF=true; //used to test with  ordb1 (peersm_client or debug)
	//#213.246.53.127:8050-213.246.53.127:8001:E0671CF9CB593F27CD389CD4DD819BF9448EA834:ca2a670479816ca562f7afc2667db1811f0efa7d595aa27cf532092a052c697b102c03d8b8dddc276050fe9cad15efe72758d9d9b0f581f5cbfd0be92ecd721711797354006625e74e0f733efee3ee779116efe87da3b5f8c1729e2d0a5f2c4de4d5906b6e383c0a0d8dddbc076cf426423f6f2b6fd46fab9f54fb8e42601a2d

	//#37.59.47.27:8050-37.59.47.27:8001:E0671CF9CB593F27CD389CD4DD819BF9448EA834:ca2a670479816ca562f7afc2667db1811f0efa7d595aa27cf532092a052c697b102c03d8b8dddc276050fe9cad15efe72758d9d9b0f581f5cbfd0be92ecd721711797354006625e74e0f733efee3ee779116efe87da3b5f8c1729e2d0a5f2c4de4d5906b6e383c0a0d8dddbc076cf426423f6f2b6fd46fab9f54fb8e42601a2d

	//node node-Tor56.js /home/ordb1/peersm_client/ 2

	var PERF=true;

	if (typeof(require)!=='undefined') {
		var Buffer = require('buffer').Buffer;
	};

	var demo=false;

	var bot=false;

	var prod=false; //open SOCKS interface OP port and OR

	var pathd='/usr/local/lib/node/node-Tor/lib/';

	var prod3=true; //log in file for download

	var STORE_PERF=false;

	var PARSER_PERF=false; //ORDB 8 Mbps - browser 1.5 Mbps - 2 - 512B 3ms

	var CLEAR_PERF=false;

	var WS_PERF=false;

	var WS_PERF2=false;

	var WSDELAY_PERF=false;

	var WSDECODE_PERF=false;

	var CLIENTTLS_PERF=false; //1.5 Mbps - from ws to dataReady before parser - 1 - 512 B 3ms

	var SENDER_PERF=false;

	var DB_PERF=false;

	var WORKER_PERF=false;

	var _WRITE_PERF=false; //4 Mbps

	var CRYPTO_PERF=false; //2 Mbps (decrypt backward) - 512B 2ms

	var MP4BOX_PERF=true;

	var OR=(typeof(WebSocket)!=="undefined")?false:(prod?false:true); //OR 8000 or ws 8002

	//OR=prod?false:true; //uncomment for prod

	var protect=prod; //protect prod w/o ws socks port 8101

	var protect=false; //8050

	var prod2=false; //ianonym and ws socks port 8200

	var window_browser=(OR||prod)?false:true; //true implements sha, aes and rsa (non OAEP for create_fast) in js and RELAY_INFO - request OAEP/DH from router

	var window_browser_server=false; //false if browser, true if tests with the server emulating browser

	var peersm_client=false; //peersm client on server side

	var peersm_ws=false;

	if ((window_browser)&&(!window_browser_server)) {
		process={}; //TODO node.js specific replace by var process
		process.on=function() {};
		process.platform={indexOf:function() {}};
		var Buffer=function() {};//TODO node.js specific replace by var require
		require=function(module) { //TODO node.js specific replace by var require
			var f=function() {};
			var g={};
			g.Rsa=f;
			g.PEM=f;
			g.Hash=null;
			g.Guards=[];
			g.Relays=[];
			g.Dirs=[];
			g.Exit=[];
			switch (module) {
				case 'fs' :
					var fs={};
					fs.openSync=f;
					fs.writeSync=f;
					fs.closeSync=f;
					fs.open=function(fd,opt,cb) {cb()};
					fs.write=f;
					fs.close=f;
					fs.unlinkSync=f;
					fs.readFileSync=f;
					fs.readFile=f;
					fs.stat=f;
					fs.rename=f;
					return fs;
				case 'domain' :
					var domain={};
					domain.create=function() {return {on:function(){},run:function(f) {f()}}};
					return domain;
				case 'child_process' :
					var child={};
					child.exec=f;
					return child;
				case pathd+'crypto.js' :
					return g;
				case pathd+'guards.js' :
					return g;
				case pathd+'relays.js' :
					return g;
				case pathd+'dirs.js' :
					return g;
				case pathd+'exit.js' :
					return g;
				default : return f;
			};
		};
	};

	/*var d=require('domain').create();

	d.on('error',function(er) {
		console.error('------ Caught error ',er.message);
	});*/

	//d.run(function() {

		var fs = require('fs'),
			child_process = require('child_process');

		var writefile=function(file,txt) {
			txt +=' '+(new Date().toDateString())+' '+(new Date().toTimeString());
			var cb=function(err,fd) {
				try {
					if (!err) {
						fs.write(fd,txt+'\n',function() {});
						fs.close(fd);
					};
					if ((window_browser)&&(typeof $_!=='undefined')) {
						var cons=$_('console');
						if (cons.innerHTML.length>1000000) {
							cons.innerHTML='';
						};
						var log=document.createElement('p');
						log.className='log';
						log.innerHTML=txt;
						cons.appendChild(log);
					};
				} catch(ee) {}
			};
			try {
				//if ((peersm_client)||(window_browser)) {
					fs.open(pathd+OR_port+'-'+file,'a',cb);
				/*} else {
					if ((txt.indexOf('db_query')!==-1)||(txt.indexOf('db_info')!==-1)||(txt.indexOf('error')!==-1)) {
						fs.open(pathd+OP_port+'-'+file,'a',cb);
					};
				};*/
			} catch(ee) {};
		};

		process.on('uncaughtException', function (err) {
			//writefile('error.txt',err.stack);
			//if (err.stack.indexOf('ECONNRESET')===-1) {
				//console.log('uncaught');
				//console.log(err.stack);
				var fd=fs.openSync('/debug.txt','a');
				fs.writeSync(fd,(new Date().toDateString())+' '+(new Date().toTimeString()));
				fs.writeSync(fd,err.stack);
				fs.closeSync(fd);
			//};
		});

		process.on('exit', function() {
			setTimeout(function() {
			console.log('Process ended');
			}, 0);
			console.log('About to exit '+(new Date().toDateString())+' '+(new Date().toTimeString()));
		});

		var ooconsole=console.log.bind(console); //modif chrome
		//var ooconsole=function() {};
		//var gconsole=ooconsole;
		//var gconsole=oconsole;
		var gconsole=function(txt) {
			//writefile(prod?'debug-prod.txt':(prod2?'debug-prod2.txt':'debug.txt'),txt+' '+(gconsole.caller?(gconsole.caller.toString().substr(0,100)):''));
			writefile((prod||prod3)?'debug-prod.txt':(prod2?'debug-prod2.txt':'debug.txt'),txt);
			if ((!prod)&&(!prod2)&&(!prod3)) {ooconsole(txt);}
		};
		var oconsole=function(txt) {
			//writefile(prod?'debug-prod.txt':(prod2?'debug-prod2.txt':'debug.txt'),txt+' '+(oconsole.caller?(oconsole.caller.toString().substr(0,100)):''));
			writefile((prod||prod3)?'debug-prod.txt':(prod2?'debug-prod2.txt':'debug.txt'),txt);
			if ((!prod)&&(!prod2)&&(!prod3)) {ooconsole(txt);}
		};
		console.log=function(txt) {
			//writefile(prod?'debug-prod.txt':(prod2?'debug-prod2.txt':'debug.txt'),txt+' '+(console.log.caller?(console.log.caller.toString().substr(0,100)):''));
			writefile((prod||prod3)?'debug-prod.txt':(prod2?'debug-prod2.txt':'debug.txt'),txt);
			if (((!prod)&&(!prod2)&&(!prod3))||(((process.platform.indexOf('win')!==-1)&&peersm_client))||TEST_CONF) {ooconsole(txt+' '+(new Date().toDateString())+' '+(new Date().toTimeString()))}
		};

		var ordbname,ordbip,ordbport,ordbwsport,ordbversion,client_band,short_path;

		//ordb node code.js orbdx 10.10.10.10:8001 8002 0.1.0
		//peersm server node code.js /home/peersm_client/ 2
		//ws server node code.js /path/ -P 80
		//OP socks server node code.js 213.246.53.127:8052:8003:20:2:3600:yt (IP:OR_port:OP_port:OP nb circuits:OP nb hops:renew circuit time s:inflate)

		if (!window_browser) {
			if (process.argv) {
				if (process.argv.length>1) {
					var args=process.argv.splice(2);
					if (args.length===5) {
						ordbname=args[0];
						ordbip=args[1];
						ordbport=args[2];
						ordbwsport=args[3];
						ordbversion=args[4];
					} else if (args.length===1) {
						var args=args[0].split(':');
						prod={};
						prod.ip=args[0];
						prod.OR_port=args[1];
						prod.OP_port=args[2];
						prod.NB=args[3];
						prod.NB_HOP=args[4];
						prod.renew=args[5]*1000;
						if (args[6]) {
							prod.inflate=true;
						};
						OR=false;
					} else {
						pathd=args[0]||'';
						if (pathd.length) {
							if (process.platform.indexOf('win')===-1) {
								var f=pathd[pathd.length-1];
								if (f!=='/') {
									pathd=pathd+'/';
								};
							};
						} else {
							ooconsole('Invalid directory parameter');
						};
						peersm_client=true;
						ordbname='';
						if (args.length>1) {
							if (args[1]!=='-P') {
								client_band=args[1];
								if (args.length>2) {
									short_path=true;
								};
							} else {
								peersm_client=false;
								peersm_ws=true;
								if (args.length>2) {
									if (!isNaN(parseInt(args[2]))) {
										ordbwsport=parseInt(args[2]);
									} else {
										return;
									}
								} else {
									return;
								};
							};
						};
					};
				};
			};

			//UPDATE ROUTERS
			var build=function() {
				console.log('update routers '+(new Date().toDateString())+' '+(new Date().toTimeString()));
				var cb=function() {console.log('child_process '+(new Date().toDateString())+' '+(new Date().toTimeString()))};
				child_process.exec('/usr/local/bin/node '+pathd+'build-relays_and_dirs7.js',{timeout: 20*60*1000},cb);
				if (ordbname) {
					child_process.exec('/usr/local/bin/node '+pathd+'publish2.js '+ordbname+' '+ordbip+':'+ordbport+' '+ordbversion,{timeout: 20*60*1000},cb);
				};
			};

			//build();

			//child_process.exec('/usr/local/bin/node '+pathd+'publish2.js '+ordbname+' '+ordbip+':'+ordbport+' '+ordbversion,{timeout: 20*60*1000},function() {});
			if ((!peersm_client)&&(!peersm_ws)) {
				setInterval(build,3600000*1);
			};

		};

		if (!peersm_client) {
			var xhr=function(url) {
				var xhr_object = new XMLHttpRequest();
				if (xhr_object)	{
					xhr_object.open("GET", url, false);
					xhr_object.send(null);
					return xhr_object.responseText;
				};
			};
		} else {
			var pac,start,update_proxy;
			var xhr=function(cb) {
				var options = {
					host: 'peersm.com',
					port: 80,
					path: '/peersmclient/proxy.pac',
					method: 'GET'
				};
				pac='';
				var length=0;
				var req=http.request(options, function(res) {
					res.on('data', function(d) {
						pac +=d.toString('utf8');
					});
					res.on('end',function() {
						cb();
					});
					res.on('error',function() {
						console.log('Error proxy.pac');
					});
				});
				req.end();
			};
		};

		var tls = require('tls'),
			net = require('net'),
			http = require('http'),
			URLn = require('url'),
			dns = require('dns'),
			os=require('os'),
			//HTTPParser = process.binding('http_parser').HTTPParser,
			crypto = require('crypto'),
			bittorrent = peersm_client?require(pathd+'node_modules/torrent-stream'):{},
			dht = peersm_client?require(pathd+'node_modules/torrent-stream/node_modules/bittorrent-dht/index.js'):{},
			blocklist = peersm_client?require(pathd+'node_modules/torrent-stream/lib/blocklist.js'):{},
			blocked,
			Arrayblocklist=[],
			myip='0.0.0.0',
			RETRY=1*1000,
			//modif Ayms
			//bncode.js https://github.com/a2800276/bncode/issues/16
			//torrent-stream index.js table.findpeers from 10000 to 100
			//torrent-stream index.js onpiececomplete remove have for (var i...)
			//torrent-stream index.js comment: fs.writeFile(torrentPath and add ontorrent(parseTorrent(buf))
			//torrent-stream index.js comment loop(0) and add onready()
			//torrent-stream storage.js add return after mem[index]=buffer and mem=[] in close
			//torrent-stream storage.js add else in read function
			Guards = (peersm_client||peersm_ws)?{}:require(pathd+'guards.js').Guards,
			Relays = (peersm_client||peersm_ws)?{}:require(pathd+'relays.js').Relays,
			Dirs = (peersm_client||peersm_ws)?{}:require(pathd+'dirs.js').Dirs,
			Exit = (peersm_client||peersm_ws)?{}:require(pathd+'exit.js').Exit,
			no_node_crypto=false, //forge createCipheriv and createHash
			no_node_rsa_hash=true,
			Rsa = no_node_rsa_hash?null:require(pathd+'crypto.js').Rsa,
			PEM = no_node_rsa_hash?null:require(pathd+'crypto.js').PEM,
			Hash = no_node_rsa_hash?null:require(pathd+'crypto.js').Hash, //See https://github.com/joyent/node/issues/3719
			PAYLOAD_STREAM=498,
			PAYLOAD_STREAM_WS=0xFFFF,
			OP_sock={},
			OP_req=[],
			OP_fake_request={},
			OP_crypto={}, //temporary - used to retrieve oaep/DH
			OR_sock={},
			OR_sock_in={},
			OR_fake={},
			OR_fake_request={},
			OR_files={},
			OR_facilitators=[],
			OR_tid={},
			OR_csid_b={},
			OR_csid_f={},
			OR_crypto={}, //temporary - used to retrieve oaep/DH
			OR_port=ordbport||(prod?(protect?8051:prod.OR_port):(prod2?8201:8001)),
			OR_wsport=ordbwsport||(prod?8052:8002),
			OR_IP=ordbip||(prod?prod.ip:''),
			OR_fing='2679B51C906158F3DF4C59AFD73E2B1FDA6535E1',
			port=443,
			OP_port=prod?(protect?8101:prod.OP_port):(prod2?8200:8000),
			exp='010001',
			IV=new Buffer('00000000000000000000000000000000','hex'),
			midc=32767,
			WS_port=ordbwsport||(prod?8052:8002),
			NB_HOP_MAX=5,
			NB_TRY_MAX=5,
			NB_HOP=prod?prod.NB_HOP:3,
			NB_DB_HOP=short_path?2:3,
			ONE_C=true,
			RSA_PUB_PFX='-----BEGIN RSA PUBLIC KEY-----',
			RSA_PUB_SFX='-----END RSA PUBLIC KEY-----',
			NB_MAX_CELLS=20,
			TC_CREATE=5000,
			TC_VERSION=1000,
			TC_EXTEND=7000,
			BEGIN_RETRY=5000,
			BEST_CIRCS=3,
			I_ID=0,
			NB_C=0,
			NB_C_MAX=30,
			NB_C_MAX2=prod?prod.NB:12,
			RESOLVED_FAILED=10,
			NB=prod?prod.NB:(bot?20:5),
			WAIT=false,
			RB_TRY=5,//not used
			NB_TRY=5,
			NB_QUERY=2,
			SOCK_RETRY=5000,
			SOCK_TO=120000,
			CIRC_KA=prod?prod.renew:(2*3600*1000),
			//CIRC_KA=60*1000,
			CIRC_DB_TIME=3600*1000,
			UPDATE_PROXY=3600*1000,
			//CIRC_DB_TIME=90000,
			CIRC_FAKE=20,
			ISOCKSin=[],
			ISOCKSout=[],
			CIRCUIT_WINDOW=1000,
			LCIRCUIT_WINDOW=100,
			STREAM_WINDOW=500,
			LSTREAM_WINDOW=50,
			FLOWC=0.8,
			SENDME_TOUT=15000,
			CHUNK_L=8092*2,
			oBuffer=Buffer,
			CRLF='0d0a',
			FAKE_SFX='.com',
			ASS_TIMER=4000,
			TIME_AVERAGE=5000,
			T_A=2000,
			OP_domains={},
			real_domain='http://www.lepoint.fr',
			//real_domain='http://www.ianonym.com',
			real_domain_a='',
			real_domain_s='',
			default_protocol='http:',
			fake_domain='www.f4116a30c08bbdfd01813b96c909.com', //www.(www.lepoint.fr).com
			//fake_domain='www.f4116a30c58fa3fd06962296cc1416.com',
			fake_cid=null,
			db_cid=null,
			DB_INFO_TIMER=1000*3600*10,
			DB_QUERY_RETRY=10000,
			DB_CIRC_TEST=5*60*1000,
			DB_CIRC_POLL=15*60*1000,
			DB_NB_TRY=5,
			DIR_AUTH=['194.109.206.212','82.94.251.203','212.112.245.170','86.59.21.38','193.23.244.244','171.25.193.9','128.31.0.34','76.73.17.194','208.83.223.34'],
			//options
			anonym=prod?false:true,//OP option for anonym
			anonym_OR=prod?false:true,//OR option for anonym
			download=prod?false:true,//OP option for download
			//TLS_OP=prod?'':'s',//TLS option for browser url
			//TLS_OR=prod?false:true,//TLS option for OP to OR side
			TLS_OP='',//tests
			TLS_OR=false,//tests
			buffer_size=1024,//TLS buffer size
			//forge_buffers=OR?(no_node_crypto?true:false):true,
			forge_buffers=true,
			_node=false,
			caStore={},
			//window=window||anonym, //true : replaces Buffer with ArrayBuffer
			//navigator=navigator||{},
			location=(typeof document!=="undefined")?document.location.href:'',
			chrome,
			window_OR=false,//true : replaces Buffer with ArrayBuffer
			window_OR_forge=true,
			WS_OP=false,//true : OP communicates with OR with direct websockets, OR implements WS server
			WS_OP_SOCKS=prod?false:true,//true : OP communicates with OR with websockets via SOCKS proxy, OR implements TCP/SOCKS server
			WS_SOCKS_PFX='socks:',
			botid=0,
			VAXIS_D=100000,
			PROGTXT='Retrieving... ',
			CHART1=true,
			CHART2=true,
			ENC_EXT='enc',
			privkey=(peersm_client||peersm_ws)?null:fs.readFileSync(pathd+ordbname+'/priv-key.pem'),
			one_OR,
			fake_cid_OR,
			WS_TLS=false,
			FILE_BLOCK=65536,
			DB_BLOCK=2097152,
			monitor,
			count_parse=0,
			count_parse_data=0,
			time_parse_data=0,
			PERF_PARSE=100,
			count_clear=0,
			count_clear_data=0,
			time_clear_data=0,
			PERF_CLEAR=100,
			count_wsdec=0,
			count_wsdec_data=0,
			time_wsdec_data=0,
			PERF_WSDEC=100,
			count_w=0,
			count_w_data=0,
			time_w_data=0,
			PERF_WRITE_=100,
			count_crypto=0,
			count_crypto_data=0,
			time_crypto_data=0,
			PERF_CRYPTO=100,
			count_mp4box=0,
			count_mp4box_data=0,
			time_mp4box_data=0,
			PERF_MP4BOX=1,
			DOWN_LIMIT=1000000,
			TORRENT_DOWN_LIMIT=2000000,
			VIDEO_APPEND=2*500,
			MP4BOX_APPEND=125,
			MP4BOX_TO=30*1000,
			MP4BOX_SAMPLE=500*1000,
			FFMPEG_BIN_PATH='/usr/bin/avconv',
			TEST_HASH='0000000000000000000000000000000000000000',
			TORRENT_TIMER=5*60*1000,
			AB_PREFIX=32,
			PREF_MIN=20,
			PREF_MAX=24,
			CLOSEST_MIN=5,
			PEER_RATE=0.1,
			PEER_MIN=20,
			FIND_MOOV=500*1024,
			MOOV='6d6f6f76',
			BL_URL='sbl-xbl.spamhaus.org',
			SPIES_FILE='/spies.txt',
			modp2='FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF';
			//one_OR={ip:OR_IP,port:OR_port,wsport:WS_port,fing:OR_fing,o_modulus:((new PEM()).modulus(fs.readFileSync('pub_or.pem')))}; //default guard OR

			var ext_to_type={
				'323' : 'text/h323',
				'3gp' : 'video/3gpp',
				'a' : 'application/octet-stream',
				'acx' : 'application/internet-property-stream',
				'ai' : 'application/postscript',
				'aif' : 'audio/x-aiff',
				'aifc' : 'audio/x-aiff',
				'aiff' : 'audio/x-aiff',
				'asc' : 'application/pgp-signature',
				'asf' : 'video/x-ms-asf',
				'asr' : 'video/x-ms-asf',
				'asm' : 'text/x-asm',
				'asx' : 'video/x-ms-asf',
				'atom' : 'application/atom+xml',
				'au' : 'audio/basic',
				'avi' : 'video/x-msvideo',
				'axs' : 'application/olescript',
				'bas' : 'text/plain',
				'bat' : 'application/x-msdownload',
				'bcpio' : 'application/x-bcpio',
				'bin' : 'application/octet-stream',
				'bmp' : 'image/bmp',
				'bz2' : 'application/x-bzip2',
				'c' : 'text/x-c',
				'cab' : 'application/vnd.ms-cab-compressed',
				'cat' : 'application/vnd.ms-pkiseccat',
				'cc' : 'text/x-c',
				'cdf' : 'application/x-netcdf',
				'cer' : 'application/x-x509-ca-cert',
				'cgm' : 'image/cgm',
				'chm' : 'application/vnd.ms-htmlhelp',
				'class' : 'application/octet-stream',
				'clp' : 'application/x-msclip',
				'cmx' : 'image/x-cmx',
				'cod' : 'image/cis-cod',
				'com' : 'application/x-msdownload',
				'conf' : 'text/plain',
				'cpio' : 'application/x-cpio',
				'cpp' : 'text/x-c',
				'cpt' : 'application/mac-compactpro',
				'crd' : 'application/x-mscardfile',
				'crl' : 'application/pkix-crl',
				'crt' : 'application/x-x509-ca-cert',
				'csh' : 'application/x-csh',
				'css' : 'text/css',
				'csv' : 'text/csv',
				'cxx' : 'text/x-c',
				'dcr' : 'application/x-director',
				'deb' : 'application/x-debian-package',
				'der' : 'application/x-x509-ca-cert',
				'diff' : 'text/x-diff',
				'dir' : 'application/x-director',
				'djv' : 'image/vnd.djvu',
				'djvu' : 'image/vnd.djvu',
				'dll' : 'application/x-msdownload',
				'dmg' : 'application/octet-stream',
				'dms' : 'application/octet-stream',
				'doc' : 'application/msword',
				'dot' : 'application/msword',
				'dtd' : 'application/xml-dtd',
				'dv' : 'video/x-dv',
				'dvi' : 'application/x-dvi',
				'dxr' : 'application/x-director',
				'ear' : 'application/java-archive',
				'eml' : 'message/rfc822',
				'eps' : 'application/postscript',
				'etx' : 'text/x-setext',
				'evy' : 'application/envoy',
				'exe' : 'application/x-msdownload',
				'ez' : 'application/andrew-inset',
				'f' : 'text/x-fortran',
				'f77' : 'text/x-fortran',
				'f90' : 'text/x-fortran',
				'fif' : 'application/fractals',
				'flr' : 'x-world/x-vrml',
				'flv' : 'video/x-flv',
				'for' : 'text/x-fortran',
				'gem' : 'application/octet-stream',
				'gemspec' : 'text/x-script.ruby',
				'gif' : 'image/gif',
				'gram' : 'application/srgs',
				'grxml' : 'application/srgs+xml',
				'gtar' : 'application/x-gtar',
				'gz' : 'application/x-gzip',
				'h' : 'text/x-c',
				'hdf' : 'application/x-hdf',
				'hh' : 'text/x-c',
				'hlp' : 'application/winhlp',
				'hqx' : 'application/mac-binhex40',
				'hta' : 'application/hta',
				'htc' : 'text/x-component',
				'htm' : 'text/html',
				'html' : 'text/html',
				'htt' : 'text/webviewhtml',
				'ice' : 'x-conference/x-cooltalk',
				'ico' : 'image/vnd.microsoft.icon',
				'ics' : 'text/calendar',
				'ief' : 'image/ief',
				'ifb' : 'text/calendar',
				'iges' : 'model/iges',
				'igs' : 'model/iges',
				'iii' : 'application/x-iphone',
				'ins' : 'application/x-internet-signup',
				'isp' : 'application/x-internet-signup',
				'iso' : 'application/octet-stream',
				'jar' : 'application/java-archive',
				'java' : 'text/x-java-source',
				'jfif' : 'image/pipeg',
				'jnlp' : 'application/x-java-jnlp-file',
				'jp2' : 'image/jp2',
				'jpe' : 'image/jpeg',
				'jpeg' : 'image/jpeg',
				'jpg' : 'image/jpeg',
				'js' : 'application/javascript',
				'json' : 'application/json',
				'kar' : 'audio/midi',
				'latex' : 'application/x-latex',
				'lha' : 'application/octet-stream',
				'lsf' : 'video/x-la-asf',
				'lsx' : 'video/x-la-asf',
				'lzh' : 'application/octet-stream',
				'log' : 'text/plain',
				'm13' : 'application/x-msmediaview',
				'm14' : 'application/x-msmediaview',
				'm3u' : 'audio/x-mpegurl',
				'm4a' : 'audio/mp4a-latm',
				'm4b' : 'audio/mp4a-latm',
				'm4p' : 'audio/mp4a-latm',
				'm4u' : 'video/vnd.mpegurl',
				'm4v' : 'video/mp4',
				'mac' : 'image/x-macpaint',
				'man' : 'text/troff',
				'mathml' : 'application/mathml+xml',
				'mbox' : 'application/mbox',
				'mdb' : 'application/x-msaccess',
				'mdoc' : 'text/troff',
				'me' : 'text/troff',
				'mesh' : 'model/mesh',
				'mkv' : 'video/x-matroska',
				'mht' : 'message/rfc822',
				'mhtml' : 'message/rfc822',
				'mid' : 'audio/midi',
				'midi' : 'audio/midi',
				'mif' : 'application/vnd.mif',
				'mime' : 'message/rfc822',
				'mml' : 'application/mathml+xml',
				'mng' : 'video/x-mng',
				'mny' : 'application/x-msmoney',
				'mov' : 'video/quicktime',
				'movie' : 'video/x-sgi-movie',
				'mp2' : 'video/mpeg',
				'mp3' : 'audio/mpeg',
				'mp4' : 'video/mp4',
				'mp4v' : 'video/mp4',
				'mpa' : 'video/mpeg',
				'mpe' : 'video/mpeg',
				'mpeg' : 'video/mpeg',
				'mpg' : 'video/mpeg',
				'mpga' : 'audio/mpeg',
				'mpp' : 'application/vnd.ms-project',
				'mpv2' : 'video/mpeg',
				'ms' : 'text/troff',
				'msh' : 'model/mesh',
				'msi' : 'application/x-msdownload',
				'mvb' : 'application/x-msmediaview',
				'mxu' : 'video/vnd.mpegurl',
				'nc' : 'application/x-netcdf',
				'nws' : 'message/rfc822',
				'oda' : 'application/oda',
				'odp' : 'application/vnd.oasis.opendocument.presentation',
				'ods' : 'application/vnd.oasis.opendocument.spreadsheet',
				'odt' : 'application/vnd.oasis.opendocument.text',
				'ogg' : 'application/ogg',
				'p' : 'text/x-pascal',
				'p10' : 'application/pkcs10',
				'p12' : 'application/x-pkcs12',
				'p7b' : 'application/x-pkcs7-certificates',
				'p7c' : 'application/x-pkcs7-mime',
				'p7m' : 'application/x-pkcs7-mime',
				'p7r' : 'application/x-pkcs7-certreqresp',
				'p7s' : 'application/x-pkcs7-signature',
				'pac' : 'text/plain',
				'pas' : 'text/x-pascal',
				'pbm' : 'image/x-portable-bitmap',
				'pct' : 'image/pict',
				'pdb' : 'chemical/x-pdb',
				'pdf' : 'application/pdf',
				'pem' : 'application/x-x509-ca-cert',
				'pfx' : 'application/x-pkcs12',
				'pgm' : 'image/x-portable-graymap',
				'pgn' : 'application/x-chess-pgn',
				'pgp' : 'application/pgp-encrypted',
				'pic' : 'image/pict',
				'pict' : 'image/pict',
				'pkg' : 'application/octet-stream',
				'pko' : 'application/ynd.ms-pkipko',
				'pl' : 'text/x-script.perl',
				'pm' : 'text/x-script.perl-module',
				'pma' : 'application/x-perfmon',
				'pmc' : 'application/x-perfmon',
				'pml' : 'application/x-perfmon',
				'pmr' : 'application/x-perfmon',
				'pmw' : 'application/x-perfmon',
				'png' : 'image/png',
				'pnm' : 'image/x-portable-anymap',
				'pnt' : 'image/x-macpaint',
				'pntg' : 'image/x-macpaint',
				'pot' : 'application/vnd.ms-powerpoint',
				'ppm' : 'image/x-portable-pixmap',
				'pps' : 'application/vnd.ms-powerpoint',
				'ppt' : 'application/vnd.ms-powerpoint',
				'prf' : 'application/pics-rules',
				'ps' : 'application/postscript',
				'psd' : 'image/vnd.adobe.photoshop',
				'pub' : 'application/x-mspublisher',
				'py' : 'text/x-script.python',
				'qt' : 'video/quicktime',
				'qti' : 'image/x-quicktime',
				'qtif' : 'image/x-quicktime',
				'ra' : 'audio/x-pn-realaudio',
				'rake' : 'text/x-script.ruby',
				'ram' : 'audio/x-pn-realaudio',
				'rar' : 'application/x-rar-compressed',
				'ras' : 'image/x-cmu-raster',
				'rb' : 'text/x-script.ruby',
				'rdf' : 'application/rdf+xml',
				'rgb' : 'image/x-rgb',
				'rm' : 'application/vnd.rn-realmedia',
				'rmi' : 'audio/mid',
				'roff' : 'text/troff',
				'rpm' : 'application/x-redhat-package-manager',
				'rss' : 'application/rss+xml',
				'rtf' : 'application/rtf',
				'rtx' : 'text/richtext',
				'ru' : 'text/x-script.ruby',
				's' : 'text/x-asm',
				'scd' : 'application/x-msschedule',
				'sct' : 'text/scriptlet',
				'setpay' : 'application/set-payment-initiation',
				'setreg' : 'application/set-registration-initiation',
				'sgm' : 'text/sgml',
				'sgml' : 'text/sgml',
				'sh' : 'application/x-sh',
				'shar' : 'application/x-shar',
				'sig' : 'application/pgp-signature',
				'silo' : 'model/mesh',
				'sit' : 'application/x-stuffit',
				'skd' : 'application/x-koan',
				'skm' : 'application/x-koan',
				'skp' : 'application/x-koan',
				'skt' : 'application/x-koan',
				'smi' : 'application/smil',
				'smil' : 'application/smil',
				'snd' : 'audio/basic',
				'so' : 'application/octet-stream',
				'spc' : 'application/x-pkcs7-certificates',
				'spl' : 'application/x-futuresplash',
				'src' : 'application/x-wais-source',
				'sst' : 'application/vnd.ms-pkicertstore',
				'stl' : 'application/vnd.ms-pkistl',
				'stm' : 'text/html',
				'sv4cpio' : 'application/x-sv4cpio',
				'sv4crc' : 'application/x-sv4crc',
				'svg' : 'image/svg+xml',
				'svgz' : 'image/svg+xml',
				'swf' : 'application/x-shockwave-flash',
				't' : 'text/troff',
				'tar' : 'application/x-tar',
				'tbz' : 'application/x-bzip-compressed-tar',
				'tcl' : 'application/x-tcl',
				'tex' : 'application/x-tex',
				'texi' : 'application/x-texinfo',
				'texinfo' : 'application/x-texinfo',
				'text' : 'text/plain',
				'tgz' : 'application/x-compressed',
				'tif' : 'image/tiff',
				'tiff' : 'image/tiff',
				'torrent' : 'application/x-bittorrent',
				'tr' : 'text/troff',
				'trm' : 'application/x-msterminal',
				'tsv' : 'text/tab-seperated-values',
				'txt' : 'text/plain',
				'uls' : 'text/iuls',
				'ustar' : 'application/x-ustar',
				'vcd' : 'application/x-cdlink',
				'vcf' : 'text/x-vcard',
				'vcs' : 'text/x-vcalendar',
				'vrml' : 'model/vrml',
				'vxml' : 'application/voicexml+xml',
				'war' : 'application/java-archive',
				'wav' : 'audio/x-wav',
				'wbmp' : 'image/vnd.wap.wbmp',
				'wbxml' : 'application/vnd.wap.wbxml',
				'wcm' : 'application/vnd.ms-works',
				'wdb' : 'application/vnd.ms-works',
				'webm' : 'video/webm',
				'wks' : 'application/vnd.ms-works',
				'wma' : 'audio/x-ms-wma',
				'wmf' : 'application/x-msmetafile',
				'wml' : 'text/vnd.wap.wml',
				'wmls' : 'text/vnd.wap.wmlscript',
				'wmlsc' : 'application/vnd.wap.wmlscriptc',
				'wmv' : 'video/x-ms-wmv',
				'wmx' : 'video/x-ms-wmx',
				'wps' : 'application/vnd.ms-works',
				'wri' : 'application/x-mswrite',
				'wrl' : 'model/vrml',
				'wrz' : 'x-world/x-vrml',
				'wsdl' : 'application/wsdl+xml',
				'xaf' : 'x-world/x-vrml',
				'xbm' : 'image/x-xbitmap',
				'xht' : 'application/xhtml+xml',
				'xhtml' : 'application/xhtml+xml',
				'xla' : 'application/vnd.ms-excel',
				'xlc' : 'application/vnd.ms-excel',
				'xlm' : 'application/vnd.ms-excel',
				'xls' : 'application/vnd.ms-excel',
				'xlt' : 'application/vnd.ms-excel',
				'xml' : 'application/xml',
				'xof' : 'x-world/x-vrml',
				'xpm' : 'image/x-xpixmap',
				'xsl' : 'application/xml',
				'xslt' : 'application/xslt+xml',
				'xul' : 'application/vnd.mozilla.xul+xml',
				'xwd' : 'image/x-xwindowdump',
				'xyz' : 'chemical/x-xyz',
				'yaml' : 'text/yaml',
				'yml' : 'text/yaml',
				'z' : 'application/x-compress',
				'zip' : 'application/zip'
			};

		if ((window_OR_forge)&&(!window_browser)) {
			var navigator={};
		};

		if ((window_browser)&&(!window_browser_server)) {
			var pac=xhr('proxy.pac');
			if (!download) {
				pac='var FindProxyForUrl='+pac;
				eval(pac);
				one_OR=FindProxyForUrl('http://'+fake_domain,fake_domain,true);
			};
		} else {
			if (!prod) {
				one_OR={ip:OR_IP,port:OR_port,wsport:WS_port,fing:OR_fing,o_modulus:'8bd0a81286858b11f2856ac2f3efd8a5e538b2f851a4c02cc717c94ea52418c587cc69515e46b5381d17cd2299cb4c59fae38250898eab31c9b8acfcf02110a96f0f03442ecd34f1befb0604e1ffbb981848534da9f3f23795ed7df047730cb9dab711722ad6504b893cebda2de4b41123d667474a166eba3ead7df8267dda15'}; //change in proxy.pac too
			};
		};

		var test_OR={ip:'86.193.111.43',port:443,fing:'9DB969BF454EEE790AAF8DCE367C3FB0ECF9743B',o_modulus:'d5af17aa81cbab95454174c3967fa46b03375087b8459330e8e2264f10d0ae8d267960f850c1afd956981600a230405510cab2ce64b75f7a1c3144fa90b556865e91e7ccf81cec265acf9850c452904ab485f5e32c2f4ffc83b4f2174138ffb57968cfb45054d2ad87c37151e28a2aaba53253614ed533df0888923ea84f1acf'};

		if (download) {
			//Ikoula 100Mbps duplex
			//user average: 500Kbps
			//100000 users - 50 Gbps
			//300 E/Y/200 users
			//150000 E/Y/100 Kusers - 500 servers
			anonym=false;
			anonym_OR=false;
			var DB_OR=one_OR;
			WS_OP=true;
			WS_OP_SOCKS=false;
			if (peersm_client) {
				var BANDWIDTH=client_band?(client_band*1000000):100000;
				var BFACTOR=1;
				var BSIZE=498;
				var FLOWB=50;
				var file_extension={};
				var STREAM_WINDOW_PEER=1000;
				var BUFFERED_AMOUNT_MAX=2000000;
				var DEF_BLOCKS=5;
				var db_id=0;
				var peersmDB={};
				OR=false;//monitor circuits
				var options = {
					host: 'www.monip.org',
					path: '/',
					port: 80,
					method: 'GET'
				};
				var destroy_torrent=function(request) {
					if (request._torrent_) {
						console.log('destroy torrent stream');
						request._torrent_stream.destroy();
						delete request._torrent_;
					};
					if (request.__ini_dht__) {
						console.log('destroying dht');
						request.__ini_dht__.destroy();
					};
					if (request.__torrent__) {
						console.log('destroying engine');
						request.__torrent__.destroy();
					};
				};
				var start_DHT=function() {
					var spies;
					try {
						spies=fs.readFileSync(SPIES_FILE).toString('utf8');
					} catch(ee) {
						console.log('could not read spies');
					};
					if (spies) {
						Arrayblocklist=JSON.parse(spies);
						blocked=blocklist(Arrayblocklist);
						console.log('number of spies in blocklist '+Arrayblocklist.length);
					};
				};
				var req=http.request(options,function(res) {
					var data_='';
					res.on('data', function(d) {
						data_ +=d.toString('utf8');
					});
					res.on('end',function() {
						if (data_) {
							try {
								var res=data_.split("<BR>");
								if (res.length) {
									res=res[1];
									res=res.split("<br>");
									if (res.length) {
										res=res[0].split(':');
										if (res.length>1) {
											myip=res[1].trim();
											console.log('myip '+myip);
										};
									};
								};
							} catch(ee) {};
						};
						start_DHT();
					});
					res.on('error',function() {
						start_DHT();
					});
				});
				req.on('error', function(e) {
					start_DHT();
				});
				req.end();
				peersmDB.list=function(cb,boo) {
					fs.readdir(pathd,function(err,files) {
						if (!err) {
							files.forEach(function(file,m) {
								if ((file.indexOf('debug.txt')===-1)&&(file.indexOf('debug-prod.txt')===-1)) {
									var h=file.split('#');
									var name,hash_name;
									if (h.length>1) {
										var tmp=h[1].split('.');
										name=h[0]+(tmp.length>1?('.'+tmp[1]):'');
										hash_name=h[1].split('.')[0];
										//console.log(name+' '+hash_name);
									};
									if (name) {
										if (!boo) {
											fs.stat(pathd+file,function(err,stats) {
												if (!err) {
													var val={file_name:file,name:name,name_hash:hash_name,hash:'0000000000000000000000000000000000000000',file_length:stats.size,current_length:stats.size,type:'application/octet-binary'};
													cb(val);
												};
											});
										};
									} else {
										//console.log(client.localAddress.toString()+client.localPort.toString());
										var H=crypto.createhash('sha1');
										H.update(new Buffer((Date.now()).toString()+client.localAddress.toString()+client.localPort.toString(),'utf8'));
										H=H.digest('hex');
										var tmp=file.split('.');
										if (tmp.length) {
											var ext=tmp.pop();
											if (tmp.length) {
												var name=tmp.join('.');
												var file2=name+'#'+H+'.'+ext;
												fs.rename(pathd+file,pathd+file2,function() {
													fs.stat(pathd+file2,function(err,stats) {
														if (!err) {
															var val={file_name:file2,name:name,name_hash:H,hash:'0000000000000000000000000000000000000000',file_length:stats.size,current_length:stats.size,type:'application/octet-binary'};
															cb(val);
														};
													})
												});
											};
										};
									};
								};
							});
						};
					});
				};
				var parse_db=function() {
					if (db_cid) {
						db_cid.send_db_info(true);
					};
				};
				var WebSocket_node=function(ws) {
					ws=ws.split('ws://')[1].split(':');
					var port=ws[1];
					var ip=ws[0];
					console.log('peersm client '+ip+' '+port);
					var pclient=new net.Socket();
					//pclient.bufferedAmount=pclient.bufferSize;
					pclient.on('connect',function() {
						pclient.key_=crypto.randomBytes(16).toString('base64');
						var hs='GET / HTTP/1.1\r\n';
						hs +='Host: '+pclient.remoteAddress+':'+pclient.remotePort+'\r\n';
						hs +='User-Agent: Mozilla/5.0 (Windows NT 6.0; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0\r\n';
						hs +='Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n';
						hs +='Accept-Language: en-us,en;q=0.5\r\n';
						hs +='Accept-Encoding: gzip, deflate\r\n';
						hs +='Connection: keep-alive, Upgrade\r\n';
						hs +='Sec-WebSocket-Version: 13\r\n';
						hs +='Origin: http://peersm.com\r\n';
						hs +='Sec-WebSocket-Key: '+pclient.key_+'\r\n';
						hs +='Pragma: no-cache\r\n';
						hs +='Cache-Control: no-cache\r\n';
						hs +='Upgrade: websocket\r\n';
						hs +='\r\n';
						console.log(hs);
						pclient.write(hs);
					});
					pclient.on('data',function(data) {
						if (!pclient.connected__) {
							pclient.on('end',function() {console.log('websocket_node end');pclient.onclose()});
							pclient.on('error',function() {console.log('websocket_node error');pclient.onclose()});
							var res=simpleParser(data.toString('utf8'));
							var key=res['Sec-WebSocket-Accept']||res['Sec-Websocket-Accept']; //bug Tor
							if (key) {
								var H = crypto.createHash('sha1');
								H.update(pclient.key_+'258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
								var hash=H.digest('base64');
								if (key===hash) {
									console.log('Client says : Handshake successfull for '+client.remoteAddress+' '+client.remotePort);
									pclient.write=function(data) {
										//console.log('Client write before wsencode:'+data.toString('hex'));
										pclient._write(wsencode(data,0x2,true),null,function(){});
									};
									pclient.send=pclient.write;
									pclient.connected__=true;
									pclient.onopen();
								} else {
									pclient.end();
								};
							} else {
								pclient.end();
							};
						} else {
							//console.log('received '+data.toString('hex'));
							var tmp=wsdecode(pclient.stream_ws__?([pclient.stream_ws__,data].concatBuffers()):data);
							pclient.stream_ws__=tmp[1].length?tmp[1]:null;
							pclient.onmessage({data:tmp[0]});
						};
					});
					pclient.close=pclient.end;
					pclient.connect(port,ip);
					return pclient;
				};
				var FileReader_torrent=function() {
				};
				FileReader_torrent.prototype.readAsArrayBuffer=function(chunk) {
					this.onload({target:{result:chunk}});
				};
				var FileReader_node=function() {
				};
				FileReader_node.prototype.readAsArrayBuffer=function(chunk) {
					this.onload({target:{result:chunk}});
				};
				var Blob_torrent=function(size) {
					this.data=[];
					this.cursor=0;
					this.size=size;
				};
				Blob_torrent.prototype.push=function(chunk) {
					this.data.push(chunk);
					this.cursor +=chunk.length;
					//this.updating=false;
					//this.rest=new Buffer(0);
				};
				Blob_torrent.prototype.unshift=function(chunk) {
					this.data.unshift(chunk);
					this.cursor -=chunk.length;
				};
				Blob_torrent.prototype.slice=function(begin,end) {
					var s=end-begin;
					//console.log('s '+s+' cursor '+this.cursor+' data '+this.data.length);
					var c=0;
					var buffer,rest;
					var temp=[];
					if ((s>0)&&(this.data.length)) {
						s=Math.min(s,this.cursor);
						while (c<s) {
							var chunk=this.data.shift();
							var l=chunk.length;
							temp.push(chunk);
							c +=l;
							this.cursor -=l;
						};
						buffer=temp.concatBuffers();
						rest=buffer.slice(s);
						buffer=buffer.slice(0,s);
						this.cursor +=rest.length;
						//if (this.data.length) {
						//	this.data[0]=[rest,this.data[0]].concatBuffers();
						//} else {
						if (rest.length) {
							this.data.unshift(rest);
						};
						//};
						//console.log('after rest '+rest.length+' cursor '+this.cursor+' data '+this.data.length);
						return buffer;
					} else {
						//console.log('blob_torrent return 0');
						return new Buffer(0);
					};
				};
				/*
				Blob_torrent.prototype.slice=function(begin,end) {
					if (!this.updating) {
						this.updating=true;
						var s=end-begin;
						var r=0;
						console.log('s '+s+' cursor '+this.cursor+' rest '+this.rest.length);
						var c=0;
						var buffer;
						var temp=[];
						if (s>0) {
							s=Math.min(s,this.cursor);
							r=this.rest.length;
							if (r) {
								chunk=this.rest.slice(0,Math.min(r,s))
								temp.push(chunk);
								this.rest=this.rest.slice(Math.min(r,s));
								c=chunk.length;
								this.cursor -=c;
								buffer=chunk;
							};
							if (!r) {
								while (c<s) {
									chunk=this.data.shift();
									var l=chunk.length;
									temp.push(chunk);
									c +=l;
									this.cursor -=l;
								};
								buffer=temp.concatBuffers();
								this.rest=buffer.slice(s);
								buffer=buffer.slice(0,s);
								this.cursor +=this.rest.length;
								//if (this.data.length) {
								//	this.data[0]=[rest,this.data[0]].concatBuffers();
								//} else {
								//if (rest.length) {
									//console.log('rest '+rest.toString('hex'));
								//	this.data.unshift(rest);
								//};
							//};
							//console.log('after rest '+rest.length+' cursor '+this.cursor+' data '+this.data.length);
							};
							this.updating=false;
							return buffer;
						} else {
							//console.log('blob_torrent return 0');
							this.updating=false;
							return new Buffer(0);
						};
					} else {
						console.log('torrent updating');
						return new Buffer(0);
					};
				};*/
				var Blob_node=function(data) {
					this.fd=data.fd;
					this.size=data.size;
					this.type=data.type;
					this.cursor=0;
				};
				Blob_node.prototype.slice=function(begin,end) {
					if (!end) {
						this.size=this.size-begin;
						this.cursor=begin;
						return this;
					} else {
						if (end>begin) {
							var buffer=new Buffer(end-begin);
							//console.log('begin '+begin+' end '+end);
							fs.readSync(this.fd,buffer,0,end-begin,begin+this.cursor);
							return buffer;
						} else {
							return new Buffer(0);
						};
					};
				};

				var open_db=function() {
					var a={};
					a.get=function(hash) {
						var b={};
						fs.readdir(pathd,function(err,files) {
							if (!err) {
								var found=false;
								var l=files.length;
								var ext;
								for (var i=0;i<l;i++) {
									var file=files[i];
									if (file.indexOf('debug.txt')===-1) {
										var h=file.split('#');
										var name,hash_name;
										if (h.length>1) {
											var tmp=h[1].split('.');
											if (tmp.length>1) {
												ext=tmp[1];
												name=h[0]+(tmp.length>1?('.'+tmp[1]):'');
											};
											hash_name=h[1].split('.')[0];
										};
										//console.log(hash_name+' '+hash);
										if (hash_name===hash) {
											found=true;
											fs.stat(pathd+file,function(err,stats) {
												if ((!err)&&(ext)) {
													fs.open(pathd+file,'r',function(err,fd) {
														if (!err) {
															var type;
															if (ext) {
																type=ext_to_type[ext];
															};
															if (!type) {
																type='application/octet-binary';
															};
															console.log(name+' '+hash_name+' '+type);
															var data={fd:fd,size:stats.size,type:type};
															b.onsuccess({target:{result:{file_length:stats.size,current_length:stats.size,type:type,data:data}}});
														} else {
															b.onsuccess({target:{result:null}});
														};
													});
												} else {
													b.onsuccess({target:{result:null}});
												};
											});
											break;
										};
									};
								};
								if (!found) {
									b.onsuccess({target:{result:null}});
								};
							} else {
								b.onsuccess({target:{result:null}});
							};
						});
						return b;
					};
					return a;
				};
				start=function() {
					pac='var FindProxyForUrl='+pac;
					eval(pac);
					var routers=FindProxyForUrl('http://'+fake_domain,fake_domain,true);
					one_OR=routers[1][simple_random(routers[1].length)];
					if (!TEST_CONF) {
						DB_OR=routers[2];
					} else {
						DB_OR={ip:'37.59.47.27',port:8001,wsport:0,fing:'E0671CF9CB593F27CD389CD4DD819BF9448EA834',o_modulus:'ca2a670479816ca562f7afc2667db1811f0efa7d595aa27cf532092a052c697b102c03d8b8dddc276050fe9cad15efe72758d9d9b0f581f5cbfd0be92ecd721711797354006625e74e0f733efee3ee779116efe87da3b5f8c1729e2d0a5f2c4de4d5906b6e383c0a0d8dddbc076cf426423f6f2b6fd46fab9f54fb8e42601a2d',name:'Tor Bridge'};
					};
					WS_TLS=true;
					Relays=Guards;
					navigator.userAgent='node-Tor';
					unleash();
				};
				update_proxy=function() {
					var proxy=function() {
						console.log('update prox --------');
						pac='var FindProxyForUrl='+pac;
						eval(pac);
						FindProxyForUrl('http://'+fake_domain,fake_domain,true);
					};
					xhr(proxy);
				};
				xhr(start);
				setInterval(parse_db,10000);
				setInterval(update_proxy,UPDATE_PROXY);
			};
			if (window_browser) {
				var BANDWIDTH=0;
				var BFACTOR=1;
				var BSIZE=498;
				var FLOWB=50;
				var file_extension={};
				var STREAM_WINDOW_PEER=1000;
				var BUFFERED_AMOUNT_MAX=2000000;
				var DEF_BLOCKS=5;
				var $_=document.getElementById.bind(document);
				var db_id=0;
				var loc=document.location.href.split('/');
				var peersmcode=loc[loc.length-1].split('#')[0];
				var peersmDB;
				var getmouseY=function (e) {
					if (e.pageY) {
						return e.pageY;
					} else {
						return e.clientY;
					};
				};
				var getmouseX=function (e) {
					if (e.pageX) {
						return e.pageX;
					} else {
						return e.clientX;
					};
				};
				var addEvent=function (objet,typeEvent,nomFunction,typePropagation){
					objet.__event=objet.__event||{};
					if (objet.__event[typeEvent]) {
						delEvent(objet,typeEvent,objet.__event[typeEvent][0],objet.__event[typeEvent][1]);
					};
					objet.__event[typeEvent]=[nomFunction,typePropagation];
					if (objet.addEventListener) {
						objet.addEventListener(typeEvent,nomFunction,typePropagation);
					} else if (objet.attachEvent) {
						objet.attachEvent('on' + typeEvent, nomFunction);
					};
				};
				var delEvent=function (objet, typeEvent, nomFunction, typePropagation){
					if (objet.addEventListener) {
						objet.removeEventListener(typeEvent, nomFunction, typePropagation);
					} else if (objet.attachEvent) {
						objet.detachEvent('on' + typeEvent, nomFunction);
					}
				};
				var ext=[ENC_EXT,"exe","com","bin","php","php3","php4","php5","phtml","inc","sql","pl","cgi","py","sh","c","cc","cpp","cxx","h","hpp","java","class","jar","html","html","shtml","dhtml","xhtml","xml","js","css","zip","tar","tgz","gz","bz2","tbz","rar","mp3","wav","3ga","midi","mid","rm","ra","ram","pls","m3u","mkw","webm","avi","mp4","m4v","mpg","mpeg","mov","swf","fla","doc","docx","xls","xlsx","rtf","pdf","txt","ppt","pptx","vcard","vcf","obj","max","3ds","3dm","kml","torrent","gpx","dxf","dwg","wsg","vb","pif","gadget","apk","msi","sxc","123","ots","nb","gsheet","xlr","ods","svgz","cdr","svg","ps","eps","orf","pef","rwl","mrw","mef","fff","erf","dcr","bay","3fr","srf","rw2","nef","cr2","arw","dng","dwt","irs","ait","art","aip","aia","ai","indd","prtpset","ppj","plb","prproj","aetx","aet","aes","aepx","aep","aec","ncorx","ncor","em","abr","csh","psb","psd","as","asc","ascs","aif","aiff","flac","iff","m4a","wma","srt","flv","3g2","3gp","asf","wmv","pcast","xlt","xltm","xltx","ans","ascii","log","odt","wpd","accdb","db","dbf","mdb","pdb","asp","aspx","asx","fnt","otf","ttf","dotx","wps2","dll","fon","cmd","srt"];
				var ext_img=["tga","gif","jpg","tiff","jpeg","bmp","png"];
				ext.forEach(function(val) {file_extension[val]='http://www.peersm.com/img/extensions/'+val+'.png'});
				var test_bandwidth=function() {
					var xhr_object = new XMLHttpRequest();
					if (xhr_object)	{
						xhr_object.open("POST",'bandwidth.html',true);
						var a=Date.now();
						var size=100000;
						xhr_object.send(new Uint8Array(size));
						xhr_object.onreadystatechange=function() {
							if (xhr_object.readyState==4) {
								var b=100000/((Date.now()-a)/1000); //Bytes per second
								BANDWIDTH=parseInt((BANDWIDTH?((BANDWIDTH+b)/2):b)/BFACTOR);
								//console.log('bandwidth : '+parseInt(b*8/1000)+' Kbps');
								console.log('bandwidth : '+(BANDWIDTH*8/1000)+' Kbps');
							};
						};
					};
				};
				setInterval(test_bandwidth,5*3600*1000);
				test_bandwidth();
			};
		}

		//OP_domains[fake_domain]={real_domain:real_domain,real_domain_a:real_domain_a,real_domain_s:real_domain_s}; //first domain test

		//Relays=["A59E1E7C7EAEE083D756EE1FF6EC31CA3D8651D7-31.172.30.2-443-4096000-31.172.30.2:80","B8F81CF0C665EA8BE947F102CBFF97615F1FF300-31.172.30.4-443-4096000-31.172.30.4:80","4E377F91D326552AAE818D5A17BC3EF79639C2CD-37.130.227.133-443-4096000-37.130.227.133:80","F3D4C7479F8789758A77FF61D2D8929311568394-109.163.233.205-443-4096000-109.163.233.205:80","DA69A7DF7820BF85E5ABB4745C15600E7AD87550-209.236.66.136-443-4096000","9F7A37446BC034B4FDB27CAE2C6CAAB83A40A361-96.47.226.20-443-4096000-96.47.226.20:80","E7649007B37BC515C035BA63726E5346B5D6A58D-204.124.83.132-443-4096000-204.124.83.132:80","C1E2CF4BB774A030FF5408FF35CC637ACE24D439-96.47.226.21-443-4096000-96.47.226.21:80","80F870DD215A0C56005266A71C46F92F39F1973B-96.44.189.102-443-4096000-96.44.189.102:80","B6DE80F076A9A64DFCE3E61F7F35FA70EB621892-31.3.214.197-9001-4096000-31.3.214.197:9030","72BD78BC713A28CC5B279A36CD63015EECDF0E97-46.105.122.93-9001-4096000-46.105.122.93:9030","4186509C707E96B77B51A76F8294D7E22FF52C61-74.120.15.150-443-4096000-74.120.15.150:80","948CDA1CE63D2165567B81706CD8C0E9F8934A47-198.96.155.3-5001-4096000-198.96.155.3:8080"];

		//Guards=Relays;

		//Exit=Relays;

		//Dirs=["31.172.30.2:80","31.172.30.4:80","37.130.227.133:80","109.163.233.205:80","96.47.226.20:80","204.124.83.132:80","96.47.226.21:80","96.44.189.102:80","31.3.214.197:9030","46.105.122.93:9030","74.120.15.150:80","198.96.155.3:8080"];

		//Test Exit
		//Exit=["948CDA1CE63D2165567B81706CD8C0E9F8934A47-198.96.155.3-5001-4096000-31.172.30.2:80-D34C4238AB95E3046BF726BF351864EE6650A082A5B58491328A65F27A2C8B90144DF6ED1D937CFDFFB054FFBA2871409F21E33CFECAC74B20838B92A0ECF2DC4577D7E5551AE101513EC218B08F88239EAA2D9F57356D31CD62DA240B70E0517EEB5D82D5C23EBE6E7199A5E5C8A7897A7F92462D5C089B57608BC395D71AC9","EB5CCD5FA868637289F426C133DC924A64D5E769-88.198.14.171-443-4096000-88.198.14.171:80-BBE585DE441750BD764C498919872D0C8AB377C1FE79DAFA94C0D5F1BF5B5085BF5E8DE4A346D725E8107C7CB09BF22FC1B7BDBBDBDC45588AE33DDBF7C8BE0A04F54A04847EAFF25739B93159922EE0FC0E5CFC5BFE6F25EEE90EA87D939CEC91A8F97E5E99397FDF4954DEDFBECD2FED759203058D27E9A2FC6555D5BC3EF5","1C68D1FF746F4F9791D7F7F3B89BA494FB81BCBD-87.236.194.191-443-4096000-87.236.194.191:80-A65E9C3F7014E002D8BC702A2EFA8838C811A645F821D4B2E1029FA24D359A8BE265615A1CF7C5525A1D0FA2164BD790D2E1093F96D476EA53A98FDF9916D78480DA379DCC84638D9FBCAD8607F85DFBFD8B8FAD90029FBF242CCDBF1449962435D5260E6EE2FB1E07862D222879B5BF2038DEF31698B6D7A70BA7D201E868AD"];

		Relays=Guards;//Test relays > 0.2.3 only (openssl > 1.0.1)

		var unlink=function() {
			fs.rename(pathd+((prod||prod3)?(OR_port+'-debug-prod.txt'):(prod2?(OR_port+'-debug-prod2.txt'):(OR_port+'-debug.txt'))),pathd+((prod||prod3)?(OR_port+'-debug-prod.txt'):(prod2?(OR_port+'-debug-prod2.txt'):(OR_port+'-debug.txt')))+'.old',function() {});
		};

		unlink();

		//setInterval(unlink,6*3600*1000);
		if ((!window_browser)&&(!peersm_client)&&(!peersm_ws)) {
			if (!prod) {
				console.log(ordbname+' '+ordbip+' '+ordbport+' '+ordbwsport+' '+ordbversion);
			} else {
				console.log(prod.ip+' OP port '+OP_port+' OR_port '+OR_port+' OP NB_C '+NB+' OP NB_HOP '+NB_HOP+' OP renew '+CIRC_KA);
			}
		};

		var magnet=function(uri) {
			var m=uri.split('magnet:?xt=urn:btih:');
			if (m.length>1) {
				return m[1];
			};
		};

		var prefix=function(node,info) {
			var n=0;
			var tmp=false;
			var i=0;
			while (!tmp) {
				tmp=(parseInt(node.substr(i,1),16))^(parseInt(info.substr(i,1),16));
				if (!tmp) {
					n+=4;
				};
				i++;
			};
			while (tmp<8) {
				tmp=tmp<<1;
				n++;
			};
			return n;
		};

		var unique=function() {
			var o={};
			var l=this.length;
			var r=[];
			this.forEach(function(val,i) {o[val]=i});
			Object.keys(o).forEach(function(i) {r.push(i)});
			o=null;
			return r;
		};

		var req_200=function() {
			var re='HTTP/1.1 200 OK\r\n';
			re +='Accept-Ranges:bytes';
			re +='Content-Encoding:gzip\r\n';

			re +='Content-Length:0\r\n';
			re +='Content-Type:*\r\n';
			//re +='Content-Type:application/x-javascript\r\n';
			re +='\r\n';
			return re;
		};

		var req_410=function() {
			var re='HTTP/1.1 410 Gone\r\n';
			re +='\r\n';
			return re;
		};

		var req_200_out=function() {
			var re='HTTP/1.1 200 OK\r\n';
			re +='Vary: Accept-Encoding\r\n';
			re +='Keep-Alive: timeout=2, max=100\r\n';
			re +='Connection: Keep-Alive\r\n';
			re +='Transfer-Encoding: chunked\r\n';
			re +='Content-Type: text/html\r\n';
			re +='\r\n1f\r\n//Outside of authorized domains\r\n0\r\n\r\n';
			return re;
		};

		var req_301=function(location) {
			var re='HTTP/1.1 301 Moved Permanently\r\n';
			//Server: GitHub.com
			//Date: Thu, 24 Jan 2013 10:37:53 GMT
			//Content-Type: text/html';
			//Content-Length: 178
			//re +='Connection: keep-alive\r\n';
			re +='Location: '+location+'\r\n';
			re +='\r\n';
			return re;
		};
		var req_503=function() {
			var re='HTTP/1.1 503 Service Unavailable\r\n';
			re +='Retry-After: '+2+'\r\n';
			re +='\r\n';
			return re;
		};
		var url_decode=function(url) {
			var URL={url:url};
			var res=url.split('/');
			var dec=function(res) {
				if (res.length) {
					var tmp=res[0];
						URL.host=tmp;
						res.shift();
					};
					URL.rest=res.join('/');
			};
			if (res.length>1) {
				if (res[1]==='') {
					URL.protocol=res[0]?res[0]:default_protocol;
					//if (!res[0]) {
						//console.log('check protocol');
						//console.log(protocol+' '+url);
					//};
					res.shift();
					res.shift();
				} else {
					URL.protocol=default_protocol;
				};
			} else {
				URL.protocol=default_protocol;
			};
			dec(res);
			return URL;
		};

		var url_encode=function(url) {
			var res=[];
			res.push(url.protocol?url.protocol:protocol);
			res.push('');
			if (url.host) {
				res.push(url.host);
			};
			if (url.rest) {
				res.push(url.rest);
			};
			return res.join('/');
		};

		var encrypt_decrypt=function(txt,boo) { //boo true : utf8 string - TODO change algorithm not to recognize urls
			var tmp=new Buffer(crypto_aes_encrypt(boo?new Buffer(txt,'utf8'):new Buffer(txt,'hex'),fake_key),'hex');
			return boo?tmp.toString('hex'):tmp.toString('utf8');
			//boo true www.lepoint.fr --> f4116a30c08bbdfd01813b96c909
			//boo false f4116a30c08bbdfd01813b96c909 --> www.lepoint.fr
		};

		var url_encrypt=function(url) {
			var URL=url_decode(url);
			//URL.host=URL.host?encrypt_decrypt(URL.host)+FAKE_SFX:null;
			URL.host=URL.host?fake_domain:null;
			//if (bool) {
				URL.rest=URL.rest?encrypt_decrypt(URL.rest):null;
			//};
			return url_encode(URL); /*TODO replace by https */
		};

		//document.body.innerHTML='<iframe src="http://rf.tniopel.www.com" width="100%" height="1000px" />'
		//http://www.viagri.fr/aymer.php
		//http://rf.irgaiv.www.com/aymer.php
		//document.body.innerHTML='<iframe src="http://rf.irgaiv.www.com/aymer.php" width="100%" height="1000px" />'

		var h_address=function(data) {
			var type=data.slice(0,1);
			var length=data.slice(1,2);
			var value=data.slice(2,2+length.readUInt());
			data=data.slice(length.readUInt()+2);
			return {data:data,addr:{type:type,length:length,value:value}};
		};

		var simple_random=function(l) {
			return parseInt(Math.random()*l);
		};

		var DateRound=function(date) {
			return new Date(date.getFullYear(),date.getMonth(),date.getDate(),date.getHours(),date.getMinutes(),date.getSeconds()+(date.getMilliseconds()>500?1:0),0);
		};

		var create_path=function(params) {
			var r=[];
			var p=[];
			var i,l;
			var list=[];
			var nb_hop=params.nb_hop||NB_HOP_MAX;
			if (nb_hop>NB_HOP_MAX) {
				nb_hop=NB_HOP_MAX;
			};
			params.nb_try=params.nb_try||1;
			if (params.nb_try>NB_TRY_MAX) {
				console.log('PATH : createPath : Too many attempts');
				return;
			};
			l=Guards.length;
			i=simple_random(l);
			i=Guards[i].split('-');
			list.push(i[1]);
			p.push({ip:i[1],fing:i[0],port:i[2],band:i[3],o_modulus:(i[5]?i[5]:i[4])});
			nb_hop--;
			l=Relays.length;
			nb_hop--;
			while (nb_hop) {
				i=simple_random(l);
				if ((r.indexOf(i)==-1)&&(Relays[i].split('-')[1]!=p[0].ip)) {
					r.push(i);
					nb_hop--;
				};
			};
			var f=function(val) {
				var o=Relays[val].split('-');
				var tmp={ip:o[1],fing:o[0],port:o[2],band:o[3],o_modulus:(o[5]?o[5]:o[4])};
				list.push(o[1]);
				p.push(tmp);
			};
			r.forEach(f);
			l=Exit.length;
			i=simple_random(l);
			while (list.indexOf((Exit[i].split('-'))[1])!=-1) {
				i=simple_random(l);
			};
			i=Exit[i].split('-');
			p.push({ip:i[1],fing:i[0],port:i[2],band:i[3],o_modulus:(i[5]?i[5]:i[4])});
			return p;
		};

		var clear_request=function(request) {
			if (request) {
				if ((request.queue_)&&(!request.eof_)) {
					request.eof_=true;
					console.log('clearing '+(request.hash_ini?request.hash_ini:''));
					request.queue_.push(fin_.bind(request));
					if (request.queue_.length===1) {
						request.queue_[0]();
					};
				};
				clearTimers(request.query_t0);
			};
		};

		var clear_requests=function(socket) {
			for (var n in socket) {
				if ((!isNaN(n))&&(n!=null)&&(typeof(n)!=='function')) {
					var cid=socket[n];
					if (cid) {
						if (cid===db_cid) {
							for (var m in cid) {
								if ((!isNaN(m))&&(m!=null)&&(typeof(m)!=='function')) {
									var request=cid[m];
									if (request) {
										clear_request(request);
									};
								};
							};
						};
						console.log('clear_requests circuit destroy');
						cid.circuit_destroy();
					};
				};
			};
		};

		var get_request=function(host,req) {
			var get='GET /'+req+' HTTP/1.1\r\n';
			get +='Host: '+host+'\r\n';
			get +='User-Agent: Mozilla/5.0 (Windows NT 6.0; rv:24.0) Gecko/20100101 Firefox/24.0\r\n';
			get +='Accept: */*\r\n';
			get +='Accept-Language: en\r\n';
			get +='Accept-Encoding: gzip, deflate\r\n';
			get +='Connection: keep-alive\r\n';
			get +='\r\n';
			return new Buffer(get,'utf8');
		};

		var get_resume=function(host,req,bytes) {
			var get='GET /'+req+' HTTP/1.1\r\n';
			get +='Host: '+host+'\r\n';
			get +='User-Agent: Mozilla/5.0 (Windows NT 6.0; rv:24.0) Gecko/20100101 Firefox/24.0\r\n';
			get +='Range: bytes='+bytes+'-\r\n';
			get +='Accept: */*\r\n';
			get +='Accept-Language: en\r\n';
			get +='Accept-Encoding: gzip, deflate\r\n';
			get +='Connection: keep-alive\r\n';
			get +='\r\n';
			return new Buffer(get,'utf8');
		};

		var choose_id=function(obj,boo,index) {
			var cid=0;
			obj.nbc_=obj.nbc_?obj.nbc_:0;
			if (!boo) {
				if (obj.nbc_===65534) {
					return false;
				};
			} else {
				if (obj.nbc_===midc) {
					return false;
				};
			};
			while ((obj[cid])||(cid===0)) {
				if (!boo) {
					cid=obj.cid?((++obj.cid)%65535):1;
				} else {
					if (!index) {
						cid=obj.cid?((++obj.cid)%midc):1; //0 to midc
					} else {
						cid=obj.cid?((++obj.cid)%midc):midc; //midc to 65534
					};
				};
			};
			obj.cid=cid;
			obj.nbc_++;
			return cid;
		};

		var crypto_expand_key=function(K0) {
			var r='';
			for (var i=0;i<5;i++) {
				var H = crypto.createhash('sha1');
				H.update(new Buffer(K0+'0'+i,'hex'));
				r +=H.digest('hex');
			};
			return new Buffer(r,'hex');
		};

		var crypto_aes_encrypt=function(m,K) {
			var C2 = crypto.createcipheriv('aes-128-ctr',K,IV);
			var K2=C2.update(m,'hex','hex');
			K2 +=C2.final('hex');
			return K2;
		};

		var crypto_onion=function(modulus,M) {
			var M1=M.slice(0,70);
			var M2=M.slice(70);
			var K=Rand(16);
			var K1=[K,M1].concatBuffers();
			var RSA=new Rsa();
			K1=RSA.encrypt(new Buffer(modulus.toString('hex'),'binary'),new Buffer(exp,'binary'),new Buffer(K1.toString('binary'),'binary'),'RSA_PKCS1_OAEP_PADDING','hex');
			var K2=crypto_aes_encrypt(M2,K);
			return [new Buffer(K1,'hex'),new Buffer(K2,'hex')].concatBuffers();
		};

		var crypto_donion=function(key,M,M2) {
			var RSA=new Rsa();
			var X=new Buffer(RSA.decrypt(key,M,'RSA_PKCS1_OAEP_PADDING','hex'),'hex');
			var K=X.slice(0,16);
			return [X.slice(16),new Buffer(crypto_aes_encrypt(M2,K),'hex')].concatBuffers();
		};

		var PemtoDer=function(pem) {
			console.log('pemtoder----------------');
			pem=pem.split('-----BEGIN CERTIFICATE-----')[1].split('-----END CERTIFICATE-----')[0].split('\n').join(''); //Warning replace with crlf if node.js buffers
			return window_OR?(BufferToArrayBuffer(new oBuffer(pem,'base64'))):(new Buffer(pem,'base64'));
		};

		var sign=function(s,k) {
			var data=new Buffer(s,'utf8');
			var H = crypto.createhash('sha1');
			H.update(data);
			H=H.digest('hex');
			var RSA=new Rsa();
			var R=RSA.encryptp(k,H,'RSA_PKCS1_PADDING','hex');
			return new Buffer(R,'hex');
		};

		var flush_=function(event) {
			if (!peersm_client) {
				if (event.source==window&&event.data==this.messageName) {
					event.stopPropagation();
					if (this.fc_t.length) {
						var fn=this.fc_t.shift();
						fn();
					};
				};
			} else {
				//console.log('flush_ '+this.fc_t.length);
				if (this.fc_t.length) {
					var fn=this.fc_t.shift();
					fn();
				};
			};
		};

		var send_data=function(cd,sid,request,nb_blocks) {
			if (request) {
				if (!request.stop_) {
					if (request._torrent_&&peersm_client&&(typeof nb_blocks==="undefined")) {
						var d=Date.now()-request.start_t0;
						nb_blocks=Math.ceil(((d*client_band*1000000/1000)/BSIZE)/(FLOWC));
						console.log('first blocks nb '+nb_blocks);
					} else {
						nb_blocks=(typeof nb_blocks==="undefined")?DEF_BLOCKS:nb_blocks;//modif zero timeout
					};
					var cells=[];
					request.reader.onload=(function(evt) {
						//console.log('reader');
						if (evt.target.result) {
							var res=(evt.target.result instanceof ArrayBuffer)?(new Uint8Array(evt.target.result)):evt.target.result;
							if (res.length) {
								for (var i=0;i<nb_blocks;i++) {
									if (request.stream_window_s!==0) {
										request.stream_window_s--;
										cells.push(res.slice(0,Math.min(res.length,PAYLOAD_STREAM)));
										if (res.length>PAYLOAD_STREAM) {
											res=res.slice(PAYLOAD_STREAM);
										} else {
											break;
										};
									} else {
										request.cursor -=res.length;
										if (request._torrent_) {
											request._torrent_.unshift(res);
										};
										break;
									};
								};
							};
						};
						//console.log('window '+request.stream_window_s);
						//console.log('window size '+request.stream_window_s+' '+this.circuit_window_s);
						var t0=Date.now();
						//request.fc_t.push(setTimeout(flush,FLOWB/2)); //to optimize//modif zero timeout
						request.fc_t.push((function() {flush.call(this,cd,cells,request,sid,t0)}).bind(this));//modif zero timeout
						if (!peersm_client) {
							window.postMessage('flush', "*");//modif zero timeout
						} else {
							setTimeout(flush_.bind(request),0);
						};
					}).bind(this);
					if (nb_blocks!==0) {
						//console.log('avant chunk '+cd.size+' '+request.cursor+' '+nb_blocks);
						var chunk=cd.slice(request.cursor,Math.min(cd.size,request.cursor+nb_blocks*PAYLOAD_STREAM));
						//console.log('chunk length '+chunk.length+' request cursor '+request.cursor+' request cursor next '+request.cursor+nb_blocks*PAYLOAD_STREAM+' tcursor '+cd.cursor);
						//console.log(chunk.toString('hex'));
						//request.cursor +=Math.min(cd.size-request.cursor,nb_blocks*PAYLOAD_STREAM);
						request.cursor +=(typeof chunk.length!=='undefined')?chunk.length:chunk.size;
						request.reader.readAsArrayBuffer(chunk);
					} else {
						request.reader.onload({target:{result:null}});
					};
				} else {
					//request.fc_t.forEach(function(val) {clearTimeout(val)});//modif zero timeout
					destroy_torrent(request);
					request=null;
					console.log('delete sid 2');
					delete(this[sid]);
				};
			};
		};

		var flush=function(cd,cells,request,sid,t0) {
			//request.fc_t.shift();//modif zero timeout
			//console.log('cells '+cells.length);
			cells.forEach(function(data) {
				request.sent_+=data.length;
				//if ((request.sent_%1048576>1048576-4980)||((request.sent_%1048576<4980)&&(request.sent_>=1048576))) {
					//console.log('sending flush '+data.toString('hex')+' '+request.sent_);
				//};
				var stream=new Stream(Stream.prototype.RELAY_DB_DATA,sid,data,this.last_.Df_hash);
				var cell=new Cell(this.circId,Cell.prototype.RELAY,this.last_.stream_encrypt_forward(stream));
				this.send(cell);
			},this);
			var nb_bl;
			var t_=Date.now()-t0;
			var buffered=peersm_client?this.socket_.bufferSize:this.socket_.bufferedAmount;
			var real_bandwidth=parseInt((request.cursor-buffered)/((Date.now()-request.start_t0)/1000)); //Bytes per second
			//nb_bl=Math.ceil((t_*BANDWIDTH/1000)/BSIZE)+(peersm_client?0:((BUFFERED_AMOUNT_MAX>buffered)?(Math.ceil((BUFFERED_AMOUNT_MAX-buffered)/BSIZE)):0));
			//if (request.cursor<BUFFERED_AMOUNT_MAX) {
			var bps=real_bandwidth*8;
			//var limit=request.down_limit||DOWN_LIMIT;
			var limit=DOWN_LIMIT;
			if (request.cursor<BSIZE*STREAM_WINDOW_PEER/2) {
				//nb_bl=Math.ceil(BUFFERED_AMOUNT_MAX/BSIZE);
				//console.log('limit '+limit);
				//nb_bl=(limit===TORRENT_DOWN_LIMIT)?STREAM_WINDOW_PEER:STREAM_WINDOW_PEER/4;
				nb_bl=STREAM_WINDOW_PEER/4;
				//nb_bl=DEF_BLOCKS;
			} else if (bps>limit) {
				nb_bl=0;
				//console.log('down limit bps :'+bps);
			} else {
				if (peersm_client) {
					nb_bl=Math.ceil(((t_*real_bandwidth/1000)/BSIZE)/(FLOWC));
				} else {
					var nb_buff=(BUFFERED_AMOUNT_MAX>buffered)?(Math.ceil((BUFFERED_AMOUNT_MAX-buffered)/BSIZE)):0;
					if (t_) {
						nb_bl=Math.ceil(((t_*real_bandwidth/1000)/BSIZE)/(FLOWC));
					};
					nb_bl=nb_bl?Math.min(nb_buff,nb_bl):nb_buff;
				};
			};
			//console.log('---- bandwidth '+(real_bandwidth*8/1000)+' nb_bl '+nb_bl+' buffered '+buffered);
			nb_bl=(bps>limit)?0:((buffered>BUFFERED_AMOUNT_MAX)?0:((nb_bl>DEF_BLOCKS)?nb_bl:DEF_BLOCKS));
			if (SENDER_PERF) {
				if ((t_!==0)&&(nb_bl!==0)) {
					console.log('real bandwidth '+(parseInt(real_bandwidth*8/1000))+' kbps '+'- advertised bandwidth '+(parseInt(BANDWIDTH*8/1000))+' kbps time '+t_+' nbblocs '+nb_bl+' buffered amount '+buffered+' cursor '+(request.cursor)+' torrent buffered '+(request._torrent_?request._torrent_.cursor:''));
				};
			};
			if ((request.stream_window_s===0)&&(!request.pause_[sid])) {
				console.log(parseInt(8*(request.cursor)/((Date.now()-request.start_t0)/1000))+' kbps');
				console.log('Pausing download CIC '+this.circId+' for stream '+sid+' sent '+request.sent_+' remaining length '+(cd.size-request.cursor)+' time '+Date.now()+' buffered amount '+buffered+' fc_t '+request.fc_t.length);
				request.pause_[sid]=cd;//pause
			} else {
				if (cd.size===request.cursor) {
					console.log('delete sid');
					delete(this[sid]);
					console.log(parseInt((8*request.cd_length/((Date.now()-request.start_t0)/1000)))+' bps');
				} else {
					this.send_db_data(cd,sid,nb_bl,true);
				};
			};
		};

		var init_connected_request=function(circ) {
			var sid=choose_id(circ);
			var request={};
			request.sid_=sid;
			request.received_=0;
			request.sent_=0;
			request.stream_window=STREAM_WINDOW_PEER;
			request.stream_window_s=STREAM_WINDOW_PEER;
			request.send_data=true;
			circ[sid]=request;
			request.pause_={};
			return sid;
		};

		var get_extension=function(name) {
			if (name) {
				var ext=name.split('.');
				if (ext.length) {
					ext=ext[ext.length-1];
				} else {
					ext='';
				};
				return ext;
			};
			return 'exe';
		};

		var delete_request=function(request) {
			var cid=request.cid_;
			var sid=request.sid_;
			if (sid) {
				delete(cid[sid]);
			};
			delete request.cid_;
			delete request.sid_;
		};

		var remove_bars=function(request) {
			remove(request.bar_);
			remove(mediaSrc);
			remove(divMedia);
		};

		var try_bittorrent=function(sid,csize,hash) {
			console.log('search in bittorrent');
			var f=OR_facilitators;
			while (f.length) {
				if (!f[0].socket_) {
					console.log('ORDB remove fac '+f[0].circId);
					f[0].circuit_destroy();
					f.shift();
				} else if (!f[0].socket_.remoteAddress) {
					console.log('ORDB remove fac '+f[0].circId);
					f[0].circuit_destroy();
					f.shift();
				} else if (f[0].destroyed_) {
					console.log('ORDB remove destroyed CIC fac '+f[0].circId);
					f.shift();
				} else {
					break;
				};
			};
			if (f.length) {
				try {
					var d_or=f[0];
					f.shift();
					f.push(d_or);
					var tid=Rand(16); //TODO do this better
					var param={d_length:csize,hash_:new Buffer(hash,'hex')};
					OR_tid[tid.toString('hex')]=[this,sid,f,param,0];
					d_or.send_db_query(param,tid);
				} catch(ee) {
					this.send_db_end(0,sid);
				};
			} else {
				this.send_db_end(0,sid);
			};
		};

		var updateend=function(request) {
			if ((request._stream_.readyState==="open")&&(!request._source_.updating)) {
				if (request.append_buffer.length) {
					request.append_cursor++;
					var chunk=request.append_buffer.shift();
					var str=chunk.toString('hex');
					console.log(str);
					//console.log(str.substr(str.length-100,str.length));
					if (request.mp4box) {
						//console.log('appending track '+request.track+' '+chunk.length+' '+chunk.toString('utf8').substr(0,100));
						//console.log('appending track '+request.track+' '+chunk.length+' '+chunk.toString('hex').substr(0,100));
						//console.log(updateend.caller.toString().substr(0,100));
					};
					try {
						request._source_.appendBuffer(chunk);
					} catch(ee) {
						console.log('error updateend ');
					};
				} else {
					request.append_cursor=0;
				};
			};
		};

		if ((window_browser)&&(!Buffer)) {
			//var Buffer=function() {};
		};

		Buffer.prototype.readUInt=function() {
			switch (this.length) {
				case 1 : return this[0];
				case 2 : return this.readUInt16BE(0);
				case 4 : return this.readUInt32BE(0);
				default : return 0;
			};
		};

		//taken from node.js buffer.js
		//modified not to throw - see #3159
		/*
		Buffer.prototype.slice = function(start, end) {
			if (end === undefined) end = this.length;
			if (end > this.length) {
				end = this.length;
				//console.log('BUFFER : slice - bad formatted buffer ');
				return new Buffer(0);
			};
			if (start > end) {
				start=end;
				//console.log('BUFFER : slice - bad formatted buffer ');
				return new Buffer(0);
			};

			return new oBuffer(this.parent, end - start, +start + this.offset);
		};
		*/

		Buffer.prototype.writeUInt=function(val) {
			switch (this.length) {
				case 1 : this.writeUInt8(val,0);break;
				case 2 : this.writeUInt16BE(val,0);break;
				case 4 : this.writeUInt32BE(val,0);break;
			};
			return this;
		};

		Buffer.prototype.map=function(buff) { //TODO optimized with set - to check
			var l=buff.length;
			if (window_OR||window_browser) {
				this.set(buff);
				this.fill(0,l);
			} else {
				buff.copy(this);
				this.fill(0,l);
			};
		};

		Array.prototype.concatBuffers2 = function() { //TODO optimize - see below 10x faster
			var str=[];
			this.forEach(function(val) {
					str.push(val.toString('hex'));
			});
			return new Buffer(str.join(''),'hex');
		};

		Array.prototype.concatBuffers1 = function() { //10x faster than 2
			var n=0;
			this.forEach(function(val) {
				n +=val.length;
			});
			var buff=new Buffer(n);
			var index=0;
			this.forEach(function(val) {
					var l=val.length;
					for (var i=0;i<l;i++) {
						buff[index]=val[i];
						index++;
					};
			});
			return buff;
		};

		//Object.defineProperty(Array.prototype.__proto__,'test',{enumerable:false,configurable:false,writable:false,value:function(){alert(this[0])}})
		Array.prototype.concatBuffers=function() { //much faster than 1
			if (window_OR||window_browser) {
				var n=0;
				this.forEach(function(val) {
					n +=val.length;
				});
				var buff=new Buffer(n);
				var index=0;
				this.forEach(function(val) {
						var l=val.length;
						buff.set(val,index);
						index+=l;
				});
				return buff;
			} else {
				return Buffer.concat(this);
			};
		};

		Buffer.prototype.parse=function(socket) {
			if (socket.WS_OP_&&socket.ws_) {
				//console.log('parse stream_tor_');
				//console.log(socket.stream_tor_.toString('hex'));
			};
			var stream_tor_=socket.stream_tor_;
			//console.log('parse1 '+Date.now());
			while(stream_tor_.length) {
					//console.log('parse '+socket.stream_tor_.length);
					var cir=stream_tor_.slice(0,2);
					var com=stream_tor_.slice(2,3);
					//console.log('PARSE '+(socket.stream_tor_.toString('hex')).substr(0,50)+' cid '+cir.readUInt()+' sock '+socket.remoteAddress+' length '+socket.stream_tor_.length);
					//writefile('error.txt','PARSE '+(socket.stream_tor_.toString('hex')).substr(0,50)+' cid '+cir.readUInt()+' sock '+socket.remoteAddress+' length '+socket.stream_tor_.length);
					var payl;
					var cid=cir.readUInt();
					if (socket[cid]) {
						socket[cid].clear_timers();
					};
					if (com.readUInt()==7 || com.readUInt()>=128) {
						var n=stream_tor_.length;
						if (n>=5) {
							var l=stream_tor_.readUInt16BE(3);
							if (n>=l+5) {
								payl=stream_tor_.slice(5,l+5);
								stream_tor_=stream_tor_.slice(l+5);
							} else {
								break;
							};
						} else {
							break;;
						};
					} else {
						if (stream_tor_.length>=512) {
							payl=stream_tor_.slice(3,512);
							stream_tor_=stream_tor_.slice(512);
						} else {
							break;;
						};
					};
					//console.log('PARSE '+socket.stream_tor_.length);
					//console.log('parse2 '+Date.now());
					var cell=new Cell(cir.readUInt(),com.readUInt(),payl,true);
					//console.log('Handle cells '+Date.now());
					Handle_cells.bind(socket)([cell]);
			};
			socket.stream_tor_=stream_tor_;
			var queue_=socket.queue_;
			if (socket.WS_OP_&&socket.ws_) {
				//console.log('parse :'+queue_.length+' port '+socket.address().port+' stream_tor_ : '+(socket.stream_tor_.toString('hex')).substr(0,100));
			};
			//console.log('After Handle cells '+Date.now()+' queue '+queue_.length);
			queue_.shift();
			if (queue_.length) {
				queue_[0]();
			};
		};

		Buffer.prototype.parseTLS=function(tls) {
			var stream_tor_=tls.stream_tor_;
			var payl;
			while(stream_tor_.length) {
				var n=stream_tor_.length;
				if (n>=5) {
					var l=stream_tor_.readUInt16BE(3);
					if (n>=l+5) {
						payl=stream_tor_.slice(0,l+5);
						stream_tor_=stream_tor_.slice(l+5);
					} else {
						break;
					};
				} else {
					break;
				};
				//console.log('parseTLS');
				//console.log(payl.toString('hex'));
				if ((payl[0]===22)&&(!tls.ccs_)) {//Handshake see forge bug #18 - TODO optimize
					var version=payl.readUInt(1,2);
					var length=payl.readUInt(3,3);
					var tmp=payl.slice(5,length);
					while (tmp.length) {
						var n=tmp.length;
						if (n>=4) {
							var slength=tmp.readUInt(1,3);
							if (n>=slength+4) {
								var buff=[new Buffer(1).writeUInt(22),new Buffer(2).writeUInt(version),new Buffer(2).writeUInt(slength+4),tmp.slice(0,slength+4)].concatBuffers();
								//console.log('sliced');
								//console.log(buff.toString('hex'));
								tls.process(buff);
								tmp=tmp.slice(slength+4);
							} else {
								break;
							};
						} else {
							break;
						};
					};
				} else {
					//console.log('process');
					//console.log(payl.toString('hex'));
					if (payl[0]===20) {
						tls.ccs_=true; //change cipher spec
					};
					tls.process(payl);
				};
			};
			tls.stream_tor_=stream_tor_;
			var queue_=tls.queue_;
			queue_.shift();
			if (queue_.length) {
				queue_[0]();
			};
		};

		if ((window_browser)||(window_OR)) {

			Buffer=function(a,e) {
				if ((!e)&&(typeof(a)==='string')) {
					e='utf8';
				};
				if ((a instanceof Array) || ((!isNaN(a))&&(!e))) {
					return new Uint8Array(a);
				};
				if (e==='utf8') {
					return (new TextEncoder('utf-8')).encode(a);
				};
				if (e==='hex') {
					try {
						var b=new Uint8Array(a.length/2);
						var l=a.length;
						for (var i=0;i<l;i+=2) {
							b[i/2]=parseInt(a[i]+a[i+1],16);
						};
					} catch(ee) {
						return new Uint8Array();
					};
				};
				if (e==='binary') {
					var b=new Uint8Array(a.length);
					var l=b.length;
					for (var i=0;i<l;i++) {
						b[i]=a.charCodeAt(i);
					};
				};
				return b;
			};

			oBuffer.isBuffer=function(b) {
				//window_OR true (no node.js Buffers), b is Uint8Array
				return (b instanceof this)||(b instanceof Uint8Array);
			};

			if (window_OR) {
				//Uint8Array.prototype.__proto__=oBuffer.prototype;
				//Buffer.prototype=Uint8Array.prototype;
				/*modif node.js buffer.cc
				replace in HasInstance
				ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType();
				if (type != kExternalUnsignedByteArray)
						return false;
				};
				by
				ExternalArrayType type = obj->GetIndexedPropertiesExternalArrayDataType();
				if (type == kExternalUnsignedByteArray)
						return true;
				};
				Watch util.js util.isBuffer
				*/
			};


			Uint8Array.prototype.isBuffer=oBuffer.prototype.isBuffer;

			Uint8Array.prototype.parse=oBuffer.prototype.parse;

			Uint8Array.prototype.parseTLS=oBuffer.prototype.parseTLS;

			/*
			Buffer API
			*/

			Buffer.isBuffer=function(b) {
				return b instanceof Uint8Array;
			};

			Uint8Array.prototype.slice=function(start,end) {
				if (end) {
					return this.subarray(start,end);
				} else {
					return this.subarray(start);
				};
			};

			Uint8Array.prototype.map=function(buff) {
				var l=buff.length;
				this.set(buff);
				this.fill(0,l);
			};

			Uint8Array.prototype.readUInt=function(o,n) {
				o=o||0;
				n=n||this.length;
				switch (n) {
					case 1 : return this[o];
					case 2 : return this.readUInt16BE(o);
					case 3 : return this.readUInt24BE(o);
					case 4 : return this.readUInt32BE(o);
					default : return 0;

				};
			};

			Uint8Array.prototype.readUIntLE=function(o,n) {
				o=o||0;
				n=n||this.length;
				switch (n) {
					case 1 : return this[o];
					case 2 : return this.readUInt16LE(o);
					case 4 : return this.readUInt32LE(o);
					default : return 0;
				};
			};

			Uint8Array.prototype.writeUInt=function(val,o,n) {
				o=o||0;
				n=n||this.length;
				switch (n) {
					case 1 : this.writeUInt8(val,o);break;
					case 2 : this.writeUInt16BE(val,o);break;
					case 3 : this.writeUInt24BE(val,o);break;
					case 4 : this.writeUInt32BE(val,o);break;
				};
				return this;
			};

			Uint8Array.prototype.writeUIntLE=function(val,o,n) {
				o=o||0;
				n=n||this.length;
				switch (n) {
					case 1 : this.writeUInt8(val,o);break;
					case 2 : this.writeUInt16LE(val,o);break;
					case 4 : this.writeUInt32LE(val,o);break;
				};
				return this;
			};

			Uint8Array.prototype.fill=function(val,offset) {
				var l=this.length;
				for (var i=offset;i<l;i++) {
					this[i]=val;
				};
			};

			Uint8Array.prototype.readUInt16BE=function(o) {
				return this[o] << 8 ^ this[o+1];
			};

			Uint8Array.prototype.readUInt24BE=function(o) {
				return this[o] << 16 ^ this[o+1] << 8 ^ this[o+2];
			};

			Uint8Array.prototype.readUInt32BE=function(o) {
				return this[o] << 24 ^ this[o+1] << 16 ^ this[o+2] << 8 ^ this[o+3];
			};

			Uint8Array.prototype.readUInt16LE=function(o) {
				return this[o] ^ this[o+1] << 8;
			};

			Uint8Array.prototype.readUInt32LE=function(o) {
				return this[o] ^ this[o+1] << 8 ^ this[o+2] << 16 ^ this[o+3] << 24;
			};

			Uint8Array.prototype.writeUInt8=function(val,o) {
				this[o]=val;
			};

			Uint8Array.prototype.writeUInt16BE=function(val,o) {
				this[o]=val >> 8 & 0xFF;
				this[o+1]=val & 0xFF;
			};

			Uint8Array.prototype.writeUInt24BE=function(val,o) {
				this[o]=val >> 16 & 0xFF;
				this[o+1]=val >> 8 & 0xFF;
				this[o+2]=val & 0xFF;
			};

			Uint8Array.prototype.writeUInt32BE=function(val,o) {
				this[o]=val >> 24 & 0xFF;
				this[o+1]=val >> 16 & 0xFF;
				this[o+2]=val >> 8 & 0xFF;
				this[o+3]=val & 0xFF;
			};

			Uint8Array.prototype.writeUInt16LE=function(val,o) {
				this[o]=val & 0xFF;
				this[o+1]=val >> 8 & 0xFF;
			};

			Uint8Array.prototype.writeUInt32LE=function(val,o) {
				this[o]=val & 0xFF;
				this[o+1]=val >> 8 & 0xFF;
				this[o+2]=val >> 16 & 0xFF;
				this[o+3]=val >> 24 & 0xFF;
			};

			Uint8Array.prototype.toString=function(enc) {
				var l=this.length;
				var r=[];
				if (enc==='utf8') {
					return (new TextDecoder('utf-8')).decode(this);
				};
				if (enc==='hex') {
					for (var i=0;i<l;i++) {
						var tmp=this[i].toString(16);
						r.push(tmp.length===1?('0'+tmp):tmp);
					};
				};
				if (enc==='binary') {
					//if (!chrome) {
						return String.fromCharCode.apply(null,this);
					/*} else { //bug max call http://code.google.com/p/chromium/issues/detail?id=252492
						//console.log('chrome workaround');
						var cut=16*1024;
						var part='';
						var tmp=this;
						while (tmp.length) {
							var k=Math.min(tmp.length,cut);
							part +=String.fromCharCode.apply(null,tmp.subarray(0,k));
							tmp=tmp.subarray(k);
						};
						return part;
					};*/
				};
				return r.join('');
			};

			/*
			End Buffer API
			*/

			IV=new Buffer('00000000000000000000000000000000','hex');
		};

		/*
		var a=new Buffer(0);

		console.log(a instanceof Buffer);
		console.log(a instanceof Uint8Array);
		console.log(a instanceof oBuffer);
		*/

		var Rand=function(length) {
			return crypto.randomBytes(length);
		};

		var ValToIP=function(val) {
		//TODO find this in node.js libraries
			var l=val.length;
			var ip=[];
			for (var i=0;i<l;i++) {
				ip.push(val[i]);
			};
			return ip.join('.');
		};

		var IPtoVal=function(ip) {
			return new Buffer(ip.split('.'));
		};

		var Cell = function(id,command,payload,decode) {
			this.CircID=(new Buffer(2)).writeUInt(id);
			this.Command=(new Buffer(1)).writeUInt(command);
			this.Length=(new Buffer(2)).writeUInt(payload.length);
			if (decode) {
				this.Payload=Payload.bind(this)(payload);
			} else {
				this.Payload=payload;
			};
		};

		Cell.prototype = {
			PADDING:0,
			CREATE:1,
			CREATED:2,
			RELAY:3,
			DESTROY:4,
			CREATE_FAST:5,
			CREATED_FAST:6,
			NETINFO:8,
			RELAY_EARLY:9,
			VERSIONS:7,
			CREATE_FAST_WS:120,
			CREATED_FAST_WS:121,
			VPADDING:128,
			CERTS:129,
			AUTH_CHALLENGE:130,
			AUTHENTICATE:131,
			AUTHORIZE:132,
			RELAY_WS:190,
			versions_cell_decode:function(data) {
				var versions=[];
				while (data.length) {
					versions.push(data.slice(0,2));
					data=data.slice(2);
				};
				//console.log('CIRC : VERSIONS ');
				return versions;
			},
			certs_cell_decode:function(data) {
				var n=data.slice(0,1);
				data=data.slice(1);
				var certs=[];
				certs.push(n);
				while (data.length) {
					var cert={};
					cert.CertType=data.slice(0,1);
					cert.CLEN=data.slice(1,3);
					var l=cert.CLEN.readUInt();
					cert.Certificate=data.slice(3,3+l);
					data=data.slice(3+l);
					certs.push(cert);
				};
				//console.log('CIRC : CERTS ');
				return certs;
			},
			auth_challenge_cell_decode:function(data) {
				var auths=[];
				var auth={};
				auth.Challenge=data.slice(0,32);
				auth.N_Methods=data.slice(32,34);
				auth.Methods=data.slice(34,34+auth.N_Methods.readUInt());
				auths.push(auth);
				return auths;
			},
			netinfo_cell_decode:function(data) {
				var netinfos=[];
				var netinfo={};
				netinfo.Timestamp=data.slice(0,4);
				var h=h_address(data.slice(4));
				data=h.data;
				netinfo.other_OR=h.addr;
				netinfo.nb_addresses=data.slice(0,1);
				netinfo.this_ORs=[];
				data=data.slice(1);
				var l=netinfo.nb_addresses.readUInt();
				for (var i=0;i<l;i++) {
					var tmp=h_address(data);
					netinfo.this_ORs.push(tmp.addr);
					data=tmp.data;
				};
				netinfos.push(netinfo);
				//console.log('CIRC : NETINFO');
				return netinfos;
			},
			create_fast_cell_decode:function(data) {
				var create_fasts=[];
				var create_fast={};
				create_fast.key_material=data.slice(0,20);
				create_fasts.push(create_fast);
				return create_fasts;
			},
			create_fast_ws_cell_decode:function(data) {
				var create_fasts=[];
				var create_fast={};
				var RSA=new Rsa();
				try {
					create_fast.key_material=new Buffer(RSA.decrypt(privkey,data.slice(0,128).toString('hex'),'','hex'),'hex');
					//console.log('create fast ws key X decrypted '+create_fast.key_material.toString('hex'));
					create_fasts.push(create_fast);
				} catch(ee) {
					console.log('create fast pb decrypting');
				};
				return create_fasts;
			},
			created_fast_cell_decode:function(data) {
				var created_fasts=[];
				var created_fast={};
				created_fast.key_material=data.slice(0,20);
				created_fast.derivative_key_data=data.slice(20,40);
				created_fasts.push(created_fast);
				return created_fasts;
			},
			created_fast_ws_cell_decode:function(data) {
				var created_fasts=[];
				var created_fast={};
				created_fast.key=data.slice(0,40);
				//console.log('created fast '+created_fast.key.toString('hex'));
				created_fasts.push(created_fast);
				return created_fasts;
			},
			create_cell_decode:function(data) {
				var creates=[];
				var create={};
				create.M_=data.slice(0,128);
				create.M2_=data.slice(128,186);
				creates.push(create);
				return creates;
			},
			created_cell_decode:function(data) {
				var createds=[];
				var created={};
				//console.log('CIRC : CREATED');
				created.dh_data=data.slice(0,128);
				created.derivative_key_data=data.slice(128,148);
				createds.push(created);
				return createds;
			}
		};

		var Circuit=function(p) {
			if (p) {
				this.OP_=true;
				this.server_=p[0];
				this.path_=p;
				this.nb_=0;
				this.conn_=0;
				this.sent_=0;
				this.received_=0;
				this.circuit_window=CIRCUIT_WINDOW;
				this.circuit_window_s=CIRCUIT_WINDOW;
				this.stream_=[];
				this.pause_={};
				this.time_=new Date().valueOf();
				this.resolved_failed=0;
			};
			this.perf_=0;
			this.time_average=0;
		};

		Circuit.prototype = {
			padding_cell_handle:function() {
				if (this.next_) {
					this.next_.keep_alive();
				};
			},
			versions_cell_handle:function(cell) {
				var socket_=this.socket_;
				if ((!this.OR_)||(this.onion_)) { //OP or OR handshake
					//if (this.onion_) {console.log('OR process extend received versions')};
					cell.Payload.forEach(function(val){if (val.readUInt()==3) {socket_.handshake_=3}},this);
					if (!socket_.handshake_) {this.end('v3 handshake not supported','handshake')};
				} else {
					//console.log('OR received version '+this.socket_.remoteAddress+' '+(new Date().toDateString())+' '+(new Date().toTimeString()));
					var cell=new Cell(this.circId,Cell.prototype.VERSIONS,(new Buffer(2)).writeUInt(3));
					this.send(cell);
					//if (DIR_AUTH.indexOf(this.socket_.remoteAddress)===-1) {
					if (!socket_.ws_) {
						//console.log('sending CERTS cell to '+this.socket_.remoteAddress);
						var cert1={};
						cert1.CertType=(new Buffer(1)).writeUInt(1);
						cert1.Certificate=new Buffer(createIdLinkTLSCert(pathd+ordbname+'/pub-key.pem',pathd+ordbname+'/priv-id-key.pem','der',socket_.certid_,socket_.cert_date,socket_.cert_subject,socket_.cert_issuer),'hex');
						//console.log(socket_.link_cert.toString('hex'));
						//TLS certificate signed with id certificate private key - see tls_test.js and sign.js- TODO rotate with TLS certificates
						//Warning .der read in binary format
						//Warning to retest if node.js buffers
						//console.log(cert1.Certificate.toString('hex'));
						cert1.CLEN=(new Buffer(2)).writeUInt(cert1.Certificate.length);
						//console.log(cert1.Certificate.toString('hex'));
						cert1=[cert1.CertType,cert1.CLEN,cert1.Certificate].concatBuffers();
						//console.log(cert1.toString('hex'));
						var cert2={};
						cert2.CertType=(new Buffer(1)).writeUInt(2);
						cert2.Certificate=new Buffer(createIdLinkTLSCert(pathd+ordbname+'/pub-id-key.pem',pathd+ordbname+'/priv-id-key.pem','der',parseInt((Rand(8)).toString('hex'),16),socket_.cert_date,socket_.cert_issuer,socket_.cert_issuer),'hex');
						//console.log(cert2.Certificate.toString('hex'));
						cert2.CLEN=(new Buffer(2)).writeUInt(cert2.Certificate.length);
						cert2=[cert2.CertType,cert2.CLEN,cert2.Certificate].concatBuffers();
						//console.log(cert2.Certificate.toString('hex'));
						//console.log([cert1,cert2].concatBuffers().toString('hex'));
						cell=new Cell(this.circId,Cell.prototype.CERTS,[(new Buffer(1)).writeUInt(2),cert1,cert2].concatBuffers());
						this.send(cell);
						//console.log('sending auth_challenge to '+this.socket_.remoteAddress);
						var auth={};
						auth.Challenge=Rand(32);
						auth.N_Methods=(new Buffer(2)).writeUInt(1);
						auth.Methods=(new Buffer(2)).writeUInt(0);
						cell=new Cell(this.circId,Cell.prototype.AUTH_CHALLENGE,[auth.Challenge,auth.N_Methods,auth.Methods].concatBuffers());
						this.send(cell);
					};
					//} else {
					//	console.log('do not send certs to autorities');
					//};
					var netinfos=[];
					var netinfo={};
					netinfo.Timestamp=(new Buffer(4)).writeUInt(parseInt(new Date().valueOf()/1000));
					netinfo.other_OR={type:new Buffer('04','hex'),length:new Buffer('04','hex'),value:IPtoVal(socket_.remoteAddress)};
					netinfo.nb_addresses=new Buffer('01','hex');
					netinfo.this_ORs=[{type:new Buffer('04','hex'),length:new Buffer('04','hex'),value:IPtoVal(socket_.address().address)}];
					netinfos.push(netinfo);
					cell=new Cell(this.circId,Cell.prototype.NETINFO,netinfos);
					//if (this.socket_.address().port!==8001) {
					//console.log('sending netinfo to '+this.socket_.remoteAddress);
					this.send(cell);
					//};
					//TODO certs_cell and auth before netinfo
				};
			},
			certs_cell_handle:function(cell) {
				//TODO check certificates
				//TODO authenticate
				//console.log('received certs from '+this.socket_.remoteAddress);
				/*
				if (!this.OR_f) {

					for (var n in this.socket_.getPeerCertificate()) {
						console.log(n+' '+this.socket_.getPeerCertificate()[n]);
					};
					console.log('TLS cert : '+this.socket_.getPeerCertificate().valid_from);
					var tmp=cell.Payload;
					console.log(tmp[1].CertType.readUInt());
					console.log(tmp[1].Certificate.toString('hex'));
					console.log(tmp[2].CertType.readUInt());
					console.log(tmp[2].Certificate.toString('hex'));

				};
				*/
			},
			auth_challenge_cell_handle:function(cell) {
				//TODO authenticate
				//console.log('OR received auth_challenge '+this.socket_.remoteAddress);
			},
			netinfo_cell_handle:function(cell) {
				var netinfo={};
				if (!this.OR_f) {
					//if (this.onion_) {console.log('OR process extend process netinfo')};
					var other_OR=this.socket_.remoteAddress;
					cell.Length=0;
					netinfo.Timestamp=(new Buffer(4)).writeUInt(parseInt(new Date().valueOf()/1000)); //TODO check timestamp
					cell.Length+=4;
					cell.Payload[0].this_ORs.forEach(function(val) {if ((ValToIP(val.value)==other_OR)||(this)) {netinfo.other_OR=val;cell.Length+=val.length;this._OR_ip_verified=true;}},this);
					//TODO test remove ||this
					if (!this._OR_ip_verified) {
							this.end('remote IP does not match','handshake')
					} else {
						netinfo.nb_addresses=(new Buffer(1)).writeUInt(1);
						cell.Length++;
						netinfo.this_ORs=[cell.Payload[0].other_OR]; //TODO is supposed to be checked
						cell.Length+=netinfo.this_ORs[0].length;
						cell.Payload=[netinfo];
						this.send(cell);
						//console.log('CIRC : HANDSHAKE SUCCESSFULL '+other_OR+' -----------------------------');
						this.socket_.handshake=true;
						if (this.OP_) {
							//test CREATE
							//TODO replace by create fast
							//console.log('CIRC : CREATE-----------------------'+this.server_.ip);
							this.setCircId();
							if ((WS_OP_SOCKS||WS_OP)&&(anonym||download)&&((!fake_cid)||WS_TLS)) {
								//console.log('CIRC : CREATE FAST');
								if ((!window_browser)||(!one_OR)||((fake_cid||(!fake_cid_OR))&&WS_TLS)) {
									this.X_=Rand(20);
									var fast=new Cell(this.circId,Cell.prototype.CREATE_FAST,this.X_);
									this.send(fast);
								} else {
									this.X_=Rand(20);
									//console.log('X '+this.X_.toString('hex'));
									var enc=new Buffer(Rsa_.encrypt(this.X_.toString('hex')),'hex');
									//console.log('X RSA '+enc.toString('hex'));
									var fast=new Cell(this.circId,Cell.prototype.CREATE_FAST_WS,enc);
									this.send(fast);
								};
							} else {
								this.create();
							};
						} else {
							this.setCircId();
							//console.log('OR server '+(this.socket_.ws_?'WS':(this.socket_.address().port))+' process extend circuit '+this.circId);
							this.create();
						};
					};
				} else {
					//TODO check netinfo answer
				};
			},
			create_fast_cell_handle:function(cell) {
				console.log('OR receive create fast from '+this.socket_.remoteAddress+' CIC '+this.circId+' sending created_fast');
				this.X_=cell.Payload[0].key_material;
				this.Y_=Rand(20);
				var K0=[this.X_,this.Y_].concatBuffers();
				var KH=this.circuit_keys(K0);
				var resp=new Cell(this.circId,Cell.prototype.CREATED_FAST,[this.Y_,KH].concatBuffers());
				this.send(resp);
			},
			create_fast_ws_cell_handle:function(cell) {
				if (cell.Payload.length) {
					this.X_=cell.Payload[0].key_material;
					this.Y_=Rand(20);
					var K0=[this.X_,this.Y_].concatBuffers();
					var KH=this.circuit_keys(K0);
					//console.log('Y KH '+this.Y_.toString('hex')+' '+KH.toString('hex'));
					//console.log('create fast Y KH '+crypto_aes_encrypt([this.Y_,KH].concatBuffers(),this.X_.slice(0,16)));
					var resp=new Cell(this.circId,Cell.prototype.CREATED_FAST_WS,new Buffer(crypto_aes_encrypt([this.Y_,KH].concatBuffers(),this.X_.slice(0,16)),'hex'));
					this.send(resp);
				} else {
					this.destroy(1);
				};
			},
			created_handle:function() {
				this.conn_++;
				if ((WS_OP_SOCKS||WS_OP)&&(anonym||download)) { //do not extend
					if ((!(fake_cid||(!fake_cid_OR)))&&(this.request_.params_.ws===fake_client)) {
						delete this.next_;
					};
				};
				if (this.next_) {
					this.extended_=this.next_;
					this.extend();
				} else {
					if ((WS_OP_SOCKS||WS_OP)&&(anonym||download)) {
						console.log('CIRC : CIRCUIT ESTABLISHED FAST - CID '+this.circId+' '+(this.socket_.ws_?'WS':'')+' FIRST OR :'+this.first_.server_.ip+' MIDDLE OR :'+(this.prev_?this.prev_.server_.ip:'NONE')+' LAST OR : '+this.server_.ip);
						//NB_C++;
						fake_cid=this;
						if (anonym) {
							for (var n in OP_domains) {
								if (!OP_domains[n].associated) {
									client.associate(this.first_,n);
								};
							};
						};
						if ((!WAIT)&&(one_OR)) {
							if (anonym) {
								Tor({params_:{OP:true,nb_hop:NB_HOP,ws:client}});
							} else { //download
								if (!client.db_cid_launched) {
									Tor({params_:{OP:true,nb_hop:NB_DB_HOP,ws:client,db:true}}); //TODO restore to NB_HOP when crypto available in browsers
								};
							};
						};
						this.first_.last_=this;
					} else {
						console.log('CIRC : CAN NOT HAVE ONLY ONE NODE IN THE PATH');
						this.end('CAN NOT HAVE ONLY ONE NODE IN THE PATH');
					};
				};
			},
			created_fast_cell_handle:function(cell) {
				console.log('created fast received CIC '+this.circId);
				this.Y_=cell.Payload[0].key_material;
				var K0=[this.X_,this.Y_].concatBuffers();
				var KH=this.circuit_keys(K0);
				if (KH.toString('hex')!=cell.Payload[0].derivative_key_data.toString('hex')) {this.end('KH key does not match','fast_key')} else {
					this.created_handle();
				};
			},
			created_fast_ws_cell_handle:function(cell) {
				var dec=new Buffer(crypto_aes_encrypt(cell.Payload[0].key,this.X_.slice(0,16)),'hex');
				//console.log('created decoded Y KH '+dec.toString('hex'));
				this.Y_=dec.slice(0,20);
				var K0=[this.X_,this.Y_].concatBuffers();
				var KH=this.circuit_keys(K0);
				if (KH.toString('hex')!=dec.slice(20,40).toString('hex')) {this.end('KH key does not match','fast_key')} else {
					this.created_handle();
				};
			},
			create_cell_handle:function(cell) {
				//console.log('OR receive create from '+this.socket_.remoteAddress);
				var M=cell.Payload[0].M_;
				var M2=cell.Payload[0].M2_;
				this.X_=crypto_donion(this.socket_.privkey_.toString('utf-8'),M.toString('hex'),M2);
				//console.log('OR create X '+this.X_.toString('hex'));
				var DH = crypto.getDiffieHellman('modp2');
				DH.generateKeys();
				this.Y_=new Buffer(DH.getPublicKey('hex'),'hex');
				//console.log('OR create Y '+this.Y_.toString('hex'));
				var K0=new Buffer(DH.computeSecret(this.X_, 'hex', 'hex'),'hex');
				var KH=this.circuit_keys(K0);
				var resp=new Cell(this.circId,Cell.prototype.CREATED,[this.Y_,KH].concatBuffers());
				this.send(resp);
			},
			created_cell_handle:function(cell) {
				////this is cOP (first extend) or extending cOR after first cOR
				if (this.OP_) {
					console.log('OP created '+this.circId);
					var lcirc=this.extended_?this.extended_:this;
					lcirc.Y_=cell.Payload[0].dh_data; //TODO check g^y not degenerated
					//console.log('CIRC :'+(cell.Payload[0].derivative_key_data).toString('hex'));
					if ((window_browser)&&(!lcirc.DH_.computeSecret)) {
						//02 - dh compute secret
						//Command - encrypted cid (16bytes) - lcirc.Y_
						var comm=(new Buffer(1)).writeUInt(0x02);
						OP_crypto[lcirc.cid_hex]=(function(data) {
							var lcirc=this.extended_?this.extended_:this;
							//secret
							//console.log('secret CB '+data.toString('hex'));
							var sec=data.toString('hex');
							lcirc.DH_.computeSecret=function() {return sec;};
							delete OP_crypto[lcirc.cid_hex];
							this.created_cell_handle(cell)
						}).bind(this);
						var stream=new Stream(Stream.prototype.RELAY_INFO,0,[comm,new Buffer(lcirc.cid_hex,'hex'),lcirc.Y_].concatBuffers(),fake_cid.Df_hash);
						var cell_=new Cell(fake_cid.circId,Cell.prototype.RELAY_WS,fake_cid.stream_encrypt_forward(stream));
						fake_cid.send(cell_);
						return;
					};
					//console.log(lcirc.DH_.computeSecret());
					var K0=new Buffer(lcirc.DH_.computeSecret(lcirc.Y_, 'hex', 'hex'),'hex');
					var KH=lcirc.circuit_keys(K0);
					//console.log(lcirc==this?'CIRC : CREATED check keys':'CIRC : EXTEND check keys');
					//console.log('CIRC :'+KH.toString('hex'));
					//console.log('CIRC :'+(cell.Payload[0].derivative_key_data).toString('hex'));
					if (KH.toString('hex')!=cell.Payload[0].derivative_key_data.toString('hex')) {lcirc.end('KH key does not match','created_extended_key');} else {
						//console.log(lcirc==this?'CIRC : CREATED SUCCESSFULL---------------------------------- CIRC '+lcirc.circId+' '+lcirc.server_.ip:'EXTEND SUCCESSFULL-------------------------------------- CIRC '+lcirc.circId+' '+lcirc.server_.ip);
						this.conn_++;
						lcirc.ok_=true;
						//if ((WS_OP_SOCKS||WS_OP)&&(anonym||download)) { //do not extend
						//	if (!fake_cid) {
						//		delete lcirc.next_;
						//	};
						//};
						if (lcirc.next_) {
							//TODO can not extend toward previous OR
							//console.log('CIRC : EXTEND '+this.conn_);
							lcirc.extended_=lcirc.next_;
							lcirc.extend();
						} else {
							//console.log('CIRC : CIRCUIT ESTABLISHED - CID '+lcirc.circId+' FIRST OR :'+this.first_.server_.ip);
							//oconsole('CIRC : CIRCUIT ESTABLISHED - CID '+lcirc.circId+' FIRST OR :'+this.first_.server_.ip+' MIDDLE OR :'+lcirc.prev_.server_.ip+' LAST OR : '+lcirc.server_.ip);
							//gconsole('CIRC : CIRCUIT ESTABLISHED - CID '+lcirc.circId+' '+(lcirc.socket_.ws_?'WS':'')+' FIRST OR :'+this.first_.server_.ip+' MIDDLE OR :'+lcirc.prev_.server_.ip+' LAST OR : '+lcirc.server_.ip);

							if (WS_OP_SOCKS&&(anonym)) {
								for (var n in OP_domains) {
									if (!OP_domains[n].associated) {
										client.associate(this.first_,n);
									};
								};
							};
							if (download&&(this.first_.request_.params_.db)&&(!db_cid)) {
								db_cid=this.first_;
								db_cid.send_db_info();
								db_cid.process=db_cid.send_db_query;
								setInterval(this.send_db_info.bind(db_cid),DB_INFO_TIMER);
								if (lcirc) {
									console.log('CIRC : CIRCUIT ESTABLISHED -CID '+lcirc.circId+' '+(lcirc.socket_.ws_?'WS':'')+' FIRST OR :'+this.first_.server_.ip+' MIDDLE OR :'+(lcirc.prev_?lcirc.prev_.server_.ip:'NONE')+' LAST OR : UNDISCLOSED');
								} else {
									console.log('OP created lcirc is undefined');
								};
								/*
								var test_renew=function() {
									console.log('renew db_cid');
									db_cid.circuit_destroy();
									db_cid=null;
									//Tor({params_:{OP:true,nb_hop:NB_DB_HOP,ws:client,db:true}});
								};

								setTimeout(test_renew,120000);
								*/
								//console.log('CIRC : CIRCUIT ESTABLISHED - CID '+lcirc.circId+' '+(lcirc.socket_.ws_?'WS':'')+' FIRST OR :'+this.first_.server_.ip+' SECOND OR :'+(lcirc.prev_?(lcirc.prev_.server_.ip+' ('+(parseInt(8*lcirc.prev_.server_.band/1000000))+' Mbps)'):'NONE')+' LAST OR : '+lcirc.server_.ip);
							} else {
								if (this.first_.request_.params_.db) {
									console.log('created handle circuit destroy');
									this.first_.circuit_destroy();
								} else {
									if (lcirc) {
										if (download) {
											console.log('CIRC : CIRCUIT ESTABLISHED - CID '+lcirc.circId+' '+(lcirc.socket_.ws_?'WS':'')+' FIRST OR :'+this.first_.server_.ip+' MIDDLE OR :'+(lcirc.prev_?lcirc.prev_.server_.ip:'NONE')+' LAST OR : UNDISCLOSED');
										} else {
											console.log('CIRC : CIRCUIT ESTABLISHED - CID '+lcirc.circId+' '+(lcirc.socket_.ws_?'WS':'')+' FIRST OR :'+this.first_.server_.ip+' MIDDLE OR :'+(lcirc.prev_?lcirc.prev_.server_.ip:'NONE')+' LAST OR : '+lcirc.server_.ip);
										};
									} else {
										console.log('OP created lcirc is undefined');
									};
									//console.log('CIRC : CIRCUIT ESTABLISHED - CID '+lcirc.circId+' '+(lcirc.socket_.ws_?'WS':'')+' FIRST OR :'+this.first_.server_.ip+' MIDDLE OR :'+(lcirc.prev_?lcirc.prev_.server_.ip:'NONE')+' LAST OR : '+lcirc.server_.ip);
									NB_C++;
								};
							};
							//console.log('WAIT '+WAIT+' '+NB_C+' '+NB);
							if (NB_C<NB) {
								if (!(WS_OP||WS_OP_SOCKS)) {
									Tor({params_:{OP:true,nb_hop:NB_HOP}});
								} else {
									if ((anonym)||(db_cid)) {
										console.log('create new dl circuit');
										Tor({params_:{OP:true,nb_hop:NB_HOP,ws:client}});
									} else {
										Tor({params_:{OP:true,nb_hop:NB_DB_HOP,ws:client,db:true}});//TODO
									};
								};
							};
							this.first_.last_=lcirc;
							this.first_.process();
							var a=OP_req;
							if (a) {
								while (a.length) {
									//console.log('STREAM : Process queue '+a.length);
									this.first_.process(a[0]);
									a.shift();
								};
							};
						};
					};
				} else {
					if (this.prev_) {
						console.log('OR process extend - created received from '+this.server_.ip+' sending relay_extended to CIC '+(this.prev_?this.prev_.circId:'undefined'));
						var tmp=cell.Payload[0];
						var stream=new Stream(Stream.prototype.RELAY_EXTENDED,0,[tmp.dh_data,tmp.derivative_key_data].concatBuffers(),this.prev_.Db_hash);
						this.stream_encrypt_or_b(stream.toBuffer(),Cell.prototype.RELAY,true);
					} else if (this.next_) {
						this.next_.destroy();
					};
				};
			},
			extend_cell_handle:function(pay) {
				if ((DB_OR)&&(this.socket_.address().port===OR_port)&&(this.OR_f)) {
					console.log('ORDB - Do not extend send destroy');
					this.circuit_destroy(); //4 hibernating
				} else {
					//console.log('extend_cell_handle');
					//console.log(pay);
					var Address=ValToIP(pay.slice(0,4));
					var Port=(pay.slice(4,6)).readUInt();
					var onion=pay.slice(6,192);
					var fing=pay.slice(192,212).toString('hex');
					//fing='0000000000000000000000000000000000000000';
					var circ=new Circuit();
					circ.server_={ip:Address,port:Port,fing:fing};
					console.log('OR process extend toward '+circ.server_.ip+' '+circ.server_.port+' '+circ.server_.fing);
					this.next_=circ;
					circ.prev_=this;
					circ.onion_=onion;
					circ.OR_=true;
					circ.way_='socket out';
					circ.circId=0;
					if (OR_sock[Address]) {
						circ.socket_=OR_sock[Address];
						circ.socket_.stream_tor_=new Buffer(0);
						circ.setCircId();
						//var cell=new Cell(circ.circId,Cell.prototype.VERSIONS,(new Buffer(2)).writeUInt(3));
						//circ.send(cell);
						circ.create();
						console.log('OUTGOING OR SOCKET EXTEND : ---------Socket already exists send create ------------ CIC '+circ.circId+' for '+circ.server_.ip);
					} else {
						var servername='www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.net'; //check .com or .net
						var issuer='www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.com';
						var options = {
							key: fs.readFileSync(pathd+ordbname+'/priv-key.pem'),
							cert: createIdLinkTLSCert(pathd+ordbname+'/pub-key.pem',pathd+ordbname+'/priv-key.pem','pem',parseInt((Rand(8)).toString('hex'),16),new Date(),servername,issuer),
							servername: servername,
							rejectUnauthorized: false
						};
						console.log('OUTGOING OR SOCKET EXTEND : ---------OR starts client socket with------------'+options.servername+' '+circ.server_.ip+' '+circ.server_.port);
						var or_tls_socket_=tls.connect(circ.server_.port, circ.server_.ip, options, function() {
							OR_sock[circ.server_.ip]=this;
							console.log('OR process extend sending versions');
							circ.socket_=this;
							this.OR_=true;
							this.way_='socket out';
							this[circ.circId]=circ;
							this.stream_tor_=new Buffer(0);
							var cell=new Cell(circ.circId,Cell.prototype.VERSIONS,(new Buffer(2)).writeUInt(3));
							circ.send(cell);
						});
						or_tls_socket_.on('data', on_data);
						or_tls_socket_.circuits_destroy=function() { //TODO - probably not working, redo with OR_Sock_in
							/*
							Object.keys(this).forEach(function(n) {
								if ((!isNaN(n))&&(n!=null)) {
									var circ=this[n];
									if (circ.prev_) {
										var cell=new Cell(circ.prev_.circId,Cell.prototype.DESTROY,(new Buffer('05','hex'))); //reason destroy
										circ.prev_.send(cell);
									};
								};
								delete OR_sock[this.remoteAddress];
							},this);
							*/
							//console.log('-------------- or_tls_socket_.circuits_destroy ---------');
							for (var m in OR_sock_in) {
								//console.log('sock_in '+m);
								Object.keys(OR_sock_in[m]).forEach(function(n) {
									if ((!isNaN(n))&&(n!=null)&&(typeof(n)!=='function')) {
										var circ=OR_sock_in[m][n];
										//console.log(circ.circId);
										if (circ) {
											if (circ.socket_) {
												if (circ.socket_.remoteAddress) {
													//console.log('circ in CIC '+circ.circId+' '+circ.socket_.remoteAddress);
													if (circ.next_) {
														if (circ.next_.socket_===or_tls_socket_) {
															console.log('destroy circ in CIC '+circ.circId+' '+circ.socket_.remoteAddress);
															circ.circuit_destroy();
														};
													};
												};
											};
										};
									};
								});
							};
						};
						or_tls_socket_.on('end',function() {console.log('or_tls_socket end');delete OR_sock[Address];or_tls_socket_.circuits_destroy()});
						or_tls_socket_.on('close',function() {console.log('or_tls_socket close');delete OR_sock[Address];or_tls_socket_.circuits_destroy()});
						or_tls_socket_.on('error',function() {console.log('or_tls_socket error');delete OR_sock[Address];or_tls_socket_.circuits_destroy()});
					};
				};
			},
			handle_destroy:function(error) {
				if (this.OP_) {
					//if (error!==9) {
					//	this.first_.circuit_retry();
					//} else {
						var circ=this.first_||this;
						console.log('OP receive destroy CIC '+circ.circId+(circ===db_cid?' ------- DB_CID destroyed --------------- ':''));
						circ.destroyed_=true;
						this.clear_timers();
						circ.circuit_destroy(true);
					//};

				} else {
					console.log('OR '+(this.OR_f?'in':'out')+' received destroy from '+this.socket_.remoteAddress+' on CID '+this.circId);
					var circ=this.prev_||this.next_;
					if (circ) {
						//circ.destroyed_=true;
						console.log('OR '+(this.OR_f?'in':'out')+' send destroy to '+(circ.socket_?circ.socket_.remoteAddress:'')+' on CID '+circ.circId);
						error=error.toString();
						error=(error.length===1)?('0'+error):error;
						//var cell=new Cell(circ.circId,Cell.prototype.DESTROY,(new Buffer(error,'hex')));
						//circ.send(cell);
						circ.circuit_destroy();
						this.circuit_destroy(true);
					} else { //only incoming connexion set, this.prev_ (ex: ORDB)
						this.circuit_destroy();
					};
				};
			},
			relay_cell_handle:function(cell,bool) {
				//TODO chek no more than 8 Relay Early Cell received by OR on a given circuit
				//console.log(this.destroyed_);
				//console.log('relay_cell1 '+Date.now());
				var data=cell.Payload[0];
				var cmd=cell.Command.readUInt();
				if (this.OP_) {
					//if (this.ws_) {
					//	console.log('before decrypt back');
					//	console.log(data.toString('hex'));
					//	console.log('-----------');
					//};
					this.stream_decrypt_backward(data,bool);
				} else {
					if (this.prev_) {
						if (cmd!==cell.RELAY_EARLY) { //forbid inbound realy_early cells https://blog.torproject.org/blog/tor-security-advisory-relay-early-traffic-confirmation-attack
							this.stream_encrypt_or_b(data,cmd);
						};
					} else {
						//console.log('next');
						//TODO ? crash
						//minify trap if (this.next_)
						this.stream_decrypt_or_f(data,cmd);
					};
				};
			},
			relay_send_truncate:function(error) {
				//console.log('STREAM :-------------------------- SEND TRUNCATE -------------------'+this.server_.ip+' CIRC '+this.circId);
				var stream=new Stream(Stream.prototype.RELAY_TRUNCATE,0,(new Buffer(1)).writeUInt(error),this.Df_hash);
				var cell=new Cell(this.circId,Cell.prototype.RELAY,this.stream_encrypt_forward(stream));
				this.send(cell);
			},
			relay_end_handle:function(id,error) {
				//var request=this.first_[id]?this.first_[id]:this.first_.request_;
				var first_=this.first_;
				var request=first_[id];
				var end_request=function() {
					if (request.download_) {
						Myalert('<p style="text-align:center">An unexpected event occured(error '+error+'), please retry</p>');
						remove(request.bar_);
					};
					request._write_(new Buffer(req_410(),'utf8'));
					request.end();
				};
				if (request) {
					//console.log('STREAM : before cid destroy on port '+request.remotePort+' for request '+request.i_id+' CID '+first_.circId+' Stream '+id);
					//console.log('RELAY_END on port '+request.remotePort+' for request '+request.i_id);
					if (request.abstract_client_tls) {
						request.abstract_client_tls.close();
					};
					//oconsole('STREAM : after cid destroy on port '+request.remotePort+' for request '+request.i_id);
					delete request.abstract_client_tls;
					if (!request.destroyed) {
						if ([6,12].indexOf(error)===-1) {
							if (!download) { //wait for relay_begin timeout
								console.log('END : RETRY error '+error+' CID '+this.first_.circId+' on port '+request.remotePort+' for request '+request.i_id+' nb_try '+request.nb_try);
								//oconsole('END : RETRY '+error+' on port '+request.remotePort+' for request '+request.i_id);
								console.log(request.nb_try);
								if (request.nb_try<NB_TRY) {
									request.nb_try++;
									first_.perf_--;
									//if ((parseInt(error)===2)||(parseInt(error)===4)) { //Resolve failed
										first_.resolved_failed++;
										if ((first_.resolved_failed>RESOLVED_FAILED)||(first_.sent_===0)) {
											//console.log('Resolve failed - destroy CIC '+first_.circId);
											first_.bad_=true;
										};
									//};
									first_.destroy_cid(request);
									first_.request_retry(request,error);
								} else {
									//console.log('no retry end for request '+request.i_id+' nb_try '+request.nb_try);
									end_request();
								};
							};
						} else {
							//if (!request._data_) {
							//console.log('REASON DONE CIC '+this.first_.circId+' on port '+request.remotePort+' for request '+request.i_id);
							//console.log(request._data_?request._data_:'_data_ undefined');
							//};
							if (!request._data_) {
								request.nb_try++;
								//console.log('END : REASON DONE no data RETRY error '+error+' CID '+this.first_.circId+' on port '+request.remotePort+' for request '+request.i_id);
								first_.perf_--;
								//if (request.no_exit.indexOf(first_)===-1) {
								//	request.no_exit.push(first_);
								//};
								first_.destroy_cid(request);
								first_.request_retry(request,error);
							} else {
								if (parseInt(error)===12) {
									console.log('END : CONNRESET RETRY '+error+' CID '+this.first_.circId+' on port '+request.remotePort+' for request '+request.i_id);
									//request.nb_try++;
									//request._data_=false;
									if (!request.download_) {
										first_.perf_--;
										//first_.request_retry(request,error);
										first_.destroy_cid(request);
										request.destroy();
									} else {
										if (request.url_) { //direct download
											var resume=function() {
												request.queue_.shift();
												Myalert('<p style="text-align:center">Remote server closed the connection (error '+error+'), download could not be completed, resuming...</p>');
												setTimeout(function(){$_('alert_box').style.display='none'},10000);
												var cb=function(request) {
													var url=url_decode(request.url_);
													//var length=request.blob_.size;
													request.blob_=chrome?new Buffer(0):(new Blob([],{type:(request.content_chrome?request.content_chrome:request.content_)}));
													//request.d_length=length;
													request.params_.stream=get_resume(url.host,url.rest,length);
													request.queue_=[];
													request.queue_s=[];
													//request.last_saved=result.data;
													delete request.eof_;
													delete first_[id];
													delete request.cid_;
													delete request.check_hash;
													delete request.file_id;
													delete request.start_t0;
													request.received_=0;
													Tor(request);
												};
												store_DB(request,true,cb);
											};
											request.queue_.push(resume);
											if (request.queue_.length===1) {
												request.queue_[0]();
											};
										} else {
											addEvent(document.body,'mousedown',function() {},false);
											Myalert('<p style="text-align:center">Remote server closed the connection (error '+error+'), download could not be completed, please use Resume to resume the download.</p>');
											if (!request.eof_) {
												request.eof_=true;
												request.queue_.push(fin_.bind(request));
												if (request.queue_.length===1) {
													request.queue_[0]();
												};
											};
										};
									};
								} else {
								//console.log('buffersize : '+request.bufferSize);
									if (!request.bufferSize) {
										if ((anonym)&&(request.abstract_client_tls)) {
											request.done_=true;
										} else {
										//console.log('END : REASON DONE - destroy port '+request.remotePort);
										//request.destroy(); //TODO : optimize for ianonym with TLS, do not destroy socks connection
											//console.log('DONE received '+request.received_+' for request '+request.i_id);
											//if (request.received_===1) { //Bad request
											//	first_.bad_=true; //destroy circuit
											//};
											if (!request.download_) {
												first_.destroy_cid(request);//do not close socks request but choose another circuit
												request.end();
											} else {
												if (!request.eof_) {
													request.eof_=true;
													request.queue_.push(fin_.bind(request));
													if (request.queue_.length===1) {
														request.queue_[0]();
													};
												};
											};
										};
									};
								};
							};
						};
					};
				} else {
					//console.log('relay_end request '+id+' does not exist any longer CID '+first_.circId);
				};
			},
			request_retry:function(request,error) {
				console.log('Retry for request '+request.i_id);
				if (!anonym) {
					Tor(request,this,'end '+error);
				} else {
					//console.log('END : RETRY WS_HANDLE '+error+' on port '+request.remotePort+' for request '+request.i_id+' '+request.relay_ws_handle);
					request.relay_ws_handle(request.ini_data,true)
				};
			},
			relay_truncated_handle:function(error) {
				console.log('STREAM : Truncated received from '+this.server_.ip+' CIRC '+this.circId+' '+error);
				var first_=this.first_;
				switch (error) {
					case 8: this.handle_destroy(0);break;
					case 11: this.handle_destroy(0);break;
					default:var tmp=first_.last_?first_.last_.ok_:null;if (!tmp) {this.next_.change_or('- relay truncated',this.extend)};
				};
			},
			relay_truncate_handle:function(error) {
				console.log('truncate '+this.circId);
				var next_=this.next_;
				var cell=new Cell(next_.circId,Cell.prototype.DESTROY,(new Buffer('05','hex'))); //reason destroy
				next_.send(cell);
				var stream=new Stream(Stream.prototype.RELAY_TRUNCATED,0,(new Buffer(1)).writeUInt(error),this.Db_hash);
				this.stream_encrypt_or_b(stream.toBuffer(),Cell.prototype.RELAY,true);
			},
			stream_handle:function(stream) {
				//console.log('decrypted and recognized '+Date.now());
				switch (stream.command.readUInt()) {
					case stream.RELAY_EXTEND : this.extend_cell_handle(stream.data);break; //OR
					case stream.RELAY_TRUNCATE : this.end('Error OR receive truncate :'+stream.data.slice(0,1).readUInt(),'truncate');break;//OR
					case stream.RELAY_EXTENDED : this.created_cell_handle(new Cell(this.circId,Cell.prototype.CREATED,stream.data,true));break;
					case stream.RELAY_TRUNCATED : this.end('Error OP receive truncated :'+stream.data.slice(0,1).readUInt(),'truncated');break;
					case stream.RELAY_END : this.end('End :'+stream.data.slice(0,1).readUInt(),'relay_end',stream.streamId.readUInt());break;
					case stream.RELAY_CONNECTED : this.stream_decode_connected(stream);this.stream_handle_connected(stream.streamId.readUInt());break;
					case stream.RELAY_DATA : this.stream_handle_data(stream);break;
					case stream.RELAY_SENDME : oconsole('RECEIVING SENDME CIC '+this.circId+' for stream '+stream.streamId.readUInt());this.stream_handle_sendme(stream.streamId.readUInt());break;
					case stream.RELAY_ASSOCIATE : this.associate((stream.data.slice(0,stream.length.readUInt())).toString('utf8'));break; //OR
					case stream.RELAY_INFO : this.info(stream.data.slice(0,stream.length.readUInt()));break; //OR and OP
					case stream.RELAY_WS : this.relay_ws_handle(stream.data.slice(0,stream.length.readUInt()));break; //OR and OP
					case stream.RELAY_DB_INFO : this.relay_db_info_handle(stream.data.slice(0,stream.length.readUInt()));break; //DB_OR
					case stream.RELAY_DB_QUERY : this.relay_db_query_handle(stream);break; //DB_OR and OP
					case stream.RELAY_DB_CONNECTED : this.relay_db_connected_handle(stream);break; //DB_OR and OP
					case stream.RELAY_DB_END : this.relay_db_end_handle(stream);break; //DB_OR and OP
					case stream.RELAY_DB_DATA : this.relay_db_data_handle(stream);break; //DB_OR and OP
					case stream.RELAY_DB_SENDME : this.relay_db_sendme_handle(stream);break; //DB_OR and OP
					case stream.RELAY_BEGIN_DIR : this.end('End begin_dir :14','begin_dir',stream.data.slice(0,1).readUInt());break;//TODO - ugly - handle relay_end errors
				};
			},
			stream_decode_connected: function(stream) {
				//var ip=ValToIP(stream.data.slice(0,4));
				//var request=this.first_[stream.streamId.readUInt()];
				//oconsole('DNS IP connected TO '+ip+' for request '+request.i_id);
			},
			stream_handle_sendme:function(id) {
				var circ=this.first_;
				var stream=circ.stream_;
				oconsole('FLUSH BUFFER CIC '+circ.circId+' sid '+(id||' whole circuit ')+' buffer length '+stream.length);
				var l=Math.min(id?LSTREAM_WINDOW:LCIRCUIT_WINDOW,stream.length);
				var i=0;
				if (!id) {
					circ.circuit_window_s +=LCIRCUIT_WINDOW;
				} else {
					circ[id].stream_window_s +=LSTREAM_WINDOW;
				};
				while (i<l) {
					var cell=id?stream[i]:stream[0];
					if (id) {
						if (id===cell[1]) {
							setTimeout(function(){circ.send(cell[0])},parseInt(1+1000/(BANDWIDTH/BSIZE)));
							cell[0]=null;
							i++;
						};
					} else {
						if (cell[0]) {
							setTimeout(function(){circ.send(cell[0])},parseInt(1+1000/(BANDWIDTH/BSIZE)));
							i++;
						};
						stream.shift();
					};
				};
			},
			stream_handle_connected:function(id,tid) {
				var first_=this.first_;
				var request=first_[id];
				if (request) {
					if (download) {
						clearTimeout(request.socket_retry);
					};
					this.clear_timers();
					console.log('STREAM RELAY :--------------RELAY_CONNECTED TO---------------------- '+this.server_.ip+' VIA '+this.first_.server_.ip+' CID '+this.circId+' Stream '+id+' for request '+request.i_id);
					if (request.params_.stream) {
						var cd=request.params_.stream;
						//console.log('---connected-send relay_data--- request '+request.i_id+' CID '+this.circId);
						//console.log(cd.toString('utf8'));
						//console.log('---end---');
						//if ((NB_C>2)&&(!request.nb_try)&&(request.i_id!==0)) {
						//	setTimeout(function() {first_.destroy();console.log('Destroy CID '+first_.circId)},200);
						//};
							while (cd.length) {
								if (request.tls_client_connected||(!request.abstract_client_tls)) {
									//console.log('STREAM RELAY :--------------SEND RELAY_DATA TO----------------------'+this.server_.ip+' CID '+this.circId+' Stream '+id+' for request '+request.i_id+' on port '+request.remotePort+' length '+cd.length);
								} else {
									//console.log('STREAM RELAY :--------------TLS HANDSHAKE SEND RELAY_DATA TO----------------------'+this.server_.ip+' CID '+this.circId+' Stream '+id+' for request '+request.i_id+' on port '+request.remotePort+' length '+cd.length);
								};
								//console.log(cd.toString('utf8'));
								//console.log(cd.toString('hex').substr(0,50));
								if (!tid) {
									var stream=new Stream(Stream.prototype.RELAY_DATA,id,cd.slice(0,Math.min(cd.length,PAYLOAD_STREAM)),this.Df_hash);
								} else {
									var stream=new Stream(Stream.prototype.RELAY_DB_DATA,id,[new Buffer(tid,'hex'),cd.slice(0,Math.min(cd.length,PAYLOAD_STREAM-16))].concatBuffers(),this.Df_hash);
								};
								var cell=new Cell(this.circId,Cell.prototype.RELAY,this.stream_encrypt_forward(stream));
								request.sent_++;
								first_.resolved_failed=0;
								first_.sent_++;
								first_.time_=Date.now(); //update time circuit used
								request.start_t0=first_.time_;
								//console.log('sent '+request.sent_+' for request '+request.i_id+' on cic '+first_.circId+' sent '+first_.sent_);
								request.stream_window_s--;
								first_.circuit_window_s--;
								if ((request.stream_window_s===0)||(first_.circuit_window_s===0)) {
									console.log('Bufferizing CIC '+first_.circId+' for request '+request.i_id);
									first_.stream_.push[cell,id];
								} else {
									this.send(cell);
								};
								if (cd.length>PAYLOAD_STREAM) {
									cd=cd.slice(PAYLOAD_STREAM);
								} else {
									break;
								};
							};
						if (request.squeue_) {
							request.squeue_.shift();
							if (request.squeue_.length) {
								request.squeue_[0]();
							};
						};
					} else {
						//console.log('INCOMING SOCKET : '+request.start_);
						request_start(request);
					};
				};
			},
			stream_handle_data:function(stream,tls,req) {
				//this is last
				var first_=this.first_;
				if (first_) {
					var sid=stream.streamId.readUInt();
					var request=req||first_[sid];
					if (request) {
						if (!tls) {
							if (!request.time_resp) {
								request.time_resp=Date.now();
								first_.time_average=(first_.time_average+(request.time_resp-request.time_ini))/(first_.time_average?2:1);
								//console.log('request '+request.i_id+' CID '+first_.circId+' '+(stream.data.slice(0,stream.length.readUInt())).toString('utf8'));
							};
						};
						if (!tls) {
							if (request.tls_client_connected||(!request.abstract_client_tls)) {
								//console.log('STREAM RELAY : --------------RECEIVE RELAY_DATA FROM---------------------- '+this.server_.ip+' CID '+this.circId+' Stream '+sid+' for request '+request.i_id+' length '+stream.length.readUInt());
								//var deb=(stream.data.slice(0,stream.length.readUInt()));
								//console.log('received from stream --- '+deb.toString('hex')+' length '+deb.length);
								//console.log('received from stream --- '+deb.toString('utf8')+'--------');
							} else {
								//console.log('STREAM RELAY : --------------TLS HANDSHAKE - RECEIVE RELAY_DATA FROM---------------------- '+this.server_.ip+' CID '+this.circId+' Stream '+sid+' for request '+request.i_id+' length '+stream.length.readUInt());
							};
						} else {
							console.log('STREAM RELAY : --------------RECEIVE DECODED TLS DATA FROM---------------------- '+this.server_.ip+' CID '+this.circId+' Stream '+sid+' for request '+request.i_id+' length '+stream.length.readUInt());
							var deb=(stream.data.slice(0,stream.length.readUInt()));
							//console.log('received from stream --- '+deb.toString('hex')+' length '+deb.length);
							//console.log('received from stream --- '+deb.toString('utf8')+'--------');
						};

						var resp=(stream.data.slice(0,stream.length.readUInt()));
						//console.log('received from stream --- '+resp.toString('hex')+' length '+resp.length);
						//console.log('received from stream --- '+resp.toString('utf8')+'--------');
						if (!tls) {
							if ((!request._data_)||(request.wait_header)) {
								//console.log('STREAM RECEIVE RELAY_DATA : RESPONSE FOR request '+request.i_id+' - '+((request.params_.stream.toString('utf8')).substr(0,200))+'xxxxxxxxxxxx'+(request.params_.stream.toString('hex'))+' xxxxxxxxxxxxxxxx '+(resp.toString('utf8')).substr(0,200)+' on port '+request.remotePort);
								//console.log('STREAM RECEIVE RELAY_DATA : RESPONSE FOR request '+request.i_id+' - '+(resp.toString('utf8')));
								var parse;
								request._data_=true;
								if (request.download_) {
									resp=request.wait_header?[request.wait_header,resp].concatBuffers():resp;
									//console.log(resp.toString('utf8'));
									parse=simpleParser(resp.toString('utf8'));
									if ((typeof parse['1a']==='undefined')||(parse['1a']!=='')) { //header not complete
										//console.log('header not complete '+request.i_id);
										request.wait_header=resp;
										return;
									} else {
										delete request.wait_header;
									};
									var status=parse['0a'].split(' ');
									status=status[1]?status[1]:null;
									if (status) {
										oconsole('STREAM : status code '+status+' for request '+request.i_id);
										status=status.toString();
										if (['2','3'].indexOf(status[0])===-1) {
											Myalert('<p style="text-align:center">Wrong URL, please check and try again</p>');
											remove(request.bar_);
											first_.send_relay_end(request.sid_);
											return;
										} else {
											if (status[0]==='3') {
												if (parse['Location']) {
													var url=url_decode(parse['Location']);
													request.params_.stream=get_request(url.host,url.rest);
													first_.send_relay_end(request.sid_);
													delete first_[request.sid_];
													delete request.cid_;
													Tor(request);
												};
												return;
											} else {
												request.d_length=request.d_length||0; //resume
												if (parse['Content-Type']) {
													request.content_=parse['Content-Type'];
												} else {
													request.content_='application/octet-binary';
												};
												if ((parse['Content-Length'])&&(!request.clength_)) { //resume
													request.clength_=parseInt(parse['Content-Length']);
												};
												if (!request.clength_) {
													for (var n in parse) {
														if (n.toLowerCase().indexOf('content-length')!==-1) {
															request.clength_=parseInt(parse[n]);
															break;
														};
													};
												};
												request.pieces=Math.ceil(request.clength_/BSIZE);
												if (parse['Transfer-Encoding']) {
													request.encoding_=parse['Transfer-Encoding'];
												};
												console.log('Start relay_data');
												console.log(request.content_);
												console.log(request.clength_);
												console.log(request.encoding_?request.encoding_:'No encoding');
												if (request._stream_) {
													init_media(request);
												};
												resp=(resp.toString('hex')).split(CRLF+CRLF);
												resp.shift();
												resp=new Buffer(resp.join(CRLF+CRLF),'hex');
											};
										};
									};
									//clearTimeout(request.socket_retry);
								};
							};
							request._data_=true;
							request.received_++;
							first_.received_++;
							first_.circuit_window--;
							request.stream_window--;
							request.resp__=request.resp__?(request.resp__+resp.length):resp.length;
							//console.log('received '+request.received_+' stream_window '+request.stream_window+' on cic '+first_.circId+' circuit_window '+first_.circuit_window+' total '+request.resp__);
							if ((first_.received_%LCIRCUIT_WINDOW===0)&&(first_.circuit_window<FLOWC*CIRCUIT_WINDOW)) {
								first_.sendme();
								first_.circuit_window +=LCIRCUIT_WINDOW;
							};
							if ((request.received_%LSTREAM_WINDOW===0)&&(request.stream_window<FLOWC*STREAM_WINDOW)) {
								//oconsole('sendme for request '+request.i_id);
								first_.sendme(request);
								request.stream_window +=LSTREAM_WINDOW;
							};
						} else {
							request.received_++;
						};
						if (request.received_===1) {
							//console.log('received '+request.received_+' for request '+request.i_id);
							//console.log(resp.toString('utf8'));
							//var parse=simpleParser(resp.toString('utf8'));
							//var status=parse['0a'].split(' ');
							//status=status[1]?status[1]:null;
							//if (status) { //TODO - HTTP response without headers (fr.yahoo.com)
							//	if ([400].indexOf(parseInt(status))!==-1) {
							//		console.log('status 400 destroy CID '+first_.circId+' received '+first_.received_);
									//console.log(request.params_.stream.toString('utf8'));

							//		first_.bad_=true;//destroy circuit
							//	};
							//};
						};

						if (anonym) {
							resp=BufferToArrayBuffer(resp);
							if (typeof request.content_==='undefined') {
								resp=request.wait_header?[request.wait_header,resp].concatBuffers():resp;
								//parse=parse?parse:simpleParser(resp.toString('utf8'));
								parse=simpleParser(resp.toString('utf8'));
								if (typeof parse['1a']==='undefined') { //header not complete
									console.log('header not complete '+request.i_id);
									//console.log(resp.toString('utf8'));
									request.wait_header=resp;
									return;
								} else {
									delete request.wait_header;
								};
								if (parse['Location']) {
									if (request.referer_===location) { //first request from the page
										request._host_=parse['Location'];
										if (request.request_) {
											request.request_._host_=request._host_;//request_decoded
										};
										console.log('navigate '+parse['Location']);
										first_.navigate(request);
									} else {
										var redir=(replace(parse['Location'],{fake_domain:fake_domain},true)).html;
										console.log('redirect '+redir);
										first_.redirect(request,redir);
									};
									return;
								};
								if (parse['Content-Type']) {
									request.content_=parse['Content-Type'];
								} else {
									request.content_='';
								};
								request.encoding_=parse['Transfer-Encoding']?parse['Transfer-Encoding']:null;
								//if (request.encoding_==='chunked') {console.log('encoding request '+request.i_id+' '+request.encoding_);}
							};
							//if ((request.content_?(request.content_.indexOf('text/html')!==-1):null)&&(['identity','chunked'].indexOf(request.encoding_)!==-1)) {
							if (request.content_?(request.content_.indexOf('text/html')!==-1):null) {
								if (!request.html_) {
									if (request._host_) { //TODO see if necessary to check that status is not equal to 204 (No Content)
										first_.navigate(request);
										return;
									};
									request.decoder_=new TextDecoder('utf-8');
									request.t0_=new Date().valueOf();
									if (!request.header_l) {
										request.html_=new Buffer(0);
										var l=(resp.toString('hex')).split(CRLF+CRLF);
										if (l.length>1) {
											request.header_=[request.header_?request.header_:(new Buffer(0)),new Buffer(l[0]+CRLF+CRLF,'hex')].concatBuffers();
											request.header_l=request.header_.length;
										} else {
											request.header_=resp;
											return;
										};
									};
									if (request.header_l) {
										l.shift();
										if (request.encoding_!=='chunked') {
											var parse=simpleParser(request.header_.toString('utf8'));
											if (parse['Content-Length']) {
												request.clength_=parseInt(parse['Content-Length']);
												request.content_l=true;
											};
										} else {
											/*	content-yyy: xx\r\n
												\r\n
												length
												\r\n
												xx
												\r\n
												length
												*/
											l=(l.join(CRLF+CRLF)).split(CRLF);
											/*	length,
												xx,
												length
												*/
											//console.log('clength head');
											//console.log(l);
											//request.header_=[request.header_,new Buffer(l[0]+CRLF,'hex')].concatBuffers();
											request.header_l =request.header_.length;
											if (l.length>1) {
												request.clength_=parseInt((new Buffer(l[0],'hex')).toString('utf8'),16);
											} else {
												request.clength_=0;
											};
											l.shift();
												/* xx,
												length
												*/
											request.pass_='';
											request.buff_='';
										};
										//console.log('response header initial : '+request.header_.toString('utf8'));
										resp=new Buffer(l.join(CRLF),'hex');
										//console.log('resp ini '+resp.toString('utf8')+' '+resp.toString('hex'));
									};
								};

								if (request.encoding_==='chunked') {
									if ((request.html_.length+resp.length>=request.clength_)||(request.wait_)) {
										var sc=chunk(resp.toString('hex'),request);
										//console.log('handle chunk result '+sc);
									};
									//request.html_ =[request.html_,sc?sc:resp].concatBuffers();//full page
									if (Buffer.isBuffer(request.html_)) {
										request.html_={length:parseInt((sc?sc:resp).length)};
									} else {
									//request.html_.length +=parseInt((sc?sc:resp).length);
									request.html_.length +=(sc?0:parseInt(resp.length));
									};
								} else {
									request.html_=[request.html_,resp].concatBuffers();
								};
								//console.log('response html_ : '+request.html_.toString('utf8')+' '+request.html_.length+' '+request.clength_);
								//oconsole('chunk '+(new Date().valueOf()-request.t0_));
								//console.log('html request '+request.clength_+' '+request.html_.length);
								//console.log('end '+(request.end_?'true':'false'));
								//console.log('html request '+request.clength_+' '+request.html_.length+' '+(request.tmp_?(request.html_.length-request.tmp_):'')+' '+(request.tmp__?request.tmp__:'')+' '+request.header_l+' '+(request.end_?'end':'not end')+' '+(sc?sc:resp).toString('hex')+' '+(sc?sc:resp).length+' '+(sc?sc:resp).toString('utf8'));
								//if (sc) {console.log('sc '+sc.toString('hex'));}
							} else {
								//TODO remove cookies for outside domains
							}
						};
						//if ([404,403,400].indexOf(parseInt(status))!==-1) {
						//if ([200].indexOf(parseInt(status))===-1) {
							//console.log('404 403 400 '+(request.params_.stream).toString('utf8')+' request '+request.i_id);
							//console.log('not 200 status '+status+' '+(request.params_.stream).toString('utf8')+' request '+request.i_id);
							//if (request.nb_try<NB_TRY) {
							//	request._data_=false;
							//	first_.perf_--;
							//	request.nb_try++;
							//	if (request.no_exit.indexOf(first_)===-1) {
							//		request.no_exit.push(first_);
							//	};
							//	request.setKeepAlive(true,500);
							//	this.relay_end_handle(sid,' exit node 404 '+sid+' '+request.remotePort);
							//} else {
								//console.log('STREAM : NOT FOUND COULD NOT RETRIEVE - '+((request.params_.stream).toString('utf8')).substr(0,50)+' on port '+request.remotePort);
								//oconsole('STREAM : NOT FOUND COULD NOT RETRIEVE for request '+request.i_id+' on port '+request.remotePort);
								//clearTimeout(request.socket_retry);
								//request._write_(resp);
							//};
						//} else {
							try {
							//console.log(request.html_.toString('hex'));
							//console.log(request.content_l);
							//console.log(request.clength_);
							//console.log(request.html_.length);
							//console.log(request.encoding_);
							} catch(ee) {};
							if (!tls) {
								first_.perf_++;
							};
							request.nb_try=0;
							var l=request.html_?((((request.content_l)&&(request.clength_===request.html_.length))||(request.encoding_==='chunked'))?false:true):false;
							if (!l) {
								//console.log('INCOMING STREAM : write response on port '+request.remotePort+' for request '+request.i_id);
								if (request.encoding_!=='chunked') {
									request.socks_s=false;
								};
								if (request.html_) {
									//oconsole('INCOMING STREAM : write html response on port '+request.remotePort+' for request '+request.i_id);
									/* only works with utf8 from here */
									//oconsole('html '+(new Date().valueOf()-request.t0_));
									//var html=request.html_.toString('utf8');
									var html=request.content_l?(request.html_.toString('utf8')):(request.decoder_.decode(sc?sc:resp,{stream:true}));
									//console.log('html1 '+html);
									if (!request.content_l) {
										request.buff_ +=html;
										if ((request.buff_.length<CHUNK_L)&&(!request.end_)) {
											return;
										} else {
											if (!request.pass_) {
												html=request.buff_;
											};
										};
									};
									//oconsole('replace '+(new Date().valueOf()-request.t0_));
									var htmlr=replace(html,request);
									//console.log(htmlr);
									if (!htmlr.pass) {
										if (request.pass_) {
											//console.log('----------pass--------------'+request.pass_);
											var tmp=replace([request.pass_,html].join(''),request);
											if (!tmp.pass) {
												html=tmp.html;
												request.pass_='';
											} else {
												return;
											}
										} else {
											html=htmlr.html;
										};
									} else {
										return;
									};
									//console.log('html2 '+html);
									if (!request.script_) {
										//oconsole('addscript '+(new Date().valueOf()-request.t0_));
										html=addScript(html,request);
										request.script_=true;
										//console.log('html3 '+html.substr(0,50));
									};
									if ((!request.header_sent)||(request.content_l)) {
										if (request.header_l) {
											var parse=simpleParser(request.header_.toString('utf8'));
											if (parse['X-Frame-Options']) {
												delete parse['X-Frame-Options'];
											};
											//console.log('edit parse');
											//for (var n in parse) {
											//	console.log(n+' '+parse[n]);
											//};
											//oconsole('head '+(new Date().valueOf()-request.t0_));
											if (request.content_l) {
												parse['Content-Length']=(new Buffer(html,'utf8')).length;
											};
											request.header_=reconstitute(parse);
											request.header_sent=true;
											if (!request.content_l) {
												//console.log('reconstituted header sent :\n'+request.header_.toString('utf8'));
												if (!tls) {
													request._write_(new Buffer(request.header_,'utf8'));
												} else {
													tls(new Buffer(request.header_,'utf8'));
												};
											} else {
												resp=new Buffer([request.header_,html].join(''),'utf8');
											};
										} else {
											return;
										};
									};
									//console.log(resp.toString('utf8'));
									if (!request.content_l) {
										var l=(new Buffer(html,'utf8')).length;
										if (request.end_) {
											html='\r\n'+(l-7).toString(16)+'\r\n'+html;
											console.log('end html-------');
											//html=html+'\r\n0\r\n\r\n';
											request._init_();
											request.socks_s=false;
										} else {
											html='\r\n'+l.toString(16)+'\r\n'+html;
											request.socks_s=true;
										};
										//console.log('html req :\n'+html);
										resp=new Buffer(html,'utf8');
										//console.log('html req :\n'+resp.toString('hex'));
										request.buff_='';
									} else {
										request.socks_s=false;
										request._init_();
									};

									//console.log('response header reconstituted :'+request.header_+' '+(new Buffer(request.header_,'utf8')).toString('hex'));
									//console.log('replaced html '+html);
									//console.log('html reconstituted '+[request.header_,html].join(''));
									//oconsole('write delay '+(request.t0_?(new Date().valueOf()-request.t0_):'end')+' length '+resp.length);
									//console.log('replaced '+resp.toString('hex'));
								};
								//console.log('write ');
								if (!tls) {
									request._write_(resp);
								} else {
									tls(resp);
								};
							};
						//};
					};
				};
			},
			send:function(cell) {
				var first_=this.first_;
				var mcell=[cell.CircID,cell.Command];
				var command=cell.Command.readUInt();
				if ((cell.Command.readUInt()==7)||(cell.Command.readUInt()>=128)) {mcell.push(cell.Length)};

				var recurse=function(val) {
					if (!val.push) {
						for (var n in val) {
							if (Buffer.isBuffer(val[n])) {
								mcell.push(val[n]);
							} else {
								recurse(val[n]);
							};
						};
					} else {
						val.forEach(function(a) {
							if (Buffer.isBuffer(a)) {
								mcell.push(a);
							} else {
								recurse(a);
							};
						});
					};
				};
				if (cell.Payload.push) {
					cell.Payload.forEach(function(val) {
						recurse(val);
					});
				} else {
					mcell.push(cell.Payload);
				};
				mcell=mcell.concatBuffers();
				if ((cell.Command.readUInt()!==7)&&(cell.Command.readUInt()<128)) {
					var tmp=new Buffer(512);
					tmp.map(mcell);
					mcell=tmp;
				};
				//console.log('CIRC : SEND CELL '+this.server_.ip+' CIRC '+cell.CircID.readUInt()+' '+(mcell.toString('hex')).substr(0,50));
				if (this.OP_) {
					if (!first_.last_) {
						//not used
						var retry;
						this.clear_timers();
						var tc_;
						if (this===first_) {
							if (this.socket_) {
								if (this.socket_.handshake_) {
									retry=function() {console.log('CIRC : Create or first extend too long '+this.circId);this.circuit_retry()};
									tc_=TC_CREATE;
								} else {
									retry=function() {this.change_or('Handshake version no answer or bad answer - change OR')};
									tc_=TC_VERSION;
								};
							} else {
								retry=function() {this.change_or('Handshake version no answer or bad answer - change OR')};
								tc_=TC_VERSION;
							};
						} else {
							retry=function() {console.log('CIRC : Extend delay expired - change or '+this.server_.ip);this.change_or('- extend too long',this.extend);};
							tc_=TC_EXTEND;
						};
						//console.log('CIRC : setTimeout circ');
						//first_.tc_.push(setTimeout(retry.bind(this),tc_));
					};
				};
				//if (parseInt(command)===Cell.prototype.CERTS) {
				//	console.log('---------------kkkkkkkkkkkkkkkkkkkkkkkk-----------------------');
				//	console.log(mcell.toString('hex'));
				//};

				if (Pchart&&Dchart) {
					if (chart1_int||chart2_int) {
						if (parseInt(command)!==Cell.prototype.PADDING) {
							var round=DateRound(new Date()).getTime();
							var l=mcell.length;
							if (first_===db_cid) {
								if (chart2_int) {
									Pchart.dynRow2[round]=Pchart.dynRow2[round]?(Pchart.dynRow2[round]+l):l;
								};
							} else {
								if (chart1_int) {
									Dchart.dynRow2[round]=Dchart.dynRow2[round]?(Dchart.dynRow2[round]+l):l;
								};
							};
						};
					};
				};

				if ((this.OP_)||(this.OR_)) {
					try {
						if ((this.OR_)&&((cell.Command.readUInt()===6))) {
							console.log('sending FAST to '+(this.server_?this.server_.ip:'OP')+' CIC '+cell.CircID.readUInt());
							//console.log(mcell.toString('hex'));
							//console.log(this.socket_.write);
						};
						if (!this.socket_.ws_) {
							//console.log('cleartext write '+mcell.toString('hex'));
							//console.log(mcell.toString('hex'));
							this.socket_.write(mcell);
						} else {
							if (this.OP_) {
							//console.log('socket port avant send/write'+this.socket_.address().port);
							//console.log('OP ws encode');
							//console.log(mcell.toString('hex'));
							if ((!window_browser||window_browser_server)&&(!peersm_client)) {
								var tmp=wsencode(mcell,0x02,true);
							} else {
								var tmp=mcell;
							};
							//console.log('OP ws encoded');
							//console.log(tmp.toString('hex'));
								this.socket_.write(tmp);
							} else {
							//console.log(mcell.toString('hex'));
							//console.log('OR wsencode '+this.socket_.remotePort);
							//console.log(mcell.toString('hex'));
							//console.log('OR wsencoded');
							//var tmp=wsencode(mcell,0x02,false);
							//console.log(tmp.toString('hex'));
								this.socket_.write(wsencode(mcell,0x02,false));
							};
						};
					} catch(ee) {
						console.log('OUTGOING SOCKET '+(this.OP_?'OP ':'OR ')+(this.socket_?(this.socket_.ws_?'WS':'TLS'):'')+' IP '+(this.server_?this.server_.ip:'')+' socket does not exist any longer');
						if (this.OP_) {
							if (this.socket_) {
								if (!peersm_client) {
									this.socket_.destroy();
								};
							} else {
								console.log('send1 circuit destroy');
								this.circuit_destroy(true);
							};
						} else if (!this.OR_f) {
							var sock=this.socket_;
							console.log('send2 circuit destroy');
							this.circuit_destroy(true);
							if (sock) {
								sock.circuits_destroy();
							};
						};
					};
				} else {
					sock.write(cell);
				};
			},
			navigate: function(request) {
				var fake_d=url_decode(request._host_);
				fake_d.host='www.'+encrypt_decrypt(fake_d.host,true)+FAKE_SFX;
				fake_domain=fake_d.host;
				protocol=fake_d.protocol;
				client.associate(fake_cid,fake_d.host);
				console.log('send 301 and associate fake_d '+fake_d.host+' location '+url_encode(fake_d));
				//console.log(req_301(url_encode(fake_d)));
				this.redirect(request,url_encode(fake_d));
			},
			redirect : function(request,url) {
				console.log('redirect send 301 for url '+url);
				if (!TLS_OP) {
					request._write_(new Buffer(req_301(url),'utf8'));
				} else {
					alert(req_301(url));
					request.__write__(new Buffer(req_301(url),'utf8'));
				};
				request.destroy(); //TODO : should be request.close() because no error
				//var stream=new Stream(Stream.prototype.RELAY_END,request.sid_,new Buffer(0),this.last_.Df_hash);
				//var cell=new Cell(this.circId,Cell.prototype.RELAY,this.last_.stream_encrypt_forward(stream));
				//this.send(cell);
				this.destroy_cid(request,true);
				delete request._host_;
			},
			send_db_info: function(boo) {
				boo=boo||null;
				var get_db_info=function(val) {
					if (val.file_length===val.current_length) {
						if (db_cid) {
							console.log('send_db_info');
							console.log(val.name_hash);
							var hash_name=new Buffer(val.name_hash,'hex');
							var hash_name_l=(new Buffer(1)).writeUInt(hash_name.length);
							var part=(new Buffer(1)).writeUInt(0);
							var facilitator=peersm_client?((new Buffer(1)).writeUInt(1)):(new Buffer(0));
							var payload=[hash_name_l,hash_name,part,facilitator].concatBuffers();
							var stream=new Stream(Stream.prototype.RELAY_DB_INFO,0,payload,db_cid.last_.Df_hash);
							var cell_=new Cell(db_cid.circId,Cell.prototype.RELAY,db_cid.last_.stream_encrypt_forward(stream));
							db_cid.send(cell_);
						};
					};
				};
				if (peersmDB) { //TODO investigate strange case doing OP
					peersmDB.list(get_db_info,boo);
				};
			},
			send_db_query: function(request,tid) {
				if (request) {
					console.log('send_db_query');
					var sid;
					var hash=request.params_?request.params_.hash_:request.hash_;
					var size=request.d_length.toString(16);
					size=size.length%2?('0'+size):size;
					size=new Buffer(size,'hex');
					size_l=(new Buffer(1)).writeUInt(size.length);
					console.log('send_db_query hash '+hash.toString('hex')+' CIC '+this.circId+' size '+size_l[0]+' size '+size.toString('hex')+' tid '+(tid?tid.toString('hex'):''));
					var payload=[(new Buffer(1)).writeUInt(hash.length),hash,size_l,size].concatBuffers();
					if (tid) {
						payload=[payload,tid].concatBuffers();
						sid=0;
					} else {
						sid=choose_id(this);
						request.cid_=this;
						if (sid) {
							this[sid]=request;
							request.sid_=sid;
							request.received_=0;
							request.sent_=0;
						} else {
							return; //TODO failure
						};
					};
					if (this.OP_) {
						console.log('OP send db_query  CID '+this.circId+' sid '+sid);
						var stream=new Stream(Stream.prototype.RELAY_DB_QUERY,sid,payload,this.last_.Df_hash);
						var cell=new Cell(this.circId,Cell.prototype.RELAY,this.last_.stream_encrypt_forward(stream));
						request.query_time=Date.now();
						this.send(cell);
						var end_p=(function() { //serving party not responding
							if (this.socket_) {
								if (!request._torrentc_) {
									console.log('serving party not responding');
									request.db_try++;
									this.send_db_end(1,sid);
									delete request.sid_;
									this.send_db_query(request);//retry
								};
							};
						}).bind(this);
						if (request.db_try<DB_NB_TRY) {
							request.query_t0=request.query_t0||[];
							request.query_t0.push(setTimeout(end_p,DB_QUERY_RETRY));
						} else {
							this.send_db_end(1,sid);
							delete request.sid_;
							Myalert('<p style="text-align:center">No answer from the network, changing peer to peer circuits, please wait and retry</p>');
							remove(request.bar_);
							console.log('db_query no answer circuit destroy');
							db_cid.circuit_destroy();
							db_cid=null;
							//Tor({params_:{OP:true,nb_hop:NB_DB_HOP,ws:client,db:true}});
						};
					} else {
						this.prev_=this;
						this.nb_query=this.nb_query||0;
						this.nb_query++;
						console.log('ORDB send db_query  CID '+this.prev_.circId+' sid '+sid+' '+this.prev_.socket_.remoteAddress);
						var stream=new Stream(Stream.prototype.RELAY_DB_QUERY,sid,payload,this.prev_.Db_hash);
						this.stream_encrypt_or_b(stream.toBuffer(),Cell.prototype.RELAY,true);
						delete this.prev_;
					};
				};
			},
			send_db_end: function(reason,sid,tid) {
				/*
				0 UNAVAILABLE
				1 FINISHED (aborted by requesting party)
				2 DESTROYED (serving party destroyed)
				3 TO BE CONTINUED (subsequent torrent file, not used)
				*/
				sid=sid||0;
				console.log('send db_end CID '+this.circId+' sid '+sid+' reason '+reason+' '+((typeof tid!=='undefined')?(tid.toString('hex')):''));
				//console.log(this.send_db_end.caller.toString().substr(0,50));
				var payload=(new Buffer(1)).writeUInt(reason);
				payload=tid?[payload,tid].concatBuffers():payload;
				if (this.OP_) {
					var stream=new Stream(Stream.prototype.RELAY_DB_END,sid,payload,this.last_.Df_hash);
					var cell=new Cell(this.circId,Cell.prototype.RELAY,this.last_.stream_encrypt_forward(stream));
					this.send(cell);
				} else {
					this.prev_=this;
					var stream=new Stream(Stream.prototype.RELAY_DB_END,sid,payload,this.prev_.Db_hash);
					this.stream_encrypt_or_b(stream.toBuffer(),Cell.prototype.RELAY,true);
					delete this.prev_;
				};
			},
			send_db_connected: function(size,sid,type,tid) {
				size=size.toString(16);
				size=size.length%2?('0'+size):size;
				size=new Buffer(size,'hex');
				size_l=(new Buffer(1)).writeUInt(size.length);
				type=new Buffer(type,'utf8');
				var type_l=(new Buffer(2)).writeUInt(type.length);
				console.log('send db_connected '+size.toString('hex')+' CID '+this.circId+' sid '+sid+' type '+type.toString('utf8'));
				var payload=[size_l,size,type_l,type].concatBuffers();
				if (tid) {
					payload=[tid,payload].concatBuffers();
				};
				if (this.OP_) {
					var stream=new Stream(Stream.prototype.RELAY_DB_CONNECTED,sid,payload,this.last_.Df_hash);
					var cell=new Cell(this.circId,Cell.prototype.RELAY,this.last_.stream_encrypt_forward(stream));
					this.send(cell);
				} else {
					this.prev_=this;
					var stream=new Stream(Stream.prototype.RELAY_DB_CONNECTED,sid,payload,this.prev_.Db_hash);
					this.stream_encrypt_or_b(stream.toBuffer(),Cell.prototype.RELAY,true);
					delete this.prev_;
				};
			},
			send_db_data: function(cd,sid,nb_blocks,boo) {
				if (this.OP_) {
					var request=this[sid];
					if (!boo) {
						request.fc_t=[];
						//request.sent_=0;
						console.log('sending db data '+ (this.OP_?'OP ':'ORDB sid ')+sid+' BANDWIDTH '+BANDWIDTH*8+' bps '+(this.OP_?(' 512 bytes every '+(1+1000/(BANDWIDTH/BSIZE))+' ms then nbblocks '+(Math.ceil((FLOWB*BANDWIDTH/1000)/BSIZE))+' every '+FLOWB+ 'ms window size '+request.stream_window_s+' '+this.circuit_window_s):'')+' cd '+cd.size);
						request.start_t0=Date.now();
						request.cd_length=cd.size;
						request.messageName='flush';
						if (!peersm_client) {
							addEvent(window,"message",flush_.bind(request),true);
						};
						request.reader=peersm_client?(new FileReader_node()):(new FileReader());
						request.cursor=0;
					};
					send_data.call(this,cd,sid,request,nb_blocks);
				} else {
					//while (cd.length) {
						this.prev_=this;
						var stream=new Stream(Stream.prototype.RELAY_DB_DATA,sid,cd.slice(0,Math.min(cd.length,PAYLOAD_STREAM)),this.prev_.Db_hash);
						this.stream_encrypt_or_b(stream.toBuffer(),Cell.prototype.RELAY,true);
						delete this.prev_;
					//	if (cd.length>PAYLOAD_STREAM) {
					//		cd=cd.slice(PAYLOAD_STREAM);
					//	} else {
					//		break;
					//	};
					//};
				};
			},
			send_db_sendme: function(sid) {
				sid=sid||0;
				//console.log('SENDING SENDME CIC '+this.circId+' for stream '+sid+' time '+Date.now());
				if (this.OP_) {
					var stream=new Stream(Stream.prototype.RELAY_DB_SENDME,sid,new Buffer(0),this.last_.Df_hash);
					var cell=new Cell(this.circId,Cell.prototype.RELAY,this.last_.stream_encrypt_forward(stream));
					this.send(cell);
				} else {
					this.prev_=this;
					var stream=new Stream(Stream.prototype.RELAY_DB_SENDME,sid,new Buffer(0),this.prev_.Db_hash);
					this.stream_encrypt_or_b(stream.toBuffer(),Cell.prototype.RELAY,true);
					delete this.prev_;
				};
			},
			circuit_keys: function(K0) {
				var exp_key=crypto_expand_key(K0.toString('hex'));
				var KH=exp_key.slice(0,20);
				this.Df_=exp_key.slice(20,40);
				this.Db_=exp_key.slice(40,60);
				this.Kf_=exp_key.slice(60,76);
				//console.log(this.Kf_.toString('hex'));
				this.Kb_=exp_key.slice(76,92);
				this.Kf_cipher=crypto.createcipheriv('aes-128-ctr',this.Kf_,IV);
				this.Kb_cipher=crypto.createcipheriv('aes-128-ctr',this.Kb_,IV);
				this.Df_hash=new Hash('sha1');
				this.Db_hash=new Hash('sha1');
				this.Df_hash.update(this.Df_);
				this.Db_hash.update(this.Db_);
				return KH;
			},
			stream_encrypt_forward: function(stream) {
				var circ=this;
				var enc=stream.toBuffer();
				//console.log('encrypt '+enc.length);
				while (circ) {
					//console.log('STREAM : encrypt for '+circ.server_.ip);
					enc=new Buffer(circ.Kf_cipher.update(enc,'hex','hex'),'hex');
					circ=circ.prev_;
				};
				return enc;
			},
			stream_decrypt_backward: function(data,error) {
				var circ=this;
				var stream;
				if (CRYPTO_PERF) {
					var t0=Date.now();
				};
				var l=data.length;
				//console.log('decrypt backward '+Date.now());
				while (circ) {
					if (circ.Kb_) {
						//console.log('decrypt1 '+Date.now());
						/* Perf 512 Bytes 0.2ms for update*/
						data=new Buffer(circ.Kb_cipher.update(data,'hex','hex'),'hex');
						stream=circ.recognized(data);
						if (stream) {break;}
					};
					circ=circ.extended_;
				};
				if (t0) {
					count_crypto++;
					count_crypto_data +=l;
					time_crypto_data +=Date.now()-t0;
					if (count_crypto%PERF_CRYPTO===0) {
						if (time_crypto_data) {
							console.log('CRYPTO perf decrypt backward: '+parseInt(count_crypto_data*8/(time_crypto_data/1000))+' bps');
							count_crypto=0;
							count_crypto_data=0;
							time_crypto_data=0;
						};
					};
				}
				if (stream) {
					if (stream.command.readUInt()===41) {
						//console.log('stream OP decrypted');
						//console.log(stream.toBuffer().toString('hex'));
					};
				};
				if (!stream) {
					this.end('Unrecognized stream','unrecognized');
					console.log(data.toString('hex'));
				} else {
					if (error) {
						var stream_=new Stream(Stream.prototype.RELAY_END,stream.streamId,new Buffer('0A','hex'),circ.Df_hash);
						var cell=new Cell(circ.circId,Cell.prototype.RELAY,circ.stream_encrypt_forward(stream_));
						circ.send(cell);
					} else {
						circ.stream_handle(stream);
					};
				};
			},
			stream_decrypt_or_f: function(data,cmd,boo) {
				if (this.Kf_cipher) {
					//var odata=data;
					//console.log('decrypt_or_f1 '+Date.now());
					data=new Buffer(this.Kf_cipher.update(data,'hex','hex'),'hex');
					//console.log('decrypt_or_f2 '+Date.now());
					//console.log('STREAM : decrypted for '+this.circId+' '+this.socket_.address().address+' '+(data.toString('hex')).substr(0,50));
					if (!boo) {
						var stream=this.recognized(data);
					};
					//console.log(this.Kf_.toString('hex'));
					//console.log('decrypt_or_f3 '+Date.now());
					if (stream) {
						this.stream_handle(stream);
						//console.log('decrypt_or_f4 '+Date.now());
					} else {
						//console.log('OR unrecognized stream');
						if (anonym_OR&&this.socket_.fake_cid===this) { //do not send forward if relay_ws
							//console.log('Bad formatted stream ws relay');
							//console.log(data.toString('hex'));
							//console.log(odata.toString('hex'));
						} else {
							if (this.next_) {
								//console.log('OR sending encrypted forward');
								var cell=new Cell(this.next_.circId,cmd,data);
								this.next_.send(cell);
							};
						};
					};
				};
			},
			stream_encrypt_or_b: function(data,cmd,boo) {
				var prev_=this.prev_;
				if (prev_) {
					if (prev_.Kb_cipher) {
						//console.log('encrypt or_b '+Date.now());
						data=new Buffer(prev_.Kb_cipher.update(data,'hex','hex'),'hex');
						//console.log('encrypt or_b2 '+Date.now());
						//console.log('STREAM : decrypt for '+circ.server_.ip+' '+(data.toString('hex')).substr(0,50));
						if (!boo) {
							var stream=prev_.recognized(data);
						};
						//console.log('encrypt or_b3 '+Date.now());
						if (stream) {
							this.stream_handle(stream);
						} else {
							if (prev_) {
								//console.log('OR sending encrypted backward');
								var cell=new Cell(prev_.circId,cmd,data);
								prev_.send(cell);
							};
						};
						//console.log('encrypt or_b4 '+Date.now());
					};
				};
			},
			recognized: function(data) {
				if (data.slice(1,3).readUInt()==0) {
					//try {
						var l=data.slice(9,11);
						//if (this.first_[(data.slice(3,5)).readUInt()]) {
							//console.log('STREAM : DECRYPTED STREAM FROM '+this.server_.ip+' CID '+this.circId+' STREAM '+(data.slice(3,5)).readUInt()+' for request '+this.first_[(data.slice(3,5)).readUInt()].i_id+' - length '+data.length+' '+(l.readUInt()+11));
						//} else {
							//console.log('CIRC : DECRYPTED STREAM FROM '+this.server_.ip+' CID '+this.circId+' - length '+data.length+' '+(l.readUInt()+11));
						//};
						//console.log(data.toString('hex'));
						var m=Math.min(11+l.readUInt(),data.length); //check bad formatted stream
						var stream=new Stream((data.slice(0,1)).readUInt(),(data.slice(3,5)).readUInt(),data.slice(11,m));
						stream.length=l;
						var digest=data.slice(5,9);
						if (!this.OR_f) {
							this.Db_hash.update(stream.toBuffer());
							var res=(new Buffer(this.Db_hash.digest('hex'),'hex')).slice(0,4);
						} else {
							this.Df_hash.update(stream.toBuffer());
							var res=(new Buffer(this.Df_hash.digest('hex'),'hex')).slice(0,4);
						};
						//console.log('STREAM '+res.toString('hex'));
						//console.log('STREAM '+digest.toString('hex'));
						if (res.toString('hex')==digest.toString('hex')) {
							return stream;
						};
					//} catch(ee) {};
				};
			},
			extend:function() {
				//console.log('CIRC :'+this.server_.ip);
				//console.log('CIRC :'+this.extended_);
				var extended_=this.extended_;
				var Address=IPtoVal(extended_.server_.ip);
				//console.log('CIRC :'+this.server_.ip+' EXTEND TOWARD '+this.extended_.server_.ip+':'+this.extended_.server_.port+' '+this.extended_.server_.fing);
				var Port=(new Buffer(2)).writeUInt(parseInt(extended_.server_.port));
				var Fing=new Buffer(extended_.server_.fing,'hex');
				if ((!window_browser)||(crypto.getDiffieHellman)) {
					var DH = crypto.getDiffieHellman('modp2');
					//TODO private key x is recommended to be 320 bits length for optimization
					//node.js improvment https://github.com/joyent/node/issues/3622
					DH.generateKeys();
					extended_.DH_=DH;
					extended_.X_=new Buffer(DH.getPublicKey('hex'),'hex');
					var cb=function() {
						var Onion=crypto_onion(new Buffer(extended_.server_.o_modulus,'hex'),extended_.X_);

						//console.log('CIRC :'+this.server_.ip+' '+this.server_.fing+' EXTEND TOWARD '+extended_.server_.ip+' '+extended_.server_.fing);
						var stream=new Stream(Stream.prototype.RELAY_EXTEND,0,[Address,Port,Onion,Fing].concatBuffers(),this.Df_hash);
						//console.log('CIRC :------------SEND RELAY EARLY EXTEND------------------- from'+this.server_.ip+' to '+this.extended_.server_.ip);
						var cell=new Cell(this.circId,Cell.prototype.RELAY_EARLY,this.stream_encrypt_forward(stream));
						this.send(cell);
					};
					if (!extended_.server_.o_modulus) {
						extended_.get_certs(cb);
					} else {
						//console.log('send extend '+this.circId+' '+extended_.server_.ip);
						cb.call(this);
					};
				} else {
					var cb=(function(data) {
						//length(2bytes) - X - Onion
						var l=data.slice(0,2).readUInt();
						extended_.X_=data.slice(2,2+l);
						//console.log('extended_ X '+extended_.X_.toString('hex'));
						extended_.DH_={};
						var Onion=data.slice(2+l);
						var stream=new Stream(Stream.prototype.RELAY_EXTEND,0,[Address,Port,Onion,Fing].concatBuffers(),this.Df_hash);
						var cell=new Cell(this.circId,Cell.prototype.RELAY_EARLY,this.stream_encrypt_forward(stream));
						delete OP_crypto[extended_.cid_hex];
						this.send(cell);
					}).bind(this);
					extended_.create_extend_info(cb);
				};
			},
			create:function() {
				if (this.onion_) {
					var cell=new Cell(this.circId,Cell.prototype.CREATE,this.onion_);
					//console.log('CIRC : SEND CREATE OR '+this.server_.ip);
					this.send(cell);
				} else {
					if (!window_browser) {
						var DH = crypto.getDiffieHellman('modp2');
						DH.generateKeys();
						this.DH_=DH;
						this.X_=new Buffer(DH.getPublicKey('hex'),'hex');
						var cb=function() {
							var Onion=crypto_onion(new Buffer(this.server_.o_modulus,'hex'),this.X_);
							var cell=new Cell(this.circId,Cell.prototype.CREATE,Onion);
							//console.log('CIRC : SEND CREATE '+(this.wait_?('OR '+this.wait_):'OP')+' '+this.server_.ip);
							this.send(cell);
						};
						if (!this.server_.o_modulus) {
							this.get_certs(cb);
						} else {
						//console.log('send create '+this.circId);
							cb.call(this);
						};
					} else {
						var cb=(function(data) {
							//length(2bytes) - X - Onion
							var l=data.slice(0,2).readUInt();
							this.X_=data.slice(2,2+l);
							var Onion=data.slice(2+l);
							//console.log(Onion.toString('hex'));
							//console.log('CIC WS browser '+this.circId);
							var cell=new Cell(this.circId,Cell.prototype.CREATE,Onion);
							delete OP_crypto[this.cid_hex];
							this.send(cell);
						}).bind(this);
						this.DH_={};
						this.create_extend_info(cb);
					};
				};
			},
			create_extend_info:function(cb) {
				//01 - onion create
				//Command - rand (16 bytes) - modulus
				var comm=(new Buffer(1)).writeUInt(0x01);
				var cid=Rand(16);
				var cid_hex=cid.toString('hex');
				this.cid_hex=cid_hex;
				var mod=new Buffer(this.server_.o_modulus,'hex');
				OP_crypto[cid_hex]=cb;
				var stream=new Stream(Stream.prototype.RELAY_INFO,0,[comm,cid,mod].concatBuffers(),fake_cid.Df_hash);
				var cell=new Cell(fake_cid.circId,Cell.prototype.RELAY_WS,fake_cid.stream_encrypt_forward(stream));
				fake_cid.send(cell);
			},
			destroy:function() {
				console.log('CIRC : -------------------------- SEND DESTROY ------------------- '+(this.server_?this.server_.ip:this.socket_.remoteAddress)+(this.OP_?' OP':' OR')+' CID '+this.circId);
				var cell=new Cell(this.circId,Cell.prototype.DESTROY,new Buffer('09','hex'));
				this.send(cell);
				/*
				if (this===db_cid) {
					db_cid=null;
					Tor({params_:{OP:true,nb_hop:NB_DB_HOP,ws:client,db:true}});
				};
				*/
			},
			send_relay_end:function(sid,reason) {
				var first_=this.first_||this;
				reason=(typeof reason==='undefined')?0:reason;
				var stream=new Stream(Stream.prototype.RELAY_END,sid,new Buffer(reason),first_.last_.Df_hash);
				var cell=new Cell(first_.circId,Cell.prototype.RELAY,first_.last_.stream_encrypt_forward(stream));
				first_.send(cell);
			},
			send_relay_end_b:function(sid,reason) {
				//console.log('sending relay end sid '+sid+' reason '+reason);
				var stream=new Stream(Stream.prototype.RELAY_END,sid,(new Buffer(1)).writeUInt(reason),this.Db_hash);
				this.stream_encrypt_or_b(stream.toBuffer(),Cell.prototype.RELAY,true);
			},
			associate:function(fake) { //fake_domain
				console.log('ASSOCIATE OR RECEIVE '+fake);
				this.socket_.fake_cid=this; //used not to forward data in the circuit (relay_ws) if bad formatted
				OR_fake[fake]={circ_:this};
			},
			info: function(data) {
				if (this.OP_) {
					var id=data.slice(0,16).toString('hex');
					if (OP_crypto[id]) {
						OP_crypto[id](data.slice(16));
					};
				} else {
					var comm=data.slice(0,1).readUInt();
					var id=data.slice(1,17);
					//console.log(comm);
					switch (comm) {
						case 1:
							var modulus=data.slice(17);
							var DH = crypto.getDiffieHellman('modp2');
							DH.generateKeys();
							OR_crypto[id.toString('hex')]=DH;
							//console.log('01 OR id '+id.toString('hex'));
							var X_=new Buffer(DH.getPublicKey('hex'),'hex');
							//console.log('01 OR X '+X_.toString('hex'));
							var Onion=crypto_onion(modulus,X_);
							data=(new Stream(Stream.prototype.RELAY_INFO,0,[id,(new Buffer(2)).writeUInt(X_.length),X_,Onion].concatBuffers(),this.Db_hash)).toBuffer();
							data=new Buffer(this.Kb_cipher.update(data,'hex','hex'),'hex');
							var cell=new Cell(this.circId,Cell.prototype.RELAY_WS,data);
							this.send(cell);
							break;
						case 2:
							//console.log('02 OR id '+id.toString('hex'));
							var id_=id.toString('hex');
							if (OR_crypto[id_]) {
								//console.log('02 OR Y '+data.slice(17).toString('hex'));
								var secret=new Buffer(OR_crypto[id_].computeSecret(data.slice(17), 'hex', 'hex'),'hex');
								data=(new Stream(Stream.prototype.RELAY_INFO,0,[id,secret].concatBuffers(),this.Db_hash)).toBuffer();
								data=new Buffer(this.Kb_cipher.update(data,'hex','hex'),'hex');
								var cell=new Cell(this.circId,Cell.prototype.RELAY_WS,data);
								delete OR_crypto[id_];
								this.send(cell);
							};
							break;
					};
				};
			},
			relay_ws_handle:function(data,boo) { //WS OP SOCKS OP or OR request
				if (this.OP_) { //WS OP SOCKS
					//console.log('OP relay_ws_handle');
					var host_=null;
					var l=data.slice(0,2).readUInt();
					var tmp=((data.slice(2,2+l)).toString('utf8')).split(':'); //request.remoteAddress+':'+request.remotePort+':'+request.port_+':'+request.i_id;
					data=data.slice(2+l);
					console.log('OP ws receive request '+tmp[3]+' adresse '+tmp[0]+' port '+tmp[1]+' data '+data.length);
					//console.log(data.toString('utf8'));
					if (data.length) {
						//console.log('OP anonym ws receive '+tmp);
						var params={};
						var request=OP_fake_request[tmp[3]];
						if (!request) {
							request={nb_try:0,no_exit:[],i_id:tmp[3]};
							OP_fake_request[tmp[3]]=request;
						} else {
							if (request.params_) { //params_ not set if https for example
								host_=request.params_.host;
							};
						};
						var _write_=(function(resp) {
							//console.log(([new Buffer(tmp[0]+WS_SOCKS_PFX,'utf8'),resp].concatBuffers()).toString('utf8'));
							//var payload=[new Buffer(tmp[0]+WS_SOCKS_PFX,'utf8'),resp].concatBuffers();
							var payload=resp;
							var add=new Buffer(new Buffer(tmp[0]+':'+tmp[1],'utf8').toString('hex'),'hex'); //socks client ip:port
							var l=add.length.toString(16);
							while (l.length!==4) {
								l='0'+l;
							};
							add=[new Buffer(l,'hex'),add].concatBuffers();
							l=add.length;
							if (payload.length===0) {
								var stream=new Stream(Stream.prototype.RELAY_WS,0,add,this.Df_hash);
								var cell=new Cell(this.circId,Cell.prototype.RELAY_WS,this.stream_encrypt_forward(stream));
								this.send(cell);
							} else {
								while (payload.length) {
									var pay;
									if (payload.length+l>PAYLOAD_STREAM_WS) {
										pay=payload.slice(0,PAYLOAD_STREAM_WS-l);
									} else {
										pay=payload;
									};
									pay=[add,pay].concatBuffers();
									//console.log('OP write ws '+pay.length);
									var stream=new Stream(Stream.prototype.RELAY_WS,0,pay,this.Df_hash);
									var cell=new Cell(this.circId,Cell.prototype.RELAY_WS,this.stream_encrypt_forward(stream));
									this.send(cell);
									if (payload.length+l>PAYLOAD_STREAM_WS) {
										payload=payload.slice(PAYLOAD_STREAM_WS-l);
									} else {
										break;
									};
								};
							};
						}).bind(fake_cid); //signaling RELAY_ASSOCIATE AND RELAY_WS on fake_cid - relay_begin/relay_data on other circuits
						if ((!TLS_OP)||(!request.tls_server_connected)) {
							//TODO wait for TLS
							var wait=function() {
								request.wait_header=request.wait_header?[request.wait_header,data].concatBuffers():data;
								//console.log('wait '+request.wait_header.toString('utf8'));
								if (request.wsqueue_) {
									//request.wsqueue_.shift();
									if (request.wsqueue_.length) {
										var a=request.wsqueue_[0];
										request.wsqueue_.shift();
										a();
									};
								};
							};
							if (data.length>=4) {
								var check=data.toString('hex');
								//console.log('wait ??? '+data.toString('utf8')+' ??? '+request.i_id+' '+data.byteOffset);
								if (check.substr(check.length-8)!==CRLF+CRLF) { //header not complete
									wait();
								} else {
									data=request.wait_header?request.wait_header:data;
									delete request.wait_header;
									//console.log('header complete request '+request.i_id+' wqueue '+(request.wsqueue_?request.wsqueue_.length:0));
									//request.wsqueue_.shift();
									//console.log('header complete '+data.toString('utf8'));
								};
							} else {
								wait();
							};
							request._write_=_write_;
							request.destroy=function() {
									//console.log('OP WS Relay destroy '+this.i_id);
									if (TLS_OP) {
										delete request.abstract_server_tls;
										if (!request.abstract_client_tls) {
											request._write_(new Buffer(0));
										} else {
											delete request.abstract_client_tls;
											_write_(new Buffer(0));
										};
									} else {
										request._write_(new Buffer(0));
									};
								};
						};
						var relay_ws_handle=function(stream) {
							var port=tmp[2]; //destination port
							//var stream=params.stream.toString('utf8');
							//console.log('before reconstitute ---'+stream);
							var parse=simpleParser(stream);
							var accept=parse['Accept']?parse['Accept']:'';
							if (accept.indexOf('text/html')!==-1) {
								delete parse['Accept-Encoding'];
							};
							request.referer_=parse['Referer'];
							delete parse['Referer'];
							//parse['Referer']='http://'+'www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+FAKE_SFX; //some sites want a referer
							//parse['Referer']=protocol+'//'+real_domain;
							parse['Connection']='keep-alive';
							//console.log('parsein---'+parse['0a']);
							var gurl=parse['0a'].split(' ');
							var rurl='';
							if (gurl.length>1) {
								var tmp2=(gurl[1].substr(0,1)==='/')?gurl[1].substr(1):gurl[1]; //remove /
								rurl=encrypt_decrypt(tmp2);
							};
							var format_request=function() {
								if (rurl.substr(0,4)==='http') { //outside domain
									var out=url_decode(rurl);
									//gurl=parse['0a'].split(' ');
									//console.log(out);
									//var f=out.rest.split('/').pop();
									//console.log(f);
									gurl[1]='/'+(out.rest?out.rest:'');
									parse['0a']=gurl.join(' ');
									if (parse['Host']) {
										parse['Host']=out.host?out.host:''; //TODO distinguish http/https
									};
									delete parse['Cookie']; //important delete real domain cookies for outside requests
									if (!parse['X-Requested-With']) { //if not ajax request save outside domain
										request._host_=out.protocol+'//'+parse['Host'];
									};
									//console.log('parseout--- '+parse['0a']+' --host-- '+parse['Host']);
								} else { //real domain
									//gurl[1]='/'+rurl;
									//parse['0a']=gurl.join(' ');
									if (parse['Host']) {
										request.fake_domain=parse['Host'];
										parse['Host']=OP_domains[parse['Host']].real_domain;
									};
									//console.log('parsein--- '+parse['0a']+' --host-- '+parse['Host']);
								};
								if (parse['Accept']) {
									//console.log('Accept request '+request.i_id+' '+parse['Accept']+' '+parse['Accept-Encoding']);
									if (parse['Accept'].indexOf('text/html')!==-1) {
										parse['Accept-Encoding']='identity';
									};
								};
								stream=reconstitute(parse);
								//console.log('Received from socks request id '+request.i_id+' ---'+stream+' -------------');
								params.OP=true;
								params.nb_hop=NB_HOP;
								params.one_c=true;
								//params.host=real_domain+':'+port;
								if (TLS_OP) {
									port=((protocol===default_protocol)&&(port==='443'))?'80':port;
								};
								params.host=parse['Host']+':'+port;
								if ((host_)&&(params.host!==host_)) { // fake_domain request are streamed in same request but real host can change - then do not reuse existing connection
									//console.log('real host changed request '+request.i_id);
									delete request.tls_client_connected;
									delete request.abstract_client_tls;
									if (request.cid_) {
										request.cid_.destroy_cid(request,true);
									};
								};
								params.stream=new Buffer(stream,'utf8');
								request.params_=params;
								request._data_=false;
								//var nb_try=request.nb_try;
								//request.cid_=fake_cid;
								request._init_=init_request;
								request._init_();
								if (boo) {
									request.nb_try=n;
								};
								request.remoteAddress=tmp[0];
								request.remotePort=tmp[1];
								request.end=request.destroy;
								//request.addr_=tmp[0];
							};
							if ((rurl.substr(0,5)==='https')||(protocol==='https:')) {
								if (!TLS_OR) {
									console.log('https not supported now '+rurl); //send fake answer
									request._write_(new Buffer(req_200(),'utf8'));
									return;
								} else {
									format_request();
									if (!request.abstract_client_tls) {
										console.log('Create TLS Client for request '+request.i_id);
										var rurld=url_decode(rurl);
										var stream=request.params_.stream;
										request.abstract_client_tls=abstract_tls(request,rurld.host);
										request.abstract_client_tls.stream_tor_=new Buffer(0);
										request.abstract_client_tls.queue_socks=[];
										var request_decoded={};
										request_decoded._init_=init_request;
										request_decoded._init_();
										request_decoded._host_=request._host_;
										request_decoded.cid_=request.cid_;
										request_decoded.i_id=request.i_id;
										request_decoded.fake_domain=request.fake_domain;
										request_decoded.socks_s=true;
										request_decoded.__write__=request.__write__;
										request_decoded.destroy=request.destroy;
										request_decoded.referer_=request.referer_;
										request.request_decoded=request_decoded;
										request_decoded.request_=request;
										request_decoded.received_=0;
										var client=request.abstract_client_tls;
										var nwrite=function(resp) {
											var execute=function(data) {
												return function() {
													this.stream_tor_=this.stream_tor_.length?([this.stream_tor_,data].concatBuffers()):data;
													this.stream_tor_.parseTLS(this);
												};
											};
											client.queue_=client.queue_||[];
											client.queue_.push(execute(resp).bind(client));
											if (client.queue_.length===1) {
												client.queue_[0]();
											};
											if ((client.queue_socks.length)&&(!request_decoded.socks_s)) {
												request_decoded.socks_s=true;
												console.log('Unqueue request '+request.i_id);
												client.queue_socks[0]();
												client.queue_socks.shift();
											};
										};
										request._write_=nwrite;
										request.write_c=function(data) { //send to target
											request.params_.stream=data;
											Tor(request);
										};
										request.abstract_client_tls.closed=function(c) {
											console.log(' TLS client disconnected.');
											delete request.abstract_client_tls;
											delete request.tls_client_connected;
											if (request.cid_) {
												request.cid_.destroy_cid(request);
											};
										};
										request.abstract_client_tls.error=function(c,error) {
											console.log(' Error TLS client disconnected '+error.message);
											delete request.abstract_client_tls;
											delete request.tls_client_connected;
											if (request.cid_) {
												request.cid_.destroy_cid(request);
											};
										};
										request.abstract_client_tls.connected=function(c) {
											console.log('TLS client connected to site for request '+request.i_id);
											request.tls_client_connected=true;
											request._data_=false;
											console.log('Sending https request for request '+request.i_id+' to site '+stream.toString('utf8'));
											c.prepare(encode(stream.toString('hex'))); //send initial stream
										};
										request.abstract_client_tls.dataReady=function(c) {
											var data=c.data.data.slice(c.data.read,c.data.length_);
											c.data.read=c.data.length_;
											var tmp={streamId:(new Buffer(2)).writeUInt(request.sid_),data:data,length:(new Buffer(2)).writeUInt(data.length)};
											//console.log('decoded tls data send by tls client to tls server '+c.data.read);
											//console.log(data.toString('utf8'));
											//owrite(c.data.data.slice(0,c.data.length_));//send to tls server or to OR via _write_
											//request._write_=owrite; //send to OR WS
											if (request.cid_) {
												request.cid_.last_.stream_handle_data(tmp,request.__write__,request_decoded);//send to tls server or to OR via _write_);
												//request._write_=nwrite; //send to process
											};
										};
										request.abstract_client_tls.handshake();
									} else {
										console.log('Reuse TLS Client for request '+request.i_id);
										var process=function(){request.abstract_client_tls.prepare(encode(request.params_.stream.toString('hex')))};
										if (request.request_decoded.socks_s) {
											console.log('Queue '+request.i_id);
											request.abstract_client_tls.queue_socks.push(process);
										} else {
											process();
										};
									};
								};
							} else {
								format_request();
								Tor(request);
							};
						};
						request.relay_ws_handle=relay_ws_handle;
						if (TLS_OP) {
							if (!request.abstract_server_tls) {
								request.abstract_server_tls=abstract_tls(request,fake_domain,true);
								request.write_s=_write_;
								request.abstract_server_tls.closed=function(c) {
									console.log(' TLS server disconnected for request '+request.i_id);
									request.destroy();
								};
								request.abstract_server_tls.error=function(c,error) {
									if (error.message.indexOf('Unknown')===-1) {
										console.log('Error TLS server disconnected for request '+request.i_id+' '+error.message);
										request.destroy();
									};
								};
								request.abstract_server_tls.connected=function(c) {
									console.log('TLS server connected to socks client for request '+request.i_id);
									request.tls_server_connected=true;
									request._write_=function(resp) {
										//prepare and send to OR via request.write
										//from relay_data in clear to OR encrypted
										//overriden if https connexion to the site
										//relay_data goes through nwrite, get decoded, get back to handle relay_data and is sent to __write__
										if (request.abstract_server_tls) { //tls connexion can be reset during handshake
											console.log('tls server send to SOCKS');
											request.abstract_server_tls.prepare(encode(resp.toString('hex'))); //TODO optimize with buffers
											if (request.done_) {
												request.destroy();
											};
										} else {
											console.log('can\'t send to SOCKS server connection closed for request '+request.i_id);
										};
									};
									request.__write__=request._write_;
									request.abstract_server_tls.dataReady=function(c) {
										console.log(c.data.read);
										var tmp=c.data.data.slice(c.data.read,c.data.length_).toString('utf8');
										if (tmp.indexOf('Host')!==-1) { //TODO check valid request (FF Bug? TLS messages sent twice)
											c.data.read=c.data.length_;
											console.log('TLS server receive '+tmp);
											request.ini_data=tmp;
											relay_ws_handle(tmp);
										};
									};
								};
							};
							//console.log('ws receive tls');
							//console.log(data.toString('hex'));
							request.abstract_server_tls.process(data);
						} else {
							var sdata=data.toString('utf8');
							request.ini_data=sdata;
							relay_ws_handle(sdata);
						};
					};
				};
				if (this.OR_) { //OR
					//console.log('OR receive ws initial');
					//console.log(data.toString('hex'));
					var l=data.slice(0,2).readUInt();
					var tmp=(data.slice(2,2+l)).toString('utf8'); //request.remoteAddress+':'+request.remotePort;
					data=data.slice(2+l);
					//console.log(data.toString('utf8'));
					//var tmp=(data.toString('utf8')).split(WS_SOCKS_PFX);
					var request=OR_fake_request[tmp];
					if (data.length) {
						//OR_fake_request[tmp].resp__=OR_fake_request[tmp].resp__?(OR_fake_request[tmp].resp__+data.length):data.length;
						//console.log('OR receive ws '+tmp);
						//console.log('OR fake request '+OR_fake_request[tmp].i_id+' total '+OR_fake_request[tmp].resp__);
						//console.log((new Buffer(data,'utf8').toString('hex')));
						request.write(data);
						return;
					} else {
						if (!request.bufferSize) {
							console.log('OR ws destroy request '+request.i_id);
							//request.destroy();
							//request.close(true); //close with true (error)
							request.end();
						} else {
							//console.log('bufferSize for OR ws destroy request '+request.i_id);
						};
					};
				};
			},
			relay_db_info_handle:function(stream) {//TODO optimize (Workers?)
				console.log('receive relay_db_info CID '+this.circId);
				var lh,p,fa,hash_name,f,boo;
				//while (stream.length) {
					lh=stream[0];
					stream=stream.slice(1);
					hash_name=stream.slice(0,lh).toString('hex');
					stream=stream.slice(lh);
					p=stream[0];
					stream=stream.slice(1);
					if (stream.length) {
						fa=stream[0];
					};
					console.log('db_info '+hash_name+' part '+p+' fac '+(fa?'yes':'no'));
					f=OR_files[hash_name];
					if (f) {
						f.forEach(function(val) {if (val[0]===this) {boo=true}},this);
						if (!boo) {
							f.push([this,null,null,0]);
						};
					} else {
						OR_files[hash_name]=[[this,null,null,0]];
					};
					if (fa) {
						console.log(OR_facilitators.indexOf(this));
						if (OR_facilitators.indexOf(this)===-1) {
							console.log('pushing facilitator CID '+this.circId);
							OR_facilitators.push(this);
						};
					};
				//};
			},
			relay_db_query_handle:function(stream) {
				var sid=stream.streamId.readUInt();
				console.log('receive relay_db_query CIC '+this.circId+' sid '+sid);
				stream=stream.data.slice(0,stream.length.readUInt());
				var l=stream[0];
				stream=stream.slice(1);
				var hash=stream.slice(0,l).toString('hex');
				stream=stream.slice(l);
				l=stream[0];
				stream=stream.slice(1);
				var csize=parseInt(stream.slice(0,l).toString('hex'),16);
				stream=stream.slice(l);
				console.log('Receive db_query for '+hash+' on CID '+this.circId+' sid '+sid+' requesting '+csize+' bytes '+stream.toString('hex'));
				if (!this.OP_) {
					console.log('ORDB receive db_query for '+hash+' on CID '+this.circId+' sid '+sid+' requesting '+csize+' bytes ');
					var f=OR_files[hash]; //[circ,hash_file,size,0]
					if (f) {
						//console.log('f length '+f.length);
						//f.forEach(function(val) {console.log('f has '+val[0].circId+' valid '+(val[0].socket_?'yes':'no'))});
						//check what happens is several access at the same time
						//TODO use one sort instead of two
						f.sort(function(a,b) { //sort min to max
							var a3=a[3];
							var b3=b[3];
							return a3===b3?0:(a3<b3?-1:1);
						}).sort(function(a,b) { //put destroyed circuits at the begining
							if (!a[0].socket_){
								return -1;
							};
							if (!b[0].socket_) {
								return 1;
							};
							return 0;
						});
						var l=f.length;
						f.forEach(function(val) {console.log('f has '+val[0].circId+' valid '+(val[0].socket_?'yes':'no'))});
						if (l) {
							while (f.length) { //remove destroyed circuits
								if (!f[0][0].socket_) {
									console.log('ORDB remove '+f[0][0].circId);
									f[0][0].circuit_destroy();
									f.shift();
								} else if (!f[0][0].socket_.remoteAddress) {
									console.log('ORDB remove '+f[0][0].circId);
									f[0][0].circuit_destroy();
									f.shift();
								} /*else if (f[0][0].nb_query>NB_QUERY) {
									console.log('ORDB remove nb_query max '+f[0][0].circId+' nb_query '+f[0][0].nb_query);
									f[0][0].circuit_destroy();
									f.shift();

								} */else if (f[0][0].destroyed_) {
									console.log('ORDB remove destroyed CIC '+f[0][0].circId);
									f.shift();

								} else {
									break;
								};
							};
							if (f.length) {
								var d_or=f[0][0];
								f[0][3]++;
								var tid=Rand(16); //TODO do this better
								//var g=f.slice(0);
								//g.shift();
								var param={d_length:csize,hash_:new Buffer(hash,'hex')};
								OR_tid[tid.toString('hex')]=[this,sid,f,param,1];
								d_or.send_db_query(param,tid);
								//TODO Timeout for response ?
							} else {
								try_bittorrent.call(this,sid,csize,hash);
							};
						} else {
							try_bittorrent.call(this,sid,csize,hash);
						};
					} else if (OR_facilitators.length) {
						try_bittorrent.call(this,sid,csize,hash);
					} else {
						console.log('ORDB does not know the file');
						this.send_db_end(0,sid);
					};
				} else {
					var tid=stream.slice(0,16);
					var first=this.first_;
					if (hash!==TEST_HASH) {
						var objectStore=open_db();
						var a=objectStore.get(hash);
						a.onsuccess=(function(evt) {
							var res=evt.target.result;
							if (res) {
								if ((res.file_length===res.current_length)&&(csize<res.file_length)) {
									res.data=peersm_client?(new Blob_node(res.data)):((res.data instanceof Blob)?res.data:(new Blob(res.data)));
									var file=res.data.slice(csize);
									var size=res.file_length;
									var type;
									if (((res.type!==res.data.type)&&(!chrome))||(chrome&&res.enc)) {
										type=res.type+';'+(res.data.type||res.enc);
									} else {
										type=res.type;
									};
									var sid=init_connected_request(first);
									console.log('Have file '+hash+' length '+size+' to be sent '+csize+' tid '+tid.toString('hex')+' '+(file.size||file.byteLength)+' CID '+first.circId+' sid '+sid);
									first.send_db_connected(size,sid,type,tid);
									first.send_db_data(file,sid);
								} else {
									first.send_db_end(0,sid,tid);
								}
							} else {
								if (peersm_client) {
									var magnet='magnet:?xt=urn:btih:'+hash;
									var CLOSEST={};
									var PEERS=[];
									console.log('looking for torrent '+magnet+' myip '+myip);
									var sid=init_connected_request(first);
									first.send_db_connected(0,sid,'torrent',tid); //wait response from torrent network, do not send db_query again for the requester
									var fakeinfohash=hash.substr(0,AB_PREFIX/4)+crypto.randomBytes((160-AB_PREFIX)/8).toString('hex');
									console.log('fake infohash '+fakeinfohash);
									start_DHT();
									var ini_dht=dht({debug:false,freerider:false,blocklist:blocked,myip:myip});
									first[sid].__ini_dht__=ini_dht;
									var nodeId=ini_dht.nodeId.toString('hex');
									ini_dht.on('ready',function() {
										first.send_db_connected(0,sid,'dhtready',tid);
										console.log('dht ready - starting lookup for infohash '+fakeinfohash+' '+(new Date().toTimeString()));
										ini_dht.lookup(fakeinfohash);
									});
									var onpeer=function(addr) {
										var ip=addr.split(':')[0];
										if (!blocked.contains(ip)) {
											console.log('new spy '+addr,true);
											blocked.add(ip);
											Arrayblocklist.push(ip);
										};
									};
									var onnode=function(addr,nodeId) {
										var pre=prefix(nodeId.toString('hex'),fakeinfohash);
										var ip=addr.split(':')[0];
										if (!blocked.contains(ip)) {
											if ((pre>=PREF_MIN)&&(pre<=PREF_MAX)) {
												var host=addr.split(':')[0];
												host=host.split('.');
												host.reverse();
												host=host.join('.')+'.'+BL_URL;
												var cb=function(bool) {
													if (bool) {
														//console.log('adding '+addr+' to closest ');
														CLOSEST[addr]=nodeId;
													} else {
														console.log('--------------- potential spy '+addr);
														onpeer(addr);
													};
												};
												//console.log('checking dnsbl '+host);
												dnsbl_lookup(host,cb);
											};
										};
									};
									var dnsbl_lookup=function(host,callback) {
										dns.resolve(host,function(err,addresses) {
											if (err) {
												if (err.code==='ENOTFOUND' ){
													return callback(true);
												} else {
													return callback();
												};
											} else {
												return callback();
											};
										});
									};
									ini_dht.on('peer',onpeer);
									ini_dht.on('node',onnode);
									ini_dht.on('closest',function() {
										ini_dht.removeListener('peer',onpeer);
										ini_dht.removeListener('node',onnode);
										first.send_db_connected(0,sid,'start',tid);
										console.log('starting torrent for '+magnet+' nb of closest nodes: '+ini_dht.closest_from_infohash.length);
										//var torrent=bittorrent(magnet,{connections:100,path:pathd+'node_modules/torrent',verify:true,dht:true});
										var torrent=bittorrent(magnet,{blocklist:Arrayblocklist||null,connections:20,path:pathd+'node_modules/torrent',verify:true,debug:false,freerider:true,dht:ini_dht});
										var onsettorrent=function() {
											var queue_speer=[];
											if (!Array.isArray(CLOSEST)) {
												var tmp=[];
												Object.keys(CLOSEST).forEach(function(val) {
													tmp.push({addr:val,id:CLOSEST[val]});
												});
												CLOSEST=tmp;
												tmp=[];
											};
											console.log('Number of closest '+CLOSEST.length)
											if (!CLOSEST.length) {
												CLOSEST=ini_dht.closest_from_infohash;
											};
											if (CLOSEST) {
												console.log('settorrent dht ready',true);
												var check_peer2=function(err,res,addr) {
													if (!err) {
														if (res.values) {
															var c=res.values.length;
															var rate=c>PEER_MIN?PEER_RATE:0;
															var l=Math.ceil(c*rate);
															res.values.forEach(function(add,i) {
																if (i>l) {
																	if (!blocked.contains(add.split(':')[0])) {
																		PEERS.push(add);
																	} else {
																		console.log('------------- blocked spy '+add,true);
																	};
																};
															})
														};
													};
													if (queue_speer.length) {
														queue_speer.shift()();
													} else {
														var tmp=[];
														PEERS=unique.call(PEERS);
														PEERS.sort(function() {return 0.5 - Math.random()});//TODO - better random
														console.log('launch torrent nb_peers '+PEERS.length);
														if (PEERS.length>=PEER_MIN) {
															PEERS.forEach(function(addr) {
																var ip=addr.split(':')[0];
																if (tmp.indexOf(ip)===-1) {
																	tmp.push(ip);
																} else {
																	console.log('----------- potential spy');
																	onpeer(addr);
																};
															});
														};
														PEERS.forEach(function(addr) {
															torrent.discovery.emit('peer',addr);
														});
													};
												};
												if (CLOSEST.length>20) {
													var tmp=[];
													var tmp2=[];
													CLOSEST.forEach(function(contact) {
														var ip=contact.addr.split(':')[0];
														if (tmp.indexOf(ip)===-1) {
															tmp.push(ip);
															tmp2.push(contact);
														};
													});
													CLOSEST=tmp2;
													CLOSEST.sort(function() {return 0.5 - Math.random()});
													CLOSEST=CLOSEST.slice(0,20);
												};
												CLOSEST.forEach(function (contact,i) {
													var addr=contact.addr;
													var ip=addr.split(':')[0];
													console.log(addr,true);
													queue_speer.push(function() {
														console.log('sending getpeer to '+addr);
														ini_dht._sendGetPeers(addr,hash,function(err,res) {check_peer2(err,res,addr)});
													});
												});
												if (queue_speer.length) {
													queue_speer.shift()();
												};
											} else {
												console.log('settorrent dht not ready, retry later');
												setTimeout(onsettorrent,RETRY);
											};
										};
										torrent.on('setTorrent',onsettorrent);
										//var torrent=bittorrent(magnet);
										first[sid].__torrent__=torrent;
										torrent.on('ready', function() {
											if (torrent.files.length) {
												console.log('torrent ready');
												var file={length:0};
												var streamres;
												torrent.files.forEach(function(fl) {if (fl.length>file.length) {file=fl}});
												console.log('sid '+sid+' csize '+csize+' file length '+file.length);
												if (csize<file.length) {
													var type;
													var ext=get_extension(file.name);
													if (ext) {
														type=ext_to_type[ext];
													};
													if (!type) {
														type='application/octet-binary';
													};
													console.log('filename '+file.name+' length '+file.length+' type '+type);
													file.size=file.length;
													if ((type.indexOf('video')!==-1)||(type.indexOf('audio')!==-1)) { //TODO change for mp4
														type='video/webm';
													};
													first.send_db_connected(file.length,sid,type,tid);
													var request=first[sid];
													request.fc_t=[];
													request.cd_length=file.length;
													request.reader=new FileReader_torrent;
													request.cursor=csize||0;
													console.log('cursor csize '+csize);
													request._torrent_=new Blob_torrent(file.length-request.cursor);
													if (!request._stream_) {
														request.down_limit=TORRENT_DOWN_LIMIT;
													};
													request._torrent_stream=file.createReadStream({start:csize,end:file.length});
													//option start:csize end:

													if (((type.indexOf('video')!==-1)||(type.indexOf('audio')!==-1))&&(ext!=='webm')) { //TODO change for mp4
														var n=0;
														var a='-i - -y -acodec libvorbis -vcodec libvpx -maxrate 750k -minrate 550k -bufsize 1600k -b:v 600k -keyint_min 48 -g 48 -sc_threshold 0 -ab 96k -f webm pipe:1';
														console.log('spawning ffmpeg '+a);
														a=a.split(' ');
														child=child_process.spawn(FFMPEG_BIN_PATH,a,{cwd:os.tmpdir()});
														child.stdin.on('error',function(err) {console.log('stdin error '+console.log(err.message))});
														child.stderr.on('data', function(chunk) {
															//if (!(n%10)) {
																console.log(' Transcoding still active: '+chunk.toString());
															//};
															n++;
														});
														child.on('exit',function(code) {
															console.log('---- Transcoding finished -----')
														});
														request._torrent_stream.pipe(child.stdin);
														streamres=child.stdout;
													} else {
														streamres=request._torrent_stream;
													};
													streamres.on('data',function(chunk) {
														var nb_peer_left=0;
														torrent.swarm._queues.forEach(function(val) {nb_peer_left +=val.length});
														console.log('got for torrent sid '+sid+' '+chunk.length+' bytes of data - offset '+request._torrent_stream._piece+' - nb peers: '+torrent.swarm.wires.length+' - other peers '+nb_peer_left);
														console.log(chunk.toString('hex'));
														request._torrent_.push(chunk);
														if (request.cursor===csize) {
															request.start_t0=Date.now();
															send_data.call(first,request._torrent_,sid,request);
														};
													});
													request._torrent_stream.on('end',function() {
														console.log('torrent stream end ----');
														console.log('destroying torrent engine');
														destroy_torrent(request);
														/*if (torrent.files.length) {
															first.send_db_end(3,sid);
															torrent.emit('ready');
														} else {
															first.send_db_end(0,sid);
														};*/
														//first.send_db_end(0,sid);
													});
													request._torrent_stream.on('close',function() {
														if (request.fc_t) {
															if (request.fc_t.length) {
																console.log('queue not empty - stop sending');
																request.stop_=true; //stop sending data
															} else {
																console.log('queue empty - delete request');
																delete first[sid];
															};
														};
														console.log('destroying torrent engine');
														destroy_torrent(request);
													});
													var nbwires=0;
													var onbwires=0;
													var peers=function() {
														nbwires=torrent.swarm.wires.length;
														if (nbwires!==onbwires) {
															console.log('--- Peers in swarn:',true)
															torrent.swarm.wires.forEach(function(wire) {
																console.log(wire.peerAddress,true);
															});
														};
														onbwires=nbwires;
													};
													setInterval(peers,10000);
												} else {
													console.log('bad torrent file');
													destroy_torrent(request);
													first.send_db_end(0,sid);
												};
											} else {
												console.log('no torrent files');
												destroy_torrent(request);
												first.send_db_end(0,sid);
											};
											//request._torrent_stream.on('download',function(p,data) {console.log('download '+p+' '+data.length)});
										});
									});
								} else {
									first.send_db_end(0,sid,tid);
								};
							};
						}).bind(this);
					} else {
						first.send_db_end(0,sid,tid);
					};
				};
			},
			relay_db_connected_handle:function(stream) {
				var sid=stream.streamId.readUInt();
				console.log('receive relay_db_connected CID '+this.circId+' sid '+sid);
				stream=stream.data.slice(0,stream.length.readUInt());
				if (this.OP_) {
					var l=stream[0];
					stream=stream.slice(1);
					var size=parseInt(stream.slice(0,l).toString('hex'),16);
					stream=stream.slice(l);
					l=stream.slice(0,2).readUInt();
					stream=stream.slice(2);
					var type=stream.slice(0,l).toString('utf8');
					var request=this.first_[sid];
					console.log('relay_db_connected type '+type);
					if (request) {
						if (type==='torrent') {
							if (!request.url_) { //do not try torrents if url
								addEvent(document.body,'mousedown',function() {},false);
								console.log('put torrent timer');
								if (!request._streaming_) {
									Myalert('<p style="text-align:center">The file is not available in Peersm network, looking for it in Bittorrent network, please wait.</p>');
								};
								request._torrentc_=setTimeout((function() {this.first_.send_db_end(1,sid);delete_request(request);remove_bars(request);Myalert('<p style="text-align:center">Could not find the file in bittorrent, stopping download</p>');}).bind(this),TORRENT_TIMER);
							} else {
								console.log('direct download');
								this.first_.send_db_end(1,sid);
								delete_request(request);
								clearTimers(request.query_t0);
								Tor(request);
								return;
							};
						} else {
							//console.log(request._torrentc_);
							$_('alert_box').style.display='none';
							if (request._torrentc_) {
								console.log('remove torrent timer');
								clearTimeout(request._torrentc_);
							};
						};
						if (['dhtready','start'].indexOf(type)!==-1) {
							if (!request._streaming_) {
								if (type==='dhtready') {
									Myalert('<p style="text-align:center">The file is not available in Peersm network<br><br>Looking for it in bittorrent network<br><br>Connected to bittorrent, routing table ready<br><br>Starting peers lookup, please wait...</p>');
								} else {
									Myalert('<p style="text-align:center">Peers found, starting torrent</p>');
								};
							};
							return;
						};
						request.flowc=request.flowc?request.flowc:(Date.now()-request.query_time);
						console.log('db_query/db_connected '+(Date.now()-request.query_time)+' ms '+request.flowc);
						clearTimers(request.query_t0);
						request.clength_=size;
						request.pieces=Math.ceil(request.clength_/BSIZE);
						request.content_=type;
						request.sid_=sid;
						request.received_=0;
						request.sent_=0;
						request.nb_try=0;
						request.stream_window=STREAM_WINDOW_PEER;
						request.stream_window_s=STREAM_WINDOW_PEER;
						if ((request._stream_)&&(type!=='torrent')) {
							//MP4Box -dash 500 -frag 500 -rap -out frag4.mp4 frag3.mp4
							//ffmpeg -an -codec:v libx264 -profile:v baseline -level 13 -b:v 2000k output.mp4 -i input.mp4
							//ffmpeg -codec:a aac -codec:v libx264 -profile:v baseline -level 13 -b:v 500k output.mp4 -i input.mp4
							//ffmpeg -movflags flag_keyframe -frag_duration 500 -frag_size 500 target -i input.mp4
							//ffprobe -show_frames file.mp4
							//ffmpeg.exe -i input.flv -vcodec copy -an output.h264
							//ffmpeg.exe -i input.flv -acodec copy -vn output.aac
							//http://www-itec.uni-klu.ac.at/dash/?page_id=746
							//ffmpeg.exe -y -i sintel_trailer-audio.flac -acodec libvorbis -i sintel_trailer_2k_480p24.y4m -vcodec libvpx -maxrate 880k -minrate 720k -bufsize 1600k -b:v 800k -keyint_min 48 -g 48 -sc_threshold 0 -ab 96k -f webm sintel_800k.webm
							//C:\Program Files\ffmpeg-20140327-git-62094e2-win32-static\bin>ffmpeg.exe -y -i G:\demo\star_wreck_in_the_pirkinning_subtitled_xvid.avi -acodec libvorbis -i G:\demo\star_wreck_in_the_pirkinning_subtitled_xvid.avi -vcodec libvpx -maxrate 750k -minrate 550k -bufsize 1600k -b:v 600k -keyint_min 48 -g 48 -sc_threshold 0 -ab 96k -f G:\demo\star_wreck_in_the_pirkinning_subtitled_xvid2.webm
							//ffmpeg -i movie.webm -vcodec libx264 movie.mp4
							init_media(request);
						};
					};
				} else {
					var tid=stream.slice(0,16).toString('hex');
					stream=stream.slice(16);
					var l=stream[0];
					stream=stream.slice(1);
					var size=parseInt(stream.slice(0,l).toString('hex'),16);
					stream=stream.slice(l);
					l=stream.slice(0,2).readUInt();
					stream=stream.slice(2);
					var type=stream.slice(0,l).toString('utf8');
					var i_or=OR_tid[tid];
					if (['torrent','dhtready','start'].indexOf(type)===-1) { //handle next db_connected
						delete OR_tid[tid];
					};
					console.log('db_connected '+tid+' type '+type+' size '+size+' '+(typeof i_or));
					this.nb_query=0;
					if (i_or) {
						if (this.socket_) {
							if (i_or[0].socket_) {
								i_or[0].send_db_connected(size,i_or[1],type);
								//if (type!=='torrent') {
									OR_csid_b[this.socket_.remotePort+'-'+this.socket_.remoteAddress+'-'+this.circId+'-'+sid]=i_or; //[this,sid]
									OR_csid_f[i_or[0].socket_.remotePort+'-'+i_or[0].socket_.remoteAddress+'-'+i_or[0].circId+'-'+i_or[1]]=[this,sid];
									this[sid]={}; //db_destroy
									i_or[0][i_or[1]]={}; //db_destroy
									console.log('i_or associated with remote port '+this.socket_.remotePort+' remote address '+this.socket_.remoteAddress+' CIC '+this.circId+' Stream '+sid);
									console.log('f_or associated with remote port '+i_or[0].socket_.remotePort+' remote address '+i_or[0].socket_.remoteAddress+' CIC '+i_or[0].circId+' Stream '+i_or[1]);
								//};
							};
						} else {
							i_or[0].send_db_end(0,i_or[1]);
						};
					};
				};
			},
			relay_db_end_handle:function(stream) {
				var sid=stream.streamId.readUInt();
				stream=stream.data.slice(0,stream.length.readUInt());
				var reason=stream.slice(0,1).readUInt();
				console.log('receive db_end sid '+sid+' reason '+reason+' CIC '+this.circId);
				if (this.OP_) {
					var first_=this.first_;
					var request=first_[sid];
					if (request) {
						if (!request.send_data) { //requesting OP
							clearTimers(request.query_t0);
							clearTimers(request.sendme_tout);
							clearTimers(request.waiting_);
							if ((reason!==2)&&(!request.reason_)&&(reason!==3)) {
								if (request.url_) {
									if ((!request.reload2_)&&(!request.reload_)) {
										this.destroy_cid(request);
										//delete request.params_.hash_; //resume
										delete request.params_.db_;
										delete request.content_;
										delete request.d_length;
										delete request.clength_;
										delete request.blob_;
										if (Dchart) { //modif chrome
											Doptions.vAxis.maxValue=VAXIS_D;
											Dchart.draw(Ddata,Doptions);
										};
										Tor(request); //direct download - request.cid_ removed to chose another one than db_cid
									} else { //resume
										console.log('resuming direct download');
										var cb=function(request) {
											var length=request.d_length;
											request.blob_=chrome?new Buffer(0):(new Blob([],{type:(request.content_chrome?request.content_chrome:request.content_)}));
											var url=url_decode(request.url_);
											request.params_.stream=get_resume(url.host,url.rest,length);
											delete request.params_.db_;
											delete request.cid_;
											delete first_[sid];
											delete request.file_id;
											delete request.start_t0;
											request.received_=0;
											//delete request.last_saved;
											Tor(request);
										};
										store_DB(request,true,cb);
									};
								} else {
									addEvent(document.body,'mousedown',function() {},false);
									Myalert('<p style="text-align:center">The file is currently not available from peers in Peersm and Bittorrent networks and can not be downloaded directly, please check the URL or the Hash Name you are using</p>');
									isStreaming=false;
									delete_request(request);
									remove_bars(request);
								} /*else if (reason===3) {
									console.log('other bittorrent file');
									var req=init_d_request(url,hash,request._stream_);
									first[sid]=req;
									req.cid_=request.cid_;
								};*/
							} else {
								clearTimers(request.sendme_tout);
								clearTimers(request.waiting_);
								request.reason_=2;
								var fin=function() {
									if (request.nb_try<DB_NB_TRY) {
										//Myalert('<p style="text-align:center">The remote host closed the connection during the download, resuming...</p>');
										console.log('Resuming peer download');
										//setTimeout(function(){$_('alert_box').style.display='none'},10000);
										request.queue_.shift();
										var cb=function(request) {
											//var length=result.data.size||result.data.length||result.data.byteLength;
											//request.d_length=length;
											request.params_.db_=true;
											delete first_[sid];
											delete request.cid_;
											delete request.eof_;
											delete request.check_hash;
											delete request.file_id;
											delete request.start_t0;
											request.received_=0;
											request.reload_=true;
											request.queue_=[];
											request.queue_s=[];
											request.blob_=chrome?(new Buffer(0)):(new Blob([],{type:(request.content_chrome?request.content_chrome:request.content_)}));
											//request.last_saved=result.data;
											request.cid_=db_cid;
											request.nb_try++;
											request.db_try=0;
											Tor(request);
										};
										if (!request._stream_) {
											store_DB(request,true,cb);
										} else {
											if (mediaSrc) {
												if (mediaSrc.pause) {
													mediaSrc.pause();
												};
											};
											if (request._parent_) {
												request._parent_.connected_sources--;
											};
											cb(request);
										};
									} else if (request.nb_try===DB_NB_TRY){
										if (!request._stream_) {
											addEvent(document.body,'mousedown',function() {},false);
											Myalert('<p style="text-align:center">The remote peers closed the connections during the download, attempts to resume failed, storing downloaded part, please wait that the file appears in Local Storage.</p><p style="text-align:center">Use resume later to get the complete file.</p>');
										//setTimeout(function(){$_('alert_box').style.display='none'},10000)
											load_Blob_Url(request);
										} else {
											addEvent(document.body,'mousedown',function() {},false);
											Myalert('<p style="text-align:center">The remote peers closed the connections, attempts to resume streaming failed.</p>');
										};
										delete request.reason_;
									};
								};
								request.queue_=request.queue_||[];
								request.queue_.push(fin);
								if (request.queue_.length===1) {
									request.queue_[0]();
								};
							};
						} else { //serving OP
							if (request.fc_t) {
								if (request.fc_t.length) {
									console.log('queue not empty - stop sending');
									request.stop_=true; //stop sending data
								} else {
									console.log('queue empty - delete request');
									delete first_[sid];
								};
							} else {
								destroy_torrent(request);
							};
						};
					};
				} else {
					this.nb_query=0;
					if (stream.length>1) { //coming from serving OP
						console.log('db_end with tid CIC '+this.circId+' db_test length '+(this.db_test?this.db_test.length:''));
						if (this.db_test) {
							this.db_test.forEach(function(val) {clearTimeout(val);});
						};
						var tid=stream.slice(1,17).toString('hex');
						var i_or=OR_tid[tid];
						// param={d_length:csize,hash_:new Buffer(hash,'hex')};
						//OR_tid[tid]=[this,sid,f,param,1];
						if (i_or) {
							var f=i_or[2];
							var cursor=i_or[4];
							//if (f.length>=cursor) {
								if (f[cursor]) {
									i_or[4]++;
									var d_or=f[cursor][0];
									//f.shift();
									d_or.send_db_query(i_or[3],new Buffer(tid,'hex'));
									console.log('try another peer '+d_or.circId+' tid '+tid+' stream length '+stream.length);
									//console.log('try another peer d_length '+i_or[3].d_length+' hash '+i_or[3].hash_);
								} else if (OR_facilitators.length) {
									try_bittorrent.call(this,sid,i_or[3].d_length,i_or[3].hash);
								} else {
									i_or[0].send_db_end(reason,i_or[1]);
									delete OR_tid[tid];
								};
							/*} else {
								i_or[0].send_db_end(reason,i_or[1]);
								delete OR_tid[tid];
							};*/
						};
					} else { //coming from requesting or serving OP without tid
						console.log('forwarding db_end');
						var f_or=OR_csid_f[this.socket_.remotePort+'-'+this.socket_.remoteAddress+'-'+this.circId+'-'+sid];
						if (f_or) {
							try {
								if (reason!==3) {
									delete OR_csid_b[f_or[0].socket_.remotePort+'-'+f_or[0].socket_.remoteAddress+'-'+f_or[0].circId+'-'+f_or[1]];
									delete OR_csid_f[this.socket_.remotePort+'-'+this.socket_.remoteAddress+'-'+this.circId+'-'+sid];
									delete f_or[0][f_or[2]];
								};
								f_or[0].send_db_end(reason,f_or[1]);
							} catch(ee) {};
						} else {
							var b_or=OR_csid_b[this.socket_.remotePort+'-'+this.socket_.remoteAddress+'-'+this.circId+'-'+sid];
							if (b_or) {
								try {
									b_or[0].send_db_end(reason,b_or[1]);
									delete OR_csid_f[b_or[0].socket_.remotePort+'-'+b_or[0].socket_.remoteAddress+'-'+b_or[0].circId+'-'+b_or[1]];
									delete OR_csid_b[this.socket_.remotePort+'-'+this.socket_.remoteAddress+'-'+this.circId+'-'+sid];
									delete b_or[0][b_or[2]];
								} catch(ee) {};
							};
						};
						delete this[sid];
					};
				};
			},
			relay_db_data_handle:function(stream) {
				if (_WRITE_PERF) {
					var t0=Date.now();
				};
				var sid=stream.streamId.readUInt();
				//console.log('receive relay_db_data sid '+sid);
				stream=stream.data.slice(0,stream.length.readUInt());
				if (this.OP_) {
					var first_=this.first_;
					var request=first_[sid];
					if (request) {
						//_write_:2-4 Mbps
						//sendme p2p: 500ms
						//console.log('db data handle '+Date.now());
						if (request.received_===0) {
							console.log('start_t0 received '+Date.now());
							request.start_t0=Date.now();
							first_.send_db_sendme(sid);
						};
						request.received_++;
						first_.received_++;
						request.stream_window--;
						request._write_(stream);
						var m=request.received_;
						var current_rate=parseInt(stream.length*request.received_/((Date.now()-request.start_t0)/1000)); //Kbps
						var nbBlocs=Math.ceil((current_rate*((request.flowc/2)/1000))/stream.length);
						//console.log('nbBlocs theorique '+nbBlocs+' rate '+current_rate*8+' bps');
						nbBlocs=Math.ceil((STREAM_WINDOW_PEER-nbBlocs>0)?(Math.min(nbBlocs,STREAM_WINDOW_PEER*(1-FLOWC))):(STREAM_WINDOW_PEER*(1-FLOWC)));
						//console.log('nbBlocs '+nbBlocs+' received '+request.received_+' stream length '+stream.length);
						//if (request.received_%(Math.ceil(STREAM_WINDOW_PEER*FLOWC))===0) {
						var timeout;
						var sendme_=function(m) {
							var sendme_tout=function(rec) {
								console.log('sendme timeout received_ '+request.received_+' rec '+rec+' stream-blocs '+(STREAM_WINDOW_PEER-nbBlocs));
								if ((request.received_===rec)&&(!request._stream_)) {
									clearTimers(request.sendme_tout);
									clearTimers(request.waiting_);
									console.log('resuming peer to peer download received '+request.received_);
									first_.send_db_end(1,sid);
									first_.relay_db_end_handle({streamId:(new Buffer(2)).writeUInt(sid),length:(new Buffer(2)).writeUInt(1),data:(new Buffer(1)).writeUInt(2)});
								} else {
									sendme_(request.received_);
								};
							};
							clearTimers(request.sendme_tout);
							clearTimers(request.waiting_);
							//console.log('nbblocks for message transmission '+Math.ceil((current_rate*(request.flowc/1000))/stream.length)+' flowc '+request.flowc);
							console.log(current_rate*8+' bps nbBlocs '+nbBlocs+' stream window '+request.stream_window+' - sending sendme stream received '+(first_.received_*stream.length)+' - Buffer Amount: '+client.bufferedAmount+' '+Date.now());
							//console.log(parseInt(8*stream.length*request.received_/((Date.now()-request.start_t0)/1000))+' bps - Buffer Amount: '+client.bufferedAmount);
							first_.send_db_sendme(sid);
							request.stream_window +=STREAM_WINDOW_PEER;
							console.log('sendme timeout '+timeout+' stream length '+stream.length);
							request.sendme_tout.push(setTimeout(function() {sendme_tout(m)},timeout));
						};
						//if (request.received_%(STREAM_WINDOW_PEER-nbBlocs)===0) {
						if (request.stream_window===nbBlocs) {
							if ((client.bufferedAmount===0)||(first_.received_*stream.length<BUFFERED_AMOUNT_MAX)||(request.waiting_)) {
								request.last_sendme=Date.now();
								if (current_rate) {
									timeout=Math.ceil(((STREAM_WINDOW_PEER-nbBlocs)*stream.length/(current_rate))*1000)+SENDME_TOUT;
								} else {
									timeout=SENDME_TOUT;
								};
								sendme_(m);
							} else {
								clearTimers(request.sendme_tout);
								clearTimers(request.waiting_);
								request.waiting_.push(setTimeout(function() {console.log('timeout waiting_ buffered '+client.bufferedAmount);sendme_(m)},(Date.now()-(request.last_sendme||request.start_t0))));
								console.log(current_rate*8+' bps nbBlocs '+nbBlocs+' - waiting before sending sendme stream received '+(first_.received_*stream.length)+' - Buffer Amount: '+client.bufferedAmount+' '+Date.now());
								//console.log(parseInt(8*stream.length*request.received_/((Date.now()-request.start_t0)/1000))+' bps - Buffer Amount: '+client.bufferedAmount);
							};
						};
						//console.log('db data handle end '+Date.now());
					};
					if (t0) {
						count_w++;
						count_w_data++;
						time_w_data +=Date.now()-t0;
						if (count_w%PERF_WRITE_===0) {
							if (time_w_data) {
								console.log('_WRITE_ perf: '+parseInt(stream.length*count_w_data*8/(time_w_data/1000))+' bps - Buffered : '+client.bufferedAmount);
								count_w=0;
								count_w_data=0;
								time_w_data=0;
							};
						};
					};
				} else {
					//Perf 800 kbps
					//512 bytes 5ms (from parser begin to parser end)
					var i_or=OR_csid_b[this.socket_.remotePort+'-'+this.socket_.remoteAddress+'-'+this.circId+'-'+sid];
					//console.log('db_data '+i_or);
					if (i_or) {
						i_or[0].send_db_data(stream,i_or[1]);
					};
				};
			},
			relay_db_sendme_handle:function(stream) {
				var sid=stream.streamId.readUInt();
				if (this.OP_) {
					console.log('OP received sendme sid '+sid+' resuming - buffered amount '+(peersm_client?this.first_.socket_.bufferSize:this.first_.socket_.bufferedAmount));
					var first_=this.first_;
					var request=first_[sid];
					if (request) {
						var pause=request.pause_[sid];
						request.stream_window_s +=STREAM_WINDOW_PEER;
						console.log('stream_window '+request.stream_window_s+' sent '+request.sent_+' fc_t '+request.fc_t.length);
						if (pause) {
							delete(request.pause_[sid]);
							if (!request._torrent_) {
								first_.send_db_data(pause,sid,false,true);
							} else {
								send_data.call(first_,pause,sid,request,0);
							};
						};
					};
				} else {
					var f_or=OR_csid_f[this.socket_.remotePort+'-'+this.socket_.remoteAddress+'-'+this.circId+'-'+sid];
					if (f_or) {
						f_or[0].send_db_sendme(f_or[1]);
					};
				};
			},
			keep_alive:function() {
				//return; //TODO remove
				var cell=new Cell(this.circId,Cell.prototype.PADDING,new Buffer('00','hex'));
				this.send(cell);
			},
			destroy_cid:function(request,bool) {
				var id=request.sid_;
				//console.log('destroy_cid');
				//console.log('STREAM destroy_cid for request '+request.i_id+' on port '+(request.remotePort?request.remotePort:' closed ')+' CID '+this.circId+' stream '+id);
				if (request) {
					if (bool) {
						var stream=new Stream(Stream.prototype.RELAY_END,request.sid_,new Buffer(0),this.last_.Df_hash);
						var cell=new Cell(this.circId,Cell.prototype.RELAY,this.last_.stream_encrypt_forward(stream));
						this.send(cell);
					};
					delete request.cid_;
					delete this.first_[id];
					delete request.sid_;
					delete this.first_.request_;
				};
			},
			setCircId:function() {
				if (this.circId==0) {delete this.socket_['0'];};
				if (this.OP_) {
					this.circId=choose_id(this.socket_);
					if (!this.circId) {
						return false;
					};
					this.socket_[this.circId]=this;
					var lcirc=this;
					var l=this.path_.length-1;
					this.first_=this;
					this.t0_=[];
					for (var i=0;i<l;i++) {
						var circuit=new Circuit();
						circuit.OP_=true;
						circuit.t0_=[];
						circuit.server_=lcirc.path_[i+1];
						lcirc.next_=circuit;
						circuit.prev_=lcirc;
						circuit.path_=lcirc.path_;
						circuit.nb_=lcirc.nb_+1;
						circuit.socket_=this.socket_;
						circuit.circId=this.circId;
						circuit.first_=this;
						lcirc=circuit;
					};
					return true;
				} else {
					var boo=(parseInt(this.server_.fing,16)<parseInt(OR_fing,16))?true:false;
					this.circId=choose_id(this.socket_,true,boo?midc:null);
					//while ((((this.circId>midc)&&boo)||((this.circId<=midc)&&(!boo)))||(this.circId===0)) { //TODO not optimized at all
					//	this.circId=choose_id(this.socket_);
					//	if (((this.circId>midc)&&boo)||((this.circId<=midc)&&(!boo))) {
					//		this.socket_.nbc_--;
					//	};
					//};
					//console.log('OR_f setcircid '+this.circId);
					this.socket_[this.circId]=this;
				};
			},
			process: function(request) { //TODO - optimistic data version > 0.2.3 - send data just after relay_begin
				//this is first
				this.last_.first_=this;
				if (!request) {
					var request=this.request_;
				};
				if ((request.download_)&&(!request._stream_)) {
					addEvent(document.body,'mousedown',function() {},false);
					if ((!request._data_)&&(!request.nb_try)) {
						Myalert('<p style="text-align:center">File not available from Peers, starting direct download from '+this.last_.server_.ip+'</p>');
					} else { //resume
						request._data_=0;
						Myalert('<p style="text-align:center">Resuming direct download from '+this.last_.server_.ip+'</p>');
					};
					setTimeout(function(){$_('alert_box').style.display='none';addEvent(document.body,'mousedown',function() {setTimeout(clear_menu,1000)},false);},12000);
				};
				request.cid_=this;
				//console.log('process '+(request.params_.host?(request.params_.host+' request '+request.i_id):''));
				if ((request.params_.host)&&(!request.destroyed)) {
					var payload=new Buffer(request.params_.host);
					//console.log('host relay_begin ');
					//console.log('request '+request.i_id);
					//console.log(payload.toString('utf8'));
					payload=new Buffer(payload.toString('hex')+'00','hex');
					//console.log('RELAY_BEGIN '+payload.toString('utf8')+' '+request.i_id+' CID '+this.circId);
					var id=choose_id(this);
					if (id) {
					console.log('STREAM : --------------SEND RELAY_BEGIN---------------------- CID '+this.circId+' on OR '+this.first_.server_.ip+' Stream '+id+' for request '+request.i_id+' on port '+request.remotePort+' host '+payload.toString('utf8'));
					//console.log(request.params_.stream.toString('utf8'));
						this[id]=request;
						request.sid_=id;
						request.received_=0;
						request.sent_=0;
						request.stream_window=STREAM_WINDOW;
						request.stream_window_s=STREAM_WINDOW;
						var socket_retry=function() {
							var cid=this;
							console.log('Timeout Bad circuit '+cid.circId);
							if (!cid.perf_>0) {
								cid.bad_=true;
							};
							cid.send_relay_end(id);
							request.nb_try++;
							delete request.cid_;
							delete cid[id];
							Tor(request);
						};
						var begin=function() {
							//console.log(payload);
							var stream=new Stream(Stream.prototype.RELAY_BEGIN,id,payload,this.last_.Df_hash);
							var cell=new Cell(this.circId,Cell.prototype.RELAY,this.last_.stream_encrypt_forward(stream));
							if (download) {
								request.socket_retry=setTimeout(socket_retry.bind(this),SOCK_RETRY);
							};
							this.send(cell);
						};
						begin.call(this);
					} else {
						//console.log('STREAM : too many streams opened');
					}
				};
			},
			sendme: function(request) {
				request=request||{sid_:0,i_id:-1};
				//oconsole('SENDING SENDME CIC '+this.first_.circId+' for request '+request.i_id);
				var stream=new Stream(Stream.prototype.RELAY_SENDME,request.sid_,new Buffer(0),this.last_.Df_hash);
				var cell=new Cell(this.circId,Cell.prototype.RELAY,this.last_.stream_encrypt_forward(stream));
				this.send(cell);
			},
			set_certs: function(val) {
				try {
					val=val.split(RSA_PUB_PFX);
					//console.log('RSA_PUB '+val.length);
					this.server_.onion_k=RSA_PUB_PFX+val[1].split(RSA_PUB_SFX)[0]+RSA_PUB_SFX;
					this.server_.sign_k=RSA_PUB_PFX+val[2].split(RSA_PUB_SFX)[0]+RSA_PUB_SFX;
					var pem=new PEM();
					this.server_.o_modulus=pem.modulus(this.server_.onion_k);
					this.server_.s_modulus=pem.modulus(this.server_.sign_k);
					return true;
				} catch(ee) {
					this.nb_error=this.nb_error?++this.nb_error:1;
					return false;
				};
			},
			get_certs: function(cb) {
				if ((this.server_)&&(!this.ok_)) {
					var d=Dirs.length;
					var fing=this.server_.fing;
					var r=simple_random(d);
					var tmp=Dirs[r].split(':');
					var ip=tmp[0];
					var port=tmp[1];
					//console.log('CERTS :'+this.server_.ip+' get_certs '+r+' dir :'+ip+':'+port+'/tor/server/fp/'+fing);
					var options = {
						host: ip,
						path: '/tor/server/fp/'+fing,
						port: port,
						headers: {'Accept':'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8','Accept-Encoding':'gzip deflate','Accept-Language':'fr,fr-fr;q=0.8,en-us;q=0.5,en;q=0.3','Cache-Control':'max-age=0','Connection':'keep-alive','Host':ip,'User-Agent':'Mozilla/5.0 (Windows NT 6.0; WOW64; rv:13.0) Gecko/20100101 Firefox/13.0'}
					};
					var req=http.request(options,(function(res) {
						req.data_='';
						if (res.statusCode!=200) {
							this.clear_t0_();
							try {
								//console.log('CERTS : Error status code http://'+ip+'/tor/server/fp/'+fing+' '+this.server_.fing);
								this.get_certs(cb);
							} catch(ee) {};
						};
						res.on('data', (function(d) {
							//console.log('CERTS : clear timeout ');
							this.clear_t0_();
							req.data_ +=d.toString('utf8');
						}).bind(this));
						res.on('end',(function() {
							if ((this.server_)&&(!req.destroy_)) {
								//console.log('CERTS : before calling cb '+this.certs_);
								if (!this.certs_) {
									if (this.set_certs(req.data_)) {
										//console.log('CERTS : calling cb '+this.server_.ip+' '+fing+' from '+ip);
										this.certs_=true;
										if (!this.prev_) {
											//console.log('CERTS : cb create');
											cb.call(this);
										} else {
											//console.log('CERTS : cb extend');
											//console.log(this.prev_.server_);
											cb.call(this.prev_);
										};
									} else {
										if (this.nb_error>4) {
											this.change_or('CERTS : get_certs wrong cert for '+fing,this.first_!==this?function(){this.get_certs(cb)}:null);
										} else {
											//console.log('CERTS : Retry get_certs wrong cert for '+fing);
											this.get_certs(cb);
										};
									};
								};
							};
						}).bind(this));
					}).bind(this));
					var error_=(function() {
						this.clear_t0_();
						if (this.server_) {
							//console.log('CERTS : error get_certs http://'+ip+'/tor/server/fp/'+fing+' '+e.message);
							this.nb_error=this.nb_error?++this.nb_error:1;
							if (this.nb_error>4) {
								this.change_or('error get_certs',this.first_!==this?function() {this.get_certs(cb)}:null);
							} else {
								this.get_certs(cb);
							};
						};
					}).bind(this);
					req.on('error',error_);
					req.end();
					var do_not_wait=(function() {
						this.clear_t0_();
						//strange behavior (node.js), to investigate, error fired after abort
						req.removeListener('error',error_);
						error_=function() {console.log('CERTS : error fired after abort for '+ip)};
						req.on('error',error_);
						req.destroy_=true;
						req.socket.destroy();
						//req.abort();
						//console.log('CERTS : 1-get_certs do not wait');
						if (this.server_) {
							//console.log('CERTS : 2-get_certs do not wait '+this.server_.ip+' for '+ip);
							this.get_certs(cb);
						};
					}).bind(this);
					this.t0_.push(setTimeout(do_not_wait,1000));
					//console.log('CERTS : timeout '+this.t0_);
				};
			},
			change_or: function(msg,cb) {
				if (this.server_) {
					//console.log('PATH : change_or - '+msg);
					console.log('change_or ----------- CIC '+this.circId);
					var list=[];
					var i,n_or;
					if (this.first_) {
						var request=this.first_.request_;
					};
					var db=(typeof(request)==='undefined')?null:(request.params_?request.params_.db:null);
					if ((!this.prev_&&one_OR)&&(typeof(this.server_)==='undefined'?false:(this.server_===one_OR))) {
						console.log('change_or one OR '+this.circId);
						n_or=one_OR;
					} else if ((this.first_===db_cid)||db) {
						//console.log('db cid - do not change, restart');
						if (this.first_) {
							console.log('change_or circuit destroy');
							this.first_.circuit_destroy();
							if (request) {
								circuit_start(request);
							};
						};
						return;
					} else {
						//console.log('change_or not one OR '+this.circId);
						var n_or=this.prev_?(this.next_?Relays:Exit):Guards;
						var l=n_or.length;
						this.clear_timers();
						this.clear_t0_();
						if (this.path_) {
							list=this.path_.map(function(val) {return val.ip});
						} else {
							list.push(this.server_.ip);
						};
						i=simple_random(l);
						while (list.indexOf(n_or[i].split('-')[1])!=-1) {
							i=simple_random(l);
						};
						var o=n_or[i].split('-');
						n_or={ip:o[1],fing:o[0],port:o[2],band:o[3],o_modulus:(o[5]?o[5]:o[4])};
					};
					//console.log('PATH :'+this.server_.ip+' ------changed or to ----------- ('+msg+') '+n_or.ip+' '+n_or.fing);
					var lcirc=this;
					while (lcirc) {
						delete lcirc.destroy_;
						delete lcirc.nb_error;
						delete lcirc.ok_;
						lcirc=lcirc.next_;
					};
					delete this.first_.last_;
					var circ=new Circuit();
					Object.keys(this).forEach(function(val) {circ[val]=this[val]},this);
					circ.server_=n_or;
					try {
						var old_ip=this.server_.ip;
						this.server_o=this.server_;
					} catch(ee) {
						console.log('_server error');
						this.clear_timers();
						for (var n in this) {if (typeof(this[n])!='function') {console.log(n+' '+this[n]);}}; //TODO tests - remove
						this.first_.destroy();
					};
					delete this.server_; //do not reuse it
					delete circ.certs_;
					if (this.next_) {
						circ.next_.prev_=circ;
					};
					if (this.prev_) {
						if (circ.prev_.extended_) {
							circ.prev_.extended_=circ;
						};

						circ.prev_.next_=circ;

						if (msg.indexOf('get_certs')==-1) {
							circ=circ.prev_;//call extend
							delete circ.certs_;
						};
					} else {
						circ.first_=circ;
					};
					circ.first_.reconstruct_path();
					if ((cb)&&(this.prev_||(this.first_===this))) {
						cb.call(circ);
					} else {
							if (this.socket_.first_===this) {
								delete OP_sock[old_ip];
								if (circ.socket_) {
									this.destroy_=true;
									circ.socket_.destroy();
									delete circ.socket_;
								};
								delete circ.extended_;
								circ.circId=0;
								circ.first_=circ;
								circ.path_.shift();
								circ.path_.unshift(circ.server_);
								tls_socket(circ);
							} else {
								this.next_.change_or('PATH : first socket exists, change path',this.create);
							};
					};
				} else {
					//this.first_.destroy();
					//this.first_.circuit_destroy();
				};
			},
			circuit_retry: function() {
				//this is first
				if ((!WAIT)&&(this.socket_)) {
					if ((!this.ok_)&&(this.socket_.first_===this)) {
						this.change_or('initial socket closed unexpectedly or unexpected circuit creation error or new circuit creation error');
					} else {
						//console.log('CIRC : circuit_retry');
						this.destroy();
						delete this.ok_;
						delete this.first_.last_;
						var tmp=this.circId;
						this.circId=choose_id(this.socket_);
						delete this.socket_[tmp];
						this.socket_[this.circId]=this;
						this.socket_.nbc_--;
						var lcirc=this;
						while (lcirc.extended_) {
							lcirc.extended_.circId=this.circId;
							lcirc=lcirc.extended_;
							delete lcirc.extended_;
						};
						if ((this===lcirc)&&(this.socket_.first_!==this)) {
							//console.log('circuit_retry create');
							lcirc.create();
						} else {
							lcirc.change_or('circuit_retry from first OR circId='+this.circId,this.create.bind(this));
						};
					};
				}
			},
			reconstruct_path: function() {
				var circ=this;
				var path=[];
				while (circ.next_) {
					path.push(circ.server_);
					circ=circ.next_;
				};
				while (circ.prev_) {
					circ.path_=path;
					circ.first_=this;
					circ=circ.prev_;
				};
			},
			circuit_destroy: function(no_destroy) {
				if (this!==db_cid) {
					//NB_C--;
				};
				//console.log('circuit_destroy '+this.circId);
				if (this.socket_) {
					if (this.socket_.remoteAddress) {
						console.log('CIRC : circuit destroy '+(this.OP_?'OP ':('OR '+(this.OR_f?'in ':'out ')))+(this.socket_.address()?this.socket_.address().address:'')+' CID '+this.circId+' remote '+this.socket_.remoteAddress);
					} else {
						console.log('circuit destroy : socket no remote address CIC '+(this.OP_?'OP ':'OR ')+' '+this.circId);
					};
					var boo;
					if (this.socket_) { //socket still exists (no OR_CONN_CLOSED for example)
						var sock=this.socket_;
						//console.log('circuit destroy check db destroy next_'+this.next_+' '+sock.OR_f);
						//console.log('circuit destroy port '+this.socket_.address().port+' '+OR_port);
						//console.log('circuit destroy '+DB_OR);
						if (!this.next_) {
							if (this.socket_.address()) {
								if ((DB_OR)&&(parseInt(this.socket_.address().port)===parseInt(OR_port))&&(sock.OR_f)) {
									console.log('ORDB - call db_destroy');
									this.circuit_db_destroy();
								};
							};
						} else {
							if (!no_destroy) {
								if (!this.destroyed_) {
									this.destroyed_=true;
									if (sock.remoteAddress) {
										console.log('circuit_destroy remote address '+sock.remoteAddress+' CIC '+this.circId);
										this.destroy();
									};
								};
							};
						};
						delete sock[this.circId];
						delete this.socket_;
						Object.keys(sock).forEach(function(n) {
							if ((!isNaN(n))&&(n!=null)) {
								boo=true;
							};
						},this);
						if (!boo) {
							if ((this!==db_cid)&&(!sock.ws_)) {
								console.log('circuit_destroy: destroy socket');
								sock.destroy();
							};
						};
					};
				};
				console.log('circuit_destroy '+(this.OP_?'OP':'OR'));
				if (this.OP_) {
					console.log('requests_destroy');
					this.requests_destroy();
				};
				delete this.next_;
				delete this.prev_;
			},
			circuit_db_destroy: function() {
				if (this.socket_) {
					Object.keys(this).forEach(function(n) {
						if ((!isNaN(n))&&(n!=null)) {
							var dest=this.socket_?(this.socket_.remotePort+'-'+this.socket_.remoteAddress+'-'+this.circId+'-'+n):(this.server_?(this.server_.port+'-'+this.server_.ip+'-'+this.circId+'-'+n):'');
							var or=OR_csid_b[dest]||OR_csid_f[dest];
							if (or) {
								console.log('------- '+or[1]);
								or[0].send_db_end(2,or[1]);
								delete OR_csid_f[dest];
								delete OR_csid_b[dest];
								delete this[n];
								delete or[0][or[1]];
							};
						}
					},this);
				};
			},
			requests_destroy: function() {
				//console.log('requests destroy');
				for (var n in this) {
					if ((!isNaN(n))&&(n!=null)) {
						var request=this[n];
						//console.log('CIC '+this.circId+' destroyed, retry request '+request.i_id+' or destroy '+request._data_);
						if (request.abstract_client_tls) {
							request.abstract_client_tls.close();
						};
						delete request.abstract_client_tls;
						this.destroy_cid(request); //do not reuse cid
						//request.end(); //close request
						if (db_cid) {
							if (this!==db_cid) {
								if (request._data_) {
									request.destroy();
								} else {
									this.request_retry(request,'requests_destroy');
								};
							} else {
								clear_request(request);
							};
						} else {
							if (download) {
							//xx
								clear_request(request);
							};
						};
					};
				};
				if (anonym) {
					if (this===fake_cid) {
						console.log('destroy CIC resend associate ');
						for (var n in OP_domains) {
							delete OP_domains[n].associated;
						};
						fake_cid=null;
						Tor({params_:{OP:true,nb_hop:NB_HOP,ws:client}}); //create a new circuit for associate
					};
				} else {
					if (db_cid) {
						if (this===db_cid) {
							db_cid=null;
							Tor({params_:{OP:true,nb_hop:NB_DB_HOP,ws:client,db:true}});
						};
					} else {
						if (download) {
							Tor({params_:{OP:true,nb_hop:NB_DB_HOP,ws:client,db:true}});
						};
					};
				};
			},
			clear_timers: function() {
				//console.log('CIRC : clear_timers');
				if (this.OP_) {
					this.first_.tc_.forEach(function(val) {clearTimeout(val)});
					this.first_.tc_=[];
				};
			},
			clear_t0_: function() {
				if (this.t0_) {
					this.t0_.forEach(function(val) {clearTimeout(val)});
					this.t0_=[];
				};
			},
			end:function(msg,retry,id) {
				//console.log('xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
				//console.log('msg:'+msg+' retry:'+retry+' CID '+this.circId+' '+(this.OP_?'OP':'OR')+' socket '+this.socket_.remoteAddress+' '+(this.server_?this.server_.fing:'')+' '+(this.server_?this.server_.port:''));
				//console.log(msg+' CID '+this.circId+' '+(this.OP_?'OP':'OR'));
				if (id) {
					var c=this.first_||this;
					var request=c[id]?c[id]:c.request_;
				};
				if (this.OP_) {
					//console.log('CALLING END : reason '+msg+' '+retry+' OR '+(this.first_.server_?this.first_.server_.ip:'')+' CID '+this.circId+(id?(' Stream '+id+' first_[id] '+(typeof(this.first_[id]))+' on port '+request.remotePort+' for request '+request.i_id+' host '+request.params_.host):''));
				} else {
					//console.log('CALLING END : reason '+msg+' '+retry+' OR '+(this.server?(this.server_.ip?this.server_.ip:''):'')+' CID '+this.circId);
				};
				msg=msg.split(':');
				console.log2=function() {};
				var circ_error=(function() {
					if (msg.length>1) {
						switch(parseInt(msg[1])) {
							case 0 : console.log2('-- CIC NONE            (No reason given.)');return 0;
							case 1 : console.log2('-- CIC PROTOCOL        (Tor protocol violation.)');return 1;
							case 2 : console.log2('-- CIC INTERNAL        (Internal error.)');return 2;
							case 3 : console.log2('-- CIC REQUESTED       (A client sent a TRUNCATE command.)');return 3;
							case 4 : console.log2('-- CIC HIBERNATING     (Not currently operating; trying to save bandwidth.)');return 4;
							case 5 : console.log2('-- CIC RESOURCELIMIT   (Out of memory, sockets, or circuit IDs.) '+this.circId+' '+(this.OP_?' OP ':' OR '));return 5;
							case 6 : console.log2(' -- CIC CONNECTFAILED   (Unable to reach relay.)');return 6;
							case 7 : console.log2('-- CIC OR_IDENTITY     (Connected to relay, but its OR identity was not as expected.)');return 7;
							case 8 : console.log2('-- CIC OR_CONN_CLOSED  (The OR connection that was carrying this circuit died CID '+this.circId+' '+(this.OP_?' OP ':' OR '));return 8;
							case 9 : console.log2('-- CIC FINISHED        (The circuit has expired for being dirty or old.)');return 9;
							case 10 : console.log2('-- CIC TIMEOUT         (Circuit construction took too long)');return 10;
							case 11 : console.log2('-- CIC DESTROYED       (The circuit was destroyed w/o client TRUNCATE)');return 11;
							case 12 : console.log2('-- CIC NOSUCHSERVICE   (Request for unknown hidden service)');return 12;
						};
					};
				}).bind(this);

				var relay_end=(function() {
					if (msg.length>1) {
						switch(parseInt(msg[1])) {
							case 1 : console.log2('-- RELAY REASON_MISC           (catch-all for unlisted reasons)'+' request '+(request?request.i_id:''));return 1;
							case 2 : console.log2('-- RELAY REASON_RESOLVEFAILED  (couldn t look up hostname)'+' request '+(request?request.i_id:''));return 2;
							case 3 : console.log2('-- RELAY REASON_CONNECTREFUSED (remote host refused connection) [*]'+' request '+(request?request.i_id:''));return 3;
							case 4 : console.log2('-- RELAY REASON_EXITPOLICY     (OR refuses to connect to host or port)'+' request '+(request?request.i_id:''));return 4;
							case 5 : console.log2('-- RELAY REASON_DESTROY        (Circuit is being destroyed)'+' request '+(request?request.i_id:''));return 5;
							case 6 : console.log2('-- RELAY REASON_DONE           (Anonymized TCP connection was closed)'+' request '+(request?request.i_id:''));return 6;
							case 7 : console.log2('-- RELAY REASON_TIMEOUT        (Connection timed out, or OR timed out while connecting)'+' request '+(request?request.i_id:''));return 7;
							case 8 : console.log2('-- RELAY REASON_NOROUTE        (Routing error while attempting to contact destination)'+' request '+(request?request.i_id:''));return 8;
							case 9 : console.log2('-- RELAY REASON_HIBERNATING    (OR is temporarily hibernating)'+' request '+(request?request.i_id:''));return 9;
							case 10 : console.log2('-- RELAY REASON_INTERNAL       (Internal error at the OR)'+' request '+(request?request.i_id:''));return 10;
							case 11 : console.log2('-- RELAY REASON_RESOURCELIMIT  (OR has no resources to fulfill request)'+' request '+(request?request.i_id:''));return 11;
							case 12 : console.log2('-- RELAY REASON_CONNRESET      (Connection was unexpectedly reset)'+' request '+(request?request.i_id:''));return 12;
							case 13 : console.log2('-- RELAY REASON_TORPROTOCOL    (Sent when closing connection because of Tor protocol violations.)'+' request '+(request?request.i_id:''));return 13;
							case 14 : console.log2('-- RELAY REASON_NOTDIRECTORY   (Client sent RELAY_BEGIN_DIR to a non-directory relay.)'+' request '+(request?request.i_id:''));return 14;
						};
					};
				}).bind(this);

				if (!retry) {
					//TODO close socket if no more circuits
					delete this.socket_[this.circId];
				} else {
					switch(retry) {
						case 'handshake':this.change_or('handshake failed');break;
						case 'fast_key':this.change_or('wrong fast key');break; //TODO replace by create fast
						case 'created_extended_key':this.change_or('wrong create or extend key',this.prev_?this.prev_.extend:null);break;
						case 'truncated':this.relay_truncated_handle(circ_error());break;
						case 'truncate':this.relay_truncate_handle(circ_error());break;
						case 'begin_dir':this.send_relay_end_b(id,relay_end());break;
						case 'destroy':this.handle_destroy(circ_error());break;
						case 'unrecognized':console.log('end unrecognized circuit destroy');this.circuit_destroy();break;
						case 'relay_end':if (request) {if (typeof request.i_id!=='undefined') {this.relay_end_handle(id,relay_end())}};break;
					};
				};
			}
		};

		var Stream=function(command,streamId,data,D) {
			this.command=(new Buffer(1)).writeUInt(command);
			this.recognize=new Buffer('0000','hex');
			this.streamId=(new Buffer(2)).writeUInt(streamId);
			this.digest=new Buffer('00000000','hex');
			this.length=(new Buffer(2)).writeUInt(data.length);
			this.data=command===this.RELAY_WS?new Buffer(data.length):new Buffer(PAYLOAD_STREAM);
			this.data.map(data);
			if (D) {
				D.update(this.toBuffer());
				this.digest=(new Buffer(D.digest('hex'),'hex')).slice(0,4);
			};
		};

		Stream.prototype={
			RELAY_BEGIN:1,
			RELAY_DATA:2,
			RELAY_END:3,
			RELAY_CONNECTED:4,
			RELAY_SENDME:5,
			RELAY_EXTEND:6,
			RELAY_EXTENDED:7,
			RELAY_TRUNCATE:8,
			RELAY_TRUNCATED:9,
			RELAY_DROP:10,
			RELAY_RESOLVE:11,
			RELAY_RESOLVED:12,
			RELAY_BEGIN_DIR:13,
			RELAY_ASSOCIATE:40,//ianonym
			RELAY_WS:41,//ianonym
			RELAY_INFO:42,//ianonym
			RELAY_DB_INFO:80,//download
			RELAY_DB_QUERY:81,//download
			RELAY_DB_CONNECTED:82,//download
			RELAY_DB_DATA:83,//download
			RELAY_DB_END:84,
			RELAY_DB_SENDME:85,
			toBuffer:function() {
				return [this.command,this.recognize,this.streamId,this.digest,this.length,this.data].concatBuffers();
			}
		};

		var Payload=function(data) {
			switch(this.Command.readUInt()) {
				case this.VERSIONS : return this.versions_cell_decode(data);
				case this.CERTS : return this.certs_cell_decode(data);
				case this.AUTH_CHALLENGE : return this.auth_challenge_cell_decode(data);
				case this.NETINFO : return this.netinfo_cell_decode(data);
				case this.CREATED_FAST : return this.created_fast_cell_decode(data);
				case this.CREATED_FAST_WS : return this.created_fast_ws_cell_decode(data);
				case this.CREATE_FAST : return this.create_fast_cell_decode(data);
				case this.CREATE_FAST_WS : return this.create_fast_ws_cell_decode(data);
				case this.CREATED : return this.created_cell_decode(data);
				case this.CREATE : return this.create_cell_decode(data);
				default : return [data];
			};
		};

		var Handle_cells=function(cells,bool) {
			if (cells) {
				var l=cells.length;
				for (var i=0;i<l;i++) {
					//console.log('handle1 '+Date.now());
					var cell=cells[i];
					var cid=cell.CircID.readUInt();
					//console.log('cell received CIC '+cid);
					if (this[cid]) {
						var circ=this[cid];
					} else {
						if (this.OR_&&this.OR_f) {
							if (this[0]) {
								//console.log('OR_f first cell received on existing socket before handshake cid '+cid);
								var circ=this[0];
								circ.circId=cid;
								this[cid]=circ;
								delete this[0];
							} else {
								//console.log('OR_f first cell received on existing socket after handshake cid '+cid);
								var circ=new Circuit();
								circ.OR_=true;
								circ.OR_f=true;
								circ.circId=cid;
								circ.socket_=this;
								circ.way_='socket in';
								this[cid]=circ;
							};
						} else {
							//console.log('STREAM : xxxxxxxxxxxxxxxxxxxxxxxxxx unknown cid - do nothing '+cid+' xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
							return;
						};
					};
					try {circ.clear_timers();} catch(ee) {};
					if (cell.Command.readUInt()===6) {
						console.log('handle cell '+cell.Command.readUInt());
					};
					//writefile('error.txt',cells.length+' '+cell.CircID.readUInt()+' '+cell.Command.readUInt()+' '+cell.Payload[0].toString('hex'));
					//console.log('handle2 '+Date.now());
					if (Pchart&&Dchart) {
						if (chart1_int||chart2_int) {
							if (cell.Command.readUInt()!==cell.PADDING) {
								var round=DateRound(new Date()).getTime();
								var l_c=0;
								cell.Payload.forEach(function(val){l_c+=val.length});
								if (circ.first_===db_cid) {
									if (chart2_int) {
										Pchart.dynRow[round]=Pchart.dynRow[round]?(Pchart.dynRow[round]+l_c):l_c;
									};
								} else {
									if (chart1_int) {
										Dchart.dynRow[round]=Dchart.dynRow[round]?(Dchart.dynRow[round]+l_c):l_c;
									};
								};
							};
						};
					};

					if (!circ.destroyed_) {
						switch(cell.Command.readUInt()) {
							case cell.PADDING : circ.padding_cell_handle(cell);break;//OR
							case cell.VERSIONS : circ.versions_cell_handle(cell);break;
							case cell.CERTS : circ.certs_cell_handle(cell);break;
							case cell.AUTH_CHALLENGE : circ.auth_challenge_cell_handle(cell);break;
							case cell.NETINFO : circ.netinfo_cell_handle(cell);break;
							case cell.CREATE_FAST : circ.create_fast_cell_handle(cell);break;//OR
							case cell.CREATE_FAST_WS : circ.create_fast_ws_cell_handle(cell);break;//OR
							case cell.CREATED_FAST : circ.created_fast_cell_handle(cell);break;
							case cell.CREATED_FAST_WS : circ.created_fast_ws_cell_handle(cell);break;
							case cell.CREATE : circ.create_cell_handle(cell);break; //OR
							case cell.CREATED : circ.created_cell_handle(cell);break;
							case cell.RELAY : circ.relay_cell_handle(cell,bool);break; //OP and OR
							case cell.RELAY_WS : circ.relay_cell_handle(cell,bool);break; //OP and OR
							case cell.RELAY_EARLY : circ.relay_cell_handle(cell,bool);break; //OP and OR
							case cell.DESTROY : circ.end('Destroy reason:'+((cell.Payload[0]).slice(0,1)).readUInt(),'destroy');break;
						};
					} else {
						console.log('received cell for destroyed CIC '+circ.circId);
					};
				};
			} else {
				//TODO banish this OR
				console.log('end or banish circuit_destroy');
				this.circuit_destroy();
			};
		};

		var circuit_start=function(request) {
			console.log('circuit_start');
			if (download&&(NB_C>NB_C_MAX)) {
				document.location.href=document.location.href;
			};
			var params=request.params_;
			//if (request.i_id) {
				//console.log('STREAM : circuit_start for request '+request.i_id+' '+params.host+' on port '+request.remotePort);
				var p=[];
				if (params.nb_hop) {
					if ((params.nb_hop>2)||prod) {
						p=create_path(params);
					};
				} else {
					p=create_path(params);
				};
				if (one_OR) {
					p[0]=one_OR;
				};
				if (anonym||download) {
					if (fake_cid_OR) {
						if (!fake_cid) {
							if (params.ws===fake_client) {
								p[0]=fake_cid_OR;
							} else {
								return; //wait that fake_cid exists
							};
						};
					};
				};
				if (params.db) {
					//p[1]=test_OR;
					p[NB_DB_HOP-1]=DB_OR; //TODO several DB_OR
					//p.pop();
				};
				console.log('PATH :'+p[0].ip+' '+p[1].ip+' '+(p.length>2?(p[2].ip):''));
				//if (p[3]) {console.log(p[3].ip)};
				var s=OP_sock[p[0].ip];
				var circ=new Circuit(p);
				circ.request_=request;
				circ.first_=circ;
				circ.tc_=[];
				//console.log(s?('circuit start on socket '+s.server_.ip):'no socket ');
				if (!s) {
					console.log('circuit_start init socket');
					circ.circId=0;
					if (!params.ws) {
						tls_socket(circ);
					} else {
						if (params.ws.wsconnected_) {
							init_socket_(params.ws,circ);
							init_socket.call(params.ws,circ);
						};
					};
				} else {
					if ((s.abstract_client_tls)&&(!s.tls_connected)&&(s.wsconnected_)) {
						console.log('start TLS handshake - circuit start ');
						s.abstract_client_tls.handshake();
					} else {
						console.log('TLS connected - start create_fast');
						circ.server_=s.server_;
						circ.socket_=s;
						circ.tc_=[];
						if (circ.setCircId()) {
							if (!WS_TLS) {
								circ.create();
							} else {
								console.log('Sending fast CIC '+circ.circId+' '+s.server_.ip+' '+circ.destroyed_);
								if (params.db) {
									client.db_cid_launched=true;
								};
								circ.X_=Rand(20);
								var fast=new Cell(circ.circId,Cell.prototype.CREATE_FAST,circ.X_);
								circ.send(fast);
							};
						} else {
							console.log('PATH : no more circuits available');
						};
					};
				};
			//} else {
			//	console.log('circuit_start request no i_id for host '+params.host);
			//}
		};

		var circuits_destroy=function(obj) {
			if (obj) {
				console.log('CIRCUITS destroy '+(obj.remoteAddress?obj.remoteAddress:''));
				if (obj.OP_) {
					delete OP_sock[obj.remoteAddress];
				};
				Object.keys(obj).forEach(function(n) {
					if ((!isNaN(n))&&(n!=null)) {
						var circ=obj[n];
						console.log('delete cic '+circ.circId+' socket '+(circ.server_?circ.server_.ip:''));
						if (obj.OP_) {
							circ.requests_destroy();
							delete OP_sock[circ.server_.ip];
						} else {
							//OR_f
							if (circ.next_) {
								circ.next_.destroy();
							} else if (circ.prev_) {
								circ.prev_.destroy();
							} else {
								circ.circuit_db_destroy(); //ORDB
							};
						};
						delete circ.socket_;
						delete obj[n];
					};
				});
			};
		};

		var choose_circuit=function(request,bool) {
			var a=[];
			for (var n in OP_sock) {
				var b=[];
				Object.keys(OP_sock[n]).forEach(function(m) {
					var tmp=OP_sock[n][m];
					if ((!isNaN(m))&&(m!=null)&&(tmp.last_)&&(request.no_exit.indexOf(tmp)===-1)&&(!tmp.bad_)) {//TODO remove no_exit ??
						if (!download||((tmp!==db_cid)&&(tmp!==fake_cid))) {
							b.push(tmp);
						};
					};
				});
				if (b.length) {
					a.push(b);
				};
			};
			if (a.length) {
				if (!bool) {
					//if (b.length) {
						var r=a[simple_random(a.length)];
						var res,l;
						if (request.nb_try) {
							//r=r.map(function(val) {return [val,val.perf_]});
							//r.sort(function(a,b) {a=a[1];b=b[1];return a==b?0:(a>b?-1:1)});
							r=r.map(function(val) {return [val,val.time_average]});
							r.sort(function(a,b) {a=a[1];b=b[1];return a==b?0:(a<b?-1:1)});
							var n=0;
							var m=r.length;
							if (!download) {
								for (var i=0;i<m;i++) {
									if (r[i][1]!==0) {
										n=i;
										break;
									};
								};
							} else {
								n=parseInt(r.length/2);
							};
							if (n) {
								r=r.slice(n,Math.min(r.length,BEST_CIRCS+n));
							};
							oconsole('best circuits '+(r[0]?r[0][0].circId:'')+' '+(r[1]?r[1][0].circId:'')+' '+(r[2]?r[2][0].circId:''));
							//gconsole('best circuits '+r);
							l=simple_random(r.length);
							res=r[l][0];
						} else {
							l=simple_random(r.length);
							res=r[l];
							res.time_=new Date().valueOf();
						};
						//console.log('circuit choosen CID '+res.circId+' for request '+request.i_id);
						return res;
					//};
				} else {
					if ((a.length===1)&&(a[0].length===1)) {
						return true;
					};
				};
			} else {
				if (download) {
					//Myalert("<p style='text-align:center'>No more good circuit available, rebuilding circuits, please wait or refresh the page</p>");
					console.log('choose circuit no circuit found for request '+request.i_id);
				};
			};
		};

		var execute=function(data) {
			return function() {
					//console.log('Before parser '+Date.now());
					//console.log('execute before uint8array');
					//console.log(data.toString('hex'));
				//try {
					if (!this.OR_) {
						//console.log('OUTGOING SOCKET : OP RECEIVE from '+this.remoteAddress);
						//console.log(data.toString('hex'));
						/*
						if ((window)&&(!(data instanceof ArrayBuffer))) {
							//console.log('ArrayBuffer');
							var a=new Uint8Array(data.length);
							a.map(data);
							data=a;
						};
						*/
					} else {
						if (this.address().port===OR_port) {
							//console.log('OR receive data '+this.remoteAddress);
						};
					};
					//console.log('execute after uint8array');
					//console.log(data.toString('hex'));
					if (this.ws_) {
						if (((!window_browser||window_browser_server)||(this.OR_))&&(!peersm_client)) {
							if (this.WS_OP_) {
								//console.log('wsdecode');
								//console.log(data.toString('hex'));
								//console.log(this.stream_ws_?this.stream_ws_.length:'vide');
								//console.log('ws OP data length : '+data.length+' stream_tor length '+this.stream_tor_.length+' '+(this.stream_tor_.toString('hex').substr(0,100)));
							} else { //OR
								//console.log('ws OR data length : '+data.length+' stream_tor length '+this.stream_tor_.length+' '+(this.stream_tor_.toString('hex').substr(0,100)));
								//console.log('OR wsdecode');
								//console.log(data.toString('hex'));
								//console.log('OR wsdecode tmp1');
								//console.log((this.stream_ws_?([this.stream_ws_,data].concatBuffers()):data).toString('hex'));
							};
							//console.log('before wsdecode: '+data.toString('hex')+' stream_ws_'+(this.stream_ws_?this.stream_ws_.toString('hex'):'null'));
							var tmp=wsdecode(this.stream_ws_?([this.stream_ws_,data].concatBuffers()):data);
							try {
								data=tmp[0]; //decoded stream
							} catch(ee) {
								console.log('wsdecode error or FIN (first bit at 1):'+(data.length?data[0]:'')+' closing WS.');
								//console.log((this.stream_ws_?([this.stream_ws_,data].concatBuffers()):data).toString('hex'));
								this.end();
								//data=new Buffer(0);
								//tmp=[data,data];
								return;
							};
							if ((this.ws_)&&(this.WS_OP_)) {
								//console.log('tmp1');
								//console.log(tmp[1].toString('hex'));
								//console.log('wsdecoded');
								//console.log(data.toString('hex'));
								if (tmp[1].length) {
									//console.log('tmp1');
									//console.log(tmp[1].toString('hex'));
								};
							};
							this.stream_ws_=tmp[1].length?tmp[1]:null;
							//console.log('rrr');
							//writefile('error.txt',(this.WS_OP_?'OP ':'OR ')+data.toString('hex'));
						};
					};
					this.stream_tor_=this.stream_tor_.length?([this.stream_tor_,data].concatBuffers()):data;

					//writefile('error.txt','data');

					//writefile('error.txt',(this.OR_?(this.OR_f?'OR_f ':'OR '):'OP ')+data.toString('hex'));

					//writefile('error.txt','stream_tor');

					//writefile('error.txt',(this.OR_?(this.OR_f?'OR_f ':'OR '):'OP ')+this.stream_tor_.toString('hex'));

					//console.log('before parse');
					//console.log(this.stream_tor_.toString('hex'));

					if (this.WS_OP_&&this.ws_) {
						//console.log('before parse');
						//console.log(this.stream_tor_.toString('hex'));
						//console.log(data.toString('hex'));
						//console.log('-------');
					};
					if (PARSER_PERF) {
						var t0=Date.now();
					};
					//console.log('before parse '+Date.now());
					if ((this.ws_&&download)&&(!window_browser)&&(!peersm_client)) {
						//console.log('Pass to encrypted: '+this.stream_tor_.length);
						if (this.pair.encrypted.t0) {
							count_wsdec++;
							count_wsdec_data +=this.stream_tor_.length;
							time_wsdec_data +=Date.now()-this.pair.encrypted.t0;
							if (count_wsdec%PERF_WSDEC===0) {
								if (time_wsdec_data) {
									console.log('wsdecode perf (pass to encrypted): '+parseInt(count_wsdec_data*8/(time_wsdec_data/1000))+' bps');
									count_wsdec=0;
									count_wsdec_data=0;
									time_wsdec_data=0;
								};
							};
						};
						this.pair.encrypted.write(this.stream_tor_);
						this.stream_tor_=new Buffer(0);
						var queue_=this.queue_;
						queue_.shift();
						if (queue_.length) {
							queue_[0]();
						};
					} else {
						//console.log('parse '+this.stream_tor_.toString('hex'));
						this.stream_tor_.parse(this);
					};
					//console.log('after parser '+Date.now());
					if (t0) {
						count_parse++;
						count_parse_data +=data.length;
						time_parse_data +=Date.now()-t0;
						if (count_parse%PERF_PARSE===0) {
							if (time_parse_data) {
								if (this.OR_) {
									if (this.address()) {
										if ((DB_OR)&&(this.address().port===OR_port)&&(this.OR_f)) {
											console.log('Parser perf (ORDB):'+parseInt(count_parse_data*8/(time_parse_data/1000))+' bps '+this.stream_tor_.length);
										};
									};
								} else {
									console.log('Parser perf:'+parseInt(count_parse_data*8/(time_parse_data/1000))+' bps '+this.stream_tor_.length);
								};
								count_parse=0;
								count_parse_data=0;
								time_parse_data=0;
							};
						};
					};
				//} catch(ee) {
				//	console.log('bad cell');
				//	console.log(data.toString('hex'));
				//	console.log(this.remoteAddress);
				//};
			};
		};

		var on_data=function(data) {
			this.queue_=this.queue_||[];
			this.queue_.push(execute(data).bind(this));
			//console.log('parse queue '+this.address().port);
			if (this.queue_.length===1) {
				this.queue_[0]();
			};
		};

		var init_request=function() {
				delete this.end_;
				delete this.html_;
				delete this.header_;
				delete this.header_l;
				delete this.content_;
				delete this.content_l;
				delete this.clength_;
				delete this.encoding_;
				delete this.pass_;
				delete this.t0_;
				delete this.script_;
				delete this.header_sent;
				delete this.decoder_;
				delete this.f_;
				delete this.wait_;
				delete this.buff_;
				//delete this._host_;
				//delete this._data_;
				this.nb_try=0;
				delete this.time_resp;
		};

		var ini_nosocks_request=function(url) {
			var request={};
			init_request.call(request);
			request.params_={};
			request.params_.OP=true;
			request.params_.nb_hop=NB_HOP;
			request.params_.one_c=true;
			request.nb_try=0;
			request.no_exit=[];
			request.squeue_=[];
			request.wsqueue_=[];
			request._date_=Date.now();
			request.i_id=db_id;
			db_id++;
			url=url_decode(url);
			request.params_.host=url.host;
			//var d=new Date();
			var get='GET /'+url.rest+' HTTP/1.1\r\n';
			get +='Host: '+url.host+'\r\n';
			get +='User-Agent: Mozilla/5.0 (Windows NT 6.0; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0.1\r\n';
			get +='Accept: */*\r\n';
			get +='Accept-Language: en\r\n';
			get +='Accept-Encoding: gzip, deflate\r\n';
			get +='Connection: keep-alive\r\n';
			get +='\r\n';
			request.params_.stream=new Buffer(get,'utf8');
			//request.stream=get_request('www.kickstarter.com','projects/450023/ianonym-internet-privacy-everywhere-from-any-devic');
			request.remotePort='60000';
			request.remoteAddress='1.2.3.4';
			request.write=function() {				};
			request.end=function() {};
			request.destroy=function() {};
			request.close=function() {};
			request._init_=init_request;
			request._write_=request.write;
			return request;
		};

		var init_socket=function(circ) {
			OP_sock[circ.server_.ip]=this;
			this[circ.circId]=circ;
			this.stream_tor_=new Buffer(0);
			var cell=new Cell(circ.circId,Cell.prototype.VERSIONS,(new Buffer(2)).writeUInt(3));
			circ.send(cell);
		};

		var init_socket_=function(socket,circ) {
			circ.socket_=socket;
			socket.first_=circ;
			socket.server_=circ.server_;
			socket.nbc_=0;
			if (WS_TLS&&(socket===client)) {
				socket.buff_w=forge_buffers?(new forge.util.ByteBuffer()):new Buffer(0);
				socket.abstract_client_tls=abstract_tls(socket,'www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.net');
				socket.abstract_client_tls.closed=function(c) {
					console.log(' TLS client disconnected.');
					clearInterval(monitor);
					clear_requests(socket);
					socket.destroy();
					update_circ();
				};
				socket.abstract_client_tls.error=function(c,error) {
					if (error.message.indexOf('MAC')===-1) {
						console.log(' Error TLS client disconnected '+error.message);
						clearInterval(monitor);
						clear_requests(socket);
						socket.destroy();
						update_circ();
					} else {
						console.log('bad MAC');
					};
				};
				socket.write=function(data) {
					if (!forge_buffers) {
						socket.buff_w=socket.buff_w.length?[socket.buff_w,data].concatBuffers():data;
					} else {
						//console.log('wbuffer '+data.toString('hex'));
						socket.buff_w.putBytes(data.toString('binary'));
					};
				};
				socket.abstract_client_tls.connected=function(c) {
					console.log('TLS client connected');
					monitor=setInterval(monitor_circuits,10000);
					socket.tls_connected=true;
					socket.write=function(data) { //data is ArrayBuffer
						//console.log('TLS prepare to send '+data.toString('hex'));
						//console.log('sending tls data');
						//console.log(data);
						if (!forge_buffers) {
							socket.abstract_client_tls.prepare(encode(data.toString('hex')));
						} else {
							socket.abstract_client_tls.prepare(data.data?data.getBytes():data.toString('binary'));
						};
					};
					socket.write(socket.buff_w);
				};
				socket.write_c=function(data) {
					if (data.length) {
						if (!forge_buffers) {
							//console.log('TLS Send '+data.toString('hex'));
							socket.send(data);
						} else { //data is string
							//console.log('TLS Send '+new Buffer(data,'binary').toString('hex'));
							socket.send(new Buffer(data,'binary'));
						};
					};
				};
				socket.abstract_client_tls.dataReady=function(c) {
					//Perf Received-->TLS Dataready: 500K 2ms (2 Mbps)
					if (!forge_buffers) {
						var data=c.data.data.slice(c.data.read,c.data.length_);
						c.data.read=c.data.length_;
						console.log(data.length);
						if (data.length) {
							//console.log('TLS dataReady '+t0);
							//console.log(data.length);
							on_data.call(socket,data);
							//console.log('TLS dataReady processed '+(Date.now()-t0));
							//processed 500K 9ms
						};
					} else {
						if (c.data.length()) {
							var data=new Buffer(c.data.getBytes(),'binary');
							if (socket.t0) {
								count_tls++;
								count_tls_data +=data.length;
								time_tls_data +=Date.now()-socket.t0;
								if (count_tls%PERF_TLS===0) {
									if (time_tls_data) {
										console.log('TLS perf (dataReady): '+parseInt(count_tls_data*8/(time_tls_data/1000))+' bps');
										count_tls=0;
										count_tls_data=0;
										time_tls_data=0;
									};
								};
							};
							//console.log('TLS data decryped '+Date.now());
							on_data.call(socket,data);
							//console.log('TLS dataReady processed '+(Date.now()-t0));
						};
					};
				};
				if (socket.wsconnected_) {
					console.log('start TLS handshake ');
					delete socket.abstract_client_tls.handshaking;
					socket.abstract_client_tls.handshake();
				};
			};
		};

		var init_socket_or=function(socket) {
			//console.log('OR : --------------------init socket with OR '+(socket.ws_?'WS':('port '+socket.address().port))+' server------------------------------------ ');
			socket.OR_=true;
			socket.OR_f=true;
			socket.way_='socket in';
			socket.privkey_=privkey;
			socket.stream_tor_=new Buffer(0);
			var circ=new Circuit();
			circ.remote_=socket.remoteAddress;
			circ.OR_=true;
			circ.OR_f=true;
			circ.circId=0;
			circ.socket_=socket;
			circ.way_='socket in';
			socket[0]=circ;
		};

		var tls_socket=function(circ) {
			//OP socket
			//TODO generate keys dynamically
			var servername='www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.net'; //check .net or .com
			var issuer='www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.com';
			var options = {
				key: fs.readFileSync(pathd+ordbname+'/priv-key.pem'),
				cert: createIdLinkTLSCert(pathd+ordbname+'/pub-key.pem',pathd+ordbname+'/priv-key.pem','pem',parseInt((Rand(8)).toString('hex'),16),new Date(),servername,issuer),
				servername: servername,
				rejectUnauthorized: false
			};
			console.log('OUTGOING SOCKET : ---------start initial socket------------'+options.servername+' '+circ.server_.ip+' '+circ.server_.port);
			var tls_socket_ = tls.connect(circ.server_.port, circ.server_.ip, options, function() {
					clearTimeout(t0);
					init_socket.call(this,circ);
			});
			//if (window) {
			//	var owrite=tls_socket.write;
			//	tls_socket.write=function(data) {
			//		data=ArrayBufferToBuffer(data);
			//		owrite(data);
			//	};
			//};

			tls_socket_.on('data', on_data);
			var end_=function() {
				if (tls_socket_.nbc_>1) {
						//console.log('OUTGOING SOCKET : destroy');
						circuits_destroy(tls_socket);
				} else {
					clearTimeout(t0);
					if (circ.destroy_) {
						//console.log('OUTGOING SOCKET : End destroyed '+circ.server_o.ip);
						circuits_destroy(tls_socket);
					} else {
						if (!circ.last_) {
							circ.clear_t0_();
							//console.log('OUTGOING SOCKET : End from '+circ.server_.ip+' : close connection');
							delete circ.ok_;
							delete OP_sock[circ.server_.ip];
							circ.circuit_retry();
						} else {
							circuits_destroy(tls_socket);
						};
					};
				};
			};
			tls_socket_.on('end',end_);
			tls_socket_.on('close',end_);
			tls_socket_.on('error',function() {
				clearTimeout(t0);
				//console.log('OUTGOING SOCKET : error client socket :'+error+' '+circ.server_.ip);
				circ.destroy_=true;
				tls_socket_.destroy();
				if (!circ.last_) {
					circ.change_or('initial socket error');
				};
			});
			var do_not_wait=function() {
				circ.destroy_=true;
				tls_socket_.destroy();
				//console.log("OUTGOING SOCKET : socket - do not wait");
				circ.change_or('initial socket failed');
			};
			init_socket_(tls_socket_,circ);
			var t0=setTimeout(do_not_wait,2000);
		};

		var Tor=function(request,circuit,msg) {
			//console.log(Tor.caller.toString().substr(0,50));
			var params=request.params_;
			request.time_ini=Date.now();
			if (params) {
				if (params.OP) { //OP
					if (!params.one_c) {
						//console.log('STREAM : one_c');
						circuit_start(request);
					} else {
						if (circuit) {
							//console.log('STREAM : '+msg+' for request '+request.i_id);
							//oconsole('STREAM : '+msg+' for request '+request.i_id);
							//if ((choose_circuit(true)===true)||(!WAIT)) { //uncomment to create circuits when retrying a request
							if (choose_circuit(request,true)===true) {
								//oconsole('no other circuit - circuit_start for request '+request.i_id);
								if (circuit) {
									//oconsole('circuit retry for request '+request.i_id+' - no other circuit - circuit start');
								};
								circuit_start(request);
							} else {
								var circ;
								if ((!anonym)||request.params_.ws) { //if anonym false or not ws circuit establishment
									circ=choose_circuit(request);
									if (circuit) {
										var nbloop=5;
										while ((circuit===circ)&&(nbloop!==0)) {
											circ=choose_circuit(request);
											nbloop--;
										};
										//console.log('circuit retry for request '+request.i_id+' CIC '+circ.circId);
									};
								} else {
									circ=fake_cid;
									while (circ===fake_cid) {
										circ=choose_circuit(request);
									};
								};
								if (circ) {
									if (circuit) {
										//oconsole('STREAM : circuit retry CIRC '+circ.circId+' '+circ.server_.ip+' choosen for request '+request.i_id);
									};
									circ.process(request);
								} else {
									//oconsole('STREAM : no other circuits for request '+request.i_id);
									if (circuit) {
										//oconsole('STREAM : circuit retry circuit_start for request '+request.i_id);
									};
									circuit_start(request);
								};
							};
						} else {
							var circ;
							if (request.cid_) {
								if (request.cid_.bad_) {
									delete request.tls_client_connected;
									delete request.abstract_client_tls;
									request.cid_.destroy_cid(request);
								};
							};
							if ((!(anonym||download))||request.params_.ws||request.params_.db_) { //if anonym false or not ws circuit establishment
								circ=request.cid_?request.cid_:(params.one_c?choose_circuit(request):null);
							} else {
								circ=request.cid_?request.cid_:fake_cid;
								while ((circ===fake_cid)||(circ===db_cid)) {
									circ=choose_circuit(request);
								};
							};
							if (circ) {
								console.log('choose circuit '+circ.circId+' for request '+request.i_id);
								//console.log('STREAM : --------------------------------'+(request.cid_?'Reuse circuit ':'Circuit choosen ')+circ.circId+' '+circ.server_.ip+' ------------------------------- for request '+request.i_id+' Stream '+(request.sid_?request.sid_:'not selected yet')+' on port '+request.remotePort);
								if (circ[request.sid_]) {
									if (circ.last_) {
										circ.last_.stream_handle_connected(request.sid_);
									} else {
										circ.destroy_cid(request);
										Tor(request);
									};
								} else {
									//alert('process');
									circ.process(request);
								};
							} else {
								console.log('no circuits');
								if (OP_req.length===0) {
									circuit_start(request);
								} else {
									OP_req.push(request);
								};
							};
						};
					};
				} else { //OR v3
					var servername='www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.net'; //check .net or .com
					var issuer='www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.com';
					var date=new Date();
					var certid=parseInt((Rand(8)).toString('hex'),16);
					var cert=createIdLinkTLSCert(pathd+ordbname+'/pub-key.pem',pathd+ordbname+'/priv-key.pem','pem',certid,date,servername,issuer);
					//console.log(createIdLinkTLSCert('pub-key.pem','priv-key.pem','der',certid,date,servername,issuer));
					var options = {
						key: fs.readFileSync(pathd+ordbname+'/priv-key.pem'),
						cert: cert,
						servername: servername,
						requestCert: true
					};
					var server = tls.createServer(options, function(socket) {
						var ip=socket.remoteAddress;
						var port=socket.remotePort;
						init_socket_or(socket);
						socket.on('data',on_data);
						socket.on('error',function(error) {console.log('OR socket error');console.log(error);delete OR_sock_in[ip+':'+port];});
						socket.on('end',function() {delete OR_sock_in[ip+':'+port];});
						socket.on('close',function() {delete OR_sock_in[ip+':'+port];});
						socket.cert_issuer=issuer;
						socket.cert_subject=servername;
						socket.cert_date=date;
						socket.certid_=certid;
						OR_sock_in[ip+':'+port]=socket;
					});
					server.listen(params.port, function() {
						console.log("OR : server launched port "+params.port);
						if (download) {
							setInterval(test_peers,DB_CIRC_POLL);
							//setTimeout(test_peers,15000);
						};
					});
					server.on('OR : clientError',function(exception) {console.log(exception)});
					server.on('error',function(error) {console.log(error)});
					server.on('end',function(error) {console.log(error)});
				};
			};
		};

		var simpleParser=function(data) {
			try {
			var res={};
			var i=0;
			data=data.split('\r\n');
			data.forEach(function(val,j) {
				val=val.split(':');
				if ((val.length>1)&&(j!=0)) {
					var p=val[0];
					val=val.map(function(v) {return v.trim()});
					val.shift();
					val=val.join(':');
					res[p]=val;
				} else {
					res[i+'a']=val.join(':'); //v8 wrong enumeration order bug #2353
					i++;
				};
			});
			return res;
			} catch(ee) {
				console.log('caller');
				console.log(simpleParser.caller.toString().substr(0,50));
			};
		};

		var add_header=function(parse,param,value) {
			var a;
			if (parse.hasOwnProperty('0b')) {
				a=parse['0b'];
				delete parse['0b'];
			};
			parse[param]=value;
			if (a) {
				parse['0b']=a;
			};
			return parse;
		};

		var reconstitute=function(res) {
			var arr=[];
			var end=0;
			for (var n in res) {
				if (isNaN(n.substr(0,1))) {
					arr.push(n+': '+res[n]);
				} else {
					if (res[n]) {
						arr.push(res[n]);
					} else {
						end++;
					};
				};
			};
			for (var i=0;i<end;i++) {
				arr.push('');
			};
			return arr.join('\r\n');
		};

		var request_start=function(request) {
			if (request.start_) {
				request.write(request.start_);
				delete request.start_;
			};
		};

		var replace=function(txt,request,boo2) {
			var reg=/(((\b(https?|ftp|file):\/\/)|\/\/)[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/gi;
			var boo=false;
			var url=function(val) {
				var test=txt.split(val);
				if ((test[test.length-1]==='')&&(!boo2)) {
					boo=true;
					return;
				};
				var rurl=txt;
				val=url_decode(val);
				var fake_domain=request.fake_domain;
				var domain=OP_domains[fake_domain];
				var r=domain.real_domain_s; //lepoint.fr
				if (val.host) {
					if (val.host.indexOf(r)!==-1) {
						var h=val.host.split('.'); //[lepoint,fr]
						var r_d=domain.real_domain_a; //[www,lepoint,fr]
						if (val.host===domain.real_domain) { //www.lepoint.fr
							//rurl='http://'+fake_domain+'/'+encrypt_decrypt(val.rest?val.rest:'',true);
							//rurl=val.rest?val.rest:'';
							rurl='http'+TLS_OP+'://'+fake_domain+'/'+(val.rest?val.rest:'');
						} else if (((h.length===2) && (r_d[0]==='www')) || ((r_d.length===2) && (h[0]==='www'))) { //lepoint.fr
							//rurl='http://'+fake_domain+'/'+encrypt_decrypt(val.rest?val.rest:'',true);
							//rurl=val.rest?val.rest:'';
							rurl='http'+TLS_OP+'://'+fake_domain+'/'+(val.rest?val.rest:'');
						} else { //sport.lepoint.fr
							rurl='http'+TLS_OP+'://'+fake_domain+'/'+encrypt_decrypt(url_encode(val),true);
						};
					} else {
						if (val.host.indexOf(fake_domain)===-1) {
							rurl='http'+TLS_OP+'://'+fake_domain+'/'+encrypt_decrypt(url_encode(val),true);
							//rurl=val.protocol+'//'+fake_domain+'/'+encrypt_decrypt(url_encode(val),true);
						};
					};
				};
				return rurl;
			};
			var tmp=txt.replace(reg,url);
			if (boo) {
				request.pass_ +=txt;
			};
			return {html:boo?txt:tmp,pass:boo};
		};

		//s_script.js
		var addScript=function(html,request) {
			var script="<SCRIPT Language='Javascript'>(function(){ /* insert after doctype - ok all browsers */ var K='';  var noT=['STYLE','BR','HEAD','META','TITLE','NOSCRIPT'];  var noA=['IFRAME','FRAME','EMBED','OBJECT','APPLET'];  var tab=['href','src','innerHTML','outerHTML'];  var attribs=['style','className','align','id','name','width','height'];  var protocol=document.location.protocol;  var url_decode=function(url) { 	var URL={url:url}; 	var res=url.split('/'); 	var dec=function(res) { 		if (res.length) { 			var tmp=res[0]; 				URL.host=tmp; 				res.shift(); 			}; 			URL.rest=res.join('/'); 	}; 	if (res.length>1) { 		if (res[1]==='') { 			URL.protocol=res[0]?res[0]:protocol; 			res.shift(); 			res.shift(); 		}; 	} 	dec(res); 	return URL; };  var url_encode=function(url) { 	var res=[]; 	if (url.protocol) { 		res.push(url.protocol); 		res.push(''); 	}; 	if (url.port) { 		res.push(url.rest); 	}; 	if (url.rest) { 		res.push(url.rest); 	}; 	return res.join('/'); }; 	 var encrypt_decrypt=function(txt,K) { 		var res=[]; 		txt=txt.split(''); 		while (txt.length) { 			res.push(txt.pop()); 		}; 		return res.join(''); };  var url_encrypt=function(url,bool,K) { 	var URL=url_decode(url); 	URL.host=URL.host?encrypt_decrypt(URL.host):null; 	if (bool) { 		URL.rest=URL.rest?encrypt_decrypt(URL.rest):null; 	}; 	return url_encode(URL); /*TODO replace by https */ };  var fake_domain=document.domain;  var real_domain=url_encrypt(fake_domain);  alert(fake_domain+' '+real_domain);  Object.Freeze=function(obj,prop) { 	try { 		Object.defineProperty(obj,prop,{value:obj[prop],configurable:false,writable:false}); 	} catch(ee) { 		/* Safari nok */ 	}; };  var tame=function(obj) { 	try { 		tab.forEach(function(prop) {observe(obj,prop)}); 		var osetAttribute=obj.setAttribute.bind(obj); 		obj.setAttribute=function(prop,val) { 			if (prop&&val) { 				if (((tab.indexOf(prop)!==-1)&&(val.indexOf('http')!==-1))||(['innerHTML','outerHTML'].indexOf(prop)!==-1)) { 					var tmp=url(val,prop); 					if (['innerHTML','outerHTML'].indexOf(prop)===-1) { 						obj['___'+prop+'___']=tmp; 					}; 					osetAttribute(prop,tmp); 				} else { 					if (tab.indexOf(prop)!==-1) { 						obj['___'+prop+'___']=val; 					}; 					osetAttribute(prop,val); 				}; 			}; 		}; 		Object.Freeze(obj,'setAttribute'); 		Object.Freeze(obj,'appendChild'); 		Object.Freeze(obj,'insertBefore'); 		Object.Freeze(obj,'replaceChild'); 	} catch(ee) {}; };  var ocreateElement=document.createElement.bind(document);  document.createElement=function(tag) { 	tag=tag.toLowerCase().trim(); 	switch (tag) { 		case ('iframe' || 'frame' || 'embed' || 'object' || 'applet') : return ocreateElement('canvas'); 		case ('script') : if (document.readyState==='complete') {return ocreateElement('span')}; 		default : var obj=ocreateElement(tag);tame(obj);return obj; 	}; };  var ocreateDocumentFragment=document.createDocumentFragment.bind(document);  document.createDocumentFragment=function() { 	var obj=ocreateDocumentFragment(); 	tame(obj); 	return obj; };  var owrite=document.write.bind(document);  document.write=function(txt) { 	if (txt.toLowerCase().indexOf('script')===-1) { 		owrite(txt); 	}; };  var url=function(val,prop) { /*TODO tame innerHTML outerHTML */ 	if (['innerHTML','outerHTML'].indexOf(prop)!==-1) { 		/* alert(prop+' '+val); */ 		return val; 	}; 	var rurl=''; 	val=url_decode(val); 	if (val.protocol) { 		if (val.host) { 			if (val.host.indexOf(real_domain)!==-1) { 				rurl=url_encrypt(url_encode(val)); 			} else { 				if (val.host.indexOf(fake_domain)===-1) { 					rurl='http://'+fake_domain+'/'+url_encrypt(url_encode(val)); 				} 			}; 		/*alert(rurl);*/ 		} else { 			rurl=url_encrypt(url_encode(val)); 		} 	} else { 		rurl=url_encode(val); 	}; 	return rurl; };  var observe=function(obj,prop) { /* watch js modification of prop */ 	if (['innerHTML','outerHTML'].indexOf(prop)!==-1) { /* if getter/setter */ 		var set_o=function(val) { 			delete this[prop]; 			this[prop]=url(val,prop); 			observe(this,prop); 		}; 	} else { 		var set_o=function(val) { 			delete this[prop]; 			this.setAttribute(prop,val); 			observe(this,prop); 		}; 	}; 	if (['innerHTML','outerHTML'].indexOf(prop)!==-1) { /* if getter/setter */ 		var get_o=function(){ 			delete this[prop]; 			var res=this[prop]; 			observe(this,prop); 			return res; 		}; 	} else { 		var get_o=function(){ 			return this['___'+prop+'___']||''; 		}; 	}; 	try { 		Object.defineProperty(obj,prop,{get:get_o,set:set_o,enumerable:true,configurable:true}); 	} catch (ee) { 		Object.freeze(obj); 	}; /*Safari crashes - unconfigurable property */ 	/* OK FF IE Chrome - NOK Safari */ };  var addEvent=function(o, e, f, p) { 	try {this.delEvent(o,e,f,p);} catch(ee){} 	if (o.addEventListener) { 		o.addEventListener(e, f, p); 	} else if (o.attachEvent) { 		o.attachEvent('on' + e, f); 	} };  var delEvent=function(o, e, f, p) { 	if (o.addEventListener) { 		o.removeEventListener(e, f, p); 	} else if (o.attachEvent) { 		o.detachEvent('on' + e, f); 	} };  var setEvtAllTree=function(obj) { 	obj=obj.nodeName?obj:this.document; 	if (!obj) { 		setTimeout(setEvtAllTree,0); 		return; 	}; 	if ((obj.nodeName=='#document')||(obj.nodeType===1)&&(noT.indexOf(obj.nodeName)===-1)) { 		if (noA.indexOf(obj.nodeName)===-1) { 			if (obj.nodeType!==9) { 				if (obj.href) { 					if (obj.getAttribute('href')) { 						if (obj.getAttribute('href').indexOf('http')!==-1) { 							obj.href=url(obj.href); 						}; 					} else { 						obj.href=url(obj.href); 					}; 				}; 				if (obj.src) { 					if (obj.getAttribute('src')) { 						if (obj.getAttribute('src').indexOf('http')!==-1) { 							obj.src=url(obj.src); 						}; 					} else { 						obj.src=url(obj.src); 					}; 				}; 				tame(obj); 			}; 			var l=obj.childNodes.length; 			for (var i=0;i<l;i++) { 				setEvtAllTree(obj.childNodes[i]); 			}; 		} else { 			var c=document.createElement('canvas'); 			attribs.forEach(function(val) {if (obj[val]) {c[val]=obj[val]}}); 			var ctx = c.getContext('2d'); 			ctx.fillStyle='#FAFAFA'; 			obj.parentNode.replaceChild(c,obj); 			ctx.fillRect(0, 0, c.offsetWidth,c.offsetHeight); 		}; 	}; };  addEvent(window,'load',setEvtAllTree,false); /*addEvent(window,'load',function() {alert('load event')},false);*/  Object.Freeze(document,'createElement'); Object.Freeze(document,'createDocumentFragment'); Object.Freeze(document,'write'); /*alert('loaded');*/  /*setEvtAllTree(window.document);*/ /* hook document.getElementsByTagName('script') */ /* hook window.open */ })();</SCRIPT>";
			script=''; //test to remove
			return [script,html].join(''); //TODO insert after doctype
		};

		/*
		3
		html \r\n
		length\r\n
		html2

		2
		length\r\n
		html
		or
		html\r\n

		1
		length
		*/

		var chunk=function(html,request) {
			/* \r\na\r\nb\r\nc\r\nlength\r\nd\r\ne\r\nf */
			var tmp=(html.toString('hex')).split(CRLF);
			/* tmp=['',a,b,c,length,e,f,length,g,h] */
			var tmp_=tmp_||[];
			var tmp__=[];
			tmp.forEach(function(val,j) {
				if (request.wait_) {
					var o=parseInt(new Buffer(val,'hex').toString('utf8'),16);
					if (o===0) {
						request.end_=true;
						tmp_.push('0d0a300d0a0d0a');
					};
					request.clength_ +=o?o:0;
					if (val.length) {
						if (j!==tmp.length-1) {
							request.wait_=false;
						};
					} else {
						if (j===0) {
							request.wait_=false;
						};
					};
					return;
				};
				var m=request.clength_;
				var l=request.html_.length;
				var n=(new Buffer(val,'hex')).length+2;
				if ((l+n<=m)||(l+n-2===m)) {
					tmp__.push(val);
				};
				if ((l+n-2===m)||(j===tmp.length-1)) {
					tmp__=tmp__.join(CRLF);
					tmp_.push(tmp__);
					request.html_.length +=n-2;
					if (j!==tmp.length-1) {
						request.wait_=true;
					};
					tmp__=[];
				} else {
					request.html_.length +=n;
				};
			});
			return new Buffer(tmp_.join(''),'hex');
		};

		var ArrayBufferToBuffer= function(data) {
			if (data instanceof Uint8Array) {
				var a=new oBuffer(data.length);
				a.map(data);
				data=a;
			};
			return data;
		};

		var BufferToArrayBuffer= function(data) {
			if (!(data instanceof Uint8Array)) {
				var a=new Buffer(data.length);
				a.map(data);
				data=a;
			};
			return data;
		};

		var destroy_ws_cid=function(request) {
			console.log('destroy ws cid');
			if (request.remoteAddress&&request.remotePort) {
				delete OR_sock_in[request.remoteAddress+':'+request.remotePort];
			} else {
				clear_circuits_OR_in();
			};
			//delete(OR_fake_request[request.remoteAddress+':'+request.remotePort]);
		};

		var websocket_answer=function(res,request) {
			var key=res['Sec-WebSocket-Key'];
			var H = crypto.createhash('sha1');
			H.update(key+'258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
			var hash=H.digest('hex');
			hash=(new Buffer(hash,'hex')).toString('base64');
			var resp='HTTP/1.1 101 WebSocket Protocol Handshake\r\n';
			resp +='Upgrade: websocket\r\n';
			resp +='Connection: Upgrade\r\n';
			resp +='Sec-WebSocket-Accept:'+hash+'\r\n';
			resp +='Access-Control-Allow-Origin:'+res['Origin']+'\r\n';
			resp +='\r\n';
			console.log('INCOMING SOCKET :'+resp);
			request.ws_=true;
			init_socket_or(request);
			request.connected_=true;
			request.wsconnected_=true;
			request.write(resp);
		};

		var websocket_request=function(server) {
			this.key_=crypto.randomBytes(16).toString('base64');
			var hs='GET / HTTP/1.1\r\n';
			hs +='Host: '+server.ip+(WS_OP?(':'+server.wsport):'')+'\r\n';
			hs +='User-Agent: Mozilla/5.0 (Windows NT 6.0; WOW64; rv:17.0) Gecko/20100101 Firefox/17.0\r\n';
			hs +='Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n';
			hs +='Accept-Language: en-us,en;q=0.5\r\n';
			hs +='Accept-Encoding: gzip, deflate\r\n';
			hs +='Connection: keep-alive, Upgrade\r\n';
			hs +='Sec-WebSocket-Version: 13\r\n';
			hs +='Origin: http://ianonym.com\r\n';
			hs +='Sec-WebSocket-Key: '+this.key_+'\r\n';
			hs +='Pragma: no-cache\r\n';
			hs +='Cache-Control: no-cache\r\n';
			hs +='Upgrade: websocket\r\n';

			hs +='\r\n';
			return hs;
		};

		var ws_time;
		var ws_time2;
		var ws_time_prev;
		var count_ws=0;
		var count_ws_data=0;
		var time_ws_data=0;
		var PERF_WS=100;
		var count_ws2=0;
		var count_ws2_data=0;
		var time_ws2_data=0;
		var PERF_WS2=100;

		var websocket_create=function(OR) {
				if (WS_PERF) {
					ws_time=Date.now();
				};
				if (WS_PERF2) {
					ws_time2=Date.now();
				};
				//console.log('start websocket ws://'+OR.ip+':'+OR.wsport);
				console.log('start websocket');
				var socket=peersm_client?(new WebSocket_node('ws://'+OR.ip+':'+OR.wsport)):(new WebSocket('ws://'+OR.ip+':'+OR.wsport));
				if (!peersm_client) {
					socket.write=socket.send;
				};
				socket.binaryType="arraybuffer";
				socket.setNoDelay=function() {};
				socket.connect=function() {};
				socket.WS_OP_=true;
				socket.onopen = websocket_start;
				/*
				Perf: 600 bytes 20ms TLS processed --> 240 kbps
				on messages are queued, waiting for end of previous message processing to process next one
				*/
				socket.onmessage = function(evt) {
					var data=(evt.data instanceof ArrayBuffer)?(new Uint8Array(evt.data)):evt.data;
					if (ws_time) {
						count_ws++;
						count_ws_data +=data.length;
						time_ws_data +=Date.now()-ws_time;
						if (count_ws%PERF_WS===0) {
							if (time_ws_data) {
								console.log('WS perf (received from WS before processing): '+parseInt(count_ws_data*8/(time_ws_data/1000))+' bps - Buffered : '+socket.bufferedAmount);
								count_ws=0;
								count_ws_data=0;
								time_ws_data=0;
							};
						};
					};
					if ((WS_PERF)||(WSDELAY_PERF)) {
						ws_time=Date.now();
					};
					if ((WS_TLS)&&(socket===client)) {
						if (CLIENTTLS_PERF) {
							socket.t0=Date.now();
						};
						if (ws_time_prev) {
							if (ws_time-ws_time_prev!==0) {
								console.log('WS delay between processed and next received: '+(ws_time-ws_time_prev)+' data length '+data.length); //586B (512 + TLS)
							};
						};
						//console.log('received');
						//console.log(data.toString('hex'));
						if (!forge_buffers) {
							socket.abstract_client_tls.process(data);
						} else {
							socket.abstract_client_tls.process(data.toString('binary'));
						};
						//console.log('TLS processed since received '+(Date.now()-t0));
						if (WSDELAY_PERF) {
							ws_time_prev=Date.now();
						};
						if (ws_time2) {
							count_ws2++;
							count_ws2_data +=data.length;
							time_ws2_data +=Date.now()-ws_time2;
							if (count_ws2%PERF_WS2===0) {
								if (time_ws2_data) {
									console.log('WS perf (received from WS after processed): '+parseInt(count_ws2_data*8/(time_ws2_data/1000))+' bps - Buffered : '+socket.bufferedAmount);
									count_ws2=0;
									count_ws2_data=0;
									time_ws2_data=0;
								};
							};
						};
						if (WS_PERF2) {
							ws_time2=Date.now();
						};
					} else {
						on_data.call(this,data)
					};
				};
				socket.onclose = function() {
					console.log('Websocket closed ws://'+OR.ip+':'+OR.wsport);
				};
				if (!peersm_client) {
					socket.destroy=socket.close;
					socket.bufferSize=socket.bufferedAmount;
				};
				socket.remoteAddress=OR.ip;
				socket.remotePort=OP_port;
				socket.address=function() {return {port: 0,family:'IPv4',address: '127.0.0.1' }};//dummy
				socket.setKeepAlive=function() {};
				return socket;
		};

		var websocket_verify=function(data) {
			var res=simpleParser(data.toString('utf8'));
			var key=res['Sec-WebSocket-Accept'];
			if (key) {
				var H = crypto.createhash('sha1');
				H.update(this.key_+'258EAFA5-E914-47DA-95CA-C5AB0DC85B11');
				var hash=H.digest('hex');
				hash=(new Buffer(hash,'hex')).toString('base64');
				if (key===hash) {
					console.log('WS Client says : Handshake successfull');
					websocket_start.call(this);
				};
			};
		};

		var websocket_start=function() {
			console.log('websocket connected');
			try {
				x_x03.innerHTML='<p>Websocket connected</p>';
			} catch(ee) {};
			this.connected_=true;
			this.wsconnected_=true;
			this.ws_=true;
			if (download) {
				if ((this===fake_client)&&fake_cid_OR) {
					Tor({params_:{OP:true,nb_hop:NB_HOP,ws:this}}); //start establishing circuits
				};
				if (WS_TLS&&(this===client)&&(fake_cid||(!fake_cid_OR))) {
					console.log('launch db_cid');
					this.db_cid_launched=true;
					Tor({params_:{OP:true,nb_hop:NB_DB_HOP,ws:this,db:true}});
				};
				if (!WS_TLS) {
					setInterval(monitor_circuits,10000);
				};
			};
		};

		var wsdecode=function(data,b) {
			b=b||[];
			var tlength=data.length;
			var length=0;
			var index=0;
			var tlength=data.length;
			var payload=new Buffer(0);
			var stream=new Buffer(0);
			var n;
			if (data.length===0) {
				return [payload,stream];
			};
			var type=data[0];
			if (data.length>1) {
				var mask=data[1]&0x80;
				var length_=data[1]&0x7f;
				if (length_===0x7E) {
					length=(data.slice(2,4)).readUInt();
					mask=mask&&data.slice(4,8);
					index=mask?8:4;
				} else if (length_===0x7F) {
					length=parseInt(data.slice(2,10).toString('hex'),16);
					mask=mask&&data.slice(10,14);
					index=mask?14:10;
				} else {
					length=length_;
					mask=mask&&data.slice(2,6);
					index=mask?6:2;
				};
				payload=data.slice(index,index+length);
				n=payload.length;
				if ((mask)&&(payload.length===length)&&(length!=0)) { //stream part, do not apply mask (slice method does modify the object)
					for (var i=0;i<n;i++) {
						payload[i]=payload[i]^mask[i%4];
					};
				};
			};
			if ((payload.length!==length)||(length===0)) {
				n=payload.length;
				index=tlength;
				stream=data;
			} else {
				b.push(payload);
			};
			if (tlength-index>n) {
				return wsdecode(data.slice(n+index),b);
			} else {
				if (type&0x01) { //string
					return [(b.concatBuffers()).toString('utf8'),stream.toString('utf8')];
				};
				if (type&0x02) { //buffer
					return [b.concatBuffers(),stream];
				};
			};
		};

		var wsencode=function(data,type,mask) {
			var l=data.length;
			var bytes;
			var b,m;
			mask=mask?crypto.randomBytes(4):mask;
			var a=type===1?'81':'82';
			if (l<0x7E) {
				b=(mask?(l|0x80):l).toString(16);
				b=b.length===1?('0'+b):b;
			} else if (l>=0x7E && l<=0xFFFF) {
				a +=mask?'FE':'7E';
				m=2;
			} else {
				a +=mask?'FF':'7F';
				m=8;
			};
			if (!b) {
				b=l.toString(16);
				b=b.length%2?('0'+b):b;
				while (b.length!==m*2) {b ='00'+b};
			};
			a +=b;
			bytes=new Buffer(a,'hex');
			if (mask) {
				var n=data.length;
				var payload=new Buffer(n);
				for (var i=0;i<n;i++) {
					payload[i]=data[i]^mask[i%4];
				};
			} else {
				payload=data;
			};
			return mask?([bytes,mask,payload].concatBuffers()):([bytes,payload].concatBuffers());
		};


		var ws_send=function(request,payload) {
			var addr=request.remoteAddress+':'+request.remotePort+':'+request.port_+':'+request.i_id;
			var circ=request.fake_;
			//console.log('OR send RELAY_WS CIC '+circ.circId+' '+addr);
			var add=new Buffer(new Buffer(addr,'utf8').toString('hex'),'hex');
			var l=add.length.toString(16);
			while (l.length!==4) {
				l='0'+l;
			};
			add=[new Buffer(l,'hex'),add].concatBuffers();
			l=add.toString('hex');
			//console.log(add.toString('hex'));
			while (payload.length) {
				var pay;
				if (payload.length+l>PAYLOAD_STREAM_WS) {
					pay=payload.slice(0,PAYLOAD_STREAM_WS-l);
				} else {
					pay=payload;
				};
				pay=[add,pay].concatBuffers();
				//console.log('OR write ws '+pay.toString('utf8'));
				var data=(new Stream(Stream.prototype.RELAY_WS,0,pay,circ.Db_hash)).toBuffer();
				data=new Buffer(circ.Kb_cipher.update(data,'hex','hex'),'hex');
				var cell=new Cell(circ.circId,Cell.prototype.RELAY_WS,data);
				circ.send(cell);
				if (payload.length+l>PAYLOAD_STREAM_WS) {
					payload=payload.slice(PAYLOAD_STREAM_WS-l);
				} else {
					break;
				};
			};
		};

		var handleRequest = function (request) {
			request.i_id=I_ID;
			request.nb_try=0;
			request.no_exit=[];
			request.squeue_=[];
			request.wsqueue_=[];
			request._date_=Date.now();
			I_ID++;
			//ISOCKSin.push([request.i_id,request.remotePort]);
			ISOCKSin.push(request.i_id);
			console.log('INCOMING SOCKET : ------------------------------------- new incoming socket ----------------------------------------------- request '+request.i_id+' on remote port '+request.remotePort+' for port '+request.address().port);
			//request.setKeepAlive(true,500);
			var socket_retry=function() {
				if (request.cid_) {
					request.socket_retry=setTimeout(socket_retry,SOCK_RETRY);
					var cid=request.cid_;
					cid.perf_--;
					if (request.no_exit.indexOf(cid)===-1) {
						request.no_exit.push(cid);
					};
					if (request.sid_) {
						cid.relay_end_handle(request.sid_,' socket retry ');
					} else {
						Tor(request,cid,'socket retry');
					};
				} else {
					request.destroy();
				};
			};
			var socket_handle=function(data) {
				var tab=data.split(':::');
				if (tab.length===3) { //specific
					request.params_={host:tab[0],OP:true,nb_hop:tab[1],stream:new Buffer((new Buffer(tab[2],'utf8')).toString('hex'),'hex'),one_c:tab[3]};
					Tor(request);
				} else { //protocols
					var params={};
					var res=simpleParser(data);
					params.OP=true;
					params.nb_hop=NB_HOP;
					params.one_c=true;
					if (data.indexOf('WebSocket')!=-1) { //websocket
						//console.log(data.toString('hex'));
						console.log('Answer websocket');
						websocket_answer(res,request);
						if (download) { //handle tls over WS
							var servername='www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.net';
							var issuer='www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.com';
							var date=new Date();
							var certid=parseInt((Rand(8)).toString('hex'),16);
							var cert=createIdLinkTLSCert(pathd+ordbname+'/pub-key.pem',pathd+ordbname+'/priv-key.pem','pem',certid,date,servername,issuer);
							//var key='-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQCL0KgShoWLEfKFasLz79il5Tiy+FGkwCzHF8lOpSQYxYfMaVFe\nRrU4HRfNIpnLTFn644JQiY6rMcm4rPzwIRCpbw8DRC7NNPG++wYE4f+7mBhIU02p\n8/I3le198EdzDLnatxFyKtZQS4k869ot5LQRI9ZnR0oWbro+rX34Jn3aFQIDAQAB\nAoGAe/QS7cmhX3gb4LiT+hwroHgl2lVYXI2bpS6rZDP4WpXVHR5J6yEYzJTOYylH\n2/rm0w0cnTY27AOJ6VZEMMe/sn8vRwLoSO3UmvJrujcWYwfZJdm70ExmbggeGq0T\nTw4ZPN1l4KesQANSROwJw6b+X9oPiA8tuZhwwOunlgnwYsECQQDs5ur4ww8a/lC3\naJvhMfmuYmawfiH/Ma+yyCACXM7QaMuHxjC4TNW1ufRYousEe1LPBMHcOVttZP8s\nGVIsI0J9AkEAlxYZiUssf6u/8FizEu+1vg36J/i8e+ZeNmboYTSHxDBe+/XUvlNo\nxN+zCFWVwlA001h6urXARivnkVvCIDexeQJBAMO8o+Z4HFH8UKrJ1wnBJgBMs3Ye\nMW7OGdKJAdGR5xtRiuEbAwQ0D9u/0kEuEyq3zprX3mlcRstjSGaiYT+wvm0CQC7z\ncTAXKlbqepUoG04RK/tJwKlXRMuEBVcGl8RNXLt9BKdr95bFuNMlaS932TgvUJs0\n7b7D6GJmFmo3h5YXeJECQQCE6UkIagUgPHVKRQFQrnHDkyaQyQZyb50frnvV0/C9\n3X+6CMCITaUqLOolRWz29YGxgNS+L/vUB4okEbAxqHke\n-----END RSA PRIVATE KEY-----';
							var sslcontext=crypto.createCredentials({key: fs.readFileSync(pathd+ordbname+'/priv-key.pem'),cert: cert,servername: servername});
							var pair=new tls.createSecurePair(sslcontext,true);
							request.pair=pair;
							var cleartext=pair.cleartext;
							cleartext.tlspair_=true;
							cleartext.server=request.server;
							cleartext.i_id=request.i_id;
							cleartext.nb_try=request.nb_try;
							cleartext.no_exit=request.no_exit;
							cleartext.squeue_=request.squeue_;
							cleartext.wsqueue_=request.wsqueue_;
							cleartext._date_=request._date_;
							//cleartext._write_=request._write_;
							cleartext._init_=request._init_;
							cleartext.OR_=request.OR_;
							cleartext.OR_f=request.OR_f;
							cleartext.way_=request.way_;
							cleartext.privkey_=request.privkey_;
							cleartext.connected_=request.connected_;
							cleartext.wsconnected_=request.wsconnected_;
							cleartext.address=function() {return {port: request.address().port,family:'IPv4',address:request.address().address}};
							cleartext.__defineGetter__('remoteAddress',function() {return request.remoteAddress}); //override stupid node getter
							cleartext.__defineGetter__('remotePort',function() {return request.remotePort}); //override stupid node getter
							cleartext.stream_tor_=new Buffer(0);
							cleartext.cert_issuer=issuer;
							cleartext.cert_subject=servername;
							cleartext.cert_date=date;
							cleartext.certid_=certid;
							request.write=function(data) {
								//console.log('Client write before wsencode:'+data.toString('hex'));
								request._write(wsencode(data,0x2,false),null,function(){});
							};
							OR_sock_in[request.remoteAddress+':'+request.remotePort]=cleartext;
							pair.encrypted.pipe(request);
							cleartext.on('data',function(data) {
								//console.log('cleartext received: '+data.toString('hex'));
								//Perf: 500 Bytes
								//wsdecode 1ms
								//encrypted to cleartext (router and browser) 1ms Typed Arrays - 0.5ms Buffers
								//ORDB parse 5ms (2 crypto ops - 800 kbps) - Typed Arrays
								//ORDB parse  1ms (4 Mbps) - node Buffers
								//OR parse 1ms (1 crypto op)
								//Browser parse 2ms (nb nodes*crypto ops)
								//PC/Aym-Ikoula:
								//Browser 2 Mbps (500B 1ms)-->600kb/s--->OR 1Mbps (500B 4ms)--> ORDB 400/500 kbps (500B 7ms) --> 2 Mbps --> Browser 2Mbps (real: 350kbps)
								//1 MB file: 2 pause/unpause espaces de 8s
								//Tor: 2 Mbps
								//Browser 2 Mbps (500B 1ms)-->600kb/s--->OR 300 kbps (500B 4ms)--> ORDB 400/500 kbps (500B 7ms) --> 2 Mbps --> Browser 2Mbps (real: 300kbps)

								if (cleartext.t0) {
									count_clear++;
									count_clear_data +=data.length;
									time_clear_data +=Date.now()-cleartext.t0;
									if (count_clear%PERF_CLEAR===0) {
										if (time_clear_data) {
											console.log('cleartext perf: '+parseInt(count_clear_data*8/(time_clear_data/1000))+' bps');
											count_clear=0;
											count_clear_data=0;
											time_clear_data=0;
										};
									};
								};
								//console.log(data.toString('hex'));
								on_data.call(cleartext,data);
							});
							cleartext.on('end',function() {request.end()});
							cleartext.on('error',function() {request.end()});
						};
						return false;
					};
					if (data.indexOf('HTTP')!=-1) { //direct proxy
						if (res.Host) {
							params.host=res.Host+':80';
							request.connected_=true;
						};
					};
					params.stream=new Buffer((new Buffer(data,'utf8')).toString('hex'),'hex');
					if (params.stream.slice(0,1).readUInt()===5) { //socks v5 proxy
						request.socks_=true;
						request.connected_=true;
						//var a=new Buffer('0500','hex');
						//console.log(Buffer.isBuffer(a));
						//console.log(a instanceof Uint8Array);
						request.write(new Buffer('0500','hex'));
						return false;
					};
					return params;
				};
			};
			request.on('data', function(data) {
				//try {
					if (!request.ws_) {
						console.log('INCOMING SOCKET :------------- RECEIVED FROM INCOMING SOCKET ------------ '+request.i_id+' on port '+request.remotePort+' '+request.remoteAddress+' '+request.address().port);
						//var deb=data.toString('utf8');
						//console.log(deb);
					};
					/*
					if (deb.indexOf('Host')!=-1) {
						request.deb_=true;
						//console.log(deb);
					} else {
						//console.log(data.toString('hex'));
					}; */
					//console.log(data.toString('hex'));
					var params;
					if (!request.connected_) {
						params=socket_handle(data.toString('utf8'));
					} else {
						params={};
						params.OP=true;
						params.nb_hop=NB_HOP;
						params.one_c=true;
						params.host=request.host_?request.host_:false;
						if (!request.params_) {
							request.params_=params;
						} else {
							request.params_.host=params.host;
						};
						params.stream=params.host?data:false;
						if (params.stream) {
							if (!request.fake_) {
								var execute=function(data) {
									return function() {
										if (prod.inflate) {
											data=data.toString('utf8');
											//data=data.toString('utf8').replace ('&splay=1','');
											data=data.replace ('%2Fplayer.html','');
											var tmp=simpleParser(data);
											if (tmp['0a']) {
											/*
											Uncomment, load www.peersm.com/player.html with FF (cookies, localStorage deactivated)
											html5yt-us.js is put in the cache as permanently moved for the initial js url
											Youtube fails (check the origin of html5yt-us.js ?)
											Comment and relauch
											load www.peersm.com/player.html
											The request will be made to html5yt-us 'Not modified'
											So html5yt-us.js will be used instead of the right js file.
											Youtube works.
											*/
											/*

												if (tmp['0a'].indexOf('html5player')!==-1) {
													//tmp['0a']='GET /html5player-us.js HTTP/1.1';
													//tmp['Host']='www.peersm.com';
													console.log('html5player');
													request._write_(new Buffer(req_301('http://www.peersm.com/html5yt-us.js','utf8')));
													request.destroy();
													return;
												};
											*/

											};
											if (tmp['User-Agent']) {
												tmp['User-Agent']='Mozilla/5.0 (Windows NT 6.1; rv:24.0) Gecko/20100101 Firefox/24.0';
											};
											if (tmp['Referer']) {
												if (tmp['Referer']==='http://www.peersm.com/player.html') {
													tmp['Referer']='http://www.peersm.com/';
												};
											};
											data=reconstitute(tmp);
											console.log(data);
											data=new Buffer(data,'utf8');
										};
										this.params_.stream=data;
										Tor(request);
									};
								};
								request.squeue_.push(execute(data).bind(request));
							} else {
								//request.params_.stream=params.host?(request.params_.stream?(request.params_.stream.length?([request.params_.stream,data].concatBuffers()):data):data):false;
								//console.log('socks OR request '+request.i_id+' '+data.toString('utf8').split('\r\n')[0]+' ------ '+(request.wsqueue_?request.wsqueue_.length:0));
								var execute=function(data) {
									return function() {
										ws_send(this,data);
									};
								};
								request.wsqueue_.push(execute(data).bind(request));
							};
						};
						if (request.ws_) { //WS_OP_SOCK and WS_OP
							if (!request.wsconnected_) {
								console.log('server ws not connected');
								websocket_answer(simpleParser(data.toString('utf8')),request); //request is OR, client is OP
								return;
							} else {
								//console.log('ws server receive '+data.toString('hex'));
								if (request.pair) {
									if (CLEAR_PERF) {
										request.pair.cleartext.t0=Date.now();
									};
									if (WSDECODE_PERF) {
										request.pair.encrypted.t0=Date.now();
									};
								};
								on_data.call(this,data);
								return;
							};
						};
						if (request.socks_) {
							if (!params.stream) {
								//var f;
								switch (data.slice(3,4).readUInt()) {
									case 1:request.host_=ValToIP(data.slice(4,8))+':'+data.slice(8).readUInt();break
									case 3:
									var l=data.slice(4,5).readUInt();
									if (!anonym_OR) {
										request.host_=(data.slice(5,5+l)).toString('utf8')+':'+data.slice(l+5).readUInt();
										break;
									} else {
										if (!request.ws_) { //OR
											//console.log('OR SOCKS '+(data.slice(5,5+l)).toString('utf8'));
											var fake=OR_fake[(data.slice(5,5+l)).toString('utf8')];
											//var fake=OR_fake;
											if (fake) {
													var addr=request.remoteAddress+':'+request.remotePort;
													//fake.circ_.socket_.write([new Buffer(addr+WS_SOCKS_PFX,'utf8'),data].concatBuffers()); //write on ws
													OR_fake_request[addr]=request; //TODO destroy after
													//console.log('OR_fake_request '+addr);
													request.fake_=fake.circ_;
													request.port_=data.slice(l+5).readUInt(); //fake_domain port
													request.host_=(data.slice(5,5+l)).toString('utf8'); //fake_domain
													break;
											} else {
													request.host_=(data.slice(5,5+l)).toString('utf8')+':'+data.slice(l+5).readUInt();
													console.log('outside domain '+request.host_+' request '+request.i_id);
													request.__outside__=true;
													break;
											};
										} else { //OP
											//if ((data.slice(5,5+l)).toString('utf8')===fake_domain) {
											//		request.host_=real_domain+':'+data.slice(l+5).readUInt();
											//		request.cid_=fake_cid;
											//		break;
											//} else if (request.host_===OR_IP+':'+OP_port) { //initial WS_OP_SOCK
											//	break;
											//};
										};
									};
									default:return
								};
								if (request.host_===OR_IP+':'+OP_port) { //WS_OP_SOCK
									request.ws_=true;
									request.setNoDelay(true);
									//setInterval(function() {console.log(request.bufferSize?('server buffersize '+request.bufferSize):'no server buffer')},10000);
								};
								params.host=request.host_;
								console.log('INCOMING SOCKET : socks request '+request.i_id+' host '+request.host_+' remote '+request.remoteAddress+':'+request.remotePort);
								if (prod&&protect) {
									var arr=['www.lepoint.fr:80','212.95.70.230:80','www.monip.org:80','217.70.182.162:80'];
									request.__connect__=arr;
									if (parseInt(request.host_.split(':')[1])===443) {
										//console.log('refuse connexion https '+request.host_);
										request.write([new Buffer('050200','hex'),data.slice(3)].concatBuffers()); //forbidden connection
										return;
									};
								};
								request.start_=[new Buffer('050000','hex'),data.slice(3)].concatBuffers();
								request_start(request);
								return;
							};
						};
					};
					if (params) {
						//console.log('params request '+request.i_id+' '+(params.stream?'stream':'')+' '+(request.fake_?'fake':''));
						if (download) {
							console.log('Received socks and doing OP, params request '+request.i_id+' '+(params.stream?(params.stream.toString('utf8').substr(0,200)):''));
							/*
							console.log('Received socks and doing OP, closing request - params request '+request.i_id+' '+(params.stream?(params.stream.toString('utf8').substr(0,200)):''));
							request.end();
							return;
							*/
						};
						if (params.stream) {
							if (!request.fake_) {
								if (anonym_OR&&request.__outside__) {
									//TODO filter css files urls
									//console.log('outside domain response request '+request.i_id);
									request.write(new Buffer(req_200(),'utf8'));
									return;
								};
								if (prod&&protect) {
									var req=simpleParser(params.stream.toString('utf8'));
									req['Host']=req['Host']||'';
									if ((['www.lepoint.fr','www.monip.org'].indexOf(req['Host'])===-1)||(request.__connect__.indexOf(request.host_)===-1)) {
										//console.log('protect domain '+request.host_);
										request.write(new Buffer(req_200_out(),'utf8'));
										return;
									};
								};
								request._data_=false;
								//request.params_=params;
								//if (!request._start_) {
								//	request._start_=true;
								//	Tor(request);
								//};
								if (request.squeue_.length===1) {
									request.squeue_[0]();
								};
							} else {
								//console.log('wsqueue '+request.i_id+' '+(request.wsqueue_?request.wsqueue_.length:0));
								//if (request.wsqueue_.length===1) {
									var a=request.wsqueue_[0];
									request.wsqueue_.shift();
									a();
								//};
							};
						};
					};
				//} catch(ee) {
				//	console.log('Bad formatted request');
				//};
			});
			request.on('end',function() {
				console.log('INCOMING SOCKET : End -------------------------end incoming socket------------------------------------- request '+request.i_id+' host '+request.host_);
				monitor_circuits_OR_out();
				setTimeout(clear_circuits_OR_out,0);
				ISOCKSout.push(request.i_id);
				if (request.ws_) {
					destroy_ws_cid(request);
				};
				try {request.cid_.destroy_cid(request,true);} catch(ee) {};
				//console.log(request.i_id);
			});

			request.on('close',function() {
				console.log('INCOMING SOCKET : Close -------------------------end incoming socket------------------------------------- request '+request.i_id);
				setTimeout(clear_circuits_OR_out,0);
				ISOCKSout.push(request.i_id);
				if (request.ws_) {
					destroy_ws_cid(request);
				};
				try {request.cid_.destroy_cid(request,true);} catch(ee) {};
				//console.log(request.i_id);
			});

			request.on('error',function(e) {
				console.log('INCOMING SOCKET : Error -------------------------end incoming socket------------------------------------- request '+request.i_id);
				setTimeout(clear_circuits_OR_out,0);
				ISOCKSout.push(request.i_id);
				//console.log(e.name);
				//console.log(e.message);
				//console.log(e.code);
				if (request.ws_) {
					destroy_ws_cid(request);
				};
				try {request.cid_.destroy_cid(request,true);} catch(ee) {};
			});

			request._write_=function(resp) {
				if (!request.destroyed) {
					try {
						request.write(resp);
					} catch(ee) {
						//console.log('INCOMING SOCKET : Erreur write '+ee.description+' RESPONSE FOR - '+((request.params_.stream).toString('utf8')).substr(0,50)+' on port '+request.remotePort?request.remotePort:'');
					};
				};
			};
			request._init_=init_request;
		};

		var monitor_circuits=function() {
			//console.log('MONITOR start ----');
			var a=[];
			var c=[];
			var nb=0;
			var na=0;
			var time=false;
			var time_=new Date().valueOf();
			for (var n in OP_sock) {
				a.push([n,OP_sock[n]]);
			};
			var times=0;
			a.forEach(function(d) {
				//var b=[];
				Object.keys(d[1]).forEach(function(n) {
					if ((!isNaN(n))&&(n!=null)) {
						var circ=d[1][n];
						var tmp=circ.last_?circ.last_:null;
						if (tmp) {
						Object.keys(circ).forEach(function(m) {
							if ((!isNaN(m))&&(m!=null)) {
								c.push(circ[m].i_id+' '+circ[m].remotePort);
							};
						});
							if ((nb<NB_C_MAX2)&&(((time_-circ.time_)<CIRC_KA)||peersm_client)&&(NB_C<NB||((circ.time_average<TIME_AVERAGE+T_A)||peersm_client))&&(circ.perf_>=0)&&((!circ.bad_)||((circ.time_average<TIME_AVERAGE)&&circ.bad_))||(circ===fake_cid)||(circ===db_cid)) {
							//if ((((time_-circ.time_)<CIRC_KA)&&(circ.time_average<TIME_AVERAGE+T_A)&&(circ.perf_>=0)&&(!circ.bad_))||(NB_C<NB)) {
							//if (((time_-circ.time_)<CIRC_KA)&&(NB_C<NB||(circ.time_average<TIME_AVERAGE+T_A))&&(circ.perf_>=0)&&(!circ.bad_)) {
							//if ((time_-circ.time_)<CIRC_KA) {
								delete circ.bad_;
								//time=(circ.time_average<TIME_AVERAGE)?false:true;
								if (circ.time_average) {
									times +=circ.time_average;
									na++;
								};
								circ.keep_alive();
								if ((circ!==fake_cid)&&(circ!==db_cid)) {
									nb++;
								};
								//oconsole(circ.circId+' '+circ.perf_+' '+circ.sent_);
								if (demo||prod||download) {
									//gconsole(circ.circId+' received '+circ.perf_+' sent '+circ.sent_+' '+(parseInt((time_-circ.time_)/1000))+'s average: '+(parseInt(circ.time_average))+'ms '+(circ.socket_.ws_?'ws':'tls')+' '+circ.socket_.address().port);
								};
								/*
								b.push([n,c]);
								*/
								if (((circ===db_cid)&&((time_-circ.time_)>CIRC_DB_TIME))&&(!short_path)) {
									var req;
									for (var n in circ) {
										if ((!isNaN(n))&&(n!=null)&&(typeof n!=='function')) {
											req=true;
											break;
										};
									};
									if (!req) {
										console.log('monitor UPDATE DB CID ---------'+circ.circId);
										circ.circuit_destroy(); //destroy if no more pending streams
									} else {
										console.log('download in progres, not changing DB CID ----------');
										circ.time_=Date.now();
									};
								};
								if (download) {
									if (circ.last_) {
										var last=circ.last_;
										if (last.server_) {
											if ((circ!==db_cid)&&(last.server_.ip===ordbip)&&(last.server_.port===ordbport)) {
												console.log('destroying ordb last CIC '+circ.circId);
												circ.circuit_destroy();
											};
										};
									}
								};
							} else {
								if (circ.bad_) {
									var req;
									for (var n in circ) {
										if ((!isNaN(n))&&(n!=null)) {
											req=true;
											break;
										};
									};
									if (!req) {
										console.log('monitor DESTROY '+circ.circId);
										circ.circuit_destroy(); //destroy if no more pending streams
									};
								} else {
									//gconsole('monitor CID '+circ.circId+' is bad '+circ.time_average+' '+circ.perf_);
									circ.bad_=true;
								};
							};
						};
					};
				});
				//console.log('MONITOR : circuits available ---------'+d[0]+' '+b.length+' '+b.toString());
			});
			var op=[];
			ISOCKSin.forEach(function(id) {if (ISOCKSout.indexOf(id)===-1) {op.push(id)}});
			//oconsole('MONITOR ---- '+op);
			//oconsole('MONITOR ---- '+nb+' circuits ');
			NB_C=nb;
			TIME_AVERAGE=na?(times/na):TIME_AVERAGE;
			if (demo||prod) {
				gconsole('MONITOR ---- '+nb+' circuits on '+NB+' TIME_AVERAGE '+TIME_AVERAGE);
				//gconsole('MONITOR ---- '+c);
			};
			if ((nb>=NB)&&(!time)) {
				if ((!download)||db_cid) {
					WAIT=true;
				} else {
					WAIT=false;
				};
			} else {
				WAIT=false;
			};
			if (!WAIT) {
				if (!OR) {
					if (!(WS_OP||WS_OP_SOCKS)) {
						Tor({params_:{OP:true,nb_hop:NB_HOP}});
					} else {
					//console.log(typeof(client));
						if (client) {
							console.log('establish new circuit');
							if ((anonym)||(db_cid)) {
								Tor({params_:{OP:true,nb_hop:NB_HOP,ws:client}});
							} else {
								if (!client.tls_connected) {
									delete client.db_cid_launched;
								};
								console.log('monitor create db circuit');
								Tor({params_:{OP:true,nb_hop:NB_DB_HOP,ws:client,db:true}});
							}
						};
					};
				};
			};
			if (download) {
				update_circ();
			};
		};

		var monitor_circuits_OR_out=function() {
			var a=[];
			var c=[];
			for (var n in OR_sock) {
				a.push([n,OR_sock[n]]);
			};
			console.log('----------------- '+a.length+' sockets out----------------');
			a.forEach(function(d) {
				Object.keys(d[1]).forEach(function(n) {
					if ((!isNaN(n))&&(n!=null)) {
						var circ=d[1][n];
						//console.log('CID '+circ.circId+' socket out remote '+circ.server_.ip+' '+(circ.prev_?(circ.prev_.socket_?('prev '+circ.prev_.socket_.remoteAddress):'no socket'):'no prev'));
					};
				});
			});
			console.log('-----------------');
		};

		var monitor_circuits_OR_in=function() {
			var a=[];
			var c=[];
			for (var n in OR_sock_in) {
				a.push([n,OR_sock_in[n]]);
			};
			console.log('----------------- '+a.length+' sockets in----------------');
			a.forEach(function(d) {
				Object.keys(d[1]).forEach(function(n) {
					if ((!isNaN(n))&&(n!=null)&&(typeof(n)!=='function')) {
						var circ=d[1][n];
						if (circ) {
							console.log(d[0]+' CID '+circ.circId);
							try {
								console.log(d[0]+' CID '+circ.circId+' '+(circ.next_?(circ.next_.socket_?('next '+circ.next_.socket_.remoteAddress+' CIC '+circ.next_.circId):'no socket'):'no next'));
							} catch(ee) {};
						};
					};
				});
			});
			console.log('-----------------');
		};

		var clear_circuits_OR_out=function() {
			var a=[];
			var c=[];
			for (var n in OR_sock) {
				a.push([n,OR_sock[n]]);
			};
			a.forEach(function(d) {
				var boo;
				Object.keys(d[1]).forEach(function(n) {
					if ((!isNaN(n))&&(n!=null)&&(typeof(n)!=='function')) {
						var boo2;
						var circ=d[1][n];
						if (circ) {
							if (circ.prev_) {
								if (circ.prev_.socket_) {
									if (circ.prev_.socket_.remoteAddress) {
										boo2=true;
										boo=true;
									};
								};
							};
						};
						//console.log('Destroy '+boo2+' CID '+circ.circId+' socket out '+circ.server_.ip+' '+(circ.prev_?(circ.prev_.socket_?circ.prev_.socket_.remoteAddress:'no socket'):'no prev'));
						if (!boo2) {
							if (circ) {
								console.log('clearing circuit out n '+n+' CIC '+circ.circId);
								circ.circuit_destroy();
								delete d[1][n];
							};
						};
					};
				});
				if (!boo) {
					console.log('delete OR_sock '+d[0]);
					delete OR_sock[d[0]];
				};
			});
		};

		var clear_circuits_OR_in=function() {
			for (var n in OR_sock_in) {
				var sock=OR_sock_in[n];
				if (sock) {
					if (!sock.remoteAddress) {
						console.log('deleting or_sock_in remoteadd '+n);
						delete OR_sock_in[n];
					};
				} else {
					console.log('deleting or_sock_in '+n);
					delete OR_sock_in[n];
				};
			};
			clear_circuits_OR_out();
		};

		var test_peers=function() {
			for (var m in OR_sock_in) {
				//console.log('testing peers '+OR_sock_in[m].address().port+' '+ordbport);
				if (parseInt(OR_sock_in[m].address().port)===parseInt(ordbport)) {
						//console.log('testing IP '+m);
						Object.keys(OR_sock_in[m]).forEach(function(n) {
							if ((!isNaN(n))&&(n!=null)) {
								try { //avoid directory servers
									var circ=OR_sock_in[m][n];
									//console.log('Before testing '+circ.circId);
									if (circ.db_test) {
										circ.db_test.forEach(function(val) {clearTimeout(val);});
									};
									circ.db_test=[];
									var dest=function() {
										if (!circ.destroyed_) {
											console.log('Destroying db_query_no_answer CID '+circ.circId+' remote '+(circ.socket_?circ.socket_.remoteAddress:'null'));
											circ.circuit_destroy();
											circ.destroyed_=true;
										};
									};
									if (circ.circId!==0) {
										if ((circ.socket_)&&(!circ.destroyed_)) {
											var boo;
											for (var sid in circ) {
												if ((!isNaN(sid))&&(sid!=null)) {
													if (OR_csid_b[circ.socket_.remotePort+'-'+circ.socket_.remoteAddress+'-'+circ.circId+'-'+sid]||OR_csid_f[circ.socket_.remotePort+'-'+circ.socket_.remoteAddress+'-'+circ.circId+'-'+sid]) {
														boo=true;
														break;
													};
												};
											};
											if (!boo) {
												console.log('Testing CID '+circ.circId+' remote '+circ.socket_.remoteAddress+' '+Date.now());
												var tid=Rand(16);
												var param={d_length:0,hash_:new Buffer('0000000000000000000000000000000000000000','hex')};
												circ.send_db_query(param,tid);
												circ.db_test.push(setTimeout(dest,DB_CIRC_TEST));
											} else {
												console.log('Download in progress - Not testing CID '+circ.circId+' remote '+circ.socket_.remoteAddress+' '+Date.now());
											};
										} else {
											dest();
										};
									};
								} catch (ee) {};
							};
						});
				};
			};
		};

		var update_circ=function() {
			if (graph_) {
				var nb=(db_cid?1:0)+(NB_C>=0?NB_C:0);
				$_('direct_text').innerHTML='P2P (Peersm, BitTorrent) and web anonymized circuits : '+nb+(nb>1?' circuits':' circuit');
				//$_('peer_text').innerHTML='Peer to Peer : '+(db_cid?1:0)+((!db_cid)?' circuit':(' circuit ('+db_cid.server_.name+')'));
			};
		};

		if ((window_browser)||(window_OR_forge)) {

			/* Copyright Joshua Bell (chromium)
			http://code.google.com/p/stringencoding/
			license Apache 2.0 */

			/* modif Ayms
			global
			*/

			function inRange(c,d,b){return d<=c&&c<=b}function div(b,a){return Math.floor(b/a)}var EOF_byte=-1;var EOF_code_point=-1;function ByteInputStream(a){var b=0;this.get=function(){return(b>=a.length)?EOF_byte:Number(a[b])};this.offset=function(c){b+=c;if(b<0){throw new Error("Seeking past start of the buffer")}if(b>a.length){throw new Error("Seeking past EOF")}};this.match=function(d){if(d.length>b+a.length){return false}var c;for(c=0;c<d.length;c+=1){if(Number(a[b+c])!==d[c]){return false}}return true}}function ByteOutputStream(a){var b=0;this.emit=function(e){var d=EOF_byte;var c;for(c=0;c<arguments.length;++c){d=Number(arguments[c]);a[b++]=d}return d}}function CodePointInputStream(a){function b(g){var k=[];var h=0,m=g.length;while(h<g.length){var l=g.charCodeAt(h);if(!inRange(l,55296,57343)){k.push(l)}else{if(inRange(l,56320,57343)){k.push(65533)}else{if(h===m-1){k.push(65533)}else{var j=g.charCodeAt(h+1);if(inRange(j,56320,57343)){var f=l&1023;var e=j&1023;h+=1;k.push(65536+(f<<10)+e)}else{k.push(65533)}}}}h+=1}return k}var d=0;var c=b(a);this.offset=function(e){d+=e;if(d<0){throw new Error("Seeking past start of the buffer")}if(d>c.length){throw new Error("Seeking past EOF")}};this.get=function(){if(d>=c.length){return EOF_code_point}return c[d]}}function CodePointOutputStream(){var a="";this.string=function(){return a};this.emit=function(b){if(b<=65535){a+=String.fromCharCode(b)}else{b-=65536;a+=String.fromCharCode(55296+((b>>10)&1023));a+=String.fromCharCode(56320+(b&1023))}}}function EncodingError(a){this.name="EncodingError";this.message=a;this.code=0}EncodingError.prototype=Error.prototype;function decoderError(b,a){if(b){throw new EncodingError("Decoder error")}return a||65533}function encoderError(a){throw new EncodingError("The code point "+a+" could not be encoded.")}function getEncoding(a){a=String(a).trim().toLowerCase();if(Object.prototype.hasOwnProperty.call(label_to_encoding,a)){return label_to_encoding[a]}return null}var encodings=[{encodings:[{labels:["unicode-1-1-utf-8","utf-8","utf8"],name:"utf-8"}],heading:"The Encoding"},{encodings:[{labels:["cp864","ibm864"],name:"ibm864"},{labels:["cp866","ibm866"],name:"ibm866"},{labels:["csisolatin2","iso-8859-2","iso-ir-101","iso8859-2","iso_8859-2","l2","latin2"],name:"iso-8859-2"},{labels:["csisolatin3","iso-8859-3","iso_8859-3","iso-ir-109","l3","latin3"],name:"iso-8859-3"},{labels:["csisolatin4","iso-8859-4","iso_8859-4","iso-ir-110","l4","latin4"],name:"iso-8859-4"},{labels:["csisolatincyrillic","cyrillic","iso-8859-5","iso_8859-5","iso-ir-144"],name:"iso-8859-5"},{labels:["arabic","csisolatinarabic","ecma-114","iso-8859-6","iso_8859-6","iso-ir-127"],name:"iso-8859-6"},{labels:["csisolatingreek","ecma-118","elot_928","greek","greek8","iso-8859-7","iso_8859-7","iso-ir-126"],name:"iso-8859-7"},{labels:["csisolatinhebrew","hebrew","iso-8859-8","iso-8859-8-i","iso-ir-138","iso_8859-8","visual"],name:"iso-8859-8"},{labels:["csisolatin6","iso-8859-10","iso-ir-157","iso8859-10","l6","latin6"],name:"iso-8859-10"},{labels:["iso-8859-13"],name:"iso-8859-13"},{labels:["iso-8859-14","iso8859-14"],name:"iso-8859-14"},{labels:["iso-8859-15","iso_8859-15"],name:"iso-8859-15"},{labels:["iso-8859-16"],name:"iso-8859-16"},{labels:["koi8-r","koi8_r"],name:"koi8-r"},{labels:["koi8-u"],name:"koi8-u"},{labels:["csmacintosh","mac","macintosh","x-mac-roman"],name:"macintosh"},{labels:["iso-8859-11","tis-620","windows-874"],name:"windows-874"},{labels:["windows-1250","x-cp1250"],name:"windows-1250"},{labels:["windows-1251","x-cp1251"],name:"windows-1251"},{labels:["ascii","ansi_x3.4-1968","csisolatin1","iso-8859-1","iso8859-1","iso_8859-1","l1","latin1","us-ascii","windows-1252"],name:"windows-1252"},{labels:["cp1253","windows-1253"],name:"windows-1253"},{labels:["csisolatin5","iso-8859-9","iso-ir-148","l5","latin5","windows-1254"],name:"windows-1254"},{labels:["cp1255","windows-1255"],name:"windows-1255"},{labels:["cp1256","windows-1256"],name:"windows-1256"},{labels:["windows-1257"],name:"windows-1257"},{labels:["cp1258","windows-1258"],name:"windows-1258"},{labels:["x-mac-cyrillic","x-mac-ukrainian"],name:"x-mac-cyrillic"}],heading:"Legacy single-byte encodings"},{encodings:[{labels:["chinese","csgb2312","csiso58gb231280","gb2312","gbk","gb_2312","gb_2312-80","iso-ir-58","x-gbk"],name:"gbk"},{labels:["gb18030"],name:"gb18030"},{labels:["hz-gb-2312"],name:"hz-gb-2312"}],heading:"Legacy multi-byte Chinese (simplified) encodings"},{encodings:[{labels:["big5","big5-hkscs","cn-big5","csbig5","x-x-big5"],name:"big5"}],heading:"Legacy multi-byte Chinese (traditional) encodings"},{encodings:[{labels:["cseucpkdfmtjapanese","euc-jp","x-euc-jp"],name:"euc-jp"},{labels:["csiso2022jp","iso-2022-jp"],name:"iso-2022-jp"},{labels:["csshiftjis","ms_kanji","shift-jis","shift_jis","sjis","windows-31j","x-sjis"],name:"shift_jis"}],heading:"Legacy multi-byte Japanese encodings"},{encodings:[{labels:["cseuckr","csksc56011987","euc-kr","iso-ir-149","korean","ks_c_5601-1987","ks_c_5601-1989","ksc5601","ksc_5601","windows-949"],name:"euc-kr"},{labels:["csiso2022kr","iso-2022-kr"],name:"iso-2022-kr"}],heading:"Legacy multi-byte Korean encodings"},{encodings:[{labels:["utf-16","utf-16le"],name:"utf-16"},{labels:["utf-16be"],name:"utf-16be"}],heading:"Legacy utf-16 encodings"}];var name_to_encoding={};var label_to_encoding={};encodings.forEach(function(a){a.encodings.forEach(function(b){name_to_encoding[b.name]=b;b.labels.forEach(function(c){label_to_encoding[c]=b})})});function indexCodePointFor(b,a){return(a||[])[b]||null}function indexPointerFor(b,a){var c=a.indexOf(b);return c===-1?null:c}var indexes={};function indexGB18030CodePointFor(f){if((f>39419&&f<189000)||(f>1237575)){return null}var e=0,c=0,a=indexes.gb18030;var b;for(b=0;b<a.length;++b){var d=a[b];if(d[0]<=f){e=d[0];c=d[1]}else{break}}return c+f-e}function indexGB18030PointerFor(d){var f=0,c=0,a=indexes.gb18030;var b;for(b=0;b<a.length;++b){var e=a[b];if(e[1]<=d){f=e[1];c=e[0]}else{break}}return c+d-f}function UTF8Decoder(a){var e=a.fatal;var c=0,f=0,b=0,d=0;this.decode=function(g){var j=g.get();if(j===EOF_byte){if(f!==0){return decoderError(e)}return EOF_code_point}g.offset(1);if(f===0){if(inRange(j,0,127)){return j}if(inRange(j,194,223)){f=1;d=128;c=j-192}else{if(inRange(j,224,239)){f=2;d=2048;c=j-224}else{if(inRange(j,240,244)){f=3;d=65536;c=j-240}else{return decoderError(e)}}}c=c*Math.pow(64,f);return null}if(!inRange(j,128,191)){c=0;f=0;b=0;d=0;g.offset(-1);return decoderError(e)}b+=1;c=c+(j-128)*Math.pow(64,f-b);if(b!==f){return null}var h=c;var i=d;c=0;f=0;b=0;d=0;if(inRange(h,i,1114111)&&!inRange(h,55296,57343)){return h}return decoderError(e)}}function UTF8Encoder(a){var b=a.fatal;this.encode=function(i,d){var f=d.get();if(f===EOF_code_point){return EOF_byte}d.offset(1);if(inRange(f,55296,57343)){return encoderError(f)}if(inRange(f,0,127)){return i.emit(f)}var g,h;if(inRange(f,128,2047)){g=1;h=192}else{if(inRange(f,2048,65535)){g=2;h=224}else{if(inRange(f,65536,1114111)){g=3;h=240}}}var c=i.emit(div(f,Math.pow(64,g))+h);while(g>0){var e=div(f,Math.pow(64,g-1));c=i.emit(128+(e%64));g-=1}return c}}name_to_encoding["utf-8"].getEncoder=function(a){return new UTF8Encoder(a)};name_to_encoding["utf-8"].getDecoder=function(a){return new UTF8Decoder(a)};function SingleByteDecoder(b,a){var c=a.fatal;this.decode=function(d){var f=d.get();if(f===EOF_byte){return EOF_code_point}d.offset(1);if(inRange(f,0,127)){return f}var e=b[f-128];if(e===null){return decoderError(c)}return e}}function SingleByteEncoder(b,a){var c=a.fatal;this.encode=function(g,d){var e=d.get();if(e===EOF_code_point){return EOF_byte}d.offset(1);if(inRange(e,0,127)){return g.emit(e)}var f=indexPointerFor(e,b);if(f===null){encoderError(e)}return g.emit(f+128)}}(function(){["ibm864","ibm866","iso-8859-2","iso-8859-3","iso-8859-4","iso-8859-5","iso-8859-6","iso-8859-7","iso-8859-8","iso-8859-10","iso-8859-13","iso-8859-14","iso-8859-15","iso-8859-16","koi8-r","koi8-u","macintosh","windows-874","windows-1250","windows-1251","windows-1252","windows-1253","windows-1254","windows-1255","windows-1256","windows-1257","windows-1258","x-mac-cyrillic"].forEach(function(b){var c=name_to_encoding[b];var a=indexes[b];c.getDecoder=function(d){return new SingleByteDecoder(a,d)};c.getEncoder=function(d){return new SingleByteEncoder(a,d)}})}());function GBKDecoder(c,b){var f=b.fatal;var a=0,e=0,d=0;this.decode=function(g){var l=g.get();if(l===EOF_byte&&a===0&&e===0&&d===0){return EOF_code_point}if(l===EOF_byte&&(a!==0||e!==0||d!==0)){a=0;e=0;d=0;decoderError(f)}g.offset(1);var i;if(d!==0){i=null;if(inRange(l,48,57)){i=indexGB18030CodePointFor((((a-129)*10+(e-48))*126+(d-129))*10+l-48)}a=0;e=0;d=0;if(i===null){g.offset(-3);return decoderError(f)}return i}if(e!==0){if(inRange(l,129,254)){d=l;return null}g.offset(-2);a=0;e=0;return decoderError(f)}if(a!==0){if(inRange(l,48,57)&&c){e=l;return null}var h=a;var k=null;a=0;var j=l<127?64:65;if(inRange(l,64,126)||inRange(l,128,254)){k=(h-129)*190+(l-j)}i=k===null?null:indexCodePointFor(k,indexes.gbk);if(k===null){g.offset(-1)}if(i===null){return decoderError(f)}return i}if(inRange(l,0,127)){return l}if(l===128){return 8364}if(inRange(l,129,254)){a=l;return null}return decoderError(f)}}function GBKEncoder(b,a){var c=a.fatal;this.encode=function(h,k){var g=k.get();if(g===EOF_code_point){return EOF_byte}k.offset(1);if(inRange(g,0,127)){return h.emit(g)}var d=indexPointerFor(g,indexes.gbk);if(d!==null){var i=div(d,190)+129;var e=d%190;var f=e<63?64:65;return h.emit(i,e+f)}if(d===null&&!b){return encoderError(g)}d=indexGB18030PointerFor(g);var n=div(div(div(d,10),126),10);d=d-n*10*126*10;var m=div(div(d,10),126);d=d-m*10*126;var l=div(d,10);var j=d-l*10;return h.emit(n+129,m+48,l+129,j+48)}}name_to_encoding.gbk.getEncoder=function(a){return new GBKEncoder(false,a)};name_to_encoding.gbk.getDecoder=function(a){return new GBKDecoder(false,a)};name_to_encoding.gb18030.getEncoder=function(a){return new GBKEncoder(true,a)};name_to_encoding.gb18030.getDecoder=function(a){return new GBKDecoder(true,a)};function HZGB2312Decoder(c){var d=c.fatal;var b=false,a=0;this.decode=function(e){var h=e.get();if(h===EOF_byte&&a===0){return EOF_code_point}if(h===EOF_byte&&a!==0){a=0;return decoderError(d)}e.offset(1);if(a===126){a=0;if(h===123){b=true;return null}if(h===125){b=false;return null}if(h===126){return 126}if(h===10){return null}e.offset(-1);return decoderError(d)}if(a!==0){var f=a;a=0;var g=null;if(inRange(h,33,126)){g=indexCodePointFor((f-1)*190+(h+63),indexes.gbk)}if(h===10){b=false}if(g===null){return decoderError(d)}return g}if(h===126){a=126;return null}if(b){if(inRange(h,32,127)){a=h;return null}if(h===10){b=false}return decoderError(d)}if(inRange(h,0,127)){return h}return decoderError(d)}}function HZGB2312Encoder(b){var c=b.fatal;var a=false;this.encode=function(i,d){var g=d.get();if(g===EOF_code_point){return EOF_byte}d.offset(1);if(inRange(g,0,127)&&a){d.offset(-1);a=false;return i.emit(126,125)}if(g===126){return i.emit(126,126)}if(inRange(g,0,127)){return i.emit(g)}if(!a){d.offset(-1);a=true;return i.emit(126,123)}var h=indexPointerFor(g,indexes.gbk);if(h===null){return encoderError(g)}var f=div(h,190)+1;var e=h%190-63;if(!inRange(f,33,126)||!inRange(e,33,126)){return encoderError(g)}return i.emit(f,e)}}name_to_encoding["hz-gb-2312"].getEncoder=function(a){return new HZGB2312Encoder(a)};name_to_encoding["hz-gb-2312"].getDecoder=function(a){return new HZGB2312Decoder(a)};function Big5Decoder(b){var d=b.fatal;var a=0,c=null;this.decode=function(e){if(c!==null){var j=c;c=null;return j}var k=e.get();if(k===EOF_byte&&a===0){return EOF_code_point}if(k===EOF_byte&&a!==0){a=0;return decoderError(d)}e.offset(1);if(a!==0){var f=a;var i=null;a=0;var h=k<127?64:98;if(inRange(k,64,126)||inRange(k,161,254)){i=(f-129)*157+(k-h)}if(i===1133){c=772;return 202}if(i===1135){c=780;return 202}if(i===1164){c=772;return 234}if(i===1166){c=780;return 234}var g=(i===null)?null:indexCodePointFor(i,indexes.big5);if(i===null){e.offset(-1)}if(g===null){return decoderError(d)}return g}if(inRange(k,0,127)){return k}if(inRange(k,129,254)){a=k;return null}return decoderError(d)}}function Big5Encoder(a){var b=a.fatal;this.encode=function(i,c){var f=c.get();if(f===EOF_code_point){return EOF_byte}c.offset(1);if(inRange(f,0,127)){return i.emit(f)}var h=indexPointerFor(f,indexes.big5);if(h===null){return encoderError(f)}var e=div(h,157)+129;var d=h%157;var g=d<63?64:98;return i.emit(e,d+g)}}name_to_encoding.big5.getEncoder=function(a){return new Big5Encoder(a)};name_to_encoding.big5.getDecoder=function(a){return new Big5Decoder(a)};function EUCJPDecoder(a){var c=a.fatal;var b=0,d=0;this.decode=function(e){var h=e.get();if(h===EOF_byte){if(b===0&&d===0){return EOF_code_point}b=0;d=0;return decoderError(c)}e.offset(1);var f,g;if(d!==0){f=d;d=0;g=null;if(inRange(f,161,254)&&inRange(h,161,254)){g=indexCodePointFor((f-161)*94+h-161,indexes.jis0212)}if(!inRange(h,161,254)){e.offset(-1)}if(g===null){return decoderError(c)}return g}if(b===142&&inRange(h,161,223)){b=0;return 65377+h-161}if(b===143&&inRange(h,161,254)){b=0;d=h;return null}if(b!==0){f=b;b=0;g=null;if(inRange(f,161,254)&&inRange(h,161,254)){g=indexCodePointFor((f-161)*94+h-161,indexes.jis0208)}if(!inRange(h,161,254)){e.offset(-1)}if(g===null){return decoderError(c)}return g}if(inRange(h,0,127)){return h}if(h===142||h===143||(inRange(h,161,254))){b=h;return null}return decoderError(c)}}function EUCJPEncoder(a){var b=a.fatal;this.encode=function(h,c){var f=c.get();if(f===EOF_code_point){return EOF_byte}c.offset(1);if(inRange(f,0,127)){return h.emit(f)}if(f===165){return h.emit(92)}if(f===8254){return h.emit(126)}if(inRange(f,65377,65439)){return h.emit(142,f-65377+161)}var g=indexPointerFor(f,indexes.jis0208);if(g===null){return encoderError(f)}var e=div(g,94)+161;var d=g%94+161;return h.emit(e,d)}}name_to_encoding["euc-jp"].getEncoder=function(a){return new EUCJPEncoder(a)};name_to_encoding["euc-jp"].getDecoder=function(a){return new EUCJPDecoder(a)};function ISO2022JPDecoder(a){var d=a.fatal;var c={ASCII:0,escape_start:1,escape_middle:2,escape_final:3,lead:4,trail:5,Katakana:6};var e=c.ASCII,f=false,b=0;this.decode=function(g){var k=g.get();if(k!==EOF_byte){g.offset(1)}switch(e){default:case c.ASCII:if(k===27){e=c.escape_start;return null}if(inRange(k,0,127)){return k}if(k===EOF_byte){return EOF_code_point}return decoderError(d);case c.escape_start:if(k===36||k===40){b=k;e=c.escape_middle;return null}if(k!==EOF_byte){g.offset(-1)}e=c.ASCII;return decoderError(d);case c.escape_middle:var h=b;b=0;if(h===36&&(k===64||k===66)){f=false;e=c.lead;return null}if(h===36&&k===40){e=c.escape_final;return null}if(h===40&&(k===66||k===74)){e=c.ASCII;return null}if(h===40&&k===73){e=c.Katakana;return null}if(k===EOF_byte){g.offset(-1)}else{g.offset(-2)}e=c.ASCII;return decoderError(d);case c.escape_final:if(k===68){f=true;e=c.lead;return null}if(k===EOF_byte){g.offset(-2)}else{g.offset(-3)}e=c.ASCII;return decoderError(d);case c.lead:if(k===10){e=c.ASCII;return decoderError(d,10)}if(k===27){e=c.escape_start;return null}if(k===EOF_byte){return EOF_code_point}b=k;e=c.trail;return null;case c.trail:e=c.lead;if(k===EOF_byte){return decoderError(d)}var i=null;var j=(b-33)*94+k-33;if(inRange(b,33,126)&&inRange(k,33,126)){i=(f===false)?indexCodePointFor(j,indexes.jis0208):indexCodePointFor(j,indexes.jis0212)}if(i===null){return decoderError(d)}return i;case c.Katakana:if(k===27){e=c.escape_start;return null}if(inRange(k,33,95)){return 65377+k-33}if(k===EOF_byte){return EOF_code_point}return decoderError(d)}}}function ISO2022JPEncoder(a){var c=a.fatal;var b={ASCII:0,lead:1,Katakana:2};var d=b.ASCII;this.encode=function(j,e){var h=e.get();if(h===EOF_code_point){return EOF_byte}e.offset(1);if((inRange(h,0,127)||h===165||h===8254)&&d!==b.ASCII){e.offset(-1);d=b.ASCII;return j.emit(27,40,66)}if(inRange(h,0,127)){return j.emit(h)}if(h===165){return j.emit(92)}if(h===8254){return j.emit(126)}if(inRange(h,65377,65439)&&d!==b.Katakana){e.offset(-1);d=b.Katakana;return j.emit(27,40,73)}if(inRange(h,65377,65439)){return j.emit(h-65377-33)}if(d!==b.lead){e.offset(-1);d=b.lead;return j.emit(27,36,66)}var i=indexPointerFor(h,indexes.jis0208);if(i===null){return encoderError(h)}var g=div(i,94)+33;var f=i%94+33;return j.emit(g,f)}}name_to_encoding["iso-2022-jp"].getEncoder=function(a){return new ISO2022JPEncoder(a)};name_to_encoding["iso-2022-jp"].getDecoder=function(a){return new ISO2022JPDecoder(a)};function ShiftJISDecoder(a){var c=a.fatal;var b=0;this.decode=function(d){var i=d.get();if(i===EOF_byte&&b===0){return EOF_code_point}if(i===EOF_byte&&b!==0){b=0;return decoderError(c)}d.offset(1);if(b!==0){var e=b;b=0;if(inRange(i,64,126)||inRange(i,128,252)){var g=(i<127)?64:65;var h=(e<160)?129:193;var f=indexCodePointFor((e-h)*188+i-g,indexes.jis0208);if(f===null){return decoderError(c)}return f}d.offset(-1);return decoderError(c)}if(inRange(i,0,128)){return i}if(inRange(i,161,223)){return 65377+i-161}if(inRange(i,129,159)||inRange(i,224,252)){b=i;return null}return decoderError(c)}}function ShiftJISEncoder(a){var b=a.fatal;this.encode=function(i,c){var f=c.get();if(f===EOF_code_point){return EOF_byte}c.offset(1);if(inRange(f,0,128)){return i.emit(f)}if(f===165){return i.emit(92)}if(f===8254){return i.emit(126)}if(inRange(f,65377,65439)){return i.emit(f-65377+161)}var h=indexPointerFor(f,indexes.jis0208);if(h===null){return encoderError(f)}var e=div(h,188);var j=e<31?129:193;var d=h%188;var g=d<63?64:65;return i.emit(e+j,d+g)}}name_to_encoding.shift_jis.getEncoder=function(a){return new ShiftJISEncoder(a)};name_to_encoding.shift_jis.getDecoder=function(a){return new ShiftJISDecoder(a)};function EUCKRDecoder(b){var c=b.fatal;var a=0;this.decode=function(e){var i=e.get();if(i===EOF_byte&&a===0){return EOF_code_point}if(i===EOF_byte&&a!==0){a=0;return decoderError(c)}e.offset(1);if(a!==0){var f=a;var h=null;a=0;if(inRange(f,129,198)){var d=(26+26+126)*(f-129);if(inRange(i,65,90)){h=d+i-65}else{if(inRange(i,97,122)){h=d+26+i-97}else{if(inRange(i,129,254)){h=d+26+26+i-129}}}}if(inRange(f,199,253)&&inRange(i,161,254)){h=(26+26+126)*(199-129)+(f-199)*94+(i-161)}var g=(h===null)?null:indexCodePointFor(h,indexes["euc-kr"]);if(h===null){e.offset(-1)}if(g===null){return decoderError(c)}return g}if(inRange(i,0,127)){return i}if(inRange(i,129,253)){a=i;return null}return decoderError(c)}}function EUCKREncoder(a){var b=a.fatal;this.encode=function(i,c){var f=c.get();if(f===EOF_code_point){return EOF_byte}c.offset(1);if(inRange(f,0,127)){return i.emit(f)}var h=indexPointerFor(f,indexes["euc-kr"]);if(h===null){return encoderError(f)}var e,d;if(h<((26+26+126)*(199-129))){e=div(h,(26+26+126))+129;d=h%(26+26+126);var g=d<26?65:d<26+26?71:77;return i.emit(e,d+g)}h=h-(26+26+126)*(199-129);e=div(h,94)+199;d=h%94+161;return i.emit(e,d)}}name_to_encoding["euc-kr"].getEncoder=function(a){return new EUCKREncoder(a)};name_to_encoding["euc-kr"].getDecoder=function(a){return new EUCKRDecoder(a)};function ISO2022KRDecoder(b){var e=b.fatal;var c={ASCII:0,escape_start:1,escape_middle:2,escape_end:3,lead:4,trail:5};var a=c.ASCII,d=0;this.decode=function(f){var h=f.get();if(h!==EOF_byte){f.offset(1)}switch(a){default:case c.ASCII:if(h===14){a=c.lead;return null}if(h===15){return null}if(h===27){a=c.escape_start;return null}if(inRange(h,0,127)){return h}if(h===EOF_byte){return EOF_code_point}return decoderError(e);case c.escape_start:if(h===36){a=c.escape_middle;return null}if(h!==EOF_byte){f.offset(-1)}a=c.ASCII;return decoderError(e);case c.escape_middle:if(h===41){a=c.escape_end;return null}if(h===EOF_byte){f.offset(-1)}else{f.offset(-2)}a=c.ASCII;return decoderError(e);case c.escape_end:if(h===67){a=c.ASCII;return null}if(h===EOF_byte){f.offset(-2)}else{f.offset(-3)}a=c.ASCII;return decoderError(e);case c.lead:if(h===10){a=c.ASCII;return decoderError(e,10)}if(h===14){return null}if(h===15){a=c.ASCII;return null}if(h===EOF_byte){return EOF_code_point}d=h;a=c.trail;return null;case c.trail:a=c.lead;if(h===EOF_byte){return decoderError(e)}var g=null;if(inRange(d,33,70)&&inRange(h,33,126)){g=indexCodePointFor((26+26+126)*(d-1)+26+26+h-1,indexes["euc-kr"])}else{if(inRange(d,71,126)&&inRange(h,33,126)){g=indexCodePointFor((26+26+126)*(199-129)+(d-71)*94+(h-33),indexes["euc-kr"])}}if(g!==null){return g}return decoderError(e)}}}function ISO2022KREncoder(b){var e=b.fatal;var d={ASCII:0,lead:1};var c=false,a=d.ASCII;this.encode=function(k,f){var i=f.get();if(i===EOF_code_point){return EOF_byte}if(!c){c=true;k.emit(27,36,41,67)}f.offset(1);if(inRange(i,0,127)&&a!==d.ASCII){f.offset(-1);a=d.ASCII;return k.emit(15)}if(inRange(i,0,127)){return k.emit(i)}if(a!==d.lead){f.offset(-1);a=d.lead;return k.emit(14)}var j=indexPointerFor(i,indexes["euc-kr"]);if(j===null){return encoderError(i)}var h,g;if(j<(26+26+126)*(199-129)){h=div(j,(26+26+126))+1;g=j%(26+26+126)-26-26+1;if(!inRange(h,33,70)||!inRange(g,33,126)){return encoderError(i)}return k.emit(h,g)}j=j-(26+26+126)*(199-129);h=div(j,94)+71;g=j%94+33;if(!inRange(h,71,126)||!inRange(g,33,126)){return encoderError(i)}return k.emit(h,g)}}name_to_encoding["iso-2022-kr"].getEncoder=function(a){return new ISO2022KREncoder(a)};name_to_encoding["iso-2022-kr"].getDecoder=function(a){return new ISO2022KRDecoder(a)};function UTF16Decoder(e,b){var d=b.fatal;var c=null,a=null;this.decode=function(f){var i=f.get();if(i===EOF_byte&&c===null&&a===null){return EOF_code_point}if(i===EOF_byte&&(c!==null||a!==null)){return decoderError(d)}f.offset(1);if(c===null){c=i;return null}var g;if(e){g=(c<<8)+i}else{g=(i<<8)+c}c=null;if(a!==null){var h=a;a=null;if(inRange(g,56320,57343)){return 65536+(h-55296)*1024+(g-56320)}f.offset(-2);return decoderError(d)}if(inRange(g,55296,56319)){a=g;return null}if(inRange(g,56320,57343)){return decoderError(d)}return g}}function UTF16Encoder(c,a){var b=a.fatal;this.encode=function(i,e){function d(l){var k=l>>8;var j=l&255;if(c){return i.emit(k,j)}return i.emit(j,k)}var h=e.get();if(h===EOF_code_point){return EOF_byte}e.offset(1);if(inRange(h,55296,57343)){encoderError(h)}if(h<=65535){return d(h)}var g=div((h-65536),1024)+55296;var f=((h-65536)%1024)+56320;d(g);return d(f)}}name_to_encoding["utf-16"].getEncoder=function(a){return new UTF16Encoder(false,a)};name_to_encoding["utf-16"].getDecoder=function(a){return new UTF16Decoder(false,a)};name_to_encoding["utf-16be"].getEncoder=function(a){return new UTF16Encoder(true,a)};name_to_encoding["utf-16be"].getDecoder=function(a){return new UTF16Decoder(true,a)};function detectEncoding(a,b){if(b.match([255,254])){b.offset(2);return"utf-16"}if(b.match([254,255])){b.offset(2);return"utf-16be"}if(b.match([239,187,191])){b.offset(3);return"utf-8"}return a}function consumeBOM(a,b){if(b.match([255,254])&&a==="utf-16"){b.offset(2);return}if(b.match([254,255])&&a=="utf-16be"){b.offset(2);return}if(b.match([239,187,191])&&a=="utf-8"){b.offset(3);return}}var DEFAULT_ENCODING="utf-8";function TextEncoder(b,a){b=b?String(b):DEFAULT_ENCODING;a=Object(a);this._encoding=getEncoding(b);if(this._encoding===null||(this._encoding.name!=="utf-8"&&this._encoding.name!=="utf-16"&&this._encoding.name!=="utf-16be")){throw new TypeError("Unknown encoding: "+b)}this._streaming=false;this._encoder=null;this._options={fatal:Boolean(a.fatal)};if(Object.defineProperty){Object.defineProperty(this,"encoding",{get:function(){return this._encoding.name}})}else{this.encoding=this._encoding.name}return this}TextEncoder.prototype={encode:function encode(b,e){b=b?String(b):"";e=Object(e);if(!this._streaming){this._encoder=this._encoding.getEncoder(this._options)}this._streaming=Boolean(e.stream);var c=[];var d=new ByteOutputStream(c);var f=new CodePointInputStream(b);while(f.get()!==EOF_code_point){this._encoder.encode(d,f)}if(!this._streaming){var a;do{a=this._encoder.encode(d,f)}while(a!==EOF_byte);this._encoder=null}return new Uint8Array(c)}};function TextDecoder(b,a){b=b?String(b):DEFAULT_ENCODING;a=Object(a);this._encoding=getEncoding(b);if(this._encoding===null){throw new TypeError("Unknown encoding: "+b)}this._streaming=false;this._decoder=null;this._options={fatal:Boolean(a.fatal)};if(Object.defineProperty){Object.defineProperty(this,"encoding",{get:function(){return this._encoding.name}})}else{this.encoding=this._encoding.name}return this}TextDecoder.prototype={decode:function decode(e,c){if(e&&!("buffer" in e&&"byteOffset" in e&&"byteLength" in e)){throw new TypeError("Expected ArrayBufferView")}else{if(!e){e=new Uint8Array(0)}}c=Object(c);if(!this._streaming){this._decoder=this._encoding.getDecoder(this._options)}this._streaming=Boolean(c.stream);var a=new Uint8Array(e.buffer,e.byteOffset,e.byteLength);var f=new ByteInputStream(a);if(!this._BOMseen){this._BOMseen=true;consumeBOM(this._encoding.name,f)}var b=new CodePointOutputStream(),d;while(f.get()!==EOF_byte){d=this._decoder.decode(f);if(d!==null&&d!==EOF_code_point){b.emit(d)}}if(!this._streaming){do{d=this._decoder.decode(f);if(d!==null&&d!==EOF_code_point){b.emit(d)}}while(d!==EOF_code_point&&f.get()!=EOF_byte);this._decoder=null}return b.string()}};

			//forge
			/* modifs from forge (tls.js):
				- remove server key exchange message (2 changes)
				- remove version check (2 changes)
				- ignore unexpected messages (1 change)
				- change fatal to true for handshake already in progress
				- change test in handle server hello when session id is not set
				- clear .input.data in process (memory leak)
			*/

			var forge={};

			//util.js (forge)

			/**
			 * Utility functions for web applications.
			 *
			 * @author Dave Longley
			 *
			 * Copyright (c) 2010-2012 Digital Bazaar, Inc.
			 */

			(function(){/*function c(q){*/var q=forge;var l=q.util=q.util||{};if(typeof process==="undefined"||!process.nextTick){if(typeof setImmediate==="function"){l.setImmediate=setImmediate;l.nextTick=function(s){return setImmediate(s)}}else{l.setImmediate=function(s){setTimeout(s,0)};l.nextTick=l.setImmediate}}else{l.nextTick=process.nextTick;if(typeof setImmediate==="function"){l.setImmediate=setImmediate}else{l.setImmediate=l.nextTick}}l.isArray=Array.isArray||function(s){return Object.prototype.toString.call(s)==="[object Array]"};l.ByteBuffer=function(s){this.data=s||"";this.read=0};l.ByteBuffer.prototype.length=function(){return this.data.length-this.read};l.ByteBuffer.prototype.isEmpty=function(){return this.length()<=0};l.ByteBuffer.prototype.putByte=function(s){this.data+=String.fromCharCode(s);return this};l.ByteBuffer.prototype.fillWithByte=function(s,u){s=String.fromCharCode(s);var t=this.data;while(u>0){if(u&1){t+=s}u>>>=1;if(u>0){s+=s}}this.data=t;return this};l.ByteBuffer.prototype.putBytes=function(s){this.data+=s;return this};l.ByteBuffer.prototype.putString=function(s){this.data+=l.encodeUtf8(s);return this};l.ByteBuffer.prototype.putInt16=function(s){this.data+=String.fromCharCode(s>>8&255)+String.fromCharCode(s&255);return this};l.ByteBuffer.prototype.putInt24=function(s){this.data+=String.fromCharCode(s>>16&255)+String.fromCharCode(s>>8&255)+String.fromCharCode(s&255);return this};l.ByteBuffer.prototype.putInt32=function(s){this.data+=String.fromCharCode(s>>24&255)+String.fromCharCode(s>>16&255)+String.fromCharCode(s>>8&255)+String.fromCharCode(s&255);return this};l.ByteBuffer.prototype.putInt16Le=function(s){this.data+=String.fromCharCode(s&255)+String.fromCharCode(s>>8&255);return this};l.ByteBuffer.prototype.putInt24Le=function(s){this.data+=String.fromCharCode(s&255)+String.fromCharCode(s>>8&255)+String.fromCharCode(s>>16&255);return this};l.ByteBuffer.prototype.putInt32Le=function(s){this.data+=String.fromCharCode(s&255)+String.fromCharCode(s>>8&255)+String.fromCharCode(s>>16&255)+String.fromCharCode(s>>24&255);return this};l.ByteBuffer.prototype.putInt=function(s,t){do{t-=8;this.data+=String.fromCharCode((s>>t)&255)}while(t>0);return this};l.ByteBuffer.prototype.putSignedInt=function(s,t){if(s<0){s+=2<<(t-1)}return this.putInt(s,t)};l.ByteBuffer.prototype.putBuffer=function(s){this.data+=s.getBytes();return this};l.ByteBuffer.prototype.getByte=function(){return this.data.charCodeAt(this.read++)};l.ByteBuffer.prototype.getInt16=function(){var s=(this.data.charCodeAt(this.read)<<8^this.data.charCodeAt(this.read+1));this.read+=2;return s};l.ByteBuffer.prototype.getInt24=function(){var s=(this.data.charCodeAt(this.read)<<16^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2));this.read+=3;return s};l.ByteBuffer.prototype.getInt32=function(){var s=(this.data.charCodeAt(this.read)<<24^this.data.charCodeAt(this.read+1)<<16^this.data.charCodeAt(this.read+2)<<8^this.data.charCodeAt(this.read+3));this.read+=4;return s};l.ByteBuffer.prototype.getInt16Le=function(){var s=(this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8);this.read+=2;return s};l.ByteBuffer.prototype.getInt24Le=function(){var s=(this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2)<<16);this.read+=3;return s};l.ByteBuffer.prototype.getInt32Le=function(){var s=(this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2)<<16^this.data.charCodeAt(this.read+3)<<24);this.read+=4;return s};l.ByteBuffer.prototype.getInt=function(t){var s=0;do{s=(s<<8)+this.data.charCodeAt(this.read++);t-=8}while(t>0);return s};l.ByteBuffer.prototype.getSignedInt=function(u){var t=this.getInt(u);var s=2<<(u-2);if(t>=s){t-=s<<1}return t};l.ByteBuffer.prototype.getBytes=function(s){var t;if(s){s=Math.min(this.length(),s);t=this.data.slice(this.read,this.read+s);this.read+=s}else{if(s===0){t=""}else{t=(this.read===0)?this.data:this.data.slice(this.read);this.clear()}}return t};l.ByteBuffer.prototype.bytes=function(s){return(typeof(s)==="undefined"?this.data.slice(this.read):this.data.slice(this.read,this.read+s))};l.ByteBuffer.prototype.at=function(s){return this.data.charCodeAt(this.read+s)};l.ByteBuffer.prototype.setAt=function(t,s){this.data=this.data.substr(0,this.read+t)+String.fromCharCode(s)+this.data.substr(this.read+t+1);return this};l.ByteBuffer.prototype.last=function(){return this.data.charCodeAt(this.data.length-1)};l.ByteBuffer.prototype.copy=function(){var s=l.createBuffer(this.data);s.read=this.read;return s};l.ByteBuffer.prototype.compact=function(){if(this.read>0){this.data=this.data.slice(this.read);this.read=0}return this};l.ByteBuffer.prototype.clear=function(){this.data="";this.read=0;return this};l.ByteBuffer.prototype.truncate=function(t){var s=Math.max(0,this.length()-t);this.data=this.data.substr(this.read,s);this.read=0;return this};l.ByteBuffer.prototype.toHex=function(){var u="";for(var t=this.read;t<this.data.length;++t){var s=this.data.charCodeAt(t);if(s<16){u+="0"}u+=s.toString(16)}return u};l.ByteBuffer.prototype.toString=function(){return l.decodeUtf8(this.bytes())};l.createBuffer=function(s,t){t=t||"raw";if(s!==undefined&&t==="utf8"){s=l.encodeUtf8(s)}return new l.ByteBuffer(s)};l.fillString=function(v,u){var t="";while(u>0){if(u&1){t+=v}u>>>=1;if(u>0){v+=v}}return t};l.xorBytes=function(y,v,A){var u="";var s="";var x="";var w=0;var z=0;for(;A>0;--A,++w){s=y.charCodeAt(w)^v.charCodeAt(w);if(z>=10){u+=x;x="";z=0}x+=String.fromCharCode(s);++z}u+=x;return u};l.hexToBytes=function(t){var u="";var s=0;if(t.length&1==1){s=1;u+=String.fromCharCode(parseInt(t[0],16))}for(;s<t.length;s+=2){u+=String.fromCharCode(parseInt(t.substr(s,2),16))}return u};l.bytesToHex=function(s){return l.createBuffer(s).toHex()};l.int32ToBytes=function(s){return(String.fromCharCode(s>>24&255)+String.fromCharCode(s>>16&255)+String.fromCharCode(s>>8&255)+String.fromCharCode(s&255))};var g="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";var r=[62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,64,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51];l.encode64=function(v,z){var s="";var u="";var y,w,t;var x=0;while(x<v.length){y=v.charCodeAt(x++);w=v.charCodeAt(x++);t=v.charCodeAt(x++);s+=g.charAt(y>>2);s+=g.charAt(((y&3)<<4)|(w>>4));if(isNaN(w)){s+="=="}else{s+=g.charAt(((w&15)<<2)|(t>>6));s+=isNaN(t)?"=":g.charAt(t&63)}if(z&&s.length>z){u+=s.substr(0,z)+"\r\n";s=s.substr(z)}}u+=s;return u};l.decode64=function(t){t=t.replace(/[^A-Za-z0-9\+\/\=]/g,"");var s="";var y,x,w,v;var u=0;while(u<t.length){y=r[t.charCodeAt(u++)-43];x=r[t.charCodeAt(u++)-43];w=r[t.charCodeAt(u++)-43];v=r[t.charCodeAt(u++)-43];s+=String.fromCharCode((y<<2)|(x>>4));if(w!==64){s+=String.fromCharCode(((x&15)<<4)|(w>>2));if(v!==64){s+=String.fromCharCode(((w&3)<<6)|v)}}}return s};l.encodeUtf8=function(s){return unescape(encodeURIComponent(s))};l.decodeUtf8=function(s){return decodeURIComponent(escape(s))};l.deflate=function(v,t,u){t=l.decode64(v.deflate(l.encode64(t)).rval);if(u){var w=2;var s=t.charCodeAt(1);if(s&32){w=6}t=t.substring(w,t.length-4)}return t};l.inflate=function(u,s,t){var v=u.inflate(l.encode64(s)).rval;return(v===null)?null:l.decode64(v)};var i=function(s,v,u){if(!s){throw {message:"WebStorage not available."}}var t;if(u===null){t=s.removeItem(v)}else{u=l.encode64(JSON.stringify(u));t=s.setItem(v,u)}if(typeof(t)!=="undefined"&&t.rval!==true){throw t.error}};var k=function(s,u){if(!s){throw {message:"WebStorage not available."}}var t=s.getItem(u);if(s.init){if(t.rval===null){if(t.error){throw t.error}t=null}else{t=t.rval}}if(t!==null){t=JSON.parse(l.decode64(t))}return t};var p=function(t,w,s,u){var v=k(t,w);if(v===null){v={}}v[s]=u;i(t,w,v)};var h=function(t,v,s){var u=k(t,v);if(u!==null){u=(s in u)?u[s]:null}return u};var j=function(t,x,s){var v=k(t,x);if(v!==null&&s in v){delete v[s];var u=true;for(var w in v){u=false;break}if(u){v=null}i(t,x,v)}};var n=function(s,t){i(s,t,null)};var m=function(t,x,z){var v=null;if(typeof(z)==="undefined"){z=["web","flash"]}var y;var u=false;var s=null;for(var A in z){y=z[A];try{if(y==="flash"||y==="both"){if(x[0]===null){throw {message:"Flash local storage not available."}}else{v=t.apply(this,x);u=(y==="flash")}}if(y==="web"||y==="both"){x[0]=localStorage;v=t.apply(this,x);u=true}}catch(w){s=w}if(u){break}}if(!u){throw s}return v};l.setItem=function(u,w,t,v,s){m(p,arguments,s)};l.getItem=function(u,v,t,s){return m(h,arguments,s)};l.removeItem=function(u,v,t,s){m(j,arguments,s)};l.clearItems=function(t,u,s){m(n,arguments,s)};l.parseUrl=function(v){var u=/^(https?):\/\/([^:&^\/]*):?(\d*)(.*)$/g;u.lastIndex=0;var s=u.exec(v);var t=(s===null)?null:{full:v,scheme:s[1],host:s[2],port:s[3],path:s[4]};if(t){t.fullHost=t.host;if(t.port){if(t.port!==80&&t.scheme==="http"){t.fullHost+=":"+t.port}else{if(t.port!==443&&t.scheme==="https"){t.fullHost+=":"+t.port}}}else{if(t.scheme==="http"){t.port=80}else{if(t.scheme==="https"){t.port=443}}}t.full=t.scheme+"://"+t.fullHost}return t};var o=null;l.getQueryVariables=function(s){var u=function(y){var z={};var x=y.split("&");for(var w=0;w<x.length;w++){var B=x[w].indexOf("=");var v;var A;if(B>0){v=x[w].substring(0,B);A=x[w].substring(B+1)}else{v=x[w];A=null}if(!(v in z)){z[v]=[]}if(!(v in Object.prototype)&&A!==null){z[v].push(unescape(A))}}return z};var t;if(typeof(s)==="undefined"){if(o===null){if(typeof(window)==="undefined"){o={}}else{o=u(window.location.search.substring(1))}}t=o}else{t=u(s)}return t};l.parseFragment=function(u){var t=u;var s="";var x=u.indexOf("?");if(x>0){t=u.substring(0,x);s=u.substring(x+1)}var w=t.split("/");if(w.length>0&&w[0]===""){w.shift()}var v=(s==="")?{}:l.getQueryVariables(s);return{pathString:t,queryString:s,path:w,query:v}};l.makeRequest=function(t){var u=l.parseFragment(t);var s={path:u.pathString,query:u.queryString,getPath:function(v){return(typeof(v)==="undefined")?u.path:u.path[v]},getQuery:function(v,w){var x;if(typeof(v)==="undefined"){x=u.query}else{x=u.query[v];if(x&&typeof(w)!=="undefined"){x=x[w]}}return x},getQueryLast:function(w,v){var y;var x=s.getQuery(w);if(x){y=x[x.length-1]}else{y=v}return y}};return s};l.makeLink=function(v,u,t){v=jQuery.isArray(v)?v.join("/"):v;var s=jQuery.param(u||{});t=t||"";return v+((s.length>0)?("?"+s):"")+((t.length>0)?("#"+t):"")};l.setPath=function(u,x,y){if(typeof(u)==="object"&&u!==null){var v=0;var t=x.length;while(v<t){var w=x[v++];if(v==t){u[w]=y}else{var s=(w in u);if(!s||(s&&typeof(u[w])!=="object")||(s&&u[w]===null)){u[w]={}}u=u[w]}}}};l.getPath=function(v,y,u){var w=0;var t=y.length;var s=true;while(s&&w<t&&typeof(v)==="object"&&v!==null){var x=y[w++];s=x in v;if(s){v=v[x]}}return(s?v:u)};l.deletePath=function(t,w){if(typeof(t)==="object"&&t!==null){var u=0;var s=w.length;while(u<s){var v=w[u++];if(u==s){delete t[v]}else{if(!(v in t)||(typeof(t[v])!=="object")||(t[v]===null)){break}t=t[v]}}}};l.isEmpty=function(s){for(var t in s){if(s.hasOwnProperty(t)){return false}}return true};l.format=function(z){var v=/%./g;var u;var t;var s=0;var y=[];var x=0;while((u=v.exec(z))){t=z.substring(x,v.lastIndex-2);if(t.length>0){y.push(t)}x=v.lastIndex;var w=u[0][1];switch(w){case"s":case"o":if(s<arguments.length){y.push(arguments[s+++1])}else{y.push("<?>")}break;case"%":y.push("%");break;default:y.push("<%"+w+"?>")}}y.push(z.substring(x));return y.join("")};l.formatNumber=function(x,v,C,w){var u=x,B=isNaN(v=Math.abs(v))?2:v;var A=C===undefined?",":C;var D=w===undefined?".":w,E=u<0?"-":"";var z=parseInt((u=Math.abs(+u||0).toFixed(B)),10)+"";var y=(z.length>3)?z.length%3:0;return E+(y?z.substr(0,y)+D:"")+z.substr(y).replace(/(\d{3})(?=\d)/g,"$1"+D)+(B?A+Math.abs(u-z).toFixed(B).slice(2):"")};l.formatSize=function(s){if(s>=1073741824){s=l.formatNumber(s/1073741824,2,".","")+" GiB"}else{if(s>=1048576){s=l.formatNumber(s/1048576,2,".","")+" MiB"}else{if(s>=1024){s=l.formatNumber(s/1024,0)+" KiB"}else{s=l.formatNumber(s,0)+" bytes"}}}return s};l.bytesFromIP=function(s){if(s.indexOf(".")!==-1){return l.bytesFromIPv4(s)}if(s.indexOf(":")!==-1){return l.bytesFromIPv6(s)}return null};l.bytesFromIPv4=function(v){v=v.split(".");if(v.length!==4){return null}var s=l.createBuffer();for(var u=0;u<v.length;++u){var t=parseInt(v[u],10);if(isNaN(t)){return null}s.putByte(t)}return s.getBytes()};l.bytesFromIPv6=function(x){var w=0;x=x.split(":").filter(function(y){if(y.length===0){++w}return true});var u=(8-x.length+w)*2;var s=l.createBuffer();for(var v=0;v<8;++v){if(!x[v]||x[v].length===0){s.fillWithByte(0,u);u=0;continue}var t=l.hexToBytes(x[v]);if(t.length<2){s.putByte(0)}s.putBytes(t)}return s.getBytes()};l.bytesToIP=function(s){if(s.length===4){return l.bytesToIPv4(s)}if(s.length===16){return l.bytesToIPv6(s)}return null};l.bytesToIPv4=function(s){if(s.length!==4){return null}var u=[];for(var t=0;t<s.length;++t){u.push(s.charCodeAt(t))}return u.join(".")};l.bytesToIPv6=function(A){if(A.length!==16){return null}var u=[];var z=[];var v=0;for(var t=0;t<A.length;t+=2){var s=l.bytesToHex(A[t]+A[t+1]);while(s[0]==="0"&&s!=="0"){s=s.substr(1)}if(s==="0"){var y=z[z.length-1];var w=u.length;if(!y||w!==y.end+1){z.push({start:w,end:w})}else{y.end=w;if((y.end-y.start)>(z[v].end-z[v].start)){v=z.length-1}}}u.push(s)}if(z.length>0){var x=z[v];if(x.end-x.start>0){u.splice(x.start,x.end-x.start+1,"");if(x.start===0){u.unshift("")}if(x.end===7){u.push("")}}}return u.join(":")}/*}var b="util";if(typeof define!=="function"){if(typeof module==="object"&&module.exports){var d=true;define=function(h,g){g(require,module)}}else{if(typeof forge==="undefined"){forge={}}return c(forge)}}var f;var a=function(g,h){h.exports=function(k){var l=f.map(function(i){return g(i)}).concat(c);k=k||{};k.defined=k.defined||{};if(k.defined[b]){return k[b]}k.defined[b]=true;for(var j=0;j<l.length;++j){l[j](k)}return k[b]}};var e=define;define=function(h,g){f=(typeof h==="string")?g.slice(2):h.slice(2);if(d){delete define;return e.apply(null,Array.prototype.slice.call(arguments,0))}define=e;return define.apply(null,Array.prototype.slice.call(arguments,0))};define(["require","module"],function(){a.apply(null,Array.prototype.slice.call(arguments,0))})*/})();

			if (!forge_buffers) {

				forge.util.ByteBuffer = function() {};

				forge.util.createBuffer = function(input, encoding) {
					var a=new forge.util.ByteBuffer();
					if (input) {
						a.data=new Buffer(input,encoding||'binary');
						a.length_=a.data.length;
					} else {
						a.data=new Buffer(buffer_size);
						a.length_=0;
					};
					a.read=0;
					return a;
				};

				forge.util.ByteBuffer.prototype.length = function() {
					return this.length_-this.read;
				};

				forge.util.ByteBuffer.prototype.isEmpty = function() {
					return (this.length_-this.read)===0;
				};

				forge.util.ByteBuffer.prototype.putByte = function(b) { //b charcode
					if (this.data.length>=this.length_+1) {
						this.data.writeUInt(b,this.length_,1);
					} else {
						this.data=this.length_?([this.data.slice(0,this.length_),new Uint8Array([b])].concatBuffers()):(new Uint8Array([b]));
					};
					this.length_ +=1;
				};

				forge.util.ByteBuffer.prototype.getByte = function() { //return charcode
					return this.data[this.read++];
				};

				forge.util.ByteBuffer.prototype.at=function(i) {
					return this.data[this.read+i];
				};

				forge.util.ByteBuffer.prototype.last = function() {
					return this.data[this.length_-1];
				};

				forge.util.ByteBuffer.prototype.fillWithByte=function(b,n) {//b charcode
					if (this.data.length>=this.length_+n) {
						var o=this.length_;
						for (var i=0;i<n;i++) {
							this.data[o+i]=b;
						};
					} else {
						var arr=[];
						for (var i=0;i<n;i++) {
							arr.push(b);
						};
						this.data=this.length_?([this.data.slice(0,this.length_),new Uint8Array(arr)].concatBuffers()):(new Uint8Array(arr));
					};
					this.length_ +=n;
				};

				forge.util.ByteBuffer.prototype.putBytes = function(bytes) { //bytes string or Buffer (from process)
					var a;
					if (typeof(bytes)==='string') {
						a=new Buffer(bytes,'binary');
					} else {
						a=bytes;
					};
					var l=a.length;
					if (this.data.length>=this.length_+l) {
						this.data.set(a,this.length_);
					} else {
						this.data=this.length_?([this.data.slice(0,this.length_),a].concatBuffers()):a;
					};
					this.length_+=l;
				};

				forge.util.ByteBuffer.prototype.getBytes = function(count) { //return string
					var rval;
					if(count) {
						count=Math.min(this.length(),count);
						rval=this.data.slice(this.read,this.read+count).toString('binary');
						this.read +=count;
					} else if(count===0) {
						rval = '';
					} else {
						rval=this.data.slice(this.read,this.length_).toString('binary');
						this.clear();
					};
					return rval;
				};

				forge.util.ByteBuffer.prototype.putBuffer = function(buffer) {
					if (this.data.length>=this.length_+buffer.length_) {
						if (buffer.length_) {
							this.data.set(buffer.data.slice(0,buffer.length_),this.length_);
						};
					} else {
						this.data=this.length_?([this.data.slice(0,this.length_),buffer.data.slice(0,buffer.length_)].concatBuffers()):(buffer.data.slice(0,buffer.length_));
					};
					this.length_+=buffer.length_;
					buffer.clear();
				};

				forge.util.ByteBuffer.prototype.bytes = function(count) {
					if (!count) {
						return (this.data.slice(this.read,this.length_)).toString('binary');
					} else {
						return this.data.slice(this.read,this.read+count).toString('binary');
					};
				};

				forge.util.ByteBuffer.prototype.putInt16 = function(i) {
					if (this.data.length>=this.length_+2) {
						this.data.writeUInt(i,this.length_,2);
					} else {
						this.data=this.length_?([this.data.slice(0,this.length_),(new Buffer(2)).writeUInt(i)].concatBuffers()):((new Buffer(2)).writeUInt(i));
					};
					this.length_ +=2;
				};

				forge.util.ByteBuffer.prototype.putInt24 = function(i) {
					if (this.data.length>=this.length_+3) {
						this.data.writeUInt(i,this.length_,3);
					} else {
						this.data=this.length_?([this.data.slice(0,this.length_),(new Buffer(3)).writeUInt(i)].concatBuffers()):((new Buffer(3)).writeUInt(i));
					};
					this.length_ +=3;
				};

				forge.util.ByteBuffer.prototype.putInt32 = function(i) {
					if (this.data.length>=this.length_+4) {
						this.data.writeUInt(i,this.length_,4);
					} else {
						this.data=this.length_?([this.data.slice(0,this.length_),(new Buffer(4)).writeUInt(i)].concatBuffers()):((new Buffer(4)).writeUInt(i));
					};
					this.length_ +=4;
				};

				forge.util.ByteBuffer.prototype.putInt32Le = function(i) {
					if (this.data.length>=this.length_+4) {
						this.data.writeUIntLE(i,this.length_,4);
					} else {
						this.data=this.length_?([this.data.slice(0,this.length_),(new Buffer(4)).writeUIntLE(i)].concatBuffers()):((new Buffer(4)).writeUIntLE(i));
					};
					this.length_ +=4;
				};

				forge.util.ByteBuffer.prototype.putInt = function(i,n) {
					n=n/8;
					if (this.data.length>=this.length_+n) {
						this.data.writeUInt(i,this.length_,n);
					} else {
						this.data=this.length_?([this.data.slice(0,this.length_),(new Buffer(n)).writeUInt(i)].concatBuffers()):((new Buffer(n)).writeUInt(i));
					};
					this.length_ +=n;
				};

				forge.util.ByteBuffer.prototype.getInt16 = function() {
					var a=this.data.readUInt(this.read,2);
					this.read += 2;
					return a;
				};

				forge.util.ByteBuffer.prototype.getInt24 = function() {
					var a=this.data.readUInt(this.read,3);
					this.read += 3;
					return a;
				};

				forge.util.ByteBuffer.prototype.getInt32 = function() {
					var a=this.data.readUInt(this.read,4);
					this.read += 4;
					return a;
				};

				//01000041 -->41000001
				forge.util.ByteBuffer.prototype.getInt32Le = function() {
					var a=this.data.readUIntLE(this.read,4);
					this.read += 4;
					return a;
				};

				forge.util.ByteBuffer.prototype.getInt = function(n) {
					n=n/8;
					var a=this.data.readUInt(this.read,n);
					this.read +=n;
					return a;
				};

				forge.util.ByteBuffer.prototype.compact = function() {
				//TODO check this - strange function
					if (this.length()) {
						//if(this.read < this.length_) {
							var a=this.data.slice(this.read,this.length_);
							this.data=new Buffer(Math.max(buffer_size,a.length));
							this.data.set(a);
							this.length_=a.length;
							this.read=0;
						//};
					} else {
						this.clear();
					};
				};

				forge.util.ByteBuffer.prototype.clear = function() {
					this.data=new Buffer(buffer_size);
					this.length_=0;
					this.read=0;
				};

				forge.util.ByteBuffer.prototype.truncate = function(count) {
					//console.log('truncate '+count+' '+this.length()+' '+this.data.length+' '+this.read);
					//console.log(this.data.toString('hex'));
					var len=Math.max(0,this.length()-count);
					var a=this.data.slice(this.read,len);
					this.data=new Buffer(buffer_size);
					if (this.data.length>a.length) {
						this.data.set(a);
					} else {
						this.data=a;
					};
					this.length_=a.length;
					this.read=0;
					//console.log(this.data.toString('hex'));
				};

				forge.util.ByteBuffer.prototype.toHex = function() {
					return this.data.slice(0,this.length_).toString('hex');
				};

				forge.util.ByteBuffer.prototype.toString = function() {
					return (new Buffer(this.data.slice(0,this.length_).toString('binary'),'utf8')).toString('utf8');
				};

			};

			/* end Buffer */

			//sha1.js (forge)

			/**
			 * Secure Hash Algorithm with 160-bit digest (SHA-1) implementation.
			 *
			 * This implementation is currently limited to message lengths (in bytes) that
			 * are up to 32-bits in size.
			 *
			 * @author Dave Longley
			 *
			 * Copyright (c) 2010-2012 Digital Bazaar, Inc.
			 */
			(function(){var e=forge.sha1=forge.sha1||{};forge.md=forge.md||{};forge.md.algorithms=forge.md.algorithms||{};forge.md.sha1=forge.md.algorithms.sha1=e;var c=null;var b=false;var d=function(){c=String.fromCharCode(128);c+=forge.util.fillString(String.fromCharCode(0),64);b=true};var a=function(r,p,u){var q,o,n,m,l,k,j,g;var h=u.length();while(h>=64){o=r.h0;n=r.h1;m=r.h2;l=r.h3;k=r.h4;for(g=0;g<16;++g){q=u.getInt32();p[g]=q;j=l^(n&(m^l));q=((o<<5)|(o>>>27))+j+k+1518500249+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}for(;g<20;++g){q=(p[g-3]^p[g-8]^p[g-14]^p[g-16]);q=(q<<1)|(q>>>31);p[g]=q;j=l^(n&(m^l));q=((o<<5)|(o>>>27))+j+k+1518500249+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}for(;g<32;++g){q=(p[g-3]^p[g-8]^p[g-14]^p[g-16]);q=(q<<1)|(q>>>31);p[g]=q;j=n^m^l;q=((o<<5)|(o>>>27))+j+k+1859775393+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}for(;g<40;++g){q=(p[g-6]^p[g-16]^p[g-28]^p[g-32]);q=(q<<2)|(q>>>30);p[g]=q;j=n^m^l;q=((o<<5)|(o>>>27))+j+k+1859775393+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}for(;g<60;++g){q=(p[g-6]^p[g-16]^p[g-28]^p[g-32]);q=(q<<2)|(q>>>30);p[g]=q;j=(n&m)|(l&(n^m));q=((o<<5)|(o>>>27))+j+k+2400959708+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}for(;g<80;++g){q=(p[g-6]^p[g-16]^p[g-28]^p[g-32]);q=(q<<2)|(q>>>30);p[g]=q;j=n^m^l;q=((o<<5)|(o>>>27))+j+k+3395469782+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}r.h0+=o;r.h1+=n;r.h2+=m;r.h3+=l;r.h4+=k;h-=64}};e.create=function(){if(!b){d()}var f=null;var i=forge.util.createBuffer();var g=new Array(80);var h={algorithm:"sha1",blockLength:64,digestLength:20,messageLength:0};h.start=function(){h.messageLength=0;i=forge.util.createBuffer();f={h0:1732584193,h1:4023233417,h2:2562383102,h3:271733878,h4:3285377520};return h};h.start();h.update=function(k,j){if(j==="utf8"){k=forge.util.encodeUtf8(k)}h.messageLength+=k.length;i.putBytes(k);a(f,g,i);if(i.read>2048||i.length()===0){i.compact()};return h};h.digest=function(){var j=h.messageLength;var m=forge.util.createBuffer();m.putBytes(i.bytes());m.putBytes(c.substr(0,64-((j+8)%64)));m.putInt32((j>>>29)&255);m.putInt32((j<<3)&4294967295);var k={h0:f.h0,h1:f.h1,h2:f.h2,h3:f.h3,h4:f.h4};a(k,g,m);var l=forge.util.createBuffer();l.putInt32(k.h0);l.putInt32(k.h1);l.putInt32(k.h2);l.putInt32(k.h3);l.putInt32(k.h4);return l};h.digest2=function(){var j=h.messageLength;var o=forge.util.createBuffer();var k=forge.util.createBuffer(i.data.slice(i.read));var m=g.slice(0);o.putBytes(i.bytes());o.putBytes(c.substr(0,64-((j+8)%64)));o.putInt32((j>>>29)&255);o.putInt32((j<<3)&4294967295);var l={h0:f.h0,h1:f.h1,h2:f.h2,h3:f.h3,h4:f.h4};a(l,g,o);var n=forge.util.createBuffer();n.putInt32(l.h0);n.putInt32(l.h1);n.putInt32(l.h2);n.putInt32(l.h3);n.putInt32(l.h4);i=k;g=m;return n};return h};e.createhash=function(){var g=e.create();var f=g.update;g.update=function(h){return f(h.toString("binary"))};g.digest=function(){return g.digest2().toHex()};return g}})();

			//hmac.js (forge)

			/**
			 * Hash-based Message Authentication Code implementation. Requires a message
			 * digest object that can be obtained, for example, from forge.md.sha1 or
			 * forge.md.md5.
			 *
			 * @author Dave Longley
			 *
			 * Copyright (c) 2010-2012 Digital Bazaar, Inc. All rights reserved.
			 */
			(function(){var a=forge;a.hmac={};/*if(typeof(window)!=="undefined"){var a=window.forge=window.forge||{};a.hmac={}}else{if(typeof(module)!=="undefined"&&module.exports){var a={md:require("./md"),util:require("./util")};module.exports=a.hmac={}}}*/;var b=a.hmac;b.create=function(){var g=null;var e=null;var f=null;var c=null;var d={};d.start=function(m,k){if(m!==null){if(m.constructor==String){m=m.toLowerCase();if(m in a.md.algorithms){e=a.md.algorithms[m].create()}else{throw'Unknown hash algorithm "'+m+'"'}}else{e=m}}if(k===null){k=g}else{if(k.constructor==String){k=a.util.createBuffer(k)}else{if(k.constructor==Array){var j=k;k=a.util.createBuffer();for(var h=0;h<j.length;++h){k.putByte(j[h])}}}var l=k.length();if(l>e.blockLength){e.start();e.update(k.bytes());k=e.digest()}f=a.util.createBuffer();c=a.util.createBuffer();l=k.length();for(var h=0;h<l;++h){var j=k.at(h);f.putByte(54^j);c.putByte(92^j)}if(l<e.blockLength){var j=e.blockLength-l;for(var h=0;h<j;++h){f.putByte(54);c.putByte(92)}}g=k;f=f.bytes();c=c.bytes()}e.start();e.update(f)};d.update=function(h){e.update(h)};d.getMac=function(){var h=e.digest().bytes();e.start();e.update(c);e.update(h);return e.digest()};d.digest=d.getMac;return d}})();


			//aes.js (forge)

			(function(){var j=false;var h=4;var f;var b;var d;var k;var g;var e=function(){j=true;d=[0,1,2,4,8,16,32,64,128,27,54];var x=new Array(256);for(var p=0;p<128;++p){x[p]=p<<1;x[p+128]=(p+128)<<1^283}f=new Array(256);b=new Array(256);k=new Array(4);g=new Array(4);for(var p=0;p<4;++p){k[p]=new Array(256);g[p]=new Array(256)}var s=0,o=0,v,t,q,w,l,u,r;for(var p=0;p<256;++p){w=o^(o<<1)^(o<<2)^(o<<3)^(o<<4);w=(w>>8)^(w&255)^99;f[s]=w;b[w]=s;l=x[w];v=x[s];t=x[v];q=x[t];u=(l<<24)^(w<<16)^(w<<8)^(w^l);r=(v^t^q)<<24^(s^q)<<16^(s^t^q)<<8^(s^v^q);for(var m=0;m<4;++m){k[m][s]=u;g[m][w]=r;u=u<<24|u>>>8;r=r<<24|r>>>8}if(s===0){s=o=1}else{s=v^x[x[x[v^q]]];o^=x[x[o]]}}};var a=function(z,o){var x=z.slice(0);var B,m=1;var r=x.length;var p=r+6+1;var s=h*p;for(var u=r;u<s;++u){B=x[u-1];if(u%r===0){B=f[B>>>16&255]<<24^f[B>>>8&255]<<16^f[B&255]<<8^f[B>>>24]^(d[m]<<24);m++}else{if(r>6&&(u%r===4)){B=f[B>>>24]<<24^f[B>>>16&255]<<16^f[B>>>8&255]<<8^f[B&255]}}x[u]=x[u-r]^B}if(o){var t;var D=g[0];var C=g[1];var A=g[2];var y=g[3];var v=x.slice(0);var s=x.length;for(var u=0,l=s-h;u<s;u+=h,l-=h){if(u===0||u===(s-h)){v[u]=x[l];v[u+1]=x[l+3];v[u+2]=x[l+2];v[u+3]=x[l+1]}else{for(var q=0;q<h;++q){t=x[l+q];v[u+(3&-q)]=D[f[t>>>24]]^C[f[t>>>16&255]]^A[f[t>>>8&255]]^y[f[t&255]]}}}x=v}return x};var c=function(u,v,t,o){var q=u.length/4-1;var p,n,m,l,s;if(o){p=g[0];n=g[1];m=g[2];l=g[3];s=b}else{p=k[0];n=k[1];m=k[2];l=k[3];s=f}var D,C,A,z,E,r,x;D=v[0]^u[0];C=v[o?3:1]^u[1];A=v[2]^u[2];z=v[o?1:3]^u[3];var y=3;for(var B=1;B<q;++B){E=p[D>>>24]^n[C>>>16&255]^m[A>>>8&255]^l[z&255]^u[++y];r=p[C>>>24]^n[A>>>16&255]^m[z>>>8&255]^l[D&255]^u[++y];x=p[A>>>24]^n[z>>>16&255]^m[D>>>8&255]^l[C&255]^u[++y];z=p[z>>>24]^n[D>>>16&255]^m[C>>>8&255]^l[A&255]^u[++y];D=E;C=r;A=x}t[0]=(s[D>>>24]<<24)^(s[C>>>16&255]<<16)^(s[A>>>8&255]<<8)^(s[z&255])^u[++y];t[o?3:1]=(s[C>>>24]<<24)^(s[A>>>16&255]<<16)^(s[z>>>8&255]<<8)^(s[D&255])^u[++y];t[2]=(s[A>>>24]<<24)^(s[z>>>16&255]<<16)^(s[D>>>8&255]<<8)^(s[C&255])^u[++y];t[o?1:3]=(s[z>>>24]<<24)^(s[D>>>16&255]<<16)^(s[C>>>8&255]<<8)^(s[A&255])^u[++y]};var i=function(H,r,u,o,v){var m=null;if(!j){e()}v=(v||"CBC").toUpperCase();if(typeof H==="string"&&(H.length===16||H.length===24||H.length===32)){H=forge.util.createBuffer(H)}else{if(forge.util.isArray(H)&&(H.length===16||H.length===24||H.length===32)){var B=H;var H=forge.util.createBuffer();for(var x=0;x<B.length;++x){H.putByte(B[x])}}}if(!forge.util.isArray(H)){var B=H;H=[];var z=B.length();if(z===16||z===24||z===32){z=z>>>2;for(var x=0;x<z;++x){H.push(B.getInt32())}}}if(!forge.util.isArray(H)||!(H.length===4||H.length===6||H.length===8)){return m}var I=(["CFB","OFB","CTR"].indexOf(v)!==-1);var p=(v==="CBC");var A=a(H,o&&!I);var w=h<<2;var n;var y;var s;var D;var q;var l;var F;m={output:null};if(v==="CBC"){F=E}else{if(v==="CFB"){F=G}else{if(v==="OFB"){F=t}else{if(v==="CTR"){F=C}else{throw {message:'Unsupported block cipher mode of operation: "'+v+'"'}}}}}m.update=function(J){if(!l){n.putBuffer(J)}while(n.length()>=w||(n.length()>0&&l)){F()}};m.update2=function(J){if(J){if(J.length()){n.data=n.data.substr(n.read);n.read=0;n.putBuffer(J)}}while(n.length()>=w){F()}if(m.overflow){y.getBytes(m.overflow)}var M=n.length()%w;if(M){var K=forge.util.createBuffer(n.data.slice(n.read));var L=s.slice(0);while(n.length()>0){F()}n=K;s=L;y.truncate(w-M)} else /*modif Aym */{n.data='';n.read=0};m.overflow=M};m.finish=function(N){var M=true;var O=n.length()%w;if(!o){if(N){M=N(w,n,o)}else{if(p){var L=(n.length()===w)?w:(w-n.length());n.fillWithByte(L,L)}}}if(M){l=true;m.update()}if(o){if(p){M=(O===0)}if(M){if(N){M=N(w,y,o)}else{if(p){var J=y.length();var K=y.at(J-1);if(K>(h<<2)){M=false}else{y.truncate(K)}}}}}if(!p&&!N&&O>0){y.truncate(w-O)}return M};m.start=function(K,J){if(K===null){K=q.slice(0)}if(typeof K==="string"&&K.length===16){K=forge.util.createBuffer(K)}else{if(forge.util.isArray(K)&&K.length===16){var M=K;var K=forge.util.createBuffer();for(var L=0;L<16;++L){K.putByte(M[L])}}}if(!forge.util.isArray(K)){var M=K;K=new Array(4);K[0]=M.getInt32();K[1]=M.getInt32();K[2]=M.getInt32();K[3]=M.getInt32()}n=forge.util.createBuffer();y=J||forge.util.createBuffer();q=K.slice(0);s=new Array(h);D=new Array(h);l=false;m.output=y;if(["CFB","OFB","CTR"].indexOf(v)!==-1){for(var L=0;L<h;++L){s[L]=q[L]}q=null}};if(r!==null){m.start(r,u)}return m;function E(){if(o){for(var J=0;J<h;++J){s[J]=n.getInt32()}}else{for(var J=0;J<h;++J){s[J]=q[J]^n.getInt32()}}c(A,s,D,o);if(o){for(var J=0;J<h;++J){y.putInt32(q[J]^D[J])}q=s.slice(0)}else{for(var J=0;J<h;++J){y.putInt32(D[J])}q=D}}function G(){c(A,s,D,false);for(var K=0;K<h;++K){s[K]=n.getInt32()}for(var K=0;K<h;++K){var J=s[K]^D[K];if(!o){s[K]=J}y.putInt32(J)}}function t(){c(A,s,D,false);for(var J=0;J<h;++J){s[J]=n.getInt32()}for(var J=0;J<h;++J){y.putInt32(s[J]^D[J]);s[J]=D[J]}}function C(){c(A,s,D,false);for(var J=h-1;J>=0;--J){if(s[J]===4294967295){s[J]=0}else{++s[J];break}}for(var J=0;J<h;++J){y.putInt32(n.getInt32()^D[J])}}};forge.aes=forge.aes||{};forge.aes.startEncrypting=function(n,m,l,o){return i(n,m,l,false,o)};forge.aes.createEncryptionCipher=function(l,m){return i(l,null,null,false,m)};forge.aes.startDecrypting=function(n,m,l,o){return i(n,m,l,true,o)};forge.aes.createDecryptionCipher=function(l,m){return i(l,null,null,true,m)};forge.aes._expandKey=function(m,l){if(!j){e()}return a(m,l)};forge.aes._updateBlock=c;forge.aes.createcipheriv=function(r,o,n){var q=r.split("-")[2];var m=forge.util.createBuffer();o=forge.util.createBuffer(o.toString("binary"));n=forge.util.createBuffer(n.toString("binary"));var l=forge.aes.startEncrypting(o,n,m,q);var p=l.update2;l.update=function(t){var s;if(t){t=forge.util.createBuffer(t.toString("binary"))}else{t=forge.util.createBuffer()}p(t);s=m.toHex();m.data="";m.read=0;return s};return l}})();

			//asn1.js (forge)

			/**
			 * Javascript implementation of Abstract Syntax Notation Number One.
			 *
			 * @author Dave Longley
			 *
			 * Copyright (c) 2010-2012 Digital Bazaar, Inc.
			 *
			**/
			(function(){var b=forge;b.asn1={};/*if(typeof(window)!=="undefined"){var b=window.forge=window.forge||{};b.asn1={}}else{if(typeof(module)!=="undefined"&&module.exports){var b={util:require("./util"),pki:{oids:require("./oids")}};module.exports=b.asn1={}}}*/;var a=b.asn1;a.Class={UNIVERSAL:0,APPLICATION:64,CONTEXT_SPECIFIC:128,PRIVATE:192};a.Type={NONE:0,BOOLEAN:1,INTEGER:2,BITSTRING:3,OCTETSTRING:4,NULL:5,OID:6,ODESC:7,EXTERNAL:8,REAL:9,ENUMERATED:10,EMBEDDED:11,UTF8:12,ROID:13,SEQUENCE:16,SET:17,PRINTABLESTRING:19,IA5STRING:22,UTCTIME:23,GENERALIZEDTIME:24,BMPSTRING:30};a.create=function(h,e,g,f){return{tagClass:h,type:e,constructed:g,composed:g||(f.constructor==Array),value:f}};var c=function(e){var f=e.getByte();if(f==128){return undefined}var h;var g=f&128;if(!g){h=f}else{h=e.getInt((f&127)<<3)}return h};a.fromDer=function(t){if(t.constructor==String){t=b.util.createBuffer(t)}if(t.length()<2){throw {message:"Too few bytes to parse DER.",bytes:t.length()}}var r=t.getByte();var m=(r&192);var q=r&31;var g=c(t);if(t.length()<g){throw {message:"Too few bytes to read ASN.1 value.",detail:t.length()+" < "+g}}var s;var k=((r&32)==32);var p=k;if(!p&&m===a.Class.UNIVERSAL&&q===a.Type.BITSTRING&&g>1){var f=t.read;var h=t.getByte();if(h===0){r=t.getByte();var j=(r&192);if(j===a.Class.UNIVERSAL||j===a.Class.CONTEXT_SPECIFIC){try{var n=c(t);p=(n===g-(t.read-f));if(p){++f;--g}}catch(o){}}}t.read=f}if(p){s=[];if(g===undefined){for(;;){if(t.bytes(2)===String.fromCharCode(0,0)){t.getBytes(2);break}s.push(a.fromDer(t))}}else{var e=t.length();while(g>0){s.push(a.fromDer(t));g-=e-t.length();e=t.length()}}}else{if(g===undefined){throw {message:"Non-constructed ASN.1 object of indefinite length."}}if(q===a.Type.BMPSTRING){s="";for(var l=0;l<g;l+=2){s+=String.fromCharCode(t.getInt16())}}else{s=t.getBytes(g)}};return a.create(m,q,k,s)};a.toDer=function(l){var g=b.util.createBuffer();var h=l.tagClass|l.type;var k=b.util.createBuffer();if(l.composed){if(l.constructed){h|=32}else{k.putByte(0)}for(var j=0;j<l.value.length;++j){if(l.value[j]!==undefined){k.putBuffer(a.toDer(l.value[j]))}}}else{k.putBytes(l.value)}g.putByte(h);if(k.length()<=127){g.putByte(k.length()&127)}else{var f=k.length();var e="";do{e+=String.fromCharCode(f&255);f=f>>>8}while(f>0);g.putByte(e.length|128);for(var j=e.length-1;j>=0;--j){g.putByte(e.charCodeAt(j))}}g.putBuffer(k);return g};a.oidToDer=function(g){var m=g.split(".");var o=b.util.createBuffer();o.putByte(40*parseInt(m[0],10)+parseInt(m[1],10));var l,e,k,j;for(var h=2;h<m.length;++h){l=true;e=[];k=parseInt(m[h],10);do{j=k&127;k=k>>>7;if(!l){j|=128}e.push(j);l=false}while(k>0);for(var f=e.length-1;f>=0;--f){o.putByte(e[f])}}return o};a.derToOid=function(f){var g;if(f.constructor==String){f=b.util.createBuffer(f)}var e=f.getByte();g=Math.floor(e/40)+"."+(e%40);var h=0;while(f.length()>0){e=f.getByte();h=h<<7;if(e&128){h+=e&127}else{g+="."+(h+e);h=0}}return g};a.utcTimeToDate=function(o){var g=new Date();var n=parseInt(o.substr(0,2),10);n=(n>=50)?1900+n:2000+n;var p=parseInt(o.substr(2,2),10)-1;var l=parseInt(o.substr(4,2),10);var h=parseInt(o.substr(6,2),10);var j=parseInt(o.substr(8,2),10);var q=0;if(o.length>11){var m=o.charAt(10);var i=10;if(m!=="+"&&m!=="-"){q=parseInt(o.substr(10,2),10);i+=2}}g.setUTCFullYear(n,p,l);g.setUTCHours(h,j,q,0);if(i){m=o.charAt(i);if(m==="+"||m==="-"){var e=parseInt(o.substr(i+1,2),10);var f=parseInt(o.substr(i+4,2),10);var k=e*60+f;k*=60000;if(m==="+"){g.setTime(+g-k)}else{g.setTime(+g+k)}}}return g};a.generalizedTimeToDate=function(p){var i=new Date();var j=parseInt(p.substr(0,4),10);var r=parseInt(p.substr(4,2),10)-1;var o=parseInt(p.substr(6,2),10);var k=parseInt(p.substr(8,2),10);var m=parseInt(p.substr(10,2),10);var s=parseInt(p.substr(12,2),10);var h=0;var n=0;var g=false;if(p.charAt(p.length-1)=="Z"){g=true}var l=p.length-5,q=p.charAt(l);if(q==="+"||q==="-"){var e=parseInt(p.substr(l+1,2),10);var f=parseInt(p.substr(l+4,2),10);n=e*60+f;n*=60000;if(q==="+"){n*=-1}g=true}if(p.charAt(14)=="."){h=parseFloat(p.substr(14),10)*1000}if(g){i.setUTCFullYear(j,r,o);i.setUTCHours(k,m,s,h);i.setTime(+i+n)}else{i.setFullYear(j,r,o);i.setHours(k,m,s,h)}return i};a.dateToUtcTime=function(e){var h="";var g=[];g.push((""+e.getUTCFullYear()).substr(2));g.push(""+(e.getUTCMonth()+1));g.push(""+e.getUTCDate());g.push(""+e.getUTCHours());g.push(""+e.getUTCMinutes());g.push(""+e.getUTCSeconds());for(var f=0;f<g.length;++f){if(g[f].length<2){h+="0"}h+=g[f]}h+="Z";return h};a.validate=function(l,f,e,m){var k=false;if((l.tagClass===f.tagClass||typeof(f.tagClass)==="undefined")&&(l.type===f.type||typeof(f.type)==="undefined")){if(l.constructed===f.constructed||typeof(f.constructed)==="undefined"){k=true;if(f.value&&f.value.constructor==Array){var g=0;for(var h=0;k&&h<f.value.length;++h){k=f.value[h].optional||false;if(l.value[g]){k=a.validate(l.value[g],f.value[h],e,m);if(k){++g}else{if(f.value[h].optional){k=true}}}if(!k&&m){m.push("["+f.name+'] Tag class "'+f.tagClass+'", type "'+f.type+'" expected value length "'+f.value.length+'", got "'+l.value.length+'"')}}}if(k&&e){if(f.capture){e[f.capture]=l.value}if(f.captureAsn1){e[f.captureAsn1]=l}}}else{if(m){m.push("["+f.name+'] Expected constructed "'+f.constructed+'", got "'+l.constructed+'"')}}}else{if(m){if(l.tagClass!==f.tagClass){m.push("["+f.name+'] Expected tag class "'+f.tagClass+'", got "'+l.tagClass+'"')}if(l.type!==f.type){m.push("["+f.name+'] Expected type "'+f.type+'", got "'+l.type+'"')}}}return k};var d=/[^\\u0000-\\u00ff]/;a.prettyPrint=function(k,l,f){var j="";l=l||0;f=f||2;if(l>0){j+="\n"}var e="";for(var g=0;g<l*f;++g){e+=" "}j+=e+"Tag: ";switch(k.tagClass){case a.Class.UNIVERSAL:j+="Universal:";break;case a.Class.APPLICATION:j+="Application:";break;case a.Class.CONTEXT_SPECIFIC:j+="Context-Specific:";break;case a.Class.PRIVATE:j+="Private:";break}if(k.tagClass===a.Class.UNIVERSAL){j+=k.type;switch(k.type){case a.Type.NONE:j+=" (None)";break;case a.Type.BOOLEAN:j+=" (Boolean)";break;case a.Type.BITSTRING:j+=" (Bit string)";break;case a.Type.INTEGER:j+=" (Integer)";break;case a.Type.OCTETSTRING:j+=" (Octet string)";break;case a.Type.NULL:j+=" (Null)";break;case a.Type.OID:j+=" (Object Identifier)";break;case a.Type.ODESC:j+=" (Object Descriptor)";break;case a.Type.EXTERNAL:j+=" (External or Instance of)";break;case a.Type.REAL:j+=" (Real)";break;case a.Type.ENUMERATED:j+=" (Enumerated)";break;case a.Type.EMBEDDED:j+=" (Embedded PDV)";break;case a.Type.UTF8:j+=" (UTF8)";break;case a.Type.ROID:j+=" (Relative Object Identifier)";break;case a.Type.SEQUENCE:j+=" (Sequence)";break;case a.Type.SET:j+=" (Set)";break;case a.Type.PRINTABLESTRING:j+=" (Printable String)";break;case a.Type.IA5String:j+=" (IA5String (ASCII))";break;case a.Type.UTCTIME:j+=" (UTC time)";break;case a.Type.GENERALIZEDTIME:j+=" (Generalized time)";break;case a.Type.BMPSTRING:j+=" (BMP String)";break}}else{j+=k.type}j+="\n";j+=e+"Constructed: "+k.constructed+"\n";if(k.composed){j+=e+"Sub values: "+k.value.length;for(var g=0;g<k.value.length;++g){j+=a.prettyPrint(k.value[g],l+1,f);if((g+1)<k.value.length){j+=","}}}else{j+=e+"Value: ";if(k.type===a.Type.OID){var h=a.derToOid(k.value);j+=h;if(b.pki&&b.pki.oids){if(h in b.pki.oids){j+=" ("+b.pki.oids[h]+")"}}}else{if(d.test(k.value)){j+="0x"+b.util.createBuffer(k.value,"utf8").toHex()}else{if(k.value.length===0){j+="[null]"}else{j+=k.value}}}}return j}})();

			/*
			jsbn.js (forge and RSA)
			Tom Wu BSD License
			http://www-cs-students.stanford.edu/~tjw/jsbn/
			*/

			var dbits;var canary=244837814094590;var j_lm=((canary&16777215)==15715070);function BigInteger(e,d,f){if(e!=null){if("number"==typeof e){this.fromNumber(e,d,f)}else{if(d==null&&"string"!=typeof e){this.fromString(e,256)}else{this.fromString(e,d)}}}}function nbi(){return new BigInteger(null)}function am1(f,a,b,e,h,g){while(--g>=0){var d=a*this[f++]+b[e]+h;h=Math.floor(d/67108864);b[e++]=d&67108863}return h}function am2(f,q,r,e,o,a){var k=q&32767,p=q>>15;while(--a>=0){var d=this[f]&32767;var g=this[f++]>>15;var b=p*d+g*k;d=k*d+((b&32767)<<15)+r[e]+(o&1073741823);o=(d>>>30)+(b>>>15)+p*g+(o>>>30);r[e++]=d&1073741823}return o}function am3(f,q,r,e,o,a){var k=q&16383,p=q>>14;while(--a>=0){var d=this[f]&16383;var g=this[f++]>>14;var b=p*d+g*k;d=k*d+((b&16383)<<14)+r[e]+o;o=(d>>28)+(b>>14)+p*g;r[e++]=d&268435455}return o}if(typeof(navigator)==="undefined"){BigInteger.prototype.am=am3;dbits=28}else{if(j_lm&&(navigator.appName=="Microsoft Internet Explorer")){BigInteger.prototype.am=am2;dbits=30}else{if(j_lm&&(navigator.appName!="Netscape")){BigInteger.prototype.am=am1;dbits=26}else{BigInteger.prototype.am=am3;dbits=28}}}BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=((1<<dbits)-1);BigInteger.prototype.DV=(1<<dbits);var BI_FP=52;BigInteger.prototype.FV=Math.pow(2,BI_FP);BigInteger.prototype.F1=BI_FP-dbits;BigInteger.prototype.F2=2*dbits-BI_FP;var BI_RM="0123456789abcdefghijklmnopqrstuvwxyz";var BI_RC=new Array();var rr,vv;rr="0".charCodeAt(0);for(vv=0;vv<=9;++vv){BI_RC[rr++]=vv}rr="a".charCodeAt(0);for(vv=10;vv<36;++vv){BI_RC[rr++]=vv}rr="A".charCodeAt(0);for(vv=10;vv<36;++vv){BI_RC[rr++]=vv}function int2char(a){return BI_RM.charAt(a)}function intAt(b,a){var d=BI_RC[b.charCodeAt(a)];return(d==null)?-1:d}function bnpCopyTo(b){for(var a=this.t-1;a>=0;--a){b[a]=this[a]}b.t=this.t;b.s=this.s}function bnpFromInt(a){this.t=1;this.s=(a<0)?-1:0;if(a>0){this[0]=a}else{if(a<-1){this[0]=a+DV}else{this.t=0}}}function nbv(a){var b=nbi();b.fromInt(a);return b}function bnpFromString(h,c){var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==256){e=8}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{this.fromRadix(h,c);return}}}}}}this.t=0;this.s=0;var g=h.length,d=false,f=0;while(--g>=0){var a=(e==8)?h[g]&255:intAt(h,g);if(a<0){if(h.charAt(g)=="-"){d=true}continue}d=false;if(f==0){this[this.t++]=a}else{if(f+e>this.DB){this[this.t-1]|=(a&((1<<(this.DB-f))-1))<<f;this[this.t++]=(a>>(this.DB-f))}else{this[this.t-1]|=a<<f}}f+=e;if(f>=this.DB){f-=this.DB}}if(e==8&&(h[0]&128)!=0){this.s=-1;if(f>0){this[this.t-1]|=((1<<(this.DB-f))-1)<<f}}this.clamp();if(d){BigInteger.ZERO.subTo(this,this)}}function bnpClamp(){var a=this.s&this.DM;while(this.t>0&&this[this.t-1]==a){--this.t}}function bnToString(c){if(this.s<0){return"-"+this.negate().toString(c)}var e;if(c==16){e=4}else{if(c==8){e=3}else{if(c==2){e=1}else{if(c==32){e=5}else{if(c==4){e=2}else{return this.toRadix(c)}}}}}var g=(1<<e)-1,l,a=false,h="",f=this.t;var j=this.DB-(f*this.DB)%e;if(f-->0){if(j<this.DB&&(l=this[f]>>j)>0){a=true;h=int2char(l)}while(f>=0){if(j<e){l=(this[f]&((1<<j)-1))<<(e-j);l|=this[--f]>>(j+=this.DB-e)}else{l=(this[f]>>(j-=e))&g;if(j<=0){j+=this.DB;--f}}if(l>0){a=true}if(a){h+=int2char(l)}}}return a?h:"0"}function bnNegate(){var a=nbi();BigInteger.ZERO.subTo(this,a);return a}function bnAbs(){return(this.s<0)?this.negate():this}function bnCompareTo(b){var d=this.s-b.s;if(d!=0){return d}var c=this.t;d=c-b.t;if(d!=0){return d}while(--c>=0){if((d=this[c]-b[c])!=0){return d}}return 0}function nbits(a){var c=1,b;if((b=a>>>16)!=0){a=b;c+=16}if((b=a>>8)!=0){a=b;c+=8}if((b=a>>4)!=0){a=b;c+=4}if((b=a>>2)!=0){a=b;c+=2}if((b=a>>1)!=0){a=b;c+=1}return c}function bnBitLength(){if(this.t<=0){return 0}return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM))}function bnpDLShiftTo(c,b){var a;for(a=this.t-1;a>=0;--a){b[a+c]=this[a]}for(a=c-1;a>=0;--a){b[a]=0}b.t=this.t+c;b.s=this.s}function bnpDRShiftTo(c,b){for(var a=c;a<this.t;++a){b[a-c]=this[a]}b.t=Math.max(this.t-c,0);b.s=this.s}function bnpLShiftTo(j,e){var b=j%this.DB;var a=this.DB-b;var g=(1<<a)-1;var f=Math.floor(j/this.DB),h=(this.s<<b)&this.DM,d;for(d=this.t-1;d>=0;--d){e[d+f+1]=(this[d]>>a)|h;h=(this[d]&g)<<b}for(d=f-1;d>=0;--d){e[d]=0}e[f]=h;e.t=this.t+f+1;e.s=this.s;e.clamp()}function bnpRShiftTo(g,d){d.s=this.s;var e=Math.floor(g/this.DB);if(e>=this.t){d.t=0;return}var b=g%this.DB;var a=this.DB-b;var f=(1<<b)-1;d[0]=this[e]>>b;for(var c=e+1;c<this.t;++c){d[c-e-1]|=(this[c]&f)<<a;d[c-e]=this[c]>>b}if(b>0){d[this.t-e-1]|=(this.s&f)<<a}d.t=this.t-e;d.clamp()}function bnpSubTo(d,f){var e=0,g=0,b=Math.min(d.t,this.t);while(e<b){g+=this[e]-d[e];f[e++]=g&this.DM;g>>=this.DB}if(d.t<this.t){g-=d.s;while(e<this.t){g+=this[e];f[e++]=g&this.DM;g>>=this.DB}g+=this.s}else{g+=this.s;while(e<d.t){g-=d[e];f[e++]=g&this.DM;g>>=this.DB}g-=d.s}f.s=(g<0)?-1:0;if(g<-1){f[e++]=this.DV+g}else{if(g>0){f[e++]=g}}f.t=e;f.clamp()}function bnpMultiplyTo(c,e){var b=this.abs(),f=c.abs();var d=b.t;e.t=d+f.t;while(--d>=0){e[d]=0}for(d=0;d<f.t;++d){e[d+b.t]=b.am(0,f[d],e,d,0,b.t)}e.s=0;e.clamp();if(this.s!=c.s){BigInteger.ZERO.subTo(e,e)}}function bnpSquareTo(d){var a=this.abs();var b=d.t=2*a.t;while(--b>=0){d[b]=0}for(b=0;b<a.t-1;++b){var e=a.am(b,a[b],d,2*b,0,1);if((d[b+a.t]+=a.am(b+1,2*a[b],d,2*b+1,e,a.t-b-1))>=a.DV){d[b+a.t]-=a.DV;d[b+a.t+1]=1}}if(d.t>0){d[d.t-1]+=a.am(b,a[b],d,2*b,0,1)}d.s=0;d.clamp()}function bnpDivRemTo(n,h,g){var w=n.abs();if(w.t<=0){return}var k=this.abs();if(k.t<w.t){if(h!=null){h.fromInt(0)}if(g!=null){this.copyTo(g)}return}if(g==null){g=nbi()}var d=nbi(),a=this.s,l=n.s;var v=this.DB-nbits(w[w.t-1]);if(v>0){w.lShiftTo(v,d);k.lShiftTo(v,g)}else{w.copyTo(d);k.copyTo(g)}var p=d.t;var b=d[p-1];if(b==0){return}var o=b*(1<<this.F1)+((p>1)?d[p-2]>>this.F2:0);var A=this.FV/o,z=(1<<this.F1)/o,x=1<<this.F2;var u=g.t,s=u-p,f=(h==null)?nbi():h;d.dlShiftTo(s,f);if(g.compareTo(f)>=0){g[g.t++]=1;g.subTo(f,g)}BigInteger.ONE.dlShiftTo(p,f);f.subTo(d,d);while(d.t<p){d[d.t++]=0}while(--s>=0){var c=(g[--u]==b)?this.DM:Math.floor(g[u]*A+(g[u-1]+x)*z);if((g[u]+=d.am(0,c,g,s,0,p))<c){d.dlShiftTo(s,f);g.subTo(f,g);while(g[u]<--c){g.subTo(f,g)}}}if(h!=null){g.drShiftTo(p,h);if(a!=l){BigInteger.ZERO.subTo(h,h)}}g.t=p;g.clamp();if(v>0){g.rShiftTo(v,g)}if(a<0){BigInteger.ZERO.subTo(g,g)}}function bnMod(b){var c=nbi();this.abs().divRemTo(b,null,c);if(this.s<0&&c.compareTo(BigInteger.ZERO)>0){b.subTo(c,c)}return c}function Classic(a){this.m=a}function cConvert(a){if(a.s<0||a.compareTo(this.m)>=0){return a.mod(this.m)}else{return a}}function cRevert(a){return a}function cReduce(a){a.divRemTo(this.m,null,a)}function cMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}function cSqrTo(a,b){a.squareTo(b);this.reduce(b)}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1){return 0}var a=this[0];if((a&1)==0){return 0}var b=a&3;b=(b*(2-(a&15)*b))&15;b=(b*(2-(a&255)*b))&255;b=(b*(2-(((a&65535)*b)&65535)))&65535;b=(b*(2-a*b%this.DV))%this.DV;return(b>0)?this.DV-b:-b}function Montgomery(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&32767;this.mph=this.mp>>15;this.um=(1<<(a.DB-15))-1;this.mt2=2*a.t}function montConvert(a){var b=nbi();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);if(a.s<0&&b.compareTo(BigInteger.ZERO)>0){this.m.subTo(b,b)}return b}function montRevert(a){var b=nbi();a.copyTo(b);this.reduce(b);return b}function montReduce(a){while(a.t<=this.mt2){a[a.t++]=0}for(var c=0;c<this.m.t;++c){var b=a[c]&32767;var d=(b*this.mpl+(((b*this.mph+(a[c]>>15)*this.mpl)&this.um)<<15))&a.DM;b=c+this.m.t;a[b]+=this.m.am(0,d,a,c,0,this.m.t);while(a[b]>=a.DV){a[b]-=a.DV;a[++b]++}}a.clamp();a.drShiftTo(this.m.t,a);if(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function montSqrTo(a,b){a.squareTo(b);this.reduce(b)}function montMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return((this.t>0)?(this[0]&1):this.s)==0}function bnpExp(h,j){if(h>4294967295||h<1){return BigInteger.ONE}var f=nbi(),a=nbi(),d=j.convert(this),c=nbits(h)-1;d.copyTo(f);while(--c>=0){j.sqrTo(f,a);if((h&(1<<c))>0){j.mulTo(a,d,f)}else{var b=f;f=a;a=b}}return j.revert(f)}function bnModPowInt(b,a){var c;if(b<256||a.isEven()){c=new Classic(a)}else{c=new Montgomery(a)}return this.exp(b,c)}BigInteger.prototype.copyTo=bnpCopyTo;BigInteger.prototype.fromInt=bnpFromInt;BigInteger.prototype.fromString=bnpFromString;BigInteger.prototype.clamp=bnpClamp;BigInteger.prototype.dlShiftTo=bnpDLShiftTo;BigInteger.prototype.drShiftTo=bnpDRShiftTo;BigInteger.prototype.lShiftTo=bnpLShiftTo;BigInteger.prototype.rShiftTo=bnpRShiftTo;BigInteger.prototype.subTo=bnpSubTo;BigInteger.prototype.multiplyTo=bnpMultiplyTo;BigInteger.prototype.squareTo=bnpSquareTo;BigInteger.prototype.divRemTo=bnpDivRemTo;BigInteger.prototype.invDigit=bnpInvDigit;BigInteger.prototype.isEven=bnpIsEven;BigInteger.prototype.exp=bnpExp;BigInteger.prototype.toString=bnToString;BigInteger.prototype.negate=bnNegate;BigInteger.prototype.abs=bnAbs;BigInteger.prototype.compareTo=bnCompareTo;BigInteger.prototype.bitLength=bnBitLength;BigInteger.prototype.mod=bnMod;BigInteger.prototype.modPowInt=bnModPowInt;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1);

			//prng4.js

			function Arcfour(){this.i=0;this.j=0;this.S=new Array()}function ARC4init(d){var c,a,b;for(c=0;c<256;++c){this.S[c]=c}a=0;for(c=0;c<256;++c){a=(a+this.S[c]+d[c%d.length])&255;b=this.S[c];this.S[c]=this.S[a];this.S[a]=b}this.i=0;this.j=0}function ARC4next(){var a;this.i=(this.i+1)&255;this.j=(this.j+this.S[this.i])&255;a=this.S[this.i];this.S[this.i]=this.S[this.j];this.S[this.j]=a;return this.S[(a+this.S[this.i])&255]}Arcfour.prototype.init=ARC4init;Arcfour.prototype.next=ARC4next;function prng_newstate(){return new Arcfour()}var rng_psize=256;

			//rng.js

			var navigator=navigator||window.navigator;

			var rng_state;var rng_pool;var rng_pptr;function rng_seed_int(a){rng_pool[rng_pptr++]^=a&255;rng_pool[rng_pptr++]^=(a>>8)&255;rng_pool[rng_pptr++]^=(a>>16)&255;rng_pool[rng_pptr++]^=(a>>24)&255;if(rng_pptr>=rng_psize){rng_pptr-=rng_psize}}function rng_seed_time(){rng_seed_int(new Date().getTime())}if(rng_pool==null){rng_pool=new Array();rng_pptr=0;var t;if(navigator.appName=="Netscape"&&navigator.appVersion<"5"&&window.crypto){var z=window.crypto.random(32);for(t=0;t<z.length;++t){rng_pool[rng_pptr++]=z.charCodeAt(t)&255}}while(rng_pptr<rng_psize){t=Math.floor(65536*Math.random());rng_pool[rng_pptr++]=t>>>8;rng_pool[rng_pptr++]=t&255}rng_pptr=0;rng_seed_time()}function rng_get_byte(){if(rng_state==null){rng_seed_time();rng_state=prng_newstate();rng_state.init(rng_pool);for(rng_pptr=0;rng_pptr<rng_pool.length;++rng_pptr){rng_pool[rng_pptr]=0}rng_pptr=0}return rng_state.next()}function rng_get_bytes(b){var a;for(a=0;a<b.length;++a){b[a]=rng_get_byte()}}function SecureRandom(){}SecureRandom.prototype.nextBytes=rng_get_bytes;

			//rsa.js

			function parseBigInt(b,a){return new BigInteger(b,a)}function linebrk(c,d){var a="";var b=0;while(b+d<c.length){a+=c.substring(b,b+d)+"\n";b+=d}return a+c.substring(b,c.length)}function byte2Hex(a){if(a<16){return"0"+a.toString(16)}else{return a.toString(16)}}function pkcs1pad2(f,h){if(h<f.length+11){alert("Message too long for RSA");return null}var g=new Array();var d=f.length-1;while(d>=1&&h>0){var c=f[d--];var e=f[d--];g[--h]=(intAt(e,0)<<4)+intAt(c,0)}g[--h]=0;var b=new SecureRandom();var a=new Array();while(h>2){a[0]=0;while(a[0]==0){b.nextBytes(a)}g[--h]=a[0]}g[--h]=2;g[--h]=0;return new BigInteger(g)}function RSAKey(){this.n=null;this.e=0;this.d=null;this.p=null;this.q=null;this.dmp1=null;this.dmq1=null;this.coeff=null}function RSASetPublic(b,a){if(b!=null&&a!=null&&b.length>0&&a.length>0){this.n=parseBigInt(b,16);this.e=parseInt(a,16)}else{alert("Invalid RSA public key")}}function RSADoPublic(a){return a.modPowInt(this.e,this.n)}function RSAEncrypt(d){var a=pkcs1pad2(d,(this.n.bitLength()+7)>>3);if(a==null){return null}var e=this.doPublic(a);if(e==null){return null}var b=e.toString(16);if((b.length&1)==0){return b}else{return"0"+b}}RSAKey.prototype.doPublic=RSADoPublic;RSAKey.prototype.setPublic=RSASetPublic;RSAKey.prototype.encrypt=RSAEncrypt;

			//jsbn2.js (used for forge only)

			function bnClone(){var a=nbi();this.copyTo(a);return a}function bnIntValue(){if(this.s<0){if(this.t==1){return this[0]-this.DV}else{if(this.t==0){return -1}}}else{if(this.t==1){return this[0]}else{if(this.t==0){return 0}}}return((this[1]&((1<<(32-this.DB))-1))<<this.DB)|this[0]}function bnByteValue(){return(this.t==0)?this.s:(this[0]<<24)>>24}function bnShortValue(){return(this.t==0)?this.s:(this[0]<<16)>>16}function bnpChunkSize(a){return Math.floor(Math.LN2*this.DB/Math.log(a))}function bnSigNum(){if(this.s<0){return -1}else{if(this.t<=0||(this.t==1&&this[0]<=0)){return 0}else{return 1}}}function bnpToRadix(c){if(c==null){c=10}if(this.signum()==0||c<2||c>36){return"0"}var f=this.chunkSize(c);var e=Math.pow(c,f);var i=nbv(e),j=nbi(),h=nbi(),g="";this.divRemTo(i,j,h);while(j.signum()>0){g=(e+h.intValue()).toString(c).substr(1)+g;j.divRemTo(i,j,h)}return h.intValue().toString(c)+g}function bnpFromRadix(m,h){this.fromInt(0);if(h==null){h=10}var f=this.chunkSize(h);var g=Math.pow(h,f),e=false,a=0,l=0;for(var c=0;c<m.length;++c){var k=intAt(m,c);if(k<0){if(m.charAt(c)=="-"&&this.signum()==0){e=true}continue}l=h*l+k;if(++a>=f){this.dMultiply(g);this.dAddOffset(l,0);a=0;l=0}}if(a>0){this.dMultiply(Math.pow(h,a));this.dAddOffset(l,0)}if(e){BigInteger.ZERO.subTo(this,this)}}function bnpFromNumber(f,e,h){if("number"==typeof e){if(f<2){this.fromInt(1)}else{this.fromNumber(f,h);if(!this.testBit(f-1)){this.bitwiseTo(BigInteger.ONE.shiftLeft(f-1),op_or,this)}if(this.isEven()){this.dAddOffset(1,0)}while(!this.isProbablePrime(e)){this.dAddOffset(2,0);if(this.bitLength()>f){this.subTo(BigInteger.ONE.shiftLeft(f-1),this)}}}}else{var d=new Array(),g=f&7;d.length=(f>>3)+1;e.nextBytes(d);if(g>0){d[0]&=((1<<g)-1)}else{d[0]=0}this.fromString(d,256)}}function bnToByteArray(){var b=this.t,c=new Array();c[0]=this.s;var e=this.DB-(b*this.DB)%8,f,a=0;if(b-->0){if(e<this.DB&&(f=this[b]>>e)!=(this.s&this.DM)>>e){c[a++]=f|(this.s<<(this.DB-e))}while(b>=0){if(e<8){f=(this[b]&((1<<e)-1))<<(8-e);f|=this[--b]>>(e+=this.DB-8)}else{f=(this[b]>>(e-=8))&255;if(e<=0){e+=this.DB;--b}}if((f&128)!=0){f|=-256}if(a==0&&(this.s&128)!=(f&128)){++a}if(a>0||f!=this.s){c[a++]=f}}}return c}function bnEquals(b){return(this.compareTo(b)==0)}function bnMin(b){return(this.compareTo(b)<0)?this:b}function bnMax(b){return(this.compareTo(b)>0)?this:b}function bnpBitwiseTo(c,h,e){var d,g,b=Math.min(c.t,this.t);for(d=0;d<b;++d){e[d]=h(this[d],c[d])}if(c.t<this.t){g=c.s&this.DM;for(d=b;d<this.t;++d){e[d]=h(this[d],g)}e.t=this.t}else{g=this.s&this.DM;for(d=b;d<c.t;++d){e[d]=h(g,c[d])}e.t=c.t}e.s=h(this.s,c.s);e.clamp()}function op_and(a,b){return a&b}function bnAnd(b){var c=nbi();this.bitwiseTo(b,op_and,c);return c}function op_or(a,b){return a|b}function bnOr(b){var c=nbi();this.bitwiseTo(b,op_or,c);return c}function op_xor(a,b){return a^b}function bnXor(b){var c=nbi();this.bitwiseTo(b,op_xor,c);return c}function op_andnot(a,b){return a&~b}function bnAndNot(b){var c=nbi();this.bitwiseTo(b,op_andnot,c);return c}function bnNot(){var b=nbi();for(var a=0;a<this.t;++a){b[a]=this.DM&~this[a]}b.t=this.t;b.s=~this.s;return b}function bnShiftLeft(b){var a=nbi();if(b<0){this.rShiftTo(-b,a)}else{this.lShiftTo(b,a)}return a}function bnShiftRight(b){var a=nbi();if(b<0){this.lShiftTo(-b,a)}else{this.rShiftTo(b,a)}return a}function lbit(a){if(a==0){return -1}var b=0;if((a&65535)==0){a>>=16;b+=16}if((a&255)==0){a>>=8;b+=8}if((a&15)==0){a>>=4;b+=4}if((a&3)==0){a>>=2;b+=2}if((a&1)==0){++b}return b}function bnGetLowestSetBit(){for(var a=0;a<this.t;++a){if(this[a]!=0){return a*this.DB+lbit(this[a])}}if(this.s<0){return this.t*this.DB}return -1}function cbit(a){var b=0;while(a!=0){a&=a-1;++b}return b}function bnBitCount(){var c=0,a=this.s&this.DM;for(var b=0;b<this.t;++b){c+=cbit(this[b]^a)}return c}function bnTestBit(b){var a=Math.floor(b/this.DB);if(a>=this.t){return(this.s!=0)}return((this[a]&(1<<(b%this.DB)))!=0)}function bnpChangeBit(c,b){var a=BigInteger.ONE.shiftLeft(c);this.bitwiseTo(a,b,a);return a}function bnSetBit(a){return this.changeBit(a,op_or)}function bnClearBit(a){return this.changeBit(a,op_andnot)}function bnFlipBit(a){return this.changeBit(a,op_xor)}function bnpAddTo(d,f){var e=0,g=0,b=Math.min(d.t,this.t);while(e<b){g+=this[e]+d[e];f[e++]=g&this.DM;g>>=this.DB}if(d.t<this.t){g+=d.s;while(e<this.t){g+=this[e];f[e++]=g&this.DM;g>>=this.DB}g+=this.s}else{g+=this.s;while(e<d.t){g+=d[e];f[e++]=g&this.DM;g>>=this.DB}g+=d.s}f.s=(g<0)?-1:0;if(g>0){f[e++]=g}else{if(g<-1){f[e++]=this.DV+g}}f.t=e;f.clamp()}function bnAdd(b){var c=nbi();this.addTo(b,c);return c}function bnSubtract(b){var c=nbi();this.subTo(b,c);return c}function bnMultiply(b){var c=nbi();this.multiplyTo(b,c);return c}function bnSquare(){var a=nbi();this.squareTo(a);return a}function bnDivide(b){var c=nbi();this.divRemTo(b,c,null);return c}function bnRemainder(b){var c=nbi();this.divRemTo(b,null,c);return c}function bnDivideAndRemainder(b){var d=nbi(),c=nbi();this.divRemTo(b,d,c);return new Array(d,c)}function bnpDMultiply(a){this[this.t]=this.am(0,a-1,this,0,0,this.t);++this.t;this.clamp()}function bnpDAddOffset(b,a){if(b==0){return}while(this.t<=a){this[this.t++]=0}this[a]+=b;while(this[a]>=this.DV){this[a]-=this.DV;if(++a>=this.t){this[this.t++]=0}++this[a]}}function NullExp(){}function nNop(a){return a}function nMulTo(a,c,b){a.multiplyTo(c,b)}function nSqrTo(a,b){a.squareTo(b)}NullExp.prototype.convert=nNop;NullExp.prototype.revert=nNop;NullExp.prototype.mulTo=nMulTo;NullExp.prototype.sqrTo=nSqrTo;function bnPow(a){return this.exp(a,new NullExp())}function bnpMultiplyLowerTo(b,f,e){var d=Math.min(this.t+b.t,f);e.s=0;e.t=d;while(d>0){e[--d]=0}var c;for(c=e.t-this.t;d<c;++d){e[d+this.t]=this.am(0,b[d],e,d,0,this.t)}for(c=Math.min(b.t,f);d<c;++d){this.am(0,b[d],e,d,0,f-d)}e.clamp()}function bnpMultiplyUpperTo(b,e,d){--e;var c=d.t=this.t+b.t-e;d.s=0;while(--c>=0){d[c]=0}for(c=Math.max(e-this.t,0);c<b.t;++c){d[this.t+c-e]=this.am(e-c,b[c],d,0,0,this.t+c-e)}d.clamp();d.drShiftTo(1,d)}function Barrett(a){this.r2=nbi();this.q3=nbi();BigInteger.ONE.dlShiftTo(2*a.t,this.r2);this.mu=this.r2.divide(a);this.m=a}function barrettConvert(a){if(a.s<0||a.t>2*this.m.t){return a.mod(this.m)}else{if(a.compareTo(this.m)<0){return a}else{var b=nbi();a.copyTo(b);this.reduce(b);return b}}}function barrettRevert(a){return a}function barrettReduce(a){a.drShiftTo(this.m.t-1,this.r2);if(a.t>this.m.t+1){a.t=this.m.t+1;a.clamp()}this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);while(a.compareTo(this.r2)<0){a.dAddOffset(1,this.m.t+1)}a.subTo(this.r2,a);while(a.compareTo(this.m)>=0){a.subTo(this.m,a)}}function barrettSqrTo(a,b){a.squareTo(b);this.reduce(b)}function barrettMulTo(a,c,b){a.multiplyTo(c,b);this.reduce(b)}Barrett.prototype.convert=barrettConvert;Barrett.prototype.revert=barrettRevert;Barrett.prototype.reduce=barrettReduce;Barrett.prototype.mulTo=barrettMulTo;Barrett.prototype.sqrTo=barrettSqrTo;function bnModPow(q,f){var o=q.bitLength(),h,b=nbv(1),v;if(o<=0){return b}else{if(o<18){h=1}else{if(o<48){h=3}else{if(o<144){h=4}else{if(o<768){h=5}else{h=6}}}}}if(o<8){v=new Classic(f)}else{if(f.isEven()){v=new Barrett(f)}else{v=new Montgomery(f)}}var p=new Array(),d=3,s=h-1,a=(1<<h)-1;p[1]=v.convert(this);if(h>1){var A=nbi();v.sqrTo(p[1],A);while(d<=a){p[d]=nbi();v.mulTo(A,p[d-2],p[d]);d+=2}}var l=q.t-1,x,u=true,c=nbi(),y;o=nbits(q[l])-1;while(l>=0){if(o>=s){x=(q[l]>>(o-s))&a}else{x=(q[l]&((1<<(o+1))-1))<<(s-o);if(l>0){x|=q[l-1]>>(this.DB+o-s)}}d=h;while((x&1)==0){x>>=1;--d}if((o-=d)<0){o+=this.DB;--l}if(u){p[x].copyTo(b);u=false}else{while(d>1){v.sqrTo(b,c);v.sqrTo(c,b);d-=2}if(d>0){v.sqrTo(b,c)}else{y=b;b=c;c=y}v.mulTo(c,p[x],b)}while(l>=0&&(q[l]&(1<<o))==0){v.sqrTo(b,c);y=b;b=c;c=y;if(--o<0){o=this.DB-1;--l}}}return v.revert(b)}function bnGCD(c){var b=(this.s<0)?this.negate():this.clone();var h=(c.s<0)?c.negate():c.clone();if(b.compareTo(h)<0){var e=b;b=h;h=e}var d=b.getLowestSetBit(),f=h.getLowestSetBit();if(f<0){return b}if(d<f){f=d}if(f>0){b.rShiftTo(f,b);h.rShiftTo(f,h)}while(b.signum()>0){if((d=b.getLowestSetBit())>0){b.rShiftTo(d,b)}if((d=h.getLowestSetBit())>0){h.rShiftTo(d,h)}if(b.compareTo(h)>=0){b.subTo(h,b);b.rShiftTo(1,b)}else{h.subTo(b,h);h.rShiftTo(1,h)}}if(f>0){h.lShiftTo(f,h)}return h}function bnpModInt(e){if(e<=0){return 0}var c=this.DV%e,b=(this.s<0)?e-1:0;if(this.t>0){if(c==0){b=this[0]%e}else{for(var a=this.t-1;a>=0;--a){b=(c*b+this[a])%e}}}return b}function bnModInverse(f){var j=f.isEven();if((this.isEven()&&j)||f.signum()==0){return BigInteger.ZERO}var i=f.clone(),h=this.clone();var g=nbv(1),e=nbv(0),l=nbv(0),k=nbv(1);while(i.signum()!=0){while(i.isEven()){i.rShiftTo(1,i);if(j){if(!g.isEven()||!e.isEven()){g.addTo(this,g);e.subTo(f,e)}g.rShiftTo(1,g)}else{if(!e.isEven()){e.subTo(f,e)}}e.rShiftTo(1,e)}while(h.isEven()){h.rShiftTo(1,h);if(j){if(!l.isEven()||!k.isEven()){l.addTo(this,l);k.subTo(f,k)}l.rShiftTo(1,l)}else{if(!k.isEven()){k.subTo(f,k)}}k.rShiftTo(1,k)}if(i.compareTo(h)>=0){i.subTo(h,i);if(j){g.subTo(l,g)}e.subTo(k,e)}else{h.subTo(i,h);if(j){l.subTo(g,l)}k.subTo(e,k)}}if(h.compareTo(BigInteger.ONE)!=0){return BigInteger.ZERO}if(k.compareTo(f)>=0){return k.subtract(f)}if(k.signum()<0){k.addTo(f,k)}else{return k}if(k.signum()<0){return k.add(f)}else{return k}}var lowprimes=[2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709,719,727,733,739,743,751,757,761,769,773,787,797,809,811,821,823,827,829,839,853,857,859,863,877,881,883,887,907,911,919,929,937,941,947,953,967,971,977,983,991,997];var lplim=(1<<26)/lowprimes[lowprimes.length-1];function bnIsProbablePrime(e){var d,b=this.abs();if(b.t==1&&b[0]<=lowprimes[lowprimes.length-1]){for(d=0;d<lowprimes.length;++d){if(b[0]==lowprimes[d]){return true}}return false}if(b.isEven()){return false}d=1;while(d<lowprimes.length){var a=lowprimes[d],c=d+1;while(c<lowprimes.length&&a<lplim){a*=lowprimes[c++]}a=b.modInt(a);while(d<c){if(a%lowprimes[d++]==0){return false}}}return b.millerRabin(e)}function bnpMillerRabin(f){var g=this.subtract(BigInteger.ONE);var c=g.getLowestSetBit();if(c<=0){return false}var h=g.shiftRight(c);f=(f+1)>>1;if(f>lowprimes.length){f=lowprimes.length}var b=nbi();for(var e=0;e<f;++e){b.fromInt(lowprimes[Math.floor(Math.random()*lowprimes.length)]);var l=b.modPow(h,this);if(l.compareTo(BigInteger.ONE)!=0&&l.compareTo(g)!=0){var d=1;while(d++<c&&l.compareTo(g)!=0){l=l.modPowInt(2,this);if(l.compareTo(BigInteger.ONE)==0){return false}}if(l.compareTo(g)!=0){return false}}}return true}BigInteger.prototype.chunkSize=bnpChunkSize;BigInteger.prototype.toRadix=bnpToRadix;BigInteger.prototype.fromRadix=bnpFromRadix;BigInteger.prototype.fromNumber=bnpFromNumber;BigInteger.prototype.bitwiseTo=bnpBitwiseTo;BigInteger.prototype.changeBit=bnpChangeBit;BigInteger.prototype.addTo=bnpAddTo;BigInteger.prototype.dMultiply=bnpDMultiply;BigInteger.prototype.dAddOffset=bnpDAddOffset;BigInteger.prototype.multiplyLowerTo=bnpMultiplyLowerTo;BigInteger.prototype.multiplyUpperTo=bnpMultiplyUpperTo;BigInteger.prototype.modInt=bnpModInt;BigInteger.prototype.millerRabin=bnpMillerRabin;BigInteger.prototype.clone=bnClone;BigInteger.prototype.intValue=bnIntValue;BigInteger.prototype.byteValue=bnByteValue;BigInteger.prototype.shortValue=bnShortValue;BigInteger.prototype.signum=bnSigNum;BigInteger.prototype.toByteArray=bnToByteArray;BigInteger.prototype.equals=bnEquals;BigInteger.prototype.min=bnMin;BigInteger.prototype.max=bnMax;BigInteger.prototype.and=bnAnd;BigInteger.prototype.or=bnOr;BigInteger.prototype.xor=bnXor;BigInteger.prototype.andNot=bnAndNot;BigInteger.prototype.not=bnNot;BigInteger.prototype.shiftLeft=bnShiftLeft;BigInteger.prototype.shiftRight=bnShiftRight;BigInteger.prototype.getLowestSetBit=bnGetLowestSetBit;BigInteger.prototype.bitCount=bnBitCount;BigInteger.prototype.testBit=bnTestBit;BigInteger.prototype.setBit=bnSetBit;BigInteger.prototype.clearBit=bnClearBit;BigInteger.prototype.flipBit=bnFlipBit;BigInteger.prototype.add=bnAdd;BigInteger.prototype.subtract=bnSubtract;BigInteger.prototype.multiply=bnMultiply;BigInteger.prototype.divide=bnDivide;BigInteger.prototype.remainder=bnRemainder;BigInteger.prototype.divideAndRemainder=bnDivideAndRemainder;BigInteger.prototype.modPow=bnModPow;BigInteger.prototype.modInverse=bnModInverse;BigInteger.prototype.pow=bnPow;BigInteger.prototype.gcd=bnGCD;BigInteger.prototype.isProbablePrime=bnIsProbablePrime;BigInteger.prototype.square=bnSquare;


			/*var dhprime=new BigInteger('FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF',16);
			var dhg=new BigInteger('02',16);
			var secretX=new BigInteger('04',16);
			alert(dhg.modPow(secretX,dhprime).toString(16));*/

			/*function discrete_exp(t,u,n) {   // args are base, exponent, modulus
			// computes s = (t ^ u) mod n
			// (see Bruce Schneier's book, _Applied Cryptography_ p. 244)
			   var s = 1;
			   while (u) { if (u&1) {s = (s*t) % n}; u >>= 1; t = (t*t)%n; };
			   return s;
			}
			*/

			//prng.js (forge)

			/**
			 * A javascript implementation of a cryptographically-secure
			 * Pseudo Random Number Generator (PRNG). The Fortuna algorithm is mostly
			 * followed here. SHA-1 is used instead of SHA-256.
			 *
			 * @author Dave Longley
			 *
			 * Copyright (c) 2010-2012 Digital Bazaar, Inc.
			 */
			(function(){var a=forge;a.prng={};/*if(typeof(window)!=="undefined"){var a=window.forge=window.forge||{};a.prng={}}else{if(typeof(module)!=="undefined"&&module.exports){var a={md:require("./md"),util:require("./util")};a.md.sha1.create();module.exports=a.prng={}}}*/;var b=a.prng;b.create=function(g){var c={plugin:g,key:null,seed:null,time:null,reseeds:0,generated:0};var h=g.md;var f=new Array(32);for(var e=0;e<32;++e){f[e]=h.create()}c.pools=f;c.pool=0;c.generate=function(o){if(c.key===null){d()}var k=c.plugin.cipher;var j=c.plugin.increment;var p=c.plugin.formatKey;var n=c.plugin.formatSeed;var i=a.util.createBuffer();while(i.length()<o){var l=k(c.key,c.seed);c.generated+=l.length;i.putBytes(l);c.key=p(k(c.key,j(c.seed)));c.seed=n(k(c.key,c.seed));if(c.generated>=1048576){var m=+new Date();if(m-c.time<100){d()}}}return i.getBytes(o)};function d(){if(c.pools[0].messageLength<32){var t=(32-c.pools[0].messageLength)<<5;var s="";var j,q,n;var o=Math.floor(Math.random()*65535);while(s.length<t){q=16807*(o&65535);j=16807*(o>>16);q+=(j&32767)<<16;q+=j>>15;q=(q&2147483647)+(q>>31);o=q&4294967295;for(var m=0;m<3;++m){n=o>>>(m<<3);n^=Math.floor(Math.random()*255);s+=String.fromCharCode(n&255)}}c.collect(s)}else{var r=a.md.sha1.create();r.update(c.pools[0].digest().getBytes());c.pools[0].start();var l=1;for(var m=1;m<32;++m){l=(l==31)?2147483648:(l<<2);if(l%c.reseeds===0){r.update(c.pools[m].digest().getBytes());c.pools[m].start()}}var p=r.digest().getBytes();r.start();r.update(p);var u=r.digest().getBytes();c.key=c.plugin.formatKey(p);c.seed=c.plugin.formatSeed(u);++c.reseeds;c.generated=0;c.time=+new Date()}}c.collect=function(j){var m=j.length;for(var l=0;l<m;++l){c.pools[c.pool].update(j.substr(l,1));c.pool=(c.pool===31)?0:c.pool+1}if(c.pools[0].messageLength>=32){var k=+new Date();if(c.time===null||(k-c.time<100)){d()}}};c.collectInt=function(k,l){var j="";do{l-=8;j+=String.fromCharCode((k>>l)&255)}while(l>0);c.collect(j)};return c}})();

			//random.js (forge)

			/**
			 * An API for getting cryptographically-secure random bytes. The bytes are
			 * generated using the Fortuna algorithm devised by Bruce Schneier and
			 * Niels Ferguson.
			 *
			 * Getting strong random bytes is not yet easy to do in javascript. The only
			 * truish random entropy that can be collected is from the mouse, keyboard, or
			 * from timing with respect to page loads, etc. This generator makes a poor
			 * attempt at providing random bytes when those sources haven't yet provided
			 * enough entropy to initially seed or to reseed the PRNG.
			 *
			 * @author Dave Longley
			 *
			 * Copyright (c) 2009-2012 Digital Bazaar, Inc.
			 */
			(function(d){var i=forge;i.random={};/*if(typeof(window)!=="undefined"){var i=window.forge=window.forge||{};i.random={}}else{if(typeof(module)!=="undefined"&&module.exports){var i={aes:require("./aes"),md:require("./md"),prng:require("./prng"),util:require("./util")};module.exports=i.random={}}}*/;var h={};var f=new Array(4);var c=i.util.createBuffer();h.formatKey=function(k){var e=i.util.createBuffer(k);k=new Array(4);k[0]=e.getInt32();k[1]=e.getInt32();k[2]=e.getInt32();k[3]=e.getInt32();return i.aes._expandKey(k,false)};h.formatSeed=function(e){var tmp=i.util.createBuffer(e);e=new Array(4);e[0]=tmp.getInt32();e[1]=tmp.getInt32();e[2]=tmp.getInt32();e[3]=tmp.getInt32();return e};h.cipher=function(k,e){i.aes._updateBlock(k,e,f,false);c.putInt32(f[0]);c.putInt32(f[1]);c.putInt32(f[2]);c.putInt32(f[3]);return c.getBytes()};h.increment=function(e){++e[3];return e};h.md=i.md.sha1;var b=i.prng.create(h);b.collectInt(+new Date(),32);if(typeof(navigator)!=="undefined"){var a="";for(var j in navigator){try{if(typeof(navigator[j])=="string"){a+=navigator[j]}}catch(g){}}b.collect(a);a=null}if(d){d().mousemove(function(k){b.collectInt(k.clientX,16);b.collectInt(k.clientY,16)});d().keypress(function(k){b.collectInt(k.charCode,8)})}i.random.getBytes=function(e){return b.generate(e)}})(typeof(jQuery)!=="undefined"?jQuery:null);

			//oid.js (forge)

			/**
			 * Object IDs for ASN.1.
			 *
			 * @author Dave Longley
			 *
			 * Copyright (c) 2010-2012 Digital Bazaar, Inc.
			 */
			(function(){var a={};var b=forge;/*if(typeof(window)!=="undefined"){var b=window.forge=window.forge||{}}else{if(typeof(module)!=="undefined"&&module.exports){var b={};module.exports=a}}*/;b.pki=b.pki||{};b.pki.oids=a;a["1.2.840.113549.1.1.1"]="rsaEncryption";a.rsaEncryption="1.2.840.113549.1.1.1";a["1.2.840.113549.1.1.4"]="md5withRSAEncryption";a.md5withRSAEncryption="1.2.840.113549.1.1.4";a["1.2.840.113549.1.1.5"]="sha1withRSAEncryption";a.sha1withRSAEncryption="1.2.840.113549.1.1.5";a["1.2.840.113549.1.1.7"]="RSAES-OAEP";a["RSAES-OAEP"]="1.2.840.113549.1.1.7";a["1.2.840.113549.1.1.8"]="mgf1";a.mgf1="1.2.840.113549.1.1.8";a["1.2.840.113549.1.1.9"]="pSpecified";a.pSpecified="1.2.840.113549.1.1.9";a["1.2.840.113549.1.1.10"]="RSASSA-PSS";a["RSASSA-PSS"]="1.2.840.113549.1.1.10";a["1.2.840.113549.1.1.11"]="sha256WithRSAEncryption";a.sha256WithRSAEncryption="1.2.840.113549.1.1.11";a["1.2.840.113549.1.1.12"]="sha384WithRSAEncryption";a.sha384WithRSAEncryption="1.2.840.113549.1.1.12";a["1.2.840.113549.1.1.13"]="sha512WithRSAEncryption";a.sha512WithRSAEncryption="1.2.840.113549.1.1.13";a["1.3.14.3.2.26"]="sha1";a.sha1="1.3.14.3.2.26";a["2.16.840.1.101.3.4.2.1"]="sha256";a.sha256="2.16.840.1.101.3.4.2.1";a["2.16.840.1.101.3.4.2.2"]="sha384";a.sha384="2.16.840.1.101.3.4.2.2";a["2.16.840.1.101.3.4.2.3"]="sha512";a.sha512="2.16.840.1.101.3.4.2.3";a["1.2.840.113549.2.5"]="md5";a.md5="1.2.840.113549.2.5";a["1.2.840.113549.1.7.1"]="data";a.data="1.2.840.113549.1.7.1";a["1.2.840.113549.1.7.2"]="signedData";a.signedData="1.2.840.113549.1.7.2";a["1.2.840.113549.1.7.3"]="envelopedData";a.envelopedData="1.2.840.113549.1.7.3";a["1.2.840.113549.1.7.4"]="signedAndEnvelopedData";a.signedAndEnvelopedData="1.2.840.113549.1.7.4";a["1.2.840.113549.1.7.5"]="digestedData";a.digestedData="1.2.840.113549.1.7.5";a["1.2.840.113549.1.7.6"]="encryptedData";a.encryptedData="1.2.840.113549.1.7.6";a["1.2.840.113549.1.9.20"]="friendlyName";a.friendlyName="1.2.840.113549.1.9.20";a["1.2.840.113549.1.9.21"]="localKeyId";a.localKeyId="1.2.840.113549.1.9.21";a["1.2.840.113549.1.9.22.1"]="x509Certificate";a.x509Certificate="1.2.840.113549.1.9.22.1";a["1.2.840.113549.1.12.10.1.1"]="keyBag";a.keyBag="1.2.840.113549.1.12.10.1.1";a["1.2.840.113549.1.12.10.1.2"]="pkcs8ShroudedKeyBag";a.pkcs8ShroudedKeyBag="1.2.840.113549.1.12.10.1.2";a["1.2.840.113549.1.12.10.1.3"]="certBag";a.certBag="1.2.840.113549.1.12.10.1.3";a["1.2.840.113549.1.12.10.1.4"]="crlBag";a.crlBag="1.2.840.113549.1.12.10.1.4";a["1.2.840.113549.1.12.10.1.5"]="secretBag";a.secretBag="1.2.840.113549.1.12.10.1.5";a["1.2.840.113549.1.12.10.1.6"]="safeContentsBag";a.safeContentsBag="1.2.840.113549.1.12.10.1.6";a["1.2.840.113549.1.5.13"]="pkcs5PBES2";a.pkcs5PBES2="1.2.840.113549.1.5.13";a["1.2.840.113549.1.5.12"]="pkcs5PBKDF2";a.pkcs5PBKDF2="1.2.840.113549.1.5.12";a["1.2.840.113549.1.12.1.1"]="pbeWithSHAAnd128BitRC4";a.pbeWithSHAAnd128BitRC4="1.2.840.113549.1.12.1.1";a["1.2.840.113549.1.12.1.2"]="pbeWithSHAAnd40BitRC4";a.pbeWithSHAAnd40BitRC4="1.2.840.113549.1.12.1.2";a["1.2.840.113549.1.12.1.3"]="pbeWithSHAAnd3-KeyTripleDES-CBC";a["pbeWithSHAAnd3-KeyTripleDES-CBC"]="1.2.840.113549.1.12.1.3";a["1.2.840.113549.1.12.1.4"]="pbeWithSHAAnd2-KeyTripleDES-CBC";a["pbeWithSHAAnd2-KeyTripleDES-CBC"]="1.2.840.113549.1.12.1.4";a["1.2.840.113549.1.12.1.5"]="pbeWithSHAAnd128BitRC2-CBC";a["pbeWithSHAAnd128BitRC2-CBC"]="1.2.840.113549.1.12.1.5";a["1.2.840.113549.1.12.1.6"]="pbewithSHAAnd40BitRC2-CBC";a["pbewithSHAAnd40BitRC2-CBC"]="1.2.840.113549.1.12.1.6";a["1.2.840.113549.3.7"]="des-EDE3-CBC";a["des-EDE3-CBC"]="1.2.840.113549.3.7";a["2.16.840.1.101.3.4.1.2"]="aes128-CBC";a["aes128-CBC"]="2.16.840.1.101.3.4.1.2";a["2.16.840.1.101.3.4.1.22"]="aes192-CBC";a["aes192-CBC"]="2.16.840.1.101.3.4.1.22";a["2.16.840.1.101.3.4.1.42"]="aes256-CBC";a["aes256-CBC"]="2.16.840.1.101.3.4.1.42";a["2.5.4.3"]="commonName";a.commonName="2.5.4.3";a["2.5.4.5"]="serialName";a.serialName="2.5.4.5";a["2.5.4.6"]="countryName";a.countryName="2.5.4.6";a["2.5.4.7"]="localityName";a.localityName="2.5.4.7";a["2.5.4.8"]="stateOrProvinceName";a.stateOrProvinceName="2.5.4.8";a["2.5.4.10"]="organizationName";a.organizationName="2.5.4.10";a["2.5.4.11"]="organizationalUnitName";a.organizationalUnitName="2.5.4.11";a["1.2.840.113549.1.9.1"]="emailAddress";a.emailAddress="1.2.840.113549.1.9.1";a["2.5.29.1"]="authorityKeyIdentifier";a["2.5.29.2"]="keyAttributes";a["2.5.29.3"]="certificatePolicies";a["2.5.29.4"]="keyUsageRestriction";a["2.5.29.5"]="policyMapping";a["2.5.29.6"]="subtreesConstraint";a["2.5.29.7"]="subjectAltName";a["2.5.29.8"]="issuerAltName";a["2.5.29.9"]="subjectDirectoryAttributes";a["2.5.29.10"]="basicConstraints";a["2.5.29.11"]="nameConstraints";a["2.5.29.12"]="policyConstraints";a["2.5.29.13"]="basicConstraints";a["2.5.29.14"]="subjectKeyIdentifier";a.subjectKeyIdentifier="2.5.29.14";a["2.5.29.15"]="keyUsage";a.keyUsage="2.5.29.15";a["2.5.29.16"]="privateKeyUsagePeriod";a["2.5.29.17"]="subjectAltName";a.subjectAltName="2.5.29.17";a["2.5.29.18"]="issuerAltName";a.issuerAltName="2.5.29.18";a["2.5.29.19"]="basicConstraints";a.basicConstraints="2.5.29.19";a["2.5.29.20"]="cRLNumber";a["2.5.29.21"]="cRLReason";a["2.5.29.22"]="expirationDate";a["2.5.29.23"]="instructionCode";a["2.5.29.24"]="invalidityDate";a["2.5.29.25"]="cRLDistributionPoints";a["2.5.29.26"]="issuingDistributionPoint";a["2.5.29.27"]="deltaCRLIndicator";a["2.5.29.28"]="issuingDistributionPoint";a["2.5.29.29"]="certificateIssuer";a["2.5.29.30"]="nameConstraints";a["2.5.29.31"]="cRLDistributionPoints";a["2.5.29.32"]="certificatePolicies";a["2.5.29.33"]="policyMappings";a["2.5.29.34"]="policyConstraints";a["2.5.29.35"]="authorityKeyIdentifier";a["2.5.29.36"]="policyConstraints";a["2.5.29.37"]="extKeyUsage";a.extKeyUsage="2.5.29.37";a["2.5.29.46"]="freshestCRL";a["2.5.29.54"]="inhibitAnyPolicy"})();

			//rsa.js (forge)

			/**
			 * Javascript implementation of a basic RSA algorithms.
			 *
			 * @author Dave Longley
			 *
			 * Copyright (c) 2010-2012 Digital Bazaar, Inc.
			 */
			(function(){var e=forge;/*if(typeof(window)!=="undefined"){var e=window.forge=window.forge||{}}else{if(typeof(module)!=="undefined"&&module.exports){var e={asn1:require("./asn1"),pki:{oids:require("./oids")},random:require("./random"),util:require("./util")};BigInteger=require("./jsbn");module.exports=e.pki.rsa={}}}*/;var d=e.asn1;e.pki=e.pki||{};e.pki.rsa=e.pki.rsa||{};var b=e.pki;var c=function(h){var g;if(h.algorithm in e.pki.oids){g=e.pki.oids[h.algorithm]}else{throw {message:"Unknown message digest algorithm.",algorithm:h.algorithm}}var k=d.oidToDer(g).getBytes();var j=d.create(d.Class.UNIVERSAL,d.Type.SEQUENCE,true,[]);var f=d.create(d.Class.UNIVERSAL,d.Type.SEQUENCE,true,[]);f.value.push(d.create(d.Class.UNIVERSAL,d.Type.OID,false,k));f.value.push(d.create(d.Class.UNIVERSAL,d.Type.NULL,false,""));var i=d.create(d.Class.UNIVERSAL,d.Type.OCTETSTRING,false,h.digest().getBytes());j.value.push(f);j.value.push(i);return d.toDer(j).getBytes()};var a=function(f,g,j){var k;if(j){k=f.modPow(g.e,g.n)}else{if(!g.dP){g.dP=g.d.mod(g.p.subtract(BigInteger.ONE))}if(!g.dQ){g.dQ=g.d.mod(g.q.subtract(BigInteger.ONE))}if(!g.qInv){g.qInv=g.q.modInverse(g.p)}var i=f.mod(g.p).modPow(g.dP,g.p);var h=f.mod(g.q).modPow(g.dQ,g.q);while(i.compareTo(h)<0){i=i.add(g.p)}k=i.subtract(h).multiply(g.qInv).mod(g.p).multiply(g.q).add(h)}return k};b.rsa.encrypt=function(h,u,v){var n=v;var q=e.util.createBuffer();var l=Math.ceil(u.n.bitLength()/8);if(v!==false&&v!==true){if(h.length>(l-11)){throw {message:"Message is too long to encrypt.",length:h.length,max:(l-11)}}q.putByte(0);q.putByte(v);var j=l-3-h.length;var f;if(v===0||v===1){n=false;f=(v===0)?0:255;for(var o=0;o<j;++o){q.putByte(f)}}else{n=true;for(var o=0;o<j;++o){f=Math.floor(Math.random()*255)+1;q.putByte(f)}}q.putByte(0)}q.putBytes(h);var t=new BigInteger(q.toHex(),16);var s=a(t,u,n);var r=s.toString(16);var p=e.util.createBuffer();var g=l-Math.ceil(r.length/2);while(g>0){p.putByte(0);--g}p.putBytes(e.util.hexToBytes(r));return p.getBytes()};b.rsa.decrypt=function(q,w,n,l){var g=e.util.createBuffer();var j=Math.ceil(w.n.bitLength()/8);if(q.length!=j){throw {message:"Encrypted message length is invalid.",length:q.length,expected:j}}var t=new BigInteger(e.util.createBuffer(q).toHex(),16);var u=a(t,w,n);var z=u.toString(16);var r=e.util.createBuffer();var f=j-Math.ceil(z.length/2);while(f>0){r.putByte(0);--f}r.putBytes(e.util.hexToBytes(z));if(l!==false){var p=r.getByte();var v=r.getByte();if(p!==0||(n&&v!==0&&v!==1)||(!n&&v!=2)||(n&&v===0&&typeof(l)==="undefined")){throw {message:"Encryption block is invalid."}}var h=0;if(v===0){h=j-3-l;for(var o=0;o<h;++o){if(r.getByte()!==0){throw {message:"Encryption block is invalid."}}}}else{if(v===1){h=0;while(r.length()>1){if(r.getByte()!==255){--r.read;break}++h}}else{if(v===2){h=0;while(r.length()>1){if(r.getByte()===0){--r.read;break}++h}}}}var s=r.getByte();if(s!==0||h!==(j-3-r.length())){throw {message:"Encryption block is invalid."}}}return r.getBytes()};b.rsa.createKeyPairGenerationState=function(g,i){if(typeof(g)==="string"){g=parseInt(g,10)}g=g||1024;var f={nextBytes:function(k){var n=+new Date();var j=e.random.getBytes(k.length);for(var l=0;l<k.length;++l){k[l]=j.charCodeAt(l)}var m=+new Date()}};var h={state:0,itrs:0,maxItrs:100,bits:g,rng:f,e:new BigInteger((i||65537).toString(16),16),p:null,q:null,qBits:g>>1,pBits:g-(g>>1),pqState:0,num:null,six:new BigInteger(null),addNext:2,keys:null};h.six.fromInt(6);return h};b.rsa.stepKeyPairGenerationState=function(g,j){var l=+new Date();var k;var o=0;while(g.keys===null&&(j<=0||o<j)){if(g.state===0){var p=(g.p===null)?g.pBits:g.qBits;var i=p-1;if(g.pqState===0){g.itrs=0;g.num=new BigInteger(p,g.rng);g.r=null;if(g.num.isEven()){g.num.dAddOffset(1,0)}if(!g.num.testBit(i)){g.num.bitwiseTo(BigInteger.ONE.shiftLeft(i),function(n,q){return n|q},g.num)}++g.pqState}else{if(g.pqState===1){if(g.addNext===null){var f=g.num.mod(g.six).byteValue();if(f===3){g.num.mod.dAddOffset(2);f=5}g.addNext=(f===1)?2:4}var h=g.num.isProbablePrime(1);if(h){++g.pqState}else{if(g.itrs<g.maxItrs){g.num.dAddOffset(g.addNext,0);if(g.num.bitLength()>p){g.addNext=null;g.num.subTo(BigInteger.ONE.shiftLeft(i),g.num)}else{g.addNext=(g.addNext===4)?2:4}++g.itrs}else{g.pqState=0}}}else{if(g.pqState===2){g.pqState=(g.num.subtract(BigInteger.ONE).gcd(g.e).compareTo(BigInteger.ONE)===0)?3:0}else{if(g.pqState===3){g.pqState=0;if(g.num.isProbablePrime(10)){if(g.p===null){g.p=g.num}else{g.q=g.num}if(g.p!==null&&g.q!==null){++g.state}}g.num=null}}}}}else{if(g.state===1){if(g.p.compareTo(g.q)<0){g.num=g.p;g.p=g.q;g.q=g.num}++g.state}else{if(g.state===2){g.p1=g.p.subtract(BigInteger.ONE);g.q1=g.q.subtract(BigInteger.ONE);g.phi=g.p1.multiply(g.q1);++g.state}else{if(g.state===3){if(g.phi.gcd(g.e).compareTo(BigInteger.ONE)===0){++g.state}else{g.p=null;g.q=null;g.state=0}}else{if(g.state===4){g.n=g.p.multiply(g.q);if(g.n.bitLength()===g.bits){++g.state}else{g.q=null;g.state=0}}else{if(g.state===5){var m=g.e.modInverse(g.phi);g.keys={privateKey:e.pki.rsa.setPrivateKey(g.n,g.e,m,g.p,g.q,m.mod(g.p1),m.mod(g.q1),g.q.modInverse(g.p)),publicKey:e.pki.rsa.setPublicKey(g.n,g.e)}}}}}}}k=+new Date();o+=k-l;l=k}return g.keys!==null};b.rsa.generateKeyPair=function(g,h){var f=b.rsa.createKeyPairGenerationState(g,h);b.rsa.stepKeyPairGenerationState(f,0);return f.keys};b.rsa.setPublicKey=function(h,g){var f={n:h,e:g};f.encrypt=function(i){return b.rsa.encrypt(i,f,2)};f.verify=function(m,i,j){var n=j===undefined?undefined:false;var l=b.rsa.decrypt(i,f,true,n);if(j===undefined){var k=d.fromDer(l);return m===k.value[1].value}else{return j.verify(m,l,f.n.bitLength())}};return f};b.rsa.setPrivateKey=function(h,i,j,g,f,l,k,o){var m={n:h,e:i,d:j,p:g,q:f,dP:l,dQ:k,qInv:o};m.decrypt=function(n){return b.rsa.decrypt(n,m,false)};m.sign=function(q,p){var n=false;if(p===undefined){p={encode:c};n=1}var r=p.encode(q,m.n.bitLength());return b.rsa.encrypt(r,m,n)};return m}})();

			//pki.js and pem.js (forge)

			/**
			 * Javascript implementation of a basic Public Key Infrastructure, including
			 * support for RSA public and private keys.
			 *
			 * @author Dave Longley
			 * @author Stefan Siegl <stesie@brokenpipe.de>
			 *
			 * Copyright (c) 2010-2012 Digital Bazaar, Inc.
			 * Copyright (c) 2012 Stefan Siegl <stesie@brokenpipe.de>
			 *
			*/
			(function(){function d(p){if(typeof BigInteger==="undefined"){BigInteger=p.jsbn.BigInteger}var t=p.asn1;var j=p.pki=p.pki||{};var m=j.oids;j.pbe={};var w={};w.CN=m.commonName;w.commonName="CN";w.C=m.countryName;w.countryName="C";w.L=m.localityName;w.localityName="L";w.ST=m.stateOrProvinceName;w.stateOrProvinceName="ST";w.O=m.organizationName;w.organizationName="O";w.OU=m.organizationalUnitName;w.organizationalUnitName="OU";w.E=m.emailAddress;w.emailAddress="E";var A={name:"SubjectPublicKeyInfo",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,captureAsn1:"subjectPublicKeyInfo",value:[{name:"SubjectPublicKeyInfo.AlgorithmIdentifier",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false,capture:"publicKeyOid"}]},{name:"SubjectPublicKeyInfo.subjectPublicKey",tagClass:t.Class.UNIVERSAL,type:t.Type.BITSTRING,constructed:false,value:[{name:"SubjectPublicKeyInfo.subjectPublicKey.RSAPublicKey",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,optional:true,captureAsn1:"rsaPublicKey"}]}]};var v={name:"RSAPublicKey",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"RSAPublicKey.modulus",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"publicKeyModulus"},{name:"RSAPublicKey.exponent",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"publicKeyExponent"}]};var x={name:"Certificate",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"Certificate.TBSCertificate",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,captureAsn1:"tbsCertificate",value:[{name:"Certificate.TBSCertificate.version",tagClass:t.Class.CONTEXT_SPECIFIC,type:0,constructed:true,optional:true,value:[{name:"Certificate.TBSCertificate.version.integer",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"certVersion"}]},{name:"Certificate.TBSCertificate.serialNumber",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"certSerialNumber"},{name:"Certificate.TBSCertificate.signature",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"Certificate.TBSCertificate.signature.algorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false,capture:"certinfoSignatureOid"},{name:"Certificate.TBSCertificate.signature.parameters",tagClass:t.Class.UNIVERSAL,optional:true,captureAsn1:"certinfoSignatureParams"}]},{name:"Certificate.TBSCertificate.issuer",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,captureAsn1:"certIssuer"},{name:"Certificate.TBSCertificate.validity",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"Certificate.TBSCertificate.validity.notBefore (utc)",tagClass:t.Class.UNIVERSAL,type:t.Type.UTCTIME,constructed:false,optional:true,capture:"certValidity1UTCTime"},{name:"Certificate.TBSCertificate.validity.notBefore (generalized)",tagClass:t.Class.UNIVERSAL,type:t.Type.GENERALIZEDTIME,constructed:false,optional:true,capture:"certValidity2GeneralizedTime"},{name:"Certificate.TBSCertificate.validity.notAfter (utc)",tagClass:t.Class.UNIVERSAL,type:t.Type.UTCTIME,constructed:false,optional:true,capture:"certValidity3UTCTime"},{name:"Certificate.TBSCertificate.validity.notAfter (generalized)",tagClass:t.Class.UNIVERSAL,type:t.Type.GENERALIZEDTIME,constructed:false,optional:true,capture:"certValidity4GeneralizedTime"}]},{name:"Certificate.TBSCertificate.subject",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,captureAsn1:"certSubject"},A,{name:"Certificate.TBSCertificate.issuerUniqueID",tagClass:t.Class.CONTEXT_SPECIFIC,type:1,constructed:true,optional:true,value:[{name:"Certificate.TBSCertificate.issuerUniqueID.id",tagClass:t.Class.UNIVERSAL,type:t.Type.BITSTRING,constructed:false,capture:"certIssuerUniqueId"}]},{name:"Certificate.TBSCertificate.subjectUniqueID",tagClass:t.Class.CONTEXT_SPECIFIC,type:2,constructed:true,optional:true,value:[{name:"Certificate.TBSCertificate.subjectUniqueID.id",tagClass:t.Class.UNIVERSAL,type:t.Type.BITSTRING,constructed:false,capture:"certSubjectUniqueId"}]},{name:"Certificate.TBSCertificate.extensions",tagClass:t.Class.CONTEXT_SPECIFIC,type:3,constructed:true,captureAsn1:"certExtensions",optional:true}]},{name:"Certificate.signatureAlgorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"Certificate.signatureAlgorithm.algorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false,capture:"certSignatureOid"},{name:"Certificate.TBSCertificate.signature.parameters",tagClass:t.Class.UNIVERSAL,optional:true,captureAsn1:"certSignatureParams"}]},{name:"Certificate.signatureValue",tagClass:t.Class.UNIVERSAL,type:t.Type.BITSTRING,constructed:false,capture:"certSignature"}]};var s={name:"PrivateKeyInfo",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"PrivateKeyInfo.version",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"privateKeyVersion"},{name:"PrivateKeyInfo.privateKeyAlgorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false,capture:"privateKeyOid"}]},{name:"PrivateKeyInfo",tagClass:t.Class.UNIVERSAL,type:t.Type.OCTETSTRING,constructed:false,capture:"privateKey"}]};var z={name:"RSAPrivateKey",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"RSAPrivateKey.version",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"privateKeyVersion"},{name:"RSAPrivateKey.modulus",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"privateKeyModulus"},{name:"RSAPrivateKey.publicExponent",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"privateKeyPublicExponent"},{name:"RSAPrivateKey.privateExponent",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"privateKeyPrivateExponent"},{name:"RSAPrivateKey.prime1",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"privateKeyPrime1"},{name:"RSAPrivateKey.prime2",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"privateKeyPrime2"},{name:"RSAPrivateKey.exponent1",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"privateKeyExponent1"},{name:"RSAPrivateKey.exponent2",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"privateKeyExponent2"},{name:"RSAPrivateKey.coefficient",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"privateKeyCoefficient"}]};var y={name:"EncryptedPrivateKeyInfo",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"EncryptedPrivateKeyInfo.encryptionAlgorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"AlgorithmIdentifier.algorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false,capture:"encryptionOid"},{name:"AlgorithmIdentifier.parameters",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,captureAsn1:"encryptionParams"}]},{name:"EncryptedPrivateKeyInfo.encryptedData",tagClass:t.Class.UNIVERSAL,type:t.Type.OCTETSTRING,constructed:false,capture:"encryptedData"}]};var C={name:"PBES2Algorithms",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"PBES2Algorithms.keyDerivationFunc",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"PBES2Algorithms.keyDerivationFunc.oid",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false,capture:"kdfOid"},{name:"PBES2Algorithms.params",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"PBES2Algorithms.params.salt",tagClass:t.Class.UNIVERSAL,type:t.Type.OCTETSTRING,constructed:false,capture:"kdfSalt"},{name:"PBES2Algorithms.params.iterationCount",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,onstructed:true,capture:"kdfIterationCount"}]}]},{name:"PBES2Algorithms.encryptionScheme",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"PBES2Algorithms.encryptionScheme.oid",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false,capture:"encOid"},{name:"PBES2Algorithms.encryptionScheme.iv",tagClass:t.Class.UNIVERSAL,type:t.Type.OCTETSTRING,constructed:false,capture:"encIv"}]}]};var F={name:"pkcs-12PbeParams",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"pkcs-12PbeParams.salt",tagClass:t.Class.UNIVERSAL,type:t.Type.OCTETSTRING,constructed:false,capture:"salt"},{name:"pkcs-12PbeParams.iterations",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"iterations"}]};var u={name:"rsapss",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"rsapss.hashAlgorithm",tagClass:t.Class.CONTEXT_SPECIFIC,type:0,constructed:true,value:[{name:"rsapss.hashAlgorithm.AlgorithmIdentifier",tagClass:t.Class.UNIVERSAL,type:t.Class.SEQUENCE,constructed:true,optional:true,value:[{name:"rsapss.hashAlgorithm.AlgorithmIdentifier.algorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false,capture:"hashOid"}]}]},{name:"rsapss.maskGenAlgorithm",tagClass:t.Class.CONTEXT_SPECIFIC,type:1,constructed:true,value:[{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier",tagClass:t.Class.UNIVERSAL,type:t.Class.SEQUENCE,constructed:true,optional:true,value:[{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier.algorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false,capture:"maskGenOid"},{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier.params",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"rsapss.maskGenAlgorithm.AlgorithmIdentifier.params.algorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false,capture:"maskGenHashOid"}]}]}]},{name:"rsapss.saltLength",tagClass:t.Class.CONTEXT_SPECIFIC,type:2,optional:true,value:[{name:"rsapss.saltLength.saltLength",tagClass:t.Class.UNIVERSAL,type:t.Class.INTEGER,constructed:false,capture:"saltLength"}]},{name:"rsapss.trailerField",tagClass:t.Class.CONTEXT_SPECIFIC,type:3,optional:true,value:[{name:"rsapss.trailer.trailer",tagClass:t.Class.UNIVERSAL,type:t.Class.INTEGER,constructed:false,capture:"trailer"}]}]};var k={name:"CertificationRequestInfo",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,captureAsn1:"certificationRequestInfo",value:[{name:"CertificationRequestInfo.integer",tagClass:t.Class.UNIVERSAL,type:t.Type.INTEGER,constructed:false,capture:"certificationRequestInfoVersion"},{name:"CertificationRequestInfo.subject",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,captureAsn1:"certificationRequestInfoSubject"},A,{name:"CertificationRequestInfo.attributes",tagClass:t.Class.CONTEXT_SPECIFIC,type:0,constructed:true,optional:true,capture:"certificationRequestInfoAttributes",value:[{name:"CertificationRequestInfo.attributes",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"CertificationRequestInfo.attributes.type",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false},{name:"CertificationRequestInfo.attributes.value",tagClass:t.Class.UNIVERSAL,type:t.Type.SET,constructed:true}]}]}]};var q={name:"CertificationRequest",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,captureAsn1:"csr",value:[k,{name:"CertificationRequest.signatureAlgorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.SEQUENCE,constructed:true,value:[{name:"CertificationRequest.signatureAlgorithm.algorithm",tagClass:t.Class.UNIVERSAL,type:t.Type.OID,constructed:false,capture:"csrSignatureOid"},{name:"CertificationRequest.signatureAlgorithm.parameters",tagClass:t.Class.UNIVERSAL,optional:true,captureAsn1:"csrSignatureParams"}]},{name:"CertificationRequest.signature",tagClass:t.Class.UNIVERSAL,type:t.Type.BITSTRING,constructed:false,capture:"csrSignature"}]};j.RDNAttributesAsArray=function(K,J){var M=[];var N,G,L;for(var H=0;H<K.value.length;++H){N=K.value[H];for(var I=0;I<N.value.length;++I){L={};G=N.value[I];L.type=t.derToOid(G.value[0].value);L.value=G.value[1].value;L.valueTagClass=G.value[1].type;if(L.type in m){L.name=m[L.type];if(L.name in w){L.shortName=w[L.name]}}if(J){J.update(L.type);J.update(L.value)}M.push(L)}}return M};j.CRIAttributesAsArray=function(I){var M=[];for(var J=0;J<I.length;++J){var H=I[J];var K=t.derToOid(H.value[0].value);var G=H.value[1].value;for(var N=0;N<G.length;++N){var L={};L.type=K;L.value=G[N].value;L.valueTagClass=G[N].type;if(L.type in m){L.name=m[L.type];if(L.name in w){L.shortName=w[L.name]}}M.push(L)}}return M};var i=function(K,H){if(typeof H==="string"){H={shortName:H}}var J=null;var G;for(var I=0;J===null&&I<K.attributes.length;++I){G=K.attributes[I];if(H.type&&H.type===G.type){J=G}else{if(H.name&&H.name===G.name){J=G}else{if(H.shortName&&H.shortName===G.shortName){J=G}}}}return J};var o=function(O){var R=[];var S,H,K;for(var Q=0;Q<O.value.length;++Q){K=O.value[Q];for(var L=0;L<K.value.length;++L){H=K.value[L];S={};S.id=t.derToOid(H.value[0].value);S.critical=false;if(H.value[1].type===t.Type.BOOLEAN){S.critical=(H.value[1].value.charCodeAt(0)!==0);S.value=H.value[2].value}else{S.value=H.value[1].value}if(S.id in m){S.name=m[S.id];if(S.name==="keyUsage"){var V=t.fromDer(S.value);var U=0;var T=0;if(V.value.length>1){U=V.value.charCodeAt(1);T=V.value.length>2?V.value.charCodeAt(2):0}S.digitalSignature=(U&128)===128;S.nonRepudiation=(U&64)===64;S.keyEncipherment=(U&32)===32;S.dataEncipherment=(U&16)===16;S.keyAgreement=(U&8)===8;S.keyCertSign=(U&4)===4;S.cRLSign=(U&2)===2;S.encipherOnly=(U&1)===1;S.decipherOnly=(T&128)===128}else{if(S.name==="basicConstraints"){var V=t.fromDer(S.value);if(V.value.length>0){S.cA=(V.value[0].value.charCodeAt(0)!==0)}else{S.cA=false}if(V.value.length>1){var P=p.util.createBuffer(V.value[1].value);S.pathLenConstraint=P.getInt(P.length()<<3)}}else{if(S.name==="extKeyUsage"){var V=t.fromDer(S.value);for(var N=0;N<V.value.length;++N){var J=t.derToOid(V.value[N].value);if(J in m){S[m[J]]=true}else{S[J]=true}}}else{if(S.name==="subjectAltName"||S.name==="issuerAltName"){S.altNames=[];var M;var V=t.fromDer(S.value);for(var I=0;I<V.value.length;++I){M=V.value[I];var G={type:M.type,value:M.value};S.altNames.push(G);switch(M.type){case 1:case 2:case 6:break;case 7:break;case 8:G.oid=t.derToOid(M.value);break;default:}}}}}}}R.push(S)}}return R};j.pemToDer=function(G){var H=p.pem.decode(G)[0];if(H.procType&&H.procType.type==="ENCRYPTED"){throw {message:"Could not convert PEM to DER; PEM is encrypted."}}return p.util.createBuffer(H.body)};var r=function(G){var H=G.toString(16);if(H[0]>="8"){H="00"+H}return p.util.hexToBytes(H)};var g=function(I,J,G){var K={};if(I!==m["RSASSA-PSS"]){return K}if(G){K={hash:{algorithmOid:m.sha1},mgf:{algorithmOid:m.mgf1,hash:{algorithmOid:m.sha1}},saltLength:20}}var H={};var L=[];if(!t.validate(J,u,H,L)){throw {message:"Cannot read RSASSA-PSS parameter block.",errors:L}}if(H.hashOid!==undefined){K.hash=K.hash||{};K.hash.algorithmOid=t.derToOid(H.hashOid)}if(H.maskGenOid!==undefined){K.mgf=K.mgf||{};K.mgf.algorithmOid=t.derToOid(H.maskGenOid);K.mgf.hash=K.mgf.hash||{};K.mgf.hash.algorithmOid=t.derToOid(H.maskGenHashOid)}if(H.saltLength!==undefined){K.saltLength=H.saltLength.charCodeAt(0)}return K};j.certificateFromPem=function(H,K,G){var J=p.pem.decode(H)[0];if(J.type!=="CERTIFICATE"&&J.type!=="X509 CERTIFICATE"&&J.type!=="TRUSTED CERTIFICATE"){throw {message:'Could not convert certificate from PEM; PEM header type is not "CERTIFICATE", "X509 CERTIFICATE", or "TRUSTED CERTIFICATE".',headerType:J.type}}if(J.procType&&J.procType.type==="ENCRYPTED"){throw {message:"Could not convert certificate from PEM; PEM is encrypted."}}var I=t.fromDer(J.body,G);return j.certificateFromAsn1(I,K)};j.certificateToPem=function(G,I){var H={type:"CERTIFICATE",body:t.toDer(j.certificateToAsn1(G)).getBytes()};return p.pem.encode(H,{maxline:I})};j.publicKeyFromPem=function(G){var I=p.pem.decode(G)[0];if(I.type!=="PUBLIC KEY"&&I.type!=="RSA PUBLIC KEY"){throw {message:'Could not convert public key from PEM; PEM header type is not "PUBLIC KEY" or "RSA PUBLIC KEY".',headerType:I.type}}if(I.procType&&I.procType.type==="ENCRYPTED"){throw {message:"Could not convert public key from PEM; PEM is encrypted."}};var H=t.fromDer(I.body);return j.publicKeyFromAsn1(H)};j.publicKeyToPem=function(G,I){var H={type:"PUBLIC KEY",body:t.toDer(j.publicKeyToAsn1(G)).getBytes()};return p.pem.encode(H,{maxline:I})};j.publicKeyToRSAPublicKeyPem=function(G,I){var H={type:"RSA PUBLIC KEY",body:t.toDer(j.publicKeyToRSAPublicKey(G)).getBytes()};return p.pem.encode(H,{maxline:I})};j.privateKeyFromPem=function(G){var I=p.pem.decode(G)[0];if(I.type!=="PRIVATE KEY"&&I.type!=="RSA PRIVATE KEY"){throw {message:'Could not convert private key from PEM; PEM header type is not "PRIVATE KEY" or "RSA PRIVATE KEY".',headerType:I.type}}if(I.procType&&I.procType.type==="ENCRYPTED"){throw {message:"Could not convert private key from PEM; PEM is encrypted."}}var H=t.fromDer(I.body);return j.privateKeyFromAsn1(H)};j.privateKeyToPem=function(G,I){var H={type:"RSA PRIVATE KEY",body:t.toDer(j.privateKeyToAsn1(G)).getBytes()};return p.pem.encode(H,{maxline:I})};j.certificationRequestFromPem=function(H,K,G){var J=p.pem.decode(H)[0];if(J.type!=="CERTIFICATE REQUEST"){throw {message:'Could not convert certification request from PEM; PEM header type is not "CERTIFICATE REQUEST".',headerType:J.type}}if(J.procType&&J.procType.type==="ENCRYPTED"){throw {message:"Could not convert certification request from PEM; PEM is encrypted."}}var I=t.fromDer(J.body,G);return j.certificationRequestFromAsn1(I,K)};j.certificationRequestToPem=function(G,I){var H={type:"CERTIFICATE REQUEST",body:t.toDer(j.certificationRequestToAsn1(G)).getBytes()};return p.pem.encode(H,{maxline:I})};j.createCertificate=function(){var G={};G.version=2;G.serialNumber="00";G.signatureOid=null;G.signature=null;G.siginfo={};G.siginfo.algorithmOid=null;G.validity={};G.validity.notBefore=new Date();G.validity.notAfter=new Date();G.issuer={};G.issuer.getField=function(I){return i(G.issuer,I)};G.issuer.addField=function(I){H([I]);G.issuer.attributes.push(I)};G.issuer.attributes=[];G.issuer.hash=null;G.subject={};G.subject.getField=function(I){return i(G.subject,I)};G.subject.addField=function(I){H([I]);G.subject.attributes.push(I)};G.subject.attributes=[];G.subject.hash=null;G.extensions=[];G.publicKey=null;G.md=null;var H=function(J){var I;for(var K=0;K<J.length;++K){I=J[K];if(typeof(I.name)==="undefined"){if(I.type&&I.type in j.oids){I.name=j.oids[I.type]}else{if(I.shortName&&I.shortName in w){I.name=j.oids[w[I.shortName]]}}}if(typeof(I.type)==="undefined"){if(I.name&&I.name in j.oids){I.type=j.oids[I.name]}else{throw {message:"Attribute type not specified.",attribute:I}}}if(typeof(I.shortName)==="undefined"){if(I.name&&I.name in w){I.shortName=w[I.name]}}if(typeof(I.value)==="undefined"){throw {message:"Attribute value not specified.",attribute:I}}}};G.setSubject=function(I,J){H(I);G.subject.attributes=I;delete G.subject.uniqueId;if(J){G.subject.uniqueId=J}G.subject.hash=null};G.setIssuer=function(I,J){H(I);G.issuer.attributes=I;delete G.issuer.uniqueId;if(J){G.issuer.uniqueId=J}G.issuer.hash=null};G.setExtensions=function(L){var P;for(var N=0;N<L.length;++N){P=L[N];if(typeof(P.name)==="undefined"){if(P.id&&P.id in j.oids){P.name=j.oids[P.id]}}if(typeof(P.id)==="undefined"){if(P.name&&P.name in j.oids){P.id=j.oids[P.name]}else{throw {message:"Extension ID not specified.",extension:P}}}if(typeof(P.value)==="undefined"){if(P.name==="keyUsage"){var K=0;var R=0;var Q=0;if(P.digitalSignature){R|=128;K=7}if(P.nonRepudiation){R|=64;K=6}if(P.keyEncipherment){R|=32;K=5}if(P.dataEncipherment){R|=16;K=4}if(P.keyAgreement){R|=8;K=3}if(P.keyCertSign){R|=4;K=2}if(P.cRLSign){R|=2;K=1}if(P.encipherOnly){R|=1;K=0}if(P.decipherOnly){Q|=128;K=7}var S=String.fromCharCode(K);if(Q!==0){S+=String.fromCharCode(R)+String.fromCharCode(Q)}else{if(R!==0){S+=String.fromCharCode(R)}}P.value=t.create(t.Class.UNIVERSAL,t.Type.BITSTRING,false,S)}else{if(P.name==="basicConstraints"){P.value=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[]);if(P.cA){P.value.value.push(t.create(t.Class.UNIVERSAL,t.Type.BOOLEAN,false,String.fromCharCode(255)))}if(P.pathLenConstraint){var O=P.pathLenConstraint;var M=p.util.createBuffer();M.putInt(O,O.toString(2).length);P.value.value.push(t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,M.getBytes()))}}else{if(P.name==="extKeyUsage"){P.value=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[]);var U=P.value.value;for(var T in P){if(P[T]!==true){continue}if(T in m){U.push(t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(m[T]).getBytes()))}else{if(T.indexOf(".")!==-1){U.push(t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(T).getBytes()))}}}}else{if(P.name==="subjectAltName"||P.name==="issuerAltName"){P.value=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[]);var I;for(var J=0;J<P.altNames.length;++J){I=P.altNames[J];var S=I.value;if(I.type===8){S=t.oidToDer(S)}P.value.value.push(t.create(t.Class.CONTEXT_SPECIFIC,I.type,false,S))}}}}}if(typeof(P.value)==="undefined"){throw {message:"Extension value not specified.",extension:P}}}}G.extensions=L};G.getExtension=function(I){if(typeof I==="string"){I={name:I}}var L=null;var K;for(var J=0;L===null&&J<G.extensions.length;++J){K=G.extensions[J];if(I.id&&K.id===I.id){L=K}else{if(I.name&&K.name===I.name){L=K}}}return L};G.sign=function(J){G.signatureOid=m.sha1withRSAEncryption;G.siginfo.algorithmOid=m.sha1withRSAEncryption;G.md=p.md.sha1.create();G.tbsCertificate=j.getTBSCertificate(G);var I=t.toDer(G.tbsCertificate);G.md.update(I.getBytes());G.signature=J.sign(G.md)};G.verify=function(I){var N=false;var O=I.md;if(O===null){if(I.signatureOid in m){var K=m[I.signatureOid];switch(K){case"sha1withRSAEncryption":O=p.md.sha1.create();break;case"md5withRSAEncryption":O=p.md.md5.create();break;case"sha256WithRSAEncryption":O=p.md.sha256.create();break;case"RSASSA-PSS":O=p.md.sha256.create();break}}if(O===null){throw {message:"Could not compute certificate digest. Unknown signature OID.",signatureOid:I.signatureOid}}var P=I.tbsCertificate||j.getTBSCertificate(I);var Q=t.toDer(P);O.update(Q.getBytes())}if(O!==null){var J=undefined;switch(I.signatureOid){case m.sha1withRSAEncryption:J=undefined;break;case m["RSASSA-PSS"]:var M,L;M=m[I.signatureParameters.mgf.hash.algorithmOid];if(M===undefined||p.md[M]===undefined){throw {message:"Unsupported MGF hash function.",oid:I.signatureParameters.mgf.hash.algorithmOid,name:M}}L=m[I.signatureParameters.mgf.algorithmOid];if(L===undefined||p.mgf[L]===undefined){throw {message:"Unsupported MGF function.",oid:I.signatureParameters.mgf.algorithmOid,name:L}}L=p.mgf[L].create(p.md[M].create());M=m[I.signatureParameters.hash.algorithmOid];if(M===undefined||p.md[M]===undefined){throw {message:"Unsupported RSASSA-PSS hash function.",oid:I.signatureParameters.hash.algorithmOid,name:M}}J=p.pss.create(p.md[M].create(),L,I.signatureParameters.saltLength);break}N=G.publicKey.verify(O.digest().getBytes(),I.signature,J)}return N};G.isIssuer=function(L){var N=false;var J=G.issuer;var K=L.subject;if(J.hash&&K.hash){N=(J.hash===K.hash)}else{if(J.attributes.length===K.attributes.length){N=true;var I,M;for(var O=0;N&&O<J.attributes.length;++O){I=J.attributes[O];M=K.attributes[O];if(I.type!==M.type||I.value!==M.value){N=false}}}}return N};return G};j.certificateFromAsn1=function(K,R){var S={};var Q=[];if(!t.validate(K,x,S,Q)){throw {message:"Cannot read X.509 certificate. ASN.1 object is not an X509v3 Certificate.",errors:Q}}if(typeof S.certSignature!=="string"){var J="\x00";for(var L=0;L<S.certSignature.length;++L){J+=t.toDer(S.certSignature[L]).getBytes()}S.certSignature=J}var I=t.derToOid(S.publicKeyOid);if(I!==j.oids.rsaEncryption){throw {message:"Cannot read public key. OID is not RSA."}}var M=j.createCertificate();M.version=S.certVersion?S.certVersion.charCodeAt(0):0;var G=p.util.createBuffer(S.certSerialNumber);M.serialNumber=G.toHex();M.signatureOid=p.asn1.derToOid(S.certSignatureOid);M.signatureParameters=g(M.signatureOid,S.certSignatureParams,true);M.siginfo.algorithmOid=p.asn1.derToOid(S.certinfoSignatureOid);M.siginfo.parameters=g(M.siginfo.algorithmOid,S.certinfoSignatureParams,false);var H=p.util.createBuffer(S.certSignature);++H.read;M.signature=H.getBytes();var O=[];if(S.certValidity1UTCTime!==undefined){O.push(t.utcTimeToDate(S.certValidity1UTCTime))}if(S.certValidity2GeneralizedTime!==undefined){O.push(t.generalizedTimeToDate(S.certValidity2GeneralizedTime))}if(S.certValidity3UTCTime!==undefined){O.push(t.utcTimeToDate(S.certValidity3UTCTime))}if(S.certValidity4GeneralizedTime!==undefined){O.push(t.generalizedTimeToDate(S.certValidity4GeneralizedTime))}if(O.length>2){throw {message:"Cannot read notBefore/notAfter validity times; more than two times were provided in the certificate."}}if(O.length<2){throw {message:"Cannot read notBefore/notAfter validity times; they were not provided as either UTCTime or GeneralizedTime."}}M.validity.notBefore=O[0];M.validity.notAfter=O[1];M.tbsCertificate=S.tbsCertificate;if(R){M.md=null;if(M.signatureOid in m){var I=m[M.signatureOid];switch(I){case"sha1withRSAEncryption":M.md=p.md.sha1.create();break;case"md5withRSAEncryption":M.md=p.md.md5.create();break;case"sha256WithRSAEncryption":M.md=p.md.sha256.create();break;case"RSASSA-PSS":M.md=p.md.sha256.create();break}}if(M.md===null){throw {message:"Could not compute certificate digest. Unknown signature OID.",signatureOid:M.signatureOid}}var T=t.toDer(M.tbsCertificate);M.md.update(T.getBytes())}var N=p.md.sha1.create();M.issuer.getField=function(U){return i(M.issuer,U)};M.issuer.addField=function(U){_fillMissingFields([U]);M.issuer.attributes.push(U)};M.issuer.attributes=j.RDNAttributesAsArray(S.certIssuer,N);if(S.certIssuerUniqueId){M.issuer.uniqueId=S.certIssuerUniqueId}M.issuer.hash=N.digest().toHex();var P=p.md.sha1.create();M.subject.getField=function(U){return i(M.subject,U)};M.subject.addField=function(U){_fillMissingFields([U]);M.subject.attributes.push(U)};M.subject.attributes=j.RDNAttributesAsArray(S.certSubject,P);if(S.certSubjectUniqueId){M.subject.uniqueId=S.certSubjectUniqueId}M.subject.hash=P.digest().toHex();if(S.certExtensions){M.extensions=o(S.certExtensions)}else{M.extensions=[]}M.publicKey=j.publicKeyFromAsn1(S.subjectPublicKeyInfo);return M};j.certificationRequestFromAsn1=function(I,O){var P={};var N=[];if(!t.validate(I,q,P,N)){throw {message:"Cannot read PKCS#10 certificate request. ASN.1 object is not a PKCS#10 CertificationRequest.",errors:N}}if(typeof P.csrSignature!=="string"){var L="\x00";for(var J=0;J<P.csrSignature.length;++J){L+=t.toDer(P.csrSignature[J]).getBytes()}P.csrSignature=L}var H=t.derToOid(P.publicKeyOid);if(H!==j.oids.rsaEncryption){throw {message:"Cannot read public key. OID is not RSA."}}var K=j.createCertificationRequest();K.version=P.csrVersion?P.csrVersion.charCodeAt(0):0;K.signatureOid=p.asn1.derToOid(P.csrSignatureOid);K.signatureParameters=g(K.signatureOid,P.csrSignatureParams,true);K.siginfo.algorithmOid=p.asn1.derToOid(P.csrSignatureOid);K.siginfo.parameters=g(K.siginfo.algorithmOid,P.csrSignatureParams,false);var G=p.util.createBuffer(P.csrSignature);++G.read;K.signature=G.getBytes();K.certificationRequestInfo=P.certificationRequestInfo;if(O){K.md=null;if(K.signatureOid in m){var H=m[K.signatureOid];switch(H){case"sha1withRSAEncryption":K.md=p.md.sha1.create();break;case"md5withRSAEncryption":K.md=p.md.md5.create();break;case"sha256WithRSAEncryption":K.md=p.md.sha256.create();break;case"RSASSA-PSS":K.md=p.md.sha256.create();break}}if(K.md===null){throw {message:"Could not compute certification request digest. Unknown signature OID.",signatureOid:K.signatureOid}}var Q=t.toDer(K.certificationRequestInfo);K.md.update(Q.getBytes())}var M=p.md.sha1.create();K.subject.getField=function(R){return i(K.subject,R)};K.subject.addField=function(R){_fillMissingFields([R]);K.subject.attributes.push(R)};K.subject.attributes=j.RDNAttributesAsArray(P.certificationRequestInfoSubject,M);K.subject.hash=M.digest().toHex();K.publicKey=j.publicKeyFromAsn1(P.subjectPublicKeyInfo);K.getAttribute=function(R){return i(K.attributes,R)};K.addAttribute=function(R){_fillMissingFields([R]);K.attributes.push(R)};K.attributes=j.CRIAttributesAsArray(P.certificationRequestInfoAttributes);return K};j.createCertificationRequest=function(){var G={};G.version=0;G.signatureOid=null;G.signature=null;G.siginfo={};G.siginfo.algorithmOid=null;G.subject={};G.subject.getField=function(I){return i(G.subject,I)};G.subject.addField=function(I){H([I]);G.subject.attributes.push(I)};G.subject.attributes=[];G.subject.hash=null;G.publicKey=null;G.attributes=[];G.getAttribute=function(I){return i(G.attributes,I)};G.addAttribute=function(I){H([I]);G.attributes.push(I)};G.md=null;var H=function(J){var I;for(var K=0;K<J.length;++K){I=J[K];if(typeof(I.name)==="undefined"){if(I.type&&I.type in j.oids){I.name=j.oids[I.type]}else{if(I.shortName&&I.shortName in w){I.name=j.oids[w[I.shortName]]}}}if(typeof(I.type)==="undefined"){if(I.name&&I.name in j.oids){I.type=j.oids[I.name]}else{throw {message:"Attribute type not specified.",attribute:I}}}if(typeof(I.shortName)==="undefined"){if(I.name&&I.name in w){I.shortName=w[I.name]}}if(typeof(I.value)==="undefined"){throw {message:"Attribute value not specified.",attribute:I}}}};G.setSubject=function(I){H(I);G.subject.attributes=I;G.subject.hash=null};G.setAttributes=function(I){H(I);G.attributes=I};G.sign=function(J){G.signatureOid=m.sha1withRSAEncryption;G.siginfo.algorithmOid=m.sha1withRSAEncryption;G.md=p.md.sha1.create();G.certificationRequestInfo=j.getCertificationRequestInfo(G);var I=t.toDer(G.certificationRequestInfo);G.md.update(I.getBytes());G.signature=J.sign(G.md)};G.verify=function(){var P=false;var M=G.md;if(M===null){if(G.signatureOid in m){var L=m[G.signatureOid];switch(L){case"sha1withRSAEncryption":M=p.md.sha1.create();break;case"md5withRSAEncryption":M=p.md.md5.create();break;case"sha256WithRSAEncryption":M=p.md.sha256.create();break;case"RSASSA-PSS":M=p.md.sha256.create();break}}if(M===null){throw {message:"Could not compute certification request digest. Unknown signature OID.",signatureOid:G.signatureOid}}var J=G.certificationRequestInfo||j.getCertificationRequestInfo(G);var I=t.toDer(J);M.update(I.getBytes())}if(M!==null){var K=undefined;switch(G.signatureOid){case m.sha1withRSAEncryption:K=undefined;break;case m["RSASSA-PSS"]:var O,N;O=m[G.signatureParameters.mgf.hash.algorithmOid];if(O===undefined||p.md[O]===undefined){throw {message:"Unsupported MGF hash function.",oid:G.signatureParameters.mgf.hash.algorithmOid,name:O}}N=m[G.signatureParameters.mgf.algorithmOid];if(N===undefined||p.mgf[N]===undefined){throw {message:"Unsupported MGF function.",oid:G.signatureParameters.mgf.algorithmOid,name:N}}N=p.mgf[N].create(p.md[O].create());O=m[G.signatureParameters.hash.algorithmOid];if(O===undefined||p.md[O]===undefined){throw {message:"Unsupported RSASSA-PSS hash function.",oid:G.signatureParameters.hash.algorithmOid,name:O}}K=p.pss.create(p.md[O].create(),N,G.signatureParameters.saltLength);break}P=G.publicKey.verify(M.digest().getBytes(),G.signature,K)}return P};return G};function n(M){var L=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[]);var G,N;var H=M.attributes;for(var I=0;I<H.length;++I){G=H[I];var K=G.value;var J=t.Type.PRINTABLESTRING;if("valueTagClass" in G){J=G.valueTagClass;if(J===t.Type.UTF8){K=p.util.encodeUtf8(K)}}N=t.create(t.Class.UNIVERSAL,t.Type.SET,true,[t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(G.type).getBytes()),t.create(t.Class.UNIVERSAL,J,false,K)])]);L.value.push(N)}return L}function h(J){var M=t.create(t.Class.CONTEXT_SPECIFIC,3,true,[]);var H=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[]);M.value.push(H);var K,G;for(var I=0;I<J.length;++I){K=J[I];G=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[]);H.value.push(G);G.value.push(t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(K.id).getBytes()));if(K.critical){G.value.push(t.create(t.Class.UNIVERSAL,t.Type.BOOLEAN,false,String.fromCharCode(255)))}var L=K.value;if(typeof K.value!=="string"){L=t.toDer(L).getBytes()}G.value.push(t.create(t.Class.UNIVERSAL,t.Type.OCTETSTRING,false,L))}return M}function B(G,I){switch(G){case m["RSASSA-PSS"]:var H=[];if(I.hash.algorithmOid!==undefined){H.push(t.create(t.Class.CONTEXT_SPECIFIC,0,true,[t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(I.hash.algorithmOid).getBytes()),t.create(t.Class.UNIVERSAL,t.Type.NULL,false,"")])]))}if(I.mgf.algorithmOid!==undefined){H.push(t.create(t.Class.CONTEXT_SPECIFIC,1,true,[t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(I.mgf.algorithmOid).getBytes()),t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(I.mgf.hash.algorithmOid).getBytes()),t.create(t.Class.UNIVERSAL,t.Type.NULL,false,"")])])]))}if(I.saltLength!==undefined){H.push(t.create(t.Class.CONTEXT_SPECIFIC,2,true,[t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,String.fromCharCode(I.saltLength))]))}return t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,H);default:return t.create(t.Class.UNIVERSAL,t.Type.NULL,false,"")}}function E(K){var N=t.create(t.Class.CONTEXT_SPECIFIC,0,true,[]);if(K.attributes.length===0){return N}var I=K.attributes;for(var J=0;J<I.length;++J){var G=I[J];var M=G.value;var L=t.Type.UTF8;if("valueTagClass" in G){L=G.valueTagClass}if(L===t.Type.UTF8){M=p.util.encodeUtf8(M)}var H=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(G.type).getBytes()),t.create(t.Class.UNIVERSAL,t.Type.SET,true,[t.create(t.Class.UNIVERSAL,L,false,M)])]);N.value.push(H)}return N}j.getTBSCertificate=function(H){var G=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.CONTEXT_SPECIFIC,0,true,[t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,String.fromCharCode(H.version))]),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,p.util.hexToBytes(H.serialNumber)),t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(H.siginfo.algorithmOid).getBytes()),B(H.siginfo.algorithmOid,H.siginfo.parameters)]),n(H.issuer),t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.UTCTIME,false,t.dateToUtcTime(H.validity.notBefore)),t.create(t.Class.UNIVERSAL,t.Type.UTCTIME,false,t.dateToUtcTime(H.validity.notAfter))]),n(H.subject),j.publicKeyToAsn1(H.publicKey)]);if(H.issuer.uniqueId){G.value.push(t.create(t.Class.CONTEXT_SPECIFIC,1,true,[t.create(t.Class.UNIVERSAL,t.Type.BITSTRING,false,String.fromCharCode(0)+H.issuer.uniqueId)]))}if(H.subject.uniqueId){G.value.push(t.create(t.Class.CONTEXT_SPECIFIC,2,true,[t.create(t.Class.UNIVERSAL,t.Type.BITSTRING,false,String.fromCharCode(0)+H.subject.uniqueId)]))}if(H.extensions.length>0){G.value.push(h(H.extensions))}return G};j.getCertificationRequestInfo=function(H){var G=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,String.fromCharCode(H.version)),n(H.subject),j.publicKeyToAsn1(H.publicKey),E(H)]);return G};j.distinguishedNameToAsn1=function(G){return n(G)};j.certificateToAsn1=function(H){var G=H.tbsCertificate||j.getTBSCertificate(H);return t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[G,t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(H.signatureOid).getBytes()),B(H.signatureOid,H.signatureParameters)]),t.create(t.Class.UNIVERSAL,t.Type.BITSTRING,false,String.fromCharCode(0)+H.signature)])};j.certificationRequestToAsn1=function(H){var G=H.certificationRequestInfo||j.getCertificationRequestInfo(H);return t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[G,t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(H.signatureOid).getBytes()),B(H.signatureOid,H.signatureParameters)]),t.create(t.Class.UNIVERSAL,t.Type.BITSTRING,false,String.fromCharCode(0)+H.signature)])};j.createCaStore=function(J){var I={certs:{}};I.getIssuer=function(K){var M=null;if(!K.issuer.hash){var L=p.md.sha1.create();K.issuer.attributes=j.RDNAttributesAsArray(n(K.issuer),L);K.issuer.hash=L.digest().toHex()}if(K.issuer.hash in I.certs){M=I.certs[K.issuer.hash];if(p.util.isArray(M)){throw {message:"Resolving multiple issuer matches not implemented yet."}}}return M};I.addCertificate=function(L){if(typeof L==="string"){L=p.pki.certificateFromPem(L)}if(!L.subject.hash){var M=p.md.sha1.create();L.subject.attributes=j.RDNAttributesAsArray(n(L.subject),M);L.subject.hash=M.digest().toHex()}if(L.subject.hash in I.certs){var K=I.certs[L.subject.hash];if(!p.util.isArray(K)){K=[K]}K.push(L)}else{I.certs[L.subject.hash]=L}};if(J){for(var H=0;H<J.length;++H){var G=J[H];I.addCertificate(G)}}return I};j.certificateError={bad_certificate:"forge.pki.BadCertificate",unsupported_certificate:"forge.pki.UnsupportedCertificate",certificate_revoked:"forge.pki.CertificateRevoked",certificate_expired:"forge.pki.CertificateExpired",certificate_unknown:"forge.pki.CertificateUnknown",unknown_ca:"forge.pki.UnknownCertificateAuthority"};j.verifyCertificateChain=function(M,O,G){O=O.slice(0);var I=O.slice(0);var H=new Date();var K=true;var Q=null;var Y=0;var L=null;do{var W=O.shift();if(H<W.validity.notBefore||H>W.validity.notAfter){Q={message:"Certificate is not valid yet or has expired.",error:j.certificateError.certificate_expired,notBefore:W.validity.notBefore,notAfter:W.validity.notAfter,now:H}}else{var P=false;if(O.length>0){L=O[0];try{P=L.verify(W)}catch(T){}}else{var V=M.getIssuer(W);if(V===null){Q={message:"Certificate is not trusted.",error:j.certificateError.unknown_ca}}else{if(!p.util.isArray(V)){V=[V]}while(!P&&V.length>0){L=V.shift();try{P=L.verify(W)}catch(T){}}}}if(Q===null&&!P){Q={message:"Certificate signature is invalid.",error:j.certificateError.bad_certificate}}}if(Q===null&&!W.isIssuer(L)){Q={message:"Certificate issuer is invalid.",error:j.certificateError.bad_certificate}}if(Q===null){var Z={keyUsage:true,basicConstraints:true};for(var S=0;Q===null&&S<W.extensions.length;++S){var J=W.extensions[S];if(J.critical&&!(J.name in Z)){Q={message:"Certificate has an unsupported critical extension.",error:j.certificateError.unsupported_certificate}}}}if(!K||(O.length===0&&!L)){var R=W.getExtension("basicConstraints");var N=W.getExtension("keyUsage");if(N!==null){if(!N.keyCertSign||R===null){Q={message:"Certificate keyUsage or basicConstraints conflict or indicate that the certificate is not a CA. If the certificate is the only one in the chain or isn't the first then the certificate must be a valid CA.",error:j.certificateError.bad_certificate}}}if(Q===null&&R!==null&&!R.cA){Q={message:"Certificate basicConstraints indicates the certificate is not a CA.",error:j.certificateError.bad_certificate}}}var U=(Q===null)?true:Q.error;var X=G?G(U,Y,I):U;if(X===true){Q=null}else{if(U===true){Q={message:"The application rejected the certificate.",error:j.certificateError.bad_certificate}}if(X||X===0){if(typeof X==="object"&&!p.util.isArray(X)){if(X.message){Q.message=X.message}if(X.error){Q.error=X.error}}else{if(typeof X==="string"){Q.error=X}}}throw Q}K=false;++Y}while(O.length>0);return true};j.publicKeyFromAsn1=function(J){var G={};var L=[];if(t.validate(J,A,G,L)){var H=t.derToOid(G.publicKeyOid);if(H!==j.oids.rsaEncryption){throw {message:"Cannot read public key. Unknown OID.",oid:H}}J=G.rsaPublicKey}L=[];if(!t.validate(J,v,G,L)){throw {message:"Cannot read public key. ASN.1 object does not contain an RSAPublicKey.",errors:L}}var K=p.util.createBuffer(G.publicKeyModulus).toHex();var I=p.util.createBuffer(G.publicKeyExponent).toHex();return j.setRsaPublicKey(new BigInteger(K,16),new BigInteger(I,16))};j.publicKeyToAsn1=j.publicKeyToSubjectPublicKeyInfo=function(G){return t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(j.oids.rsaEncryption).getBytes()),t.create(t.Class.UNIVERSAL,t.Type.NULL,false,"")]),t.create(t.Class.UNIVERSAL,t.Type.BITSTRING,false,[j.publicKeyToRSAPublicKey(G)])])};j.publicKeyToRSAPublicKey=function(G){return t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,r(G.n)),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,r(G.e))])};j.privateKeyFromAsn1=function(J){var P={};var O=[];if(t.validate(J,s,P,O)){J=t.fromDer(p.util.createBuffer(P.privateKey))}P={};O=[];if(!t.validate(J,z,P,O)){throw {message:"Cannot read private key. ASN.1 object does not contain an RSAPrivateKey.",errors:O}}var I,K,L,H,G,N,M,Q;I=p.util.createBuffer(P.privateKeyModulus).toHex();K=p.util.createBuffer(P.privateKeyPublicExponent).toHex();L=p.util.createBuffer(P.privateKeyPrivateExponent).toHex();H=p.util.createBuffer(P.privateKeyPrime1).toHex();G=p.util.createBuffer(P.privateKeyPrime2).toHex();N=p.util.createBuffer(P.privateKeyExponent1).toHex();M=p.util.createBuffer(P.privateKeyExponent2).toHex();Q=p.util.createBuffer(P.privateKeyCoefficient).toHex();return j.setRsaPrivateKey(new BigInteger(I,16),new BigInteger(K,16),new BigInteger(L,16),new BigInteger(H,16),new BigInteger(G,16),new BigInteger(N,16),new BigInteger(M,16),new BigInteger(Q,16))};j.privateKeyToAsn1=j.privateKeyToRSAPrivateKey=function(G){return t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,String.fromCharCode(0)),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,r(G.n)),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,r(G.e)),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,r(G.d)),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,r(G.p)),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,r(G.q)),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,r(G.dP)),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,r(G.dQ)),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,r(G.qInv))])};j.wrapRsaPrivateKey=function(G){return t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,"\x00"),t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(m.rsaEncryption).getBytes()),t.create(t.Class.UNIVERSAL,t.Type.NULL,false,"")]),t.create(t.Class.UNIVERSAL,t.Type.OCTETSTRING,false,t.toDer(G).getBytes())])};j.encryptPrivateKeyInfo=function(J,R,T){T=T||{};T.saltSize=T.saltSize||8;T.count=T.count||2048;T.algorithm=T.algorithm||"aes128";var L=p.random.getBytes(T.saltSize);var P=T.count;var Q=p.util.createBuffer();Q.putInt16(P);var U;var M;var G;if(T.algorithm.indexOf("aes")===0){var S;if(T.algorithm==="aes128"){U=16;S=m["aes128-CBC"]}else{if(T.algorithm==="aes192"){U=24;S=m["aes192-CBC"]}else{if(T.algorithm==="aes256"){U=32;S=m["aes256-CBC"]}else{throw {message:"Cannot encrypt private key. Unknown encryption algorithm.",algorithm:T.algorithm}}}}var O=p.pkcs5.pbkdf2(R,L,P,U);var H=p.random.getBytes(16);var N=p.aes.createEncryptionCipher(O);N.start(H);N.update(t.toDer(J));N.finish();G=N.output.getBytes();M=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(m.pkcs5PBES2).getBytes()),t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(m.pkcs5PBKDF2).getBytes()),t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OCTETSTRING,false,L),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,Q.getBytes())])]),t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(S).getBytes()),t.create(t.Class.UNIVERSAL,t.Type.OCTETSTRING,false,H)])])])}else{if(T.algorithm==="3des"){U=24;var I=new p.util.ByteBuffer(L);var O=p.pkcs12.generateKey(R,I,1,P,U);var H=p.pkcs12.generateKey(R,I,2,P,U);var N=p.des.createEncryptionCipher(O);N.start(H);N.update(t.toDer(J));N.finish();G=N.output.getBytes();M=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OID,false,t.oidToDer(m["pbeWithSHAAnd3-KeyTripleDES-CBC"]).getBytes()),t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[t.create(t.Class.UNIVERSAL,t.Type.OCTETSTRING,false,L),t.create(t.Class.UNIVERSAL,t.Type.INTEGER,false,Q.getBytes())])])}else{throw {message:"Cannot encrypt private key. Unknown encryption algorithm.",algorithm:T.algorithm}}}var K=t.create(t.Class.UNIVERSAL,t.Type.SEQUENCE,true,[M,t.create(t.Class.UNIVERSAL,t.Type.OCTETSTRING,false,G)]);return K};j.pbe.getCipherForPBES2=function(G,I,O){var P={};var N=[];if(!t.validate(I,C,P,N)){throw {message:"Cannot read password-based-encryption algorithm parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.",errors:N}}G=t.derToOid(P.kdfOid);if(G!==j.oids.pkcs5PBKDF2){throw {message:"Cannot read encrypted private key. Unsupported key derivation function OID.",oid:G,supportedOids:["pkcs5PBKDF2"]}}G=t.derToOid(P.encOid);if(G!==j.oids["aes128-CBC"]&&G!==j.oids["aes192-CBC"]&&G!==j.oids["aes256-CBC"]){throw {message:"Cannot read encrypted private key. Unsupported encryption scheme OID.",oid:G,supportedOids:["aes128-CBC","aes192-CBC","aes256-CBC"]}}var J=P.kdfSalt;var M=p.util.createBuffer(P.kdfIterationCount);M=M.getInt(M.length()<<3);var Q;if(G===j.oids["aes128-CBC"]){Q=16}else{if(G===j.oids["aes192-CBC"]){Q=24}else{if(G===j.oids["aes256-CBC"]){Q=32}}}var L=p.pkcs5.pbkdf2(O,J,M,Q);var H=P.encIv;var K=p.aes.createDecryptionCipher(L);K.start(H);return K};j.pbe.getCipherForPKCS12PBE=function(H,J,P){var Q={};var M=[];if(!t.validate(J,F,Q,M)){throw {message:"Cannot read password-based-encryption algorithm parameters. ASN.1 object is not a supported EncryptedPrivateKeyInfo.",errors:M}}var K=p.util.createBuffer(Q.salt);var L=p.util.createBuffer(Q.iterations);L=L.getInt(L.length()<<3);var R,G,O;switch(H){case j.oids["pbeWithSHAAnd3-KeyTripleDES-CBC"]:R=24;G=8;O=p.des.startDecrypting;break;case j.oids["pbewithSHAAnd40BitRC2-CBC"]:R=5;G=8;O=function(U,T){var S=p.rc2.createDecryptionCipher(U,40);S.start(T,null);return S};break;default:throw {message:"Cannot read PKCS #12 PBE data block. Unsupported OID.",oid:H}}var N=p.pkcs12.generateKey(P,K,1,L,R);var I=p.pkcs12.generateKey(P,K,2,L,G);return O(N,I)};j.pbe.getCipher=function(H,I,G){switch(H){case j.oids.pkcs5PBES2:return j.pbe.getCipherForPBES2(H,I,G);break;case j.oids["pbeWithSHAAnd3-KeyTripleDES-CBC"]:case j.oids["pbewithSHAAnd40BitRC2-CBC"]:return j.pbe.getCipherForPKCS12PBE(H,I,G);break;default:throw {message:"Cannot read encrypted PBE data block. Unsupported OID.",oid:H,supportedOids:["pkcs5PBES2","pbeWithSHAAnd3-KeyTripleDES-CBC","pbewithSHAAnd40BitRC2-CBC"]}}};j.decryptPrivateKeyInfo=function(M,I){var L=null;var H={};var N=[];if(!t.validate(M,y,H,N)){throw {message:"Cannot read encrypted private key. ASN.1 object is not a supported EncryptedPrivateKeyInfo.",errors:N}}var J=t.derToOid(H.encryptionOid);var G=j.pbe.getCipher(J,H.encryptionParams,I);var K=p.util.createBuffer(H.encryptedData);G.update(K);if(G.finish()){L=t.fromDer(G.output)}return L};j.encryptedPrivateKeyToPem=function(G,I){var H={type:"ENCRYPTED PRIVATE KEY",body:t.toDer(G).getBytes()};return p.pem.encode(H,{maxline:I})};j.encryptedPrivateKeyFromPem=function(G){var H=p.pem.decode(G)[0];if(H.type!=="ENCRYPTED PRIVATE KEY"){throw {message:'Could not convert encrypted private key from PEM; PEM header type is "ENCRYPTED PRIVATE KEY".',headerType:H.type}}if(H.procType&&H.procType.type==="ENCRYPTED"){throw {message:"Could not convert encrypted private key from PEM; PEM is encrypted."}}return t.fromDer(H.body)};j.encryptRsaPrivateKey=function(L,N,P){P=P||{};if(!P.legacy){var I=j.wrapRsaPrivateKey(j.privateKeyToAsn1(L));I=j.encryptPrivateKeyInfo(I,N,P);return j.encryptedPrivateKeyToPem(I)}var M;var H;var Q;var O;switch(P.algorithm){case"aes128":M="AES-128-CBC";Q=16;H=p.random.getBytes(16);O=p.aes.createEncryptionCipher;break;case"aes192":M="AES-192-CBC";Q=24;H=p.random.getBytes(16);O=p.aes.createEncryptionCipher;break;case"aes256":M="AES-256-CBC";Q=32;H=p.random.getBytes(16);O=p.aes.createEncryptionCipher;break;case"3des":M="DES-EDE3-CBC";Q=24;H=p.random.getBytes(8);O=p.des.createEncryptionCipher;break;default:throw {message:'Could not encrypt RSA private key; unsupported encryption algorithm "'+P.algorithm+'".',algorithm:P.algorithm}}var K=D(N,H.substr(0,8),Q);var J=O(K);J.start(H);J.update(t.toDer(j.privateKeyToAsn1(L)));J.finish();var G={type:"RSA PRIVATE KEY",procType:{version:"4",type:"ENCRYPTED"},dekInfo:{algorithm:M,parameters:p.util.bytesToHex(H).toUpperCase()},body:J.output.getBytes()};return p.pem.encode(G)};j.decryptRsaPrivateKey=function(J,M){var I=null;var G=p.pem.decode(J)[0];if(G.type!=="ENCRYPTED PRIVATE KEY"&&G.type!=="PRIVATE KEY"&&G.type!=="RSA PRIVATE KEY"){throw {message:'Could not convert private key from PEM; PEM header type is not "ENCRYPTED PRIVATE KEY", "PRIVATE KEY", or "RSA PRIVATE KEY".',headerType:G.type}}if(G.procType&&G.procType.type==="ENCRYPTED"){var O;var N;switch(G.dekInfo.algorithm){case"DES-EDE3-CBC":O=24;N=p.des.createDecryptionCipher;break;case"AES-128-CBC":O=16;N=p.aes.createDecryptionCipher;break;case"AES-192-CBC":O=24;N=p.aes.createDecryptionCipher;break;case"AES-256-CBC":O=32;N=p.aes.createDecryptionCipher;break;case"RC2-40-CBC":O=5;N=function(P){return p.rc2.createDecryptionCipher(P,40)};break;case"RC2-64-CBC":O=8;N=function(P){return p.rc2.createDecryptionCipher(P,64)};break;case"RC2-128-CBC":O=16;N=function(P){return p.rc2.createDecryptionCipher(P,128)};break;default:throw {message:'Could not decrypt private key; unsupported encryption algorithm "'+G.dekInfo.algorithm+'".',algorithm:G.dekInfo.algorithm}}var H=p.util.hexToBytes(G.dekInfo.parameters);var L=D(M,H.substr(0,8),O);var K=N(L);K.start(H);K.update(p.util.createBuffer(G.body));if(K.finish()){I=K.output.getBytes()}else{return I}}else{I=G.body}if(G.type==="ENCRYPTED PRIVATE KEY"){I=j.decryptPrivateKeyInfo(t.fromDer(I),M)}else{I=t.fromDer(I)}if(I!==null){I=j.privateKeyFromAsn1(I)}return I};function D(H,J,G){var L=[l(H+J)];for(var K=16,I=1;K<G;++I,K+=16){L.push(l(L[I-1]+H+J))}return L.join("").substr(0,G)}function l(G){return p.md.md5.create().update(G).digest().getBytes()}j.setRsaPublicKey=j.rsa.setPublicKey;j.setRsaPrivateKey=j.rsa.setPrivateKey}var b="pki";var f=["./aes","./asn1","./des","./jsbn","./md","./mgf","./oids","./pem","./pbkdf2","./pkcs12","./pss","./random","./rc2","./rsa","./util"];var e=null;if(typeof define!=="function"){d(forge);return}var c=["require","module"].concat(f);var a=function(g,h){h.exports=function(k){var l=f.map(function(i){return g(i)}).concat(d);k=k||{};k.defined=k.defined||{};if(k.defined[b]){return k[b]}k.defined[b]=true;for(var j=0;j<l.length;++j){l[j](k)}return k[b]}};if(e){e(c,a)}else{if(typeof define==="function"){define([].concat(c),function(){a.apply(null,Array.prototype.slice.call(arguments,0))})}}})();(function(){function d(g){var i=g.pem=g.pem||{};i.encode=function(n,k){k=k||{};var m="-----BEGIN "+n.type+"-----\r\n";var o;if(n.procType){o={name:"Proc-Type",values:[String(n.procType.version),n.procType.type]};m+=h(o)}if(n.contentDomain){o={name:"Content-Domain",values:[n.contentDomain]};m+=h(o)}if(n.dekInfo){o={name:"DEK-Info",values:[n.dekInfo.algorithm]};if(n.dekInfo.parameters){o.values.push(n.dekInfo.parameters)}m+=h(o)}if(n.headers){for(var l=0;l<n.headers.length;++l){m+=h(n.headers[l])}}if(n.procType){m+="\r\n"}m+=g.util.encode64(n.body,k.maxline||64)+"\r\n";m+="-----END "+n.type+"-----\r\n";return m};i.decode=function(t){var q=[];var l=/\s*-----BEGIN ([A-Z0-9- ]+)-----\r?\n([\x21-\x7e\s]+?(?:\r?\n\r?\n))?([:A-Za-z0-9+\/=\s]+?)-----END \1-----/g;var s=/([\x21-\x7e]+):\s*([\x21-\x7e\s^:]+)/;var w=/\r?\n/;var r;while(true){r=l.exec(t);if(!r){break}var m={type:r[1],procType:null,contentDomain:null,dekInfo:null,headers:[],body:g.util.decode64(r[3])};q.push(m);if(!r[2]){continue}var y=r[2].split(w);var v=0;while(r&&v<y.length){var x=y[v].replace(/\s+$/,"");for(var k=v+1;k<y.length;++k){var p=y[k];if(!/\s/.test(p[0])){break}x+=p;v=k}r=x.match(s);if(r){var o={name:r[1],values:[]};var u=r[2].split(",");for(var n=0;n<u.length;++n){o.values.push(j(u[n]))}if(!m.procType){if(o.name!=="Proc-Type"){throw {message:'Invalid PEM formatted message. The first encapsulated header must be "Proc-Type".'}}else{if(o.values.length!==2){throw {message:'Invalid PEM formatted message. The "Proc-Type" header must have two subfields.'}}}m.procType={version:u[0],type:u[1]}}else{if(!m.contentDomain&&o.name==="Content-Domain"){m.contentDomain=u[0]||""}else{if(!m.dekInfo&&o.name==="DEK-Info"){if(o.values.length===0){throw {message:'Invalid PEM formatted message. The "DEK-Info" header must have at least one subfield.'}}m.dekInfo={algorithm:u[0],parameters:u[1]||null}}else{m.headers.push(o)}}}}++v}if(m.procType==="ENCRYPTED"&&!m.dekInfo){throw {message:'Invalid PEM formatted message. The "DEK-Info" header must be present if "Proc-Type" is "ENCRYPTED".'}}}if(q.length===0){throw {message:"Invalid PEM formatted message."}}return q};function h(q){var p=q.name+": ";var k=[];for(var l=0;l<q.values.length;++l){k.push(q.values[l].replace(/^(\S+\r\n)/,function(s,r){return" "+r}))}p+=k.join(",")+"\r\n";var n=0;var m=-1;for(var l=0;l<p.length;++l,++n){if(n>65&&m!==-1){var o=p[m];if(o===","){++m;o=" "}p=p.substr(0,m)+"\r\n"+o+p.substr(m+1);n=(l-m-1);m=-1;++l}if(p[l]===" "||p[l]==="\t"||p[l]===","){m=l}}return p}function j(k){return k.replace(/^\s+/,"")}}var b="pem";var f=["./util"];var e=null;if(typeof define!=="function"){d(forge);return}var c=["require","module"].concat(f);var a=function(g,h){h.exports=function(k){var l=f.map(function(i){return g(i)}).concat(d);k=k||{};k.defined=k.defined||{};if(k.defined[b]){return k[b]}k.defined[b]=true;for(var j=0;j<l.length;++j){l[j](k)}return k[b]}};if(e){e(c,a)}else{if(typeof define==="function"){define([].concat(c),function(){a.apply(null,Array.prototype.slice.call(arguments,0))})}}})();

			//tls.js (forge)

			/**
			 * A Javascript implementation of Transport Layer Security (TLS).
			 *
			 * @author Dave Longley
			 *
			 * Copyright (c) 2009-2012 Digital Bazaar, Inc.
			*/

			(function(){var N=forge;N.tls={};/*if(typeof(window)!=="undefined"){var N=window.forge=window.forge||{};N.tls={}}else{if(typeof(module)!=="undefined"&&module.exports){var N={aes:require("./aes"),asn1:require("./asn1"),hmac:require("./hmac"),md:require("./md"),pki:require("./pki"),random:require("./random"),util:require("./util")};N.pki.oids=require("./oids");N.pki.rsa=require("./rsa");module.exports=N.tls={}}}*/var O=function(Y,ak,ad,X){var ac=N.util.createBuffer();var am=(Y.length>>1);var ag=am+(Y.length&1);var ao=Y.substr(0,ag);var al=Y.substr(am,ag);var aj=N.util.createBuffer();var ae=N.hmac.create();ad=ak+ad;var aa=Math.ceil(X/16);var ab=Math.ceil(X/20);ae.start("MD5",ao);var an=N.util.createBuffer();aj.putBytes(ad);for(var Z=0;Z<aa;++Z){var af=new Date().valueOf();ae.start(null,null);ae.update(aj.getBytes());aj.putBuffer(ae.digest());ae.start(null,null);ae.update(aj.bytes()+ad);an.putBuffer(ae.digest())}ae.start("SHA1",al);var ah=N.util.createBuffer();aj.clear();aj.putBytes(ad);for(var Z=0;Z<ab;++Z){ae.start(null,null);ae.update(aj.getBytes());aj.putBuffer(ae.digest());ae.start(null,null);ae.update(aj.bytes()+ad);ah.putBuffer(ae.digest())}ac.putBytes(N.util.xorBytes(an.getBytes(),ah.getBytes(),X));return ac};var e=function(Y,Z,X,aa){};/* hmac tls */var j=function(Z,ab,Y){/*console.log('key '+decode(Z))*/;var aa=N.hmac.create();aa.start("SHA1",Z);var X=N.util.createBuffer();X.putInt32(ab[0]);X.putInt32(ab[1]);X.putByte(Y.type);X.putByte(Y.version.major);X.putByte(Y.version.minor);X.putInt16(Y.length);X.putBytes(Y.fragment.bytes());/*console.log(X.data.toString('hex'));console.log('update');*/var tmp=X.getBytes();/*console.log(decode(tmp));*/aa.update(tmp);/*console.log('digest');*/var tmp2=aa.digest().getBytes();/*console.log(decode(tmp2));*/return tmp2};var I=function(ac,Y,aa){var ab=false;try{var X=ac.deflate(Y.fragment.getBytes());Y.fragment=N.util.createBuffer(X);Y.length=X.length;ab=true}catch(Z){}return ab};var p=function(ac,Y,aa){var ab=false;try{var X=ac.inflate(Y.fragment.getBytes());Y.fragment=N.util.createBuffer(X);Y.length=X.length;ab=true}catch(Z){}return ab};var f=function(Y,aa){var ab=false;var ac=aa.macFunction(aa.macKey,aa.sequenceNumber,Y);Y.fragment.putBytes(ac);aa.updateSequenceNumber();var Z=aa.cipherState.init?null:aa.cipherState.iv;aa.cipherState.init=true;var X=aa.cipherState.cipher;X.start(Z);X.update(Y.fragment);if(X.finish(V)){Y.fragment=X.output;Y.length=Y.fragment.length();ab=true}return ab};var V=function(Z,X,Y){if(!Y){var aa=(X.length()==Z)?(Z-1):(Z-X.length()-1);X.fillWithByte(aa,aa+1)}return true};/* decrypt aes 128 */var r=function(ac,Z,aa){var ad=true;if(aa){var X=Z.length();var Y=Z.last();for(var ab=X-1-Y;ab<X-1;++ab){ad=ad&&(Z.at(ab)==Y)}if(ad){Z.truncate(Y+1)}};/*console.log('decrypt 128 cbc');console.log(Z.data.toString('hex'));*/return ad};/*decrypt aes*/var S=function(ab,ag){/*console.log('data to decrypt ')+console.log(ab.fragment.data.toString('hex'));*/var aa=false;var Y=ag.cipherState.init?null:ag.cipherState.iv;ag.cipherState.init=true;var ad=ag.cipherState.cipher;ad.start(Y);ad.update(ab.fragment);aa=ad.finish(r);var af=ag.macLength;var ae="";for(var Z=0;Z<af;++Z){ae+=String.fromCharCode(0)}var ac=ad.output.length();if(ac>=af){ab.fragment=ad.output.getBytes(ac-af);ae=ad.output.getBytes(af)}else{ab.fragment=ad.output.getBytes()}ab.fragment=N.util.createBuffer(ab.fragment);ab.length=ab.fragment.length();var X=ag.macFunction(ag.macKey,ag.sequenceNumber,ab);ag.updateSequenceNumber();/*console.log('decrypted fragment ');console.log(ab.fragment.data.toString('hex'));console.log('Mac '+decode(X)+' '+decode(ae));*/aa=(X===ae)&&aa;return aa};var d=function(Z,Y){var X=0;switch(Y){case 1:X=Z.getByte();break;case 2:X=Z.getInt16();break;case 3:X=Z.getInt24();break;case 4:X=Z.getInt32();break}return N.util.createBuffer(Z.getBytes(X))};var n=function(Y,X,Z){Y.putInt(Z.length(),X<<3);Y.putBuffer(Z)};var g={};g.Version={major:3,minor:1};g.MaxFragment=(1<<14)-1024;g.ConnectionEnd={server:0,client:1};g.PRFAlgorithm={tls_prf_sha256:0};g.BulkCipherAlgorithm={none:null,rc4:0,des3:1,aes:2};g.CipherType={stream:0,block:1,aead:2};g.MACAlgorithm={none:null,hmac_md5:0,hmac_sha1:1,hmac_sha256:2,hmac_sha384:3,hmac_sha512:4};g.CompressionMethod={none:0,deflate:1};g.ContentType={change_cipher_spec:20,alert:21,handshake:22,application_data:23};g.HandshakeType={hello_request:0,client_hello:1,server_hello:2,certificate:11,server_key_exchange:12,certificate_request:13,server_hello_done:14,certificate_verify:15,client_key_exchange:16,finished:20};g.Alert={};g.Alert.Level={warning:1,fatal:2};g.Alert.Description={close_notify:0,unexpected_message:10,bad_record_mac:20,decryption_failed:21,record_overflow:22,decompression_failure:30,handshake_failure:40,bad_certificate:42,unsupported_certificate:43,certificate_revoked:44,certificate_expired:45,certificate_unknown:46,illegal_parameter:47,unknown_ca:48,access_denied:49,decode_error:50,decrypt_error:51,export_restriction:60,protocol_version:70,insufficient_security:71,internal_error:80,user_canceled:90,no_renegotiation:100};g.CipherSuites={TLS_RSA_WITH_AES_128_CBC_SHA: [0x00,0x2f],TLS_RSA_WITH_AES_256_CBC_SHA: [0x00,0x35],TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: [0xc0,0x0a],TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: [0xc0,0x14],TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: [0x00,0x88],TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: [0x00,0x87],TLS_DHE_RSA_WITH_AES_256_CBC_SHA: [0x00,0x39],TLS_DHE_DSS_WITH_AES_256_CBC_SHA: [0x00,0x38],TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: [0xc0,0x0f],TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: [0xc0,0x05],TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: [0x00,0x84],TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: [0xc0,0x07],TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: [0xc0,0x09],TLS_ECDHE_RSA_WITH_RC4_128_SHA: [0xc0,0x11],TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: [0xc0,0x13],TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: [0x00,0x45],TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: [0x00,0x44],TLS_DHE_RSA_WITH_AES_128_CBC_SHA: [0x00,0x33],TLS_DHE_DSS_WITH_AES_128_CBC_SHA: [0x00,0x32],TLS_ECDH_RSA_WITH_RC4_128_SHA: [0xc0,0x0c],TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: [0xc0,0x0e],TLS_ECDH_ECDSA_WITH_RC4_128_SHA: [0xc0,0x02],TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: [0xc0,0x04],TLS_RSA_WITH_SEED_CBC_SHA: [0x00,0x96],TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: [0x00,0x41],TLS_RSA_WITH_RC4_128_MD5: [0x00,0x04],TLS_RSA_WITH_RC4_128_SHA: [0x00,0x05],TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: [0xc0,0x08],TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: [0xc0,0x12],TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: [0x00,0x16],TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: [0x00,0x13],TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: [0xc0,0x0d],TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: [0xc0,0x03],SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA: [0xfe,0xff],TLS_RSA_WITH_3DES_EDE_CBC_SHA: [0x00,0x0a]};
			/*
					c.session = {
					  serverNameList: [],
					  cipherSuite: null,
					  compressionMethod: null,
					  serverCertificate: null,
					  clientCertificate: null,
					  md5: forge.md.md5.create(),
					  sha1: forge.md.sha1.create()
					};

					c.session.sp = {
					entity: c.entity,
					prf_algorithm: tls.PRFAlgorithm.tls_prf_sha256,
					bulk_cipher_algorithm: tls.BulkCipherAlgorithm.aes,
					cipher_type: tls.CipherType.block,
					enc_key_length: keyLength,
					block_length: 16,
					fixed_iv_length: 16,
					record_iv_length: 16,
					mac_algorithm: tls.MACAlgorithm.hmac_sha1,
					mac_length: 20,
					mac_key_length: 20,
					compression_algorithm: c.session.compressionMethod,
					pre_master_secret: null,
					master_secret: null,
					client_random: cRandom,
					server_random: sRandom
				  };
				*/

				/*
				var dhprime=new BigInteger('FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF',16);
				var dhg=new BigInteger('02',16);
				var secretX=new BigInteger('04',16);
				alert(dhg.modPow(secretX,dhprime).toString(16));
				*/

				/*{if(Y>0){Z.error(Z,{message:"Invalid key parameters. Only RSA is supported.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.unsupported_certificate}})}else{Z.expect=L;Z.process()}}*/

				var DHE_handleServerKeyExchange=function(c, record, length) {
				  // this implementation only supports RSA, no Diffie-Hellman support
				  // so any length > 0 is invalid
				  if(length > 0) {

					/*
					select (KeyExchangeAlgorithm) {
						   case dh_anon:
							   ServerDHParams params;
						   case dhe_dss:
						   case dhe_rsa:
							   ServerDHParams params;
							   digitally-signed struct {
								   opaque client_random[32];
								   opaque server_random[32];
								   ServerDHParams params;
							   } signed_params;
						   case rsa:
						   case dh_dss:
						   case dh_rsa:
							   struct {} ;
							  message is omitted for rsa, dh_dss, and dh_rsa
						   may be extended, e.g., for ECDH -- see [TLSECC]
					} ServerKeyExchange;
					struct { Lenght 2 bytes + data
					  opaque dh_p<1..2^16-1>;
					  opaque dh_g<1..2^16-1>;
					  opaque dh_Ys<1..2^16-1>;
					} ServerDHParams;
					dh_p
					 The prime modulus used for the Diffie-Hellman operation.

					dh_g
					 The generator used for the Diffie-Hellman operation.

					dh_Ys
					The server's Diffie-Hellman public value (g^X mod p).
					*/

						var b=record.fragment;
						var dhe=c.session.sp.DHE={};
						dhe.prime=b.getBytes(b.getInt(16));
						//alert((new Buffer(prime,'binary')).toString('hex'));
						dhe.g=b.getBytes(b.getInt(16));
						dhe.server_public_key=b.getBytes(b.getInt(16));
						dhe.signature=b.getBytes(b.getInt(16));
						//TODO check signature

					}
					//else {
					// expect an optional CertificateRequest message next
					c.expect = L;

					// continue
					c.process();
					//}
				};

				/*g.createClientKeyExchange=function(ac){var X=N.util.createBuffer();X.putByte(g.Version.major);X.putByte(g.Version.minor);X.putBytes(N.random.getBytes(46));var aa=ac.session.sp;aa.pre_master_secret=X.getBytes();var Y=ac.session.serverCertificate.publicKey;X=Y.encrypt(aa.pre_master_secret);var Z=X.length+2;var ab=N.util.createBuffer();ab.putByte(g.HandshakeType.client_key_exchange);ab.putInt24(Z);ab.putInt16(X.length);ab.putBytes(X);return ab}*/

				var DHE_createClientKeyExchange = function(ac) {
					/*
					createSecurityParameters modified
					switch(ac.session.cipherSuite) {
					  case g.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA:
						keyLength = 16;
						break;
					  case g.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA:
						keyLength = 32;
						break;
					  }
					  struct {
						  select (KeyExchangeAlgorithm) {
							  case rsa:
								  EncryptedPreMasterSecret;
							  case dhe_dss:
							  case dhe_rsa:
							  case dh_dss:
							  case dh_rsa:
							  case dh_anon:
								  ClientDiffieHellmanPublic;
						  } exchange_keys;
					  } ClientKeyExchange;

					  struct {
						  select (PublicValueEncoding) {
							  case implicit: struct { };
							  case explicit: opaque dh_Yc<1..2^16-1>;
						  } dh_public;
					  } ClientDiffieHellmanPublic;

					  dh_Yc
						 The client's Diffie-Hellman public value (Yc).

					*/
					// create buffer to encrypt
					var aa = ac.session.sp;
					var X;
					if (!aa.DHE) {
						// add highest client-supported protocol to help server avoid version
						// rollback attacks
						X = N.util.createBuffer();

						X.putByte(g.Version.major);
						X.putByte(g.Version.minor);

						// generate and add 46 random bytes
						X.putBytes(N.random.getBytes(46));

						// save pre-master secret
						aa.pre_master_secret = X.getBytes();

						// RSA-encrypt the pre-master secret
						var Y = ac.session.serverCertificate.publicKey;
						X = Y.encrypt(aa.pre_master_secret);

						/* Note: The encrypted pre-master secret will be stored in a
						public-key-encrypted opaque vector that has the length prefixed using
						2 bytes, so include those 2 bytes in the handshake message length. This
						is done as a minor optimization instead of calling writeVector(). */
					} else {
						var dhe=aa.DHE;
						var client_key=new BigInteger(Rand(128).toString('hex'),16);
						var prime=new BigInteger(new Buffer(dhe.prime,'binary').toString('hex'),16);
						var gdh=new BigInteger(new Buffer(dhe.g,'binary').toString('hex'),16);
						var server_key=new BigInteger(new Buffer(dhe.server_public_key,'binary').toString('hex'),16);
						var sec=new Buffer(server_key.modPow(client_key,prime).toString(16),'hex');
						aa.pre_master_secret=sec.toString('binary');
						//console.log('Pre Master: '+sec.toString('hex'));
						if (!forge_buffers) {
							X=new Buffer(gdh.modPow(client_key,prime).toString(16),'hex');
						//console.log('Client public key: '+X.toString('hex'));
						} else {
							X=(new Buffer(gdh.modPow(client_key,prime).toString(16),'hex')).toString('binary');
						};
					};
					// determine length of the handshake message
					var Z = X.length + 2;

					// build record fragment
					var ab = N.util.createBuffer();
					ab.putByte(g.HandshakeType.client_key_exchange);
					ab.putInt24(Z);
					// add vector length bytes
					ab.putInt16(X.length);
					ab.putBytes(X);
					return ab;
				};
			g.getCipherSuite=function(X){var aa=null;for(var Y in g.CipherSuites){var Z=g.CipherSuites[Y];if(Z[0]===X.charCodeAt(0)&&Z[1]===X.charCodeAt(1)){aa=Z;break}}return aa};g.handleUnexpected=function(Z,X){var Y=(!Z.open&&Z.entity===g.ConnectionEnd.client);/* modif Ayms ignore unexpected */;console.log('unexpected message ----------------');Y=true;if(!Y){Z.error(Z,{message:"Unexpected message. Received TLS record out of order.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.unexpected_message}})}};g.handleHelloRequest=function(Z,X,Y){if(!Z.handshaking&&Z.handshakes>0){g.queue(Z,g.createAlert({level:g.Alert.Level.warning,description:g.Alert.Description.no_renegotiation}));g.flush(Z)}Z.process()};g.parseHelloMessage=function(ag,af,Z){var aa=null;var ab=(ag.entity==g.ConnectionEnd.client);if(Z<38){ag.error(ag,{message:ab?"Invalid ServerHello message. Message too short.":"Invalid ClientHello message. Message too short.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.illegal_parameter}})}else{var ai=af.fragment;aa={version:{major:ai.getByte(),minor:ai.getByte()},random:N.util.createBuffer(ai.getBytes(32)),session_id:d(ai,1),extensions:[]};if(ab){aa.cipher_suite=ai.getBytes(2);aa.compression_method=ai.getByte()}else{aa.cipher_suites=d(ai,2);aa.compression_methods=d(ai,1)};if(ai.length()>0){var ac=d(ai,2);while(ac.length()>0){aa.extensions.push({type:[ac.getByte(),ac.getByte()],data:d(ac,2)})}if(!ab){for(var ae=0;ae<aa.extensions.length;++ae){var Y=aa.extensions[ae];if(Y.type[0]===0&&Y.type[1]===0){var X=d(Y.data,2);while(X.length()>0){var ah=X.getByte();if(ah!==0){break}ag.session.serverNameList.push(d(X,2).getBytes())}}}}};/*if(aa.version.major!==g.Version.major||aa.version.minor!==g.Version.minor){ag.error(ag,{message:"Incompatible TLS version.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.protocol_version}})}*/if(ab){ag.session.cipherSuite=g.getCipherSuite(aa.cipher_suite)}else{var ad=N.util.createBuffer(aa.cipher_suites.bytes());while(ad.length()>0){ag.session.cipherSuite=g.getCipherSuite(ad.getBytes(2));if(ag.session.cipherSuite!==null){break}}}if(ag.session.cipherSuite===null){ag.error(ag,{message:"No cipher suites in common.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.handshake_failure},cipherSuite:N.util.bytesToHex(aa.cipher_suite)})}if(ab){ag.session.compressionMethod=aa.compression_method}else{ag.session.compressionMethod=g.CompressionMethod.none}};return aa};g.createSecurityParameters=function(ad,aa){var Z;switch(ad.session.cipherSuite){case g.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA:Z=16;break;case g.CipherSuites.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:Z=16;break;case g.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA:Z=32;break;case g.CipherSuites.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:Z=32;break}var X=(ad.entity===g.ConnectionEnd.client);var Y=aa.random.bytes();var ac=X?ad.session.sp.client_random:Y;var ab=X?Y:g.createRandom().getBytes();ad.session.sp={entity:ad.entity,prf_algorithm:g.PRFAlgorithm.tls_prf_sha256,bulk_cipher_algorithm:g.BulkCipherAlgorithm.aes,cipher_type:g.CipherType.block,enc_key_length:Z,block_length:16,fixed_iv_length:16,record_iv_length:16,mac_algorithm:g.MACAlgorithm.hmac_sha1,mac_length:20,mac_key_length:20,compression_algorithm:ad.session.compressionMethod,pre_master_secret:null,master_secret:null,client_random:ac,server_random:ab}};g.handleServerHello=function(ab,X,Y){var aa=g.parseHelloMessage(ab,X,Y);if(!ab.fail){var Z=aa.session_id.bytes();/*modif*/if(Z&&(Z===ab.session.id)){ab.expect=T;ab.session.resuming=true;ab.session.sp.server_random=aa.random.bytes()}else{ab.expect=R;ab.session.resuming=false;g.createSecurityParameters(ab,aa)};ab.session.id=Z;ab.process()}};g.handleClientHello=function(ac,X,Y){var ab=g.parseHelloMessage(ac,X,Y);if(!ac.fail){var aa=ab.session_id.bytes();var Z=null;if(ac.sessionCache){Z=ac.sessionCache.getSession(aa);if(Z===null){aa=""}}if(aa.length===0){aa=N.random.getBytes(32)}ac.session.id=aa;ac.session.clientHelloVersion=ab.version;ac.session.sp=Z?Z.sp:{};if(Z!==null){ac.expect=M;ac.session.resuming=true;ac.session.sp.client_random=ab.random.bytes()}else{ac.expect=(ac.verifyClient!==false)?K:c;ac.session.resuming=false;g.createSecurityParameters(ac,ab)}ac.open=true;g.queue(ac,g.createRecord({type:g.ContentType.handshake,data:g.createServerHello(ac)}));if(ac.session.resuming){g.queue(ac,g.createRecord({type:g.ContentType.change_cipher_spec,data:g.createChangeCipherSpec()}));ac.state.pending=g.createConnectionState(ac);ac.state.current.write=ac.state.pending.write;g.queue(ac,g.createRecord({type:g.ContentType.handshake,data:g.createFinished(ac)}))}else{g.queue(ac,g.createRecord({type:g.ContentType.handshake,data:g.createCertificate(ac)}));/*g.queue(ac,g.createRecord({type:g.ContentType.handshake,data:g.createServerKeyExchange(ac)}))*/;if(ac.verifyClient!==false){g.queue(ac,g.createRecord({type:g.ContentType.handshake,data:g.createCertificateRequest(ac)}))}g.queue(ac,g.createRecord({type:g.ContentType.handshake,data:g.createServerHelloDone(ac)}))}g.flush(ac);ac.process()}};g.handleCertificate=function(af,ad,X){if(X<3){af.error(af,{message:"Invalid Certificate message. Message too short.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.illegal_parameter}})}else{var ag=ad.fragment;var Y={certificate_list:d(ag,3)};var ab,aa;var ac=[];try{while(Y.certificate_list.length()>0){ab=d(Y.certificate_list,3);aa=N.asn1.fromDer(ab);ab=N.pki.certificateFromAsn1(aa,true);ac.push(ab)}}catch(ae){af.error(af,{message:"Could not parse certificate list.",cause:ae,send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.bad_certificate}})}if(!af.fail){var Z=(af.entity===g.ConnectionEnd.client);if((Z||af.verifyClient===true)&&ac.length===0){af.error(af,{message:Z?"No server certificate provided.":"No client certificate provided.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.illegal_parameter}})}else{if(ac.length===0){af.expect=Z?k:c}else{if(Z){af.session.serverCertificate=ac[0]}else{af.session.clientCertificate=ac[0]}if(g.verifyCertificateChain(af,ac)){af.expect=Z?k:c}}}af.process()}}};g.handleServerKeyExchange=DHE_handleServerKeyExchange;g.handleClientKeyExchange=function(ae,aa,ac){if(ac<48){ae.error(ae,{message:"Invalid key parameters. Only RSA is supported.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.unsupported_certificate}})}else{var X=aa.fragment;msg={enc_pre_master_secret:d(X,2).getBytes()};var Z=null;if(ae.getPrivateKey){try{Z=ae.getPrivateKey(ae,ae.session.serverCertificate);Z=N.pki.privateKeyFromPem(Z)}catch(ab){ae.error(ae,{message:"Could not get private key.",cause:ab,send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.internal_error}})}}if(Z===null){ae.error(ae,{message:"No private key set.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.internal_error}})}else{try{var ad=ae.session.sp;ad.pre_master_secret=Z.decrypt(msg.enc_pre_master_secret);var Y=ae.session.clientHelloVersion;if(Y.major!==ad.pre_master_secret.charCodeAt(0)||Y.minor!==ad.pre_master_secret.charCodeAt(1)){throw {message:"TLS version rollback attack detected."}}}catch(ab){ad.pre_master_secret=N.random.getBytes(48)}}}if(!ae.fail){ae.expect=M;if(ae.session.clientCertificate!==null){ae.expect=x}ae.process()}};g.handleCertificateRequest=function(ab,Y,Z){if(Z<3){ab.error(ab,{message:"Invalid CertificateRequest. Message too short.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.illegal_parameter}})}else{var X=Y.fragment;var aa={certificate_types:d(X,1),certificate_authorities:d(X,2)};ab.session.certificateRequest=aa;ab.expect=i;ab.process()}};g.handleCertificateVerify=function(ae,Y,ac){if(ac<2){ae.error(ae,{message:"Invalid CertificateVerify. Message too short.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.illegal_parameter}})}else{var X=Y.fragment;X.read-=4;var Z=X.bytes();X.read+=4;msg={signature:d(X,2).getBytes()};var ad=N.util.createBuffer();ad.putBuffer(ae.session.md5.digest());ad.putBuffer(ae.session.sha1.digest());ad=ad.getBytes();try{var ab=ae.session.clientCertificate;X=N.pki.rsa.decrypt(msg.signature,ab.publicKey,true,ad.length);if(X!==ad){throw {message:"CertificateVerify signature does not match."}}ae.session.md5.update(Z);ae.session.sha1.update(Z)}catch(aa){ae.error(ae,{message:"Bad signature in CertificateVerify.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.handshake_failure}})}if(!ae.fail){ae.expect=M;ae.process()}}};g.handleServerHelloDone=function(ac,X,aa){if(aa>0){ac.error(ac,{message:"Invalid ServerHelloDone message. Invalid length.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.record_overflow}})}else{if(ac.serverCertificate===null){var Z={message:"No server certificate provided. Not enough security.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.insufficient_security}};var Y=ac.verify(ac,Z.alert.description,depth,[]);if(Y===true){Z=null}else{if(Y||Y===0){if(Y.constructor==Object){if(Y.message){Z.message=Y.message}if(Y.alert){Z.alert.description=Y.alert}}else{if(Y.constructor==Number){Z.alert.description=Y}}}ac.error(ac,Z)}}}if(!ac.fail&&ac.session.certificateRequest!==null){X=g.createRecord({type:g.ContentType.handshake,data:g.createCertificate(ac)});g.queue(ac,X)}if(!ac.fail){X=g.createRecord({type:g.ContentType.handshake,data:g.createClientKeyExchange(ac)});g.queue(ac,X);ac.expect=a;var ab=function(ae,ad){if(ae.session.certificateRequest!==null&&ae.session.clientCertificate!==null){g.queue(ae,g.createRecord({type:g.ContentType.handshake,data:g.createCertificateVerify(ae,ad)}))}g.queue(ae,g.createRecord({type:g.ContentType.change_cipher_spec,data:g.createChangeCipherSpec()}));ae.state.pending=g.createConnectionState(ae);ae.state.current.write=ae.state.pending.write;g.queue(ae,g.createRecord({type:g.ContentType.handshake,data:g.createFinished(ae)}));ae.expect=T;g.flush(ae);ae.process()};if(ac.session.certificateRequest===null||ac.session.clientCertificate===null){ab(ac,null)}else{g.getClientSignature(ac,ab)}}};g.handleChangeCipherSpec=function(Z,Y){if(Y.fragment.getByte()!=1){Z.error(Z,{message:"Invalid ChangeCipherSpec message received.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.illegal_parameter}})}else{var X=(Z.entity===g.ConnectionEnd.client);if((Z.session.resuming&&X)||(!Z.session.resuming&&!X)){Z.state.pending=g.createConnectionState(Z)}Z.state.current.read=Z.state.pending.read;if((!Z.session.resuming&&X)||(Z.session.resuming&&!X)){Z.state.pending=null}Z.expect=X?P:G;Z.process()}};g.handleFinished=function(af,ad,Z){var ag=ad.fragment;ag.read-=4;var aa=ag.bytes();ag.read+=4;var ae=ad.fragment.getBytes();ag=N.util.createBuffer();ag.putBuffer(af.session.md5.digest());ag.putBuffer(af.session.sha1.digest());var ac=(af.entity===g.ConnectionEnd.client);var ah=ac?"server finished":"client finished";var X=af.session.sp;var Y=12;var ab=O;ag=ab(X.master_secret,ah,ag.getBytes(),Y);if(ag.getBytes()!==ae){af.error(af,{message:"Invalid verify_data in Finished message.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.decrypt_error}})}else{af.session.md5.update(aa);af.session.sha1.update(aa);if((af.session.resuming&&ac)||(!af.session.resuming&&!ac)){g.queue(af,g.createRecord({type:g.ContentType.change_cipher_spec,data:g.createChangeCipherSpec()}));af.state.current.write=af.state.pending.write;af.state.pending=null;g.queue(af,g.createRecord({type:g.ContentType.handshake,data:g.createFinished(af)}))}af.expect=ac?v:q;af.handshaking=false;++af.handshakes;af.peerCertificate=ac?af.session.serverCertificate:af.session.clientCertificate;if(af.sessionCache){af.session={id:af.session.id,sp:af.session.sp};af.session.sp.keys=null}else{af.session=null}g.flush(af);af.isConnected=true;af.connected(af);af.process()}};g.handleAlert=function(ab,Y){var X=Y.fragment;var aa={level:X.getByte(),description:X.getByte()};var Z;switch(aa.description){case g.Alert.Description.close_notify:Z="Connection closed.";break;case g.Alert.Description.unexpected_message:Z="Unexpected message.";break;case g.Alert.Description.bad_record_mac:Z="Bad record MAC.";break;case g.Alert.Description.decryption_failed:Z="Decryption failed.";break;case g.Alert.Description.record_overflow:Z="Record overflow.";break;case g.Alert.Description.decompression_failure:Z="Decompression failed.";break;case g.Alert.Description.handshake_failure:Z="Handshake failure.";break;case g.Alert.Description.bad_certificate:Z="Bad certificate.";break;case g.Alert.Description.unsupported_certificate:Z="Unsupported certificate.";break;case g.Alert.Description.certificate_revoked:Z="Certificate revoked.";break;case g.Alert.Description.certificate_expired:Z="Certificate expired.";break;case g.Alert.Description.certificate_unknown:Z="Certificate unknown.";break;case g.Alert.Description.illegal_parameter:Z="Illegal parameter.";break;case g.Alert.Description.unknown_ca:Z="Unknown certificate authority.";break;case g.Alert.Description.access_denied:Z="Access denied.";break;case g.Alert.Description.decode_error:Z="Decode error.";break;case g.Alert.Description.decrypt_error:Z="Decrypt error.";break;case g.Alert.Description.export_restriction:Z="Export restriction.";break;case g.Alert.Description.protocol_version:Z="Unsupported protocol version.";break;case g.Alert.Description.insufficient_security:Z="Insufficient security.";break;case g.Alert.Description.internal_error:Z="Internal error.";break;case g.Alert.Description.user_canceled:Z="User canceled.";break;case g.Alert.Description.no_renegotiation:Z="Renegotiation not supported.";break;default:Z="Unknown error.";break}if(aa.description===g.Alert.Description.close_notify){ab.close()}else{ab.error(ab,{message:Z,send:false,origin:(ab.entity===g.ConnectionEnd.client)?"server":"client",alert:aa});ab.process()}};g.handleHandshake=function(ac,Z){var X=Z.fragment;var aa=X.getByte();var ab=X.getInt24();if(ab>X.length()){ac.fragmented=Z;Z.fragment=N.util.createBuffer();X.read-=4;ac.process()}else{ac.fragmented=null;X.read-=4;var Y=X.bytes(ab+4);X.read+=4;if(aa in Q[ac.entity][ac.expect]){if(ac.entity===g.ConnectionEnd.server&&!ac.open&&!ac.fail){ac.handshaking=true;ac.session={serverNameList:[],cipherSuite:null,compressionMethod:null,serverCertificate:null,clientCertificate:null,md5:N.md.md5.create(),sha1:N.md.sha1.create()}}if(aa!==g.HandshakeType.hello_request&&aa!==g.HandshakeType.certificate_verify&&aa!==g.HandshakeType.finished){ac.session.md5.update(Y);ac.session.sha1.update(Y)}Q[ac.entity][ac.expect][aa](ac,Z,ab)}else{g.handleUnexpected(ac,Z)}}};g.handleApplicationData=function(Y,X){Y.data.putBuffer(X.fragment);Y.dataReady(Y);Y.process()};var h=0;var R=1;var k=2;var L=3;var i=4;var T=5;var P=6;var v=7;var a=8;var b=0;var K=1;var c=2;var x=3;var M=4;var G=5;var q=6;var U=7;var W=g.handleUnexpected;var J=g.handleChangeCipherSpec;var H=g.handleAlert;var F=g.handleHandshake;var E=g.handleApplicationData;var o=[];o[g.ConnectionEnd.client]=[[W,W,F,W],[W,H,F,W],[W,H,F,W],[W,H,F,W],[W,H,F,W],[J,H,W,W],[W,H,F,W],[W,H,F,E],[W,H,F,W]];o[g.ConnectionEnd.server]=[[W,W,F,W],[W,H,F,W],[W,H,F,W],[W,H,F,W],[J,H,W,W],[W,H,F,W],[W,H,F,E],[W,H,F,W]];var D=g.handleHelloRequest;var C=g.handleServerHello;var B=g.handleCertificate;var A=g.handleServerKeyExchange;var z=g.handleCertificateRequest;var y=g.handleServerHelloDone;var w=g.handleFinished;var Q=[];Q[g.ConnectionEnd.client]=[[W,W,C,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W],[D,W,W,W,W,W,W,W,W,W,W,B,A,z,y,W,W,W,W,W,W],[D,W,W,W,W,W,W,W,W,W,W,W,A,z,y,W,W,W,W,W,W],[D,W,W,W,W,W,W,W,W,W,W,W,W,z,y,W,W,W,W,W,W],[D,W,W,W,W,W,W,W,W,W,W,W,W,W,y,W,W,W,W,W,W],[D,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W],[D,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,w],[D,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W],[D,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W]];var u=g.handleClientHello;var t=g.handleClientKeyExchange;var s=g.handleCertificateVerify;Q[g.ConnectionEnd.server]=[[W,u,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W],[W,W,W,W,W,W,W,W,W,W,W,B,W,W,W,W,W,W,W,W,W],[W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,t,W,W,W,W],[W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,s,W,W,W,W,W],[W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W],[W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,w],[W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W],[W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W,W]];g.generateKeys=function(ac,ab){var aa=O;var Z=ab.client_random+ab.server_random;if(!ac.session.resuming){ab.master_secret=aa(ab.pre_master_secret,"master secret",Z,48).bytes();ab.pre_master_secret=null}Z=ab.server_random+ab.client_random;var Y=2*ab.mac_key_length+2*ab.enc_key_length+2*ab.fixed_iv_length;var X=aa(ab.master_secret,"key expansion",Z,Y);return{client_write_MAC_key:X.getBytes(ab.mac_key_length),server_write_MAC_key:X.getBytes(ab.mac_key_length),client_write_key:X.getBytes(ab.enc_key_length),server_write_key:X.getBytes(ab.enc_key_length),client_write_IV:X.getBytes(ab.fixed_iv_length),server_write_IV:X.getBytes(ab.fixed_iv_length)}};g.createConnectionState=function(ab){var Y=(ab.entity===g.ConnectionEnd.client);var X=function(){var ac={sequenceNumber:[0,0],macKey:null,macLength:0,macFunction:null,cipherState:null,cipherFunction:function(ad){return true},compressionState:null,compressFunction:function(ad){return true},updateSequenceNumber:function(){if(ac.sequenceNumber[1]==4294967295){ac.sequenceNumber[1]=0;++ac.sequenceNumber[0]}else{++ac.sequenceNumber[1]}}};return ac};var aa={read:X(),write:X()};aa.read.update=function(ad,ac){if(!aa.read.cipherFunction(ac,aa.read)){ad.error(ad,{message:"Could not decrypt record or bad MAC.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.bad_record_mac}})}else{if(!aa.read.compressFunction(ad,ac,aa.read)){ad.error(ad,{message:"Could not decompress record.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.decompression_failure}})}}return !ad.fail};aa.write.update=function(ad,ac){if(!aa.write.compressFunction(ad,ac,aa.write)){ad.error(ad,{message:"Could not compress record.",send:false,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.internal_error}})}else{if(!aa.write.cipherFunction(ac,aa.write)){ad.error(ad,{message:"Could not encrypt record.",send:false,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.internal_error}})}}return !ad.fail};if(ab.session){var Z=ab.session.sp;Z.keys=g.generateKeys(ab,Z);aa.read.macKey=Y?Z.keys.server_write_MAC_key:Z.keys.client_write_MAC_key;aa.write.macKey=Y?Z.keys.client_write_MAC_key:Z.keys.server_write_MAC_key;aa.read.macLength=aa.write.macLength=Z.mac_length;switch(Z.mac_algorithm){case g.MACAlgorithm.hmac_sha1:aa.read.macFunction=aa.write.macFunction=j;break;default:throw {message:"Unsupported MAC algorithm."}}switch(Z.bulk_cipher_algorithm){case g.BulkCipherAlgorithm.aes:aa.read.cipherState={init:false,cipher:N.aes.createDecryptionCipher(Y?Z.keys.server_write_key:Z.keys.client_write_key),iv:Y?Z.keys.server_write_IV:Z.keys.client_write_IV};aa.write.cipherState={init:false,cipher:N.aes.createEncryptionCipher(Y?Z.keys.client_write_key:Z.keys.server_write_key),iv:Y?Z.keys.client_write_IV:Z.keys.server_write_IV};aa.read.cipherFunction=S;aa.write.cipherFunction=f;break;default:throw {message:"Unsupported cipher algorithm."}}switch(Z.cipher_type){case g.CipherType.block:break;default:throw {message:"Unsupported cipher type."}}switch(Z.compression_algorithm){case g.CompressionMethod.none:break;case g.CompressionMethod.deflate:aa.read.compressFunction=p;aa.write.compressFunction=I;break;default:throw {message:"Unsupported compression algorithm."}}}return aa};g.createRandom=function(){var Z=new Date();var X=+Z+Z.getTimezoneOffset()*60000;var Y=N.util.createBuffer();Y.putInt32(X);Y.putBytes(N.random.getBytes(28));return Y};g.createRecord=function(Y){var X={type:Y.type,version:{major:g.Version.major,minor:g.Version.minor},length:Y.data.length(),fragment:Y.data};return X};g.createAlert=function(Y){var X=N.util.createBuffer();X.putByte(Y.level);X.putByte(Y.description);return g.createRecord({type:g.ContentType.alert,data:X})};g.createClientHello=function(ah){var ak=N.util.createBuffer();for(var ad=0;ad<ah.cipherSuites.length;++ad){var af=ah.cipherSuites[ad];ak.putByte(af[0]);ak.putByte(af[1])}var aj=ak.length();var al=N.util.createBuffer();al.putByte(g.CompressionMethod.none);var Y=al.length();var ai=N.util.createBuffer();if(ah.virtualHost){var aa=N.util.createBuffer();aa.putByte(0);aa.putByte(0);var X=N.util.createBuffer();X.putByte(0);n(X,2,N.util.createBuffer(ah.virtualHost));var ag=N.util.createBuffer();n(ag,2,X);n(aa,2,ag);ai.putBuffer(aa)}var ac=ai.length();if(ac>0){ac+=2}var ab=ah.session.id;var Z=ab.length+1+2+4+28+2+aj+1+Y+ac;var ae=N.util.createBuffer();ae.putByte(g.HandshakeType.client_hello);ae.putInt24(Z);ae.putByte(g.Version.major);ae.putByte(g.Version.minor);ae.putBytes(ah.session.sp.client_random);n(ae,1,N.util.createBuffer(ab));n(ae,2,ak);n(ae,1,al);if(ac>0){n(ae,2,ai)}return ae};g.createServerHello=function(aa){var Z=aa.session.id;var X=Z.length+1+2+4+28+2+1;var Y=N.util.createBuffer();Y.putByte(g.HandshakeType.server_hello);Y.putInt24(X);Y.putByte(g.Version.major);Y.putByte(g.Version.minor);Y.putBytes(aa.session.sp.server_random);n(Y,1,N.util.createBuffer(Z));Y.putByte(aa.session.cipherSuite[0]);Y.putByte(aa.session.cipherSuite[1]);Y.putByte(aa.session.compressionMethod);return Y};g.createCertificate=function(ag){var Y=(ag.entity===g.ConnectionEnd.client);var ab=null;if(ag.getCertificate){ab=ag.getCertificate(ag,Y?ag.session.certificateRequest:ag.session.serverNameList)}var ah=N.util.createBuffer();if(ab!==null){try{if((Array.isArray&&!Array.isArray(ab))||ab.constructor!==Array){ab=[ab]}var Z=null;for(var ac=0;ac<ab.length;++ac){var af=N.pki.pemToDer(ab);if(Z===null){Z=N.asn1.fromDer(af.bytes())}var aa=N.util.createBuffer();n(aa,3,af);ah.putBuffer(aa)}ab=N.pki.certificateFromAsn1(Z);if(Y){ag.session.clientCertificate=ab}else{ag.session.serverCertificate=ab}}catch(ae){ag.error(ag,{message:"Could not send certificate list.",cause:ae,send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.bad_certificate}})}}var X=3+ah.length();var ad=N.util.createBuffer();ad.putByte(g.HandshakeType.certificate);ad.putInt24(X);n(ad,3,ah);return ad};g.createClientKeyExchange=DHE_createClientKeyExchange;g.createServerKeyExchange=function(Z){var X=0;var Y=N.util.createBuffer();/*Y.putByte(g.HandshakeType.server_key_exchange);Y.putInt24(X);*/return Y};g.getClientSignature=function(Z,Y){var X=N.util.createBuffer();X.putBuffer(Z.session.md5.digest());X.putBuffer(Z.session.sha1.digest());X=X.getBytes();Z.getSignature=Z.getSignature||function(ae,aa,ad){var ab=null;if(ae.getPrivateKey){try{ab=ae.getPrivateKey(ae,ae.session.clientCertificate);ab=N.pki.privateKeyFromPem(ab)}catch(ac){ae.error(ae,{message:"Could not get private key.",cause:ac,send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.internal_error}})}}if(ab===null){ae.error(ae,{message:"No private key set.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.internal_error}})}else{aa=N.pki.rsa.encrypt(aa,ab,1)}ad(ae,aa)};Z.getSignature(Z,X,Y)};g.createCertificateVerify=function(aa,X){var Y=X.length+2;var Z=N.util.createBuffer();Z.putByte(g.HandshakeType.certificate_verify);Z.putInt24(Y);Z.putInt16(X.length);Z.putBytes(X);return Z};g.createCertificateRequest=function(ae){var Z=N.util.createBuffer();Z.putByte(1);var Y=N.util.createBuffer();for(var ab in ae.caStore.certs){var aa=ae.caStore.certs[ab];var X=N.pki.distinguishedNameToAsn1(aa.subject);Y.putBuffer(N.asn1.toDer(X))}var ac=1+Z.length()+2+Y.length();var ad=N.util.createBuffer();ad.putByte(g.HandshakeType.certificate_request);ad.putInt24(ac);n(ad,1,Z);n(ad,2,Y);return ad};g.createServerHelloDone=function(Y){var X=N.util.createBuffer();X.putByte(g.HandshakeType.server_hello_done);X.putInt24(0);return X};g.createChangeCipherSpec=function(){var X=N.util.createBuffer();X.putByte(1);return X};g.createFinished=function(ae){var X=N.util.createBuffer();X.putBuffer(ae.session.md5.digest());X.putBuffer(ae.session.sha1.digest());var Y=(ae.entity===g.ConnectionEnd.client);var ac=ae.session.sp;var aa=12;var ab=O;var Z=Y?"client finished":"server finished";X=ab(ac.master_secret,Z,X.getBytes(),aa);var ad=N.util.createBuffer();ad.putByte(g.HandshakeType.finished);ad.putInt24(X.length());ad.putBuffer(X);return ad};g.queue=function(ae,Y){if(Y.type===g.ContentType.handshake){var X=Y.fragment.bytes();ae.session.md5.update(X);ae.session.sha1.update(X);X=null}var Z;if(Y.fragment.length()<=g.MaxFragment){Z=[Y]}else{Z=[];var ac=Y.fragment.bytes();while(ac.length>g.MaxFragment){Z.push(g.createRecord({type:Y.type,data:N.util.createBuffer(ac.slice(0,g.MaxFragment))}));ac=ac.slice(g.MaxFragment)}if(ac.length>0){Z.push(g.createRecord({type:Y.type,data:N.util.createBuffer(ac)}))}}for(var aa=0;aa<Z.length&&!ae.fail;++aa){var ad=Z[aa];var ab=ae.state.current.write;if(ab.update(ae,ad)){ae.records.push(ad)}}};g.flush=function(Z){for(var Y=0;Y<Z.records.length;++Y){var X=Z.records[Y];Z.tlsData.putByte(X.type);Z.tlsData.putByte(X.version.major);Z.tlsData.putByte(X.version.minor);Z.tlsData.putInt16(X.fragment.length());Z.tlsData.putBuffer(Z.records[Y].fragment)}Z.records=[];return Z.tlsDataReady(Z)};var m=function(X){switch(X){case true:return true;case N.pki.certificateError.bad_certificate:return g.Alert.Description.bad_certificate;case N.pki.certificateError.unsupported_certificate:return g.Alert.Description.unsupported_certificate;case N.pki.certificateError.certificate_revoked:return g.Alert.Description.certificate_revoked;case N.pki.certificateError.certificate_expired:return g.Alert.Description.certificate_expired;case N.pki.certificateError.certificate_unknown:return g.Alert.Description.certificate_unknown;case N.pki.certificateError.unknown_ca:return g.Alert.Description.unknown_ca;default:return g.Alert.Description.bad_certificate}};var l=function(X){switch(X){case true:return true;case g.Alert.Description.bad_certificate:return N.pki.certificateError.bad_certificate;case g.Alert.Description.unsupported_certificate:return N.pki.certificateError.unsupported_certificate;case g.Alert.Description.certificate_revoked:return N.pki.certificateError.certificate_revoked;case g.Alert.Description.certificate_expired:return N.pki.certificateError.certificate_expired;case g.Alert.Description.certificate_unknown:return N.pki.certificateError.certificate_unknown;case g.Alert.Description.unknown_ca:return N.pki.certificateError.unknown_ca;default:return N.pki.certificateError.bad_certificate}};g.verifyCertificateChain=function(aa,Y){try{N.pki.verifyCertificateChain(aa.caStore,Y,function Z(ae,ag,ad){var af=m(ae);var ac=aa.verify(aa,ae,ag,ad);if(ac!==true){if(ac.constructor===Object){var ab={message:"The application rejected the certificate.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.bad_certificate}};if(ac.message){ab.message=ac.message}if(ac.alert){ab.alert.description=ac.alert}throw ab}if(ac!==ae){ac=l(ac)}}return ac})}catch(X){if(X.constructor!==Object){X={send:true,alert:{level:g.Alert.Level.fatal,description:m(X)}}}if(!("send" in X)){X.send=true}if(!("alert" in X)){X.alert={level:g.Alert.Level.fatal,description:m(X.error)}}aa.error(aa,X)}return !aa.fail};g.createSessionCache=function(Y,X){var aa=null;if(Y&&Y.getSession&&Y.setSession&&Y.order){aa=Y}else{aa={};aa.cache=Y||{};aa.capacity=Math.max(X||100,1);aa.order=[];for(var Z in Y){if(aa.order.length<=X){aa.order.push(Z)}else{delete Y[Z]}}aa.getSession=function(ae){var ad=null;var ac=null;if(ae){ac=N.util.bytesToHex(ae)}else{if(aa.order.length>0){ac=aa.order[0]}}if(ac!==null&&ac in aa.cache){ad=aa.cache[ac];delete aa.cache[ac];for(var ab in aa.order){if(aa.order[ab]===ac){aa.order.splice(ab,1);break}}}return ad};aa.setSession=function(ad,ac){if(aa.order.length===aa.capacity){var ab=aa.order.shift();delete aa.cache[ab]}var ab=N.util.bytesToHex(ad);aa.order.push(ab);aa.cache[ab]=ac}}return aa};g.createConnection=function(af){var X=null;if(af.caStore){if((Array.isArray&&Array.isArray(af.caStore))||af.caStore.constructor==Array){X=N.pki.createCaStore(af.caStore)}else{X=af.caStore}}else{X=N.pki.createCaStore()}var ad=af.cipherSuites||null;if(ad===null){ad=[];ad.push(g.CipherSuites.TLS_RSA_WITH_AES_128_CBC_SHA);ad.push(g.CipherSuites.TLS_RSA_WITH_AES_256_CBC_SHA)}var aa=(af.server||false)?g.ConnectionEnd.server:g.ConnectionEnd.client;var Z=af.sessionCache?g.createSessionCache(af.sessionCache):null;var ab={entity:aa,sessionId:af.sessionId,caStore:X,sessionCache:Z,cipherSuites:ad,connected:af.connected,virtualHost:af.virtualHost||null,verifyClient:af.verifyClient||false,verify:af.verify||function(aj,ag,ai,ah){return ag},getCertificate:af.getCertificate||null,getPrivateKey:af.getPrivateKey||null,getSignature:af.getSignature||null,input:N.util.createBuffer(),tlsData:N.util.createBuffer(),data:N.util.createBuffer(),tlsDataReady:af.tlsDataReady,dataReady:af.dataReady,closed:af.closed,error:function(ai,ag){ag.origin=ag.origin||((ai.entity===g.ConnectionEnd.client)?"client":"server");if(ag.send){g.queue(ai,g.createAlert(ag.alert));g.flush(ai)}var ah=(ag.fatal!==false);if(ah){ai.fail=true}af.error(ai,ag);if(ah){ai.close(false)}},deflate:af.deflate||null,inflate:af.inflate||null};ab.reset=function(ag){ab.record=null;ab.session=null;ab.peerCertificate=null;ab.state={pending:null,current:null};ab.expect=(ab.entity===g.ConnectionEnd.client)?h:b;ab.fragmented=null;ab.records=[];ab.open=false;ab.handshakes=0;ab.handshaking=false;ab.isConnected=false;ab.fail=!(ag||typeof(ag)==="undefined");ab.input.clear();ab.tlsData.clear();ab.data.clear();ab.state.current=g.createConnectionState(ab)};ab.reset();var ae=function(aj,ag){var ai=ag.type-g.ContentType.change_cipher_spec;var ah=o[aj.entity][aj.expect];if(ai in ah){ah[ai](aj,ag)}else{g.handleUnexpected(aj,ag)}};var ac=function(aj){var ai=0;var ah=aj.input;var ag=ah.length();if(ag<5){ai=5-ag}else{aj.record={type:ah.getByte(),version:{major:ah.getByte(),minor:ah.getByte()},length:ah.getInt16(),fragment:N.util.createBuffer(),ready:false};/*if(aj.record.version.major!=g.Version.major||aj.record.version.minor!=g.Version.minor){aj.error(aj,{message:"Incompatible TLS version.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.protocol_version}})}*/}return ai};var Y=function(ak){var aj=0;var ah=ak.input;var ag=ah.length();if(ag<ak.record.length){aj=ak.record.length-ag}else{ak.record.fragment.putBytes(ah.getBytes(ak.record.length));/*modif Ayms*/ah.compact();var ai=ak.state.current.read;if(ai.update(ak,ak.record)){if(ak.fragmented!==null){if(ak.fragmented.type===ak.record.type){ak.fragmented.fragment.putBuffer(ak.record.fragment);ak.record=ak.fragmented}else{ak.error(ak,{message:"Invalid fragmented record.",send:true,alert:{level:g.Alert.Level.fatal,description:g.Alert.Description.unexpected_message}})}}ak.record.ready=true}}return aj};ab.handshake=function(ah){if(ab.entity!==g.ConnectionEnd.client){ab.error(ab,{message:"Cannot initiate handshake as a server.",fatal:false})}else{if(ab.handshaking){ab.error(ab,{message:"Handshake already in progress.",fatal:true/*modif Ayms*/})}else{if(ab.fail&&!ab.open&&ab.handshakes===0){ab.fail=false}ab.handshaking=true;ah=ah||"";var ag=null;if(ah.length>0){if(ab.sessionCache){ag=ab.sessionCache.getSession(ah)}if(ag===null){ah=""}}if(ah.length===0&&ab.sessionCache){ag=ab.sessionCache.getSession();if(ag!==null){ah=ag.id}}ab.session={id:ah,cipherSuite:null,compressionMethod:null,serverCertificate:null,certificateRequest:null,clientCertificate:null,sp:ag?ag.sp:{},md5:N.md.md5.create(),sha1:N.md.sha1.create()};ab.session.sp.client_random=g.createRandom().getBytes();ab.open=true;g.queue(ab,g.createRecord({type:g.ContentType.handshake,data:g.createClientHello(ab)}));g.flush(ab)}}};ab.process=function(ag){var ah=0;if(ag){ab.input.putBytes(ag)};if(!ab.fail){if(ab.record!==null&&ab.record.ready&&ab.record.fragment.isEmpty()){ab.record=null}if(ab.record===null){ah=ac(ab)};if(!ab.fail&&ab.record!==null&&!ab.record.ready){ah=Y(ab)};if(!ab.fail&&ab.record!==null&&ab.record.ready){ae(ab,ab.record)}};return ah};ab.prepare=function(ag){g.queue(ab,g.createRecord({type:g.ContentType.application_data,data:N.util.createBuffer(ag)}));return g.flush(ab)};ab.close=function(ag){if(!ab.fail&&ab.sessionCache&&ab.session){ab.sessionCache.setSession(ab.session.id,ab.session)}if(ab.open){ab.open=false;ab.input.clear();if(ab.isConnected||ab.handshaking){ab.isConnected=ab.handshaking=false;g.queue(ab,g.createAlert({level:g.Alert.Level.warning,description:g.Alert.Description.close_notify}));g.flush(ab)}ab.closed(ab)}ab.reset(ag)};return ab};N.tls.prf_tls1=O;N.tls.Alert=g.Alert;N.tls.CipherSuites=g.CipherSuites;N.tls.createSessionCache=g.createSessionCache;N.tls.createConnection=g.createConnection;})();(function(){var d={};var g=forge;/*if(typeof(window)!=="undefined"){var g=window.forge=window.forge||{}}else{if(typeof(module)!=="undefined"&&module.exports){var g={util:require("./util")};module.exports=d}}*/g.md=g.md||{};g.md.algorithms=g.md.algorithms||{};g.md.md5=g.md.algorithms.md5=d;var f=null;var c=null;var i=null;var a=null;var b=false;var e=function(){f=String.fromCharCode(128);f+=g.util.fillString(String.fromCharCode(0),64);c=[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12,5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2,0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9];i=[7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21];a=new Array(64);for(var j=0;j<64;++j){a[j]=Math.floor(Math.abs(Math.sin(j+1))*4294967296)}b=true};var h=function(x,u,y){var v,q,p,o,n,m,j,k;var l=y.length();while(l>=64){q=x.h0;p=x.h1;o=x.h2;n=x.h3;for(k=0;k<16;++k){u[k]=y.getInt32Le();m=n^(p&(o^n));v=(q+m+a[k]+u[k]);j=i[k];q=n;n=o;o=p;p+=(v<<j)|(v>>>(32-j))}for(;k<32;++k){m=o^(n&(p^o));v=(q+m+a[k]+u[c[k]]);j=i[k];q=n;n=o;o=p;p+=(v<<j)|(v>>>(32-j))}for(;k<48;++k){m=p^o^n;v=(q+m+a[k]+u[c[k]]);j=i[k];q=n;n=o;o=p;p+=(v<<j)|(v>>>(32-j))}for(;k<64;++k){m=o^(p|~n);v=(q+m+a[k]+u[c[k]]);j=i[k];q=n;n=o;o=p;p+=(v<<j)|(v>>>(32-j))}x.h0=(x.h0+q)&4294967295;x.h1=(x.h1+p)&4294967295;x.h2=(x.h2+o)&4294967295;x.h3=(x.h3+n)&4294967295;l-=64}};d.create=function(){if(!b){e()}var j=null;var m=g.util.createBuffer();var k=new Array(16);var l={algorithm:"md5",blockLength:64,digestLength:16,messageLength:0};l.start=function(){l.messageLength=0;m=g.util.createBuffer();j={h0:1732584193,h1:4023233417,h2:2562383102,h3:271733878}};l.start();l.update=function(o,n){if(n==="utf8"){o=g.util.encodeUtf8(o)}l.messageLength+=o.length;m.putBytes(o);h(j,k,m);if(m.read>2048||m.length()===0){m.compact()}};l.digest=function(){var n=l.messageLength;var q=g.util.createBuffer();q.putBytes(m.bytes());q.putBytes(f.substr(0,64-((n+8)%64)));q.putInt32Le((n<<3)&4294967295);q.putInt32Le((n>>>29)&255);var o={h0:j.h0,h1:j.h1,h2:j.h2,h3:j.h3};h(o,k,q);var p=g.util.createBuffer();p.putInt32Le(o.h0);p.putInt32Le(o.h1);p.putInt32Le(o.h2);p.putInt32Le(o.h3);return p};return l}})();

			/*
			encode/decode : equivalent to utils functions (forge)
			*/
			var decode=function(data) {
				var l=data.length;
				var arr=[];
				for (var i=0;i<l;i++) {
					var n=(data[i].charCodeAt()).toString(16); //utf8 SYN --> hex 16
					arr.push(n.length>1?n:'0'+n);
				};
				return arr.join('');
			};

			var encode=function(data) {
				var l=data.length;
				var arr=[];
				for (var i=0;i<l;i=i+2) {
					arr.push(String.fromCharCode(parseInt(data[i]+data[i+1],16))); //hex 16 --> dec 22 --> utf8 SYN
				};
				return arr.join('');
			};

			/* hmac test
			var N=forge;
			var Z=(new Buffer('0329579a83d5f25c76e255f53b740f905b776e14','hex')).toString('binary');
			var tmp=(new Buffer('000000000000008417030102f32577fefd65081e3bf2a7e6c119b0ca385f3c64803aa5c33f76568886ea3b100042a5837fddd29aa1928f436e9c5e573edd40e44afd5f34eec9a255b65e8de38d37c0cbcc80b569722eca4041b60500cb96ce10d6f01efb0a4838b0cdadf06e83181055c687725a25552a4e9476201315449d18b5424acb8c190783123bf4246f298dd024597d1100124fad86ab34042868480c7f2ef380df378b97e875ddba77e42fb3b2a9a6b53e903605767cc01e6c14606dc195511e6890992cf49c4bf0fd7701a8cac0ea0b496f547e7bfe6eccf0476987f5f8826f2f64cd83624c507e672c90f293ecf980ac63f70cd3fc5767cd863a6900ef561e3572ed3f2f68312c95a4f1e20f7d582390226f3258b41147c1bb48b2f9eb69102731178067ee344bdae81b7f27a68de62dccfb49d35a2c784f73dc49028ce8f98be57876701d2533fcb6c4bd5b83885b7aa0ed781d45a496b7d5393f28f32dcdbe60231b3eaabc0e211255fceb97b63e85861072067fc913f89a381dce24527ad79d7cc5fa208f72f8c3cdfda6379cd6cfbaab2ad82db3bcba76968f7bd635a2a452b24c7e54aff3b07cd73cb3a55f83f9d6f30fb97672bc046c27ee69e9d16c3f7a361ef9f5f2e580b357b220b0fc7f5c125136285b12149a9dd7fb5901db79083dab1222b91336e41ca7a43efa363440702fb36588d8f8281a7d1b82caf7dbb03ac66f70fe735adb7e72b6f5c0413c8107432327ad996eecdb10d28d41584318d8ef7a307cea6371f113a22158ae83d8f1642740601f82249584f9c59e4dee97a1560c9c1aa2b29c1afa993a1e1fdfddf9c5809a9fa851d39c41b6389f47ebf867827f2673fc13c58b0c2fe219d5ceafe9ef35d5cfa9369b5b5966d6f3806b6494eb04ed1b19c89fca2f4aae17c97c128c4e63848845800fb1a4732b63f8fcc38968e1a79dc472d4d0608c7784b40680563e015a9157277ef0bba121caea2113f723e465d956a1555ddc2a54feabad7227ee8013f9e1c3f5e7d3d90ff4ce5f6fd46a8ded46adde5c3fc22eac9f4ca2eadaff05cc140ea327c10700','hex')).toString('binary');
			var j=function(Z,tmp){console.log('key '+decode(Z));var aa=N.hmac.create();aa.start("SHA1",Z);console.log(decode(tmp));aa.update(tmp);console.log('digest');var tmp2=aa.digest().getBytes();console.log(decode(tmp2));return tmp2};
			alert(decode(j(Z,tmp)));
			*/

			//end forge

			/*
			abstrac-tls
			*/
			//TODO correct RSA key in forge
			var createIdLinkTLSCert=function(pub,priv,format,certid,date,subject,issuer,cert) {
				var publicKey=forge.pki.publicKeyFromPem(fs.readFileSync(pub).toString('utf8'));
				var privateKey=forge.pki.privateKeyFromPem(fs.readFileSync(priv).toString('utf8'));
				if (!cert) {
					var cert = forge.pki.createCertificate();
					cert.serialNumber = certid; //20 bytes max
					var date2=new Date(date.valueOf());
					date2.setHours(date2.getHours() - 2);
					cert.validity.notBefore = date2;
					cert.validity.notAfter = new Date(cert.validity.notBefore.valueOf());
					cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
					//console.log(cert.validity.notBefore.toUTCString());
					//console.log(cert.validity.notAfter.toUTCString());
					var attrs = [{
						name: 'commonName',
						value: subject||('www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.com')
					}];
					var attri = [{
						name: 'commonName',
						value: issuer||('www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.com')
					}];
					cert.setSubject(attrs);
					cert.setIssuer(attri);
				};
				cert.publicKey = publicKey;
				cert.sign(privateKey);
				var pem=forge.pki.certificateToPem(cert);
				if (format==='pem') {
					return pem;
				} else if (format==='der') {
					if (!forge_buffers) {
						return forge.pki.pemToDer(pem).data.toString('hex');
					} else {
						return forge.pki.pemToDer(pem).toHex();
					};
				} else {
					return cert;
				};
			};

			/*
			var servername='www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.net'; //check .com or .net
			var issuer='www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+'.com';
			console.log(servername);
			console.log(createIdLinkTLSCert('pub-key.pem','priv-key.pem','pem',parseInt((Rand(8)).toString('hex'),16),new Date(),servername,issuer));

			console.log('start test');
			console.log(createIdLinkTLSCert('pub-key.pem','priv-id-key.pem','der',(Date.now()).toString(),new Date()));
			console.log(createIdLinkTLSCert('pub-id-key.pem','priv-id-key.pem','der',(Date.now()).toString(),new Date()));
			var cert=createIdLinkTLSCert('pub-key.pem','priv-key.pem',false,(Date.now()).toString(),new Date(),'www.lsdfjklj.net');
			console.log(createIdLinkTLSCert('pub-key.pem','priv-key.pem','der',(Date.now()).toString(),false,false,false,cert));
			var cert2=createIdLinkTLSCert('pub-key.pem','priv-key.pem',new Date(),(Date.now()).toString(),false,false,'www.lsdfjklj.net');
			console.log(createIdLinkTLSCert('pub-key.pem','priv-id-key.pem','der',(Date.now()).toString(),false,false,false,cert));
			*/
			//createIdLinkTLSCert('/home/peersm/certs/pub.pem','/home/peersm/certs/key.pem','pem',parseInt((Rand(8)).toString('hex'),16),new Date(),'peersm.com','peersm.com')

			if (!window_browser) {
				//var fing=forge.pki.pemToDer(fs.readFileSync('pub-id-key.pem').toString('utf8')).data;
				//var H = crypto.createhash('sha1');
				//H.update(fing);
				//OR_fing=H.digest('hex');
			};

			//console.log(OR_fing);

			var createCert=function(cn) {
				console.log('Generating 512-bit key-pair and certificate for \"' + cn + '\".');
				var keys = forge.pki.rsa.generateKeyPair(512);
				console.log('Key-pair created.');
				var cert = forge.pki.createCertificate();
				cert.serialNumber = (Date.now()).toString(); //20 bytes max
				cert.validity.notBefore = new Date();
				cert.validity.notAfter = new Date();
				cert.validity.notAfter.setFullYear(cert.validity.notBefore.getFullYear() + 1);
				var attrs = [{
					name: 'commonName',
					value: cn
				}, {
					name: 'countryName',
					value: 'US'
				}, {
					shortName: 'ST',
					value: 'Virginia'
				}, {
					name: 'localityName',
					value: 'Blacksburg'
				}, {
					name: 'organizationName',
					value: 'Internet Widgits Pty Ltd'
				}, {
					shortName: 'OU',
					value: 'Internet Widgits Pty Ltd'
				}];
				cert.setSubject(attrs);
				cert.setIssuer(attrs);
				cert.setExtensions([{
					name: 'basicConstraints',
					cA: true
				}, {
					name: 'keyUsage',
					keyCertSign: true,
					digitalSignature: true,
					nonRepudiation: true,
					keyEncipherment: true,
					dataEncipherment: true
				}, {
					name: 'subjectAltName',
					altNames: [{
						type: 6, // URI
						value: 'http://www.ianonym.com'
				  }]
				}]);
				// FIXME: add subjectKeyIdentifier extension
				// FIXME: add authorityKeyIdentifier extension
				cert.publicKey = keys.publicKey;
				// self-sign certificate
				cert.sign(keys.privateKey);
				//console.log('certificate created for \"' + cn + '\": \n' + forge.pki.certificateToPem(cert));
				//console.log('private key for \"' + cn + '\": \n' + forge.pki.privateKeyToPem(keys.privateKey));
				console.log('Certificate created for \"' + cn + '\", signature : <br><br>' + decode(cert.signature).toUpperCase());
				return {
					cert: forge.pki.certificateToPem(cert),
					privateKey: forge.pki.privateKeyToPem(keys.privateKey)
				};
			};

			//Test cert a1b1c1.google.com
			var test_signature='7A13B5FFBC0AA44C14547A73C4C5C1AF3E3280953425B804FF3E52E55EAC64ABE52F8A75DC30734397896FA169BA1A240CDC22693356CDCC3CA35252D73FB906';

			var test_CERT={cert:'-----BEGIN CERTIFICATE----- MIICZzCCAhGgAwIBAgIHATYzVFIHVTANBgkqhkiG9w0BAQUFADCBlzEaMBgGA1UE AxMRYTFiMWMxLmdvb2dsZS5jb20xCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhWaXJn aW5pYTETMBEGA1UEBxMKQmxhY2tzYnVyZzEhMB8GA1UEChMYSW50ZXJuZXQgV2lk Z2l0cyBQdHkgTHRkMSEwHwYDVQQLExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQw HhcNMTMwMzE1MTMzNTIwWhcNMTQwMzE1MTMzNTIwWjCBlzEaMBgGA1UEAxMRYTFi MWMxLmdvb2dsZS5jb20xCzAJBgNVBAYTAlVTMREwDwYDVQQIEwhWaXJnaW5pYTET MBEGA1UEBxMKQmxhY2tzYnVyZzEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQ dHkgTHRkMSEwHwYDVQQLExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwXDANBgkq hkiG9w0BAQEFAANLADBIAkEAhx5uYEyHlRM2PUQy37ue87NzQ+8kJPSrxYomPnNF jPEqeoA6FWUpegLAQMQEe3ODdF16cbFHXy3Tlogw7VZ/0wIDAQABo0AwPjAMBgNV HRMEBTADAQH/MAsGA1UdDwQEAwIC9DAhBgNVHREEGjAYhhZodHRwOi8vd3d3Lmlh bm9ueW0uY29tMA0GCSqGSIb3DQEBBQUAA0EAehO1/7wKpEwUVHpzxMXBrz4ygJU0 JbgE/z5S5V6sZKvlL4p13DBzQ5eJb6FpuhokDNwiaTNWzcw8o1JS1z+5Bg== -----END CERTIFICATE-----',privateKey:' -----BEGIN RSA PRIVATE KEY----- MIIBOQIBAAJBAIcebmBMh5UTNj1EMt+7nvOzc0PvJCT0q8WKJj5zRYzxKnqAOhVl KXoCwEDEBHtzg3RdenGxR18t05aIMO1Wf9MCAwEAAQJAK8jjsZb7FIa+O8o+xhtx Xb1X67qxtrwGcaPb0r7d2dbPy1Ca7gVJVgDJRaL4xNQGNCY5dKfD8oTkyI7CrOeS IQIhAMYTn13PM1jwusQbCiuJC3sAQqsAP6rVA/gmIiejf9ZjAiEArqGs0YnH/ANa 3vVfaQMILQAftY06AlkXgbZkYcvOc9ECIDh5Db3UPmKEN+uDfhHNf91wxJE2f/B3 zSrpzGEax27bAiAyPdOBqXk+2RxAvymHBddJUxrMWehRmp43tGjm9gLcsQIgUI3f Jo+GCZP79rVmRRfBYFvZjKeIMJc103Rl51TAk7E= -----END RSA PRIVATE KEY-----'};


			var abstract_tls=function(socket,domain,server) {
				var type=server?'server':'client';
				var data;
				if (server) {
					if (!caStore[domain]) {
						data=createCert(server?domain:'client');
						caStore[domain]=data;
					} else {
						data=caStore[domain];
					};
				} else {
					data={cert:{},privateKey:{}};
				};
				//var data=test_CERT;
				//console.log('Certificate for \"' + domain + '\", signature : <br><br>' + test_signature);
				return forge.tls.createConnection({
					server: server?true:false,
					caStore: server?[data.cert]:'', //TODO populate caStore
					sessionCache: {},
					// supported cipher suites in order of preference
					//TLS_RSA_WITH_AES_128_CBC_SHA: [0x00,0x2f],TLS_RSA_WITH_AES_256_CBC_SHA: [0x00,0x35],TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: [0xc0,0x0a],TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA: [0xc0,0x14],TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA: [0x00,0x88],TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA: [0x00,0x87],TLS_DHE_RSA_WITH_AES_256_CBC_SHA: [0x00,0x39],TLS_DHE_DSS_WITH_AES_256_CBC_SHA: [0x00,0x38],TLS_ECDH_RSA_WITH_AES_256_CBC_SHA: [0xc0,0x0f],TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA: [0xc0,0x05],TLS_RSA_WITH_CAMELLIA_256_CBC_SHA: [0x00,0x84],TLS_ECDHE_ECDSA_WITH_RC4_128_SHA: [0xc0,0x07],TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: [0xc0,0x09],TLS_ECDHE_RSA_WITH_RC4_128_SHA: [0xc0,0x11],TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA: [0xc0,0x13],TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA: [0x00,0x45],TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA: [0x00,0x44],TLS_DHE_RSA_WITH_AES_128_CBC_SHA: [0x00,0x33],TLS_DHE_DSS_WITH_AES_128_CBC_SHA: [0x00,0x32],TLS_ECDH_RSA_WITH_RC4_128_SHA: [0xc0,0x0c],TLS_ECDH_RSA_WITH_AES_128_CBC_SHA: [0xc0,0x0e],TLS_ECDH_ECDSA_WITH_RC4_128_SHA: [0xc0,0x02],TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA: [0xc0,0x04],TLS_RSA_WITH_SEED_CBC_SHA: [0x00,0x96],TLS_RSA_WITH_CAMELLIA_128_CBC_SHA: [0x00,0x41],TLS_RSA_WITH_RC4_128_MD5: [0x00,0x04],TLS_RSA_WITH_RC4_128_SHA: [0x00,0x05],TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA: [0xc0,0x08],TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA: [0xc0,0x12],TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA: [0x00,0x16],TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA: [0x00,0x13],TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA: [0xc0,0x0d],TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA: [0xc0,0x03],SSL_RSA_FIPS_WITH_3DES_EDE_CBC_SHA: [0xfe,0xff],TLS_RSA_WITH_3DES_EDE_CBC_SHA: [0x00,0x0a]
					cipherSuites: [
						[0x00,0x2f],[0x00,0x35],[0xc0,0x0a],[0xc0,0x14],[0x00,0x88],[0x00,0x87],[0x00,0x39],[0x00,0x38],[0xc0,0x0f],[0xc0,0x05],[0x00,0x84],[0xc0,0x07],[0xc0,0x09],[0xc0,0x11],[0xc0,0x13],[0x00,0x45],[0x00,0x44],[0x00,0x33],[0x00,0x32],[0xc0,0x0c],[0xc0,0x0e],[0xc0,0x02],[0xc0,0x04],[0x00,0x96],[0x00,0x41],[0x00,0x04],[0x00,0x05],[0xc0,0x08],[0xc0,0x12],[0x00,0x16],[0x00,0x13],[0xc0,0x0d],[0xc0,0x03],[0xfe,0xff],[0x00,0x0a]],
					virtualHost: server?'':domain,
					verifyClient: false,
					verify: function(c, verified, depth, certs) {
						return true;
						try {
							console.log('TLS '+type+' '+domain+' verifying certificate w/CN: \"' +certs[0].subject.getField('CN').value +'\", verified: ' + verified + '...');
						} catch(ee) {
							console.log('TLS '+type+' '+domain+' certificate verified');
						}; //TODO - Bug(?) crashes with openssl certificates
						return true; //TODO - remove when caStore is populated
					  //return verified;
					},
					connected: function(c) {
						console.log('TLS '+type+' '+domain+' connected...');
						// send message to server
						setTimeout(function() {
							var txt=forge.util.encodeUtf8('Hello '+((type==='server')?'client':'server')+' I want 100 â‚¬ ');
							c.prepare(txt);
						}, 1);
					},
					getCertificate: function(c, hint) {
						//console.log(type+' '+domain+' getting certificate ...');
						return data.cert;
					},
					getPrivateKey: function(c, cert) {
						return data.privateKey;
					},
					tlsDataReady: function(c) {
						// send TLS data to server
						if (!forge_buffers) {
							var a=c.tlsData.data.slice(0,c.tlsData.length_);
							if (a.length) {
								if (a[0]!==0) { //Bug to investigate - empty buffer sent by client after handshake
									c.tlsData.clear();
									//console.log('ws '+type+' send tls',true);
									//console.log(a.toString('hex'));
									if (type==='server') {
										socket.write_s(a);
									} else {
										socket.write_c(a);
									};
								};
							};
						} else {
							var tmp=c.tlsData.getBytes();
							if (_node) {
								if (type==='server') {
									socket.write_s(new Buffer(decode(tmp),'hex'));
								} else {
									socket.write_c(new Buffer(decode(tmp),'hex'));
								};
							} else {
								//console.log((new Buffer(tmp,'binary')).toString('hex'));
								if (type==='server') {
									socket.write_s(tmp);
								} else {
									socket.write_c(tmp);
								};
							};
						};
					},
					dataReady: function(c) {
						if (!forge_buffers) {
							var response = c.data.data.toString('utf8');
							console.log(type+' '+domain+' received : '+response);
						} else {
							console.log(type+' '+domain+' received : '+forge.util.decodeUtf8(c.data.getBytes()));
						};
					},
					closed: function(c) {
						console.log(type+' '+domain+' disconnected.');
					},
					error: function(c, error) {
						console.log(type+' '+domain+' notification: ' + error.message);
						if (error.message.indexOf('Unknown')===-1) {
							console.log('Unexpected error - please retry');
						} else {
							console.log('Normal error message, so far, so good.');
						};
						c.close();
					}
				});
			};


			/*
			end abstract-tls
			*/
		};

		var xorBytes=function(s1,s2,n) {
			var s3='';
			var b='';
			var t='';
			var i=0;
			var c=0;
			for(;n>0;--n,++i) {
				b=s1.charCodeAt(i)^s2.charCodeAt(i);
				if(c>=10) {
					s3+=t;
					t='';
					c=0;
				};
				t +=String.fromCharCode(b);
				++c;
			};
			s3 +=t;
			return s3;
		};

		var rsa_mgf1=function rsa_mgf1(seed,maskLength,digestLength) {
			var t='';
			var hash;
			var count=Math.ceil(maskLength/digestLength);
			for(var i=0;i<count;++i) {
				var c=String.fromCharCode((i >> 24) & 0xFF, (i >> 16) & 0xFF, (i >> 8) & 0xFF, i & 0xFF);
				hash=new Hash('sha1');
				hash.update(new Buffer(seed+c,'binary'));
				t+=(new Buffer(hash.digest('hex'),'hex')).toString('binary');
			};
			return t.substring(0,maskLength);
		};

		var encode_rsa_oaep=function(keyLength,message,options) {
			var label='';
			var seed;
			var md;
			var digestLength=20;
			if (options) {
				label=options.label;
				seed=options.seed;
			};
			md=new Hash('sha1');
			var maxLength=keyLength-2*digestLength-2; //86
			if(message.length>maxLength) {
				throw {
				  message: 'RSAES-OAEP input message length is too long.',
				  length: message.length,
				  maxLength: maxLength
				};
			};
			md.update(new Buffer(label,'binary'));
			var lHash=md.digest('hex'); //txt_hex
			lHash=(new Buffer(lHash,'hex')).toString('binary');
			var PS='';
			var PS_length=maxLength-message.length;
			for (var i=0;i<PS_length;i++) {
				PS += '\x00';
			};
			var DB=lHash+PS+'\x01'+message;
			if(!seed) {
				seed=Rand(digestLength).toString('binary');
			};
			var dbMask=rsa_mgf1(seed,keyLength-digestLength-1,digestLength);
			var maskedDB=xorBytes(DB,dbMask,DB.length);
			var seedMask=rsa_mgf1(maskedDB,digestLength,digestLength);
			var maskedSeed=xorBytes(seed,seedMask,seed.length);
			return '\x00'+maskedSeed+maskedDB;
		};
		var decode_rsa_oaep=function(keyLength,em,options) {
			var label='';
			var md;
			var digestLength=20;
			if(options) {
				label=options.label;
				md=options.md;
			};
			if(em.length!==keyLength) {
				 throw {
					 message:'RSAES-OAEP encoded message length is invalid.',
					 length:em.length,
					 expectedLength:keyLength
				 };
			};
			md=new Hash('sha1');
			if(keyLength<2*digestLength+2) {
				throw {
				message: 'RSAES-OAEP key is too short for the hash function.'
				};
			};
			md.update(new Buffer(label,'binary'));
			var lHash=md.digest('hex');
			lHash=(new Buffer(lHash,'hex')).toString('binary');
			var y=em.charAt(0);
			var maskedSeed=em.substring(1,digestLength+1);
			var maskedDB=em.substring(1+digestLength);
			var seedMask=rsa_mgf1(maskedDB,digestLength,digestLength);
			var seed=forge.util.xorBytes(maskedSeed,seedMask,maskedSeed.length);
			var dbMask=rsa_mgf1(seed,keyLength-digestLength-1,digestLength);
			var db=forge.util.xorBytes(maskedDB,dbMask,maskedDB.length);
			var lHashPrime=db.substring(0,digestLength);
			var error=(y !== '\x00');
			for(var i=0; i<digestLength;++i) {
				error|=(lHash.charAt(i)!==lHashPrime.charAt(i));
			};
			var in_ps=1;
			var index=digestLength;
			for(var j=digestLength;j<db.length;j++) {
				var code=db.charCodeAt(j);
				var is_0=(code & 0x1) ^ 0x1;
				var error_mask=in_ps?0xfffe:0x0000;
				error|=(code&error_mask);
				in_ps=in_ps&is_0;
				index+=in_ps;
			};
			if(error||db.charCodeAt(index)!==0x1) {
				throw {
					message:'Invalid RSAES-OAEP padding.'
				};
			};
			return db.substring(index + 1);
		};

		var Rsa_forge=function() {
		};

		Rsa_forge.prototype.encrypt=function(buff_modulus_bin,buff_exp_bin,buff_mess_bin,mode) {
			//var key=forge.pki.publicKeyFromPem('-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCL0KgShoWLEfKFasLz79il5Tiy\n+FGkwCzHF8lOpSQYxYfMaVFeRrU4HRfNIpnLTFn644JQiY6rMcm4rPzwIRCpbw8D\nRC7NNPG++wYE4f+7mBhIU02p8/I3le198EdzDLnatxFyKtZQS4k869ot5LQRI9Zn\nR0oWbro+rX34Jn3aFQIDAQAB\n-----END PUBLIC KEY-----');
			var key={};
			key.n=new BigInteger(buff_modulus_bin.toString('binary'),16);
			key.e=new BigInteger(buff_exp_bin.toString('binary'),16);
			if (mode==='RSA_PKCS1_OAEP_PADDING') {
				var oaep=encode_rsa_oaep(buff_modulus_bin.length/2,buff_mess_bin.toString('binary'));
				var res=new Buffer(forge.pki.rsa.encrypt(oaep,key,true),'binary').toString('hex');
				return res;
			} else {
				return new Buffer(forge.pki.rsa.encrypt(buff_mess_bin.toString('binary'),key),'binary').toString('hex');
			};
		};

		Rsa_forge.prototype.decrypt=function(key,string_mess_hex,mode) {
			key=forge.pki.privateKeyFromPem(key);
			if (mode==='RSA_PKCS1_OAEP_PADDING') {
				var res=forge.pki.rsa.decrypt((new Buffer(string_mess_hex,'hex')).toString('binary'),key,false,false);
				res=decode_rsa_oaep(key.n.bitLength()/8,res);
				return (new Buffer(res,'binary').toString('hex'));
			} else {
				return new Buffer(forge.pki.rsa.decrypt(buff_mess_bin.toString('binary'),key),'binary').toString('hex');
			};
		};

		// tests
		/*
		var RSA=new Rsa_forge();
		var exp='010001';
		var modulus=new Buffer('8bd0a81286858b11f2856ac2f3efd8a5e538b2f851a4c02cc717c94ea52418c587cc69515e46b5381d17cd2299cb4c59fae38250898eab31c9b8acfcf02110a96f0f03442ecd34f1befb0604e1ffbb981848534da9f3f23795ed7df047730cb9dab711722ad6504b893cebda2de4b41123d667474a166eba3ead7df8267dda15','hex');
		var K1=new Buffer('00112233445566778899001122334455b361004bd95f91cfeb79f57a7dfbf6ec3a5cb54ac0e80de6371e0ea64acd40022919264e42d3999b8b1f82ac5b5927b7ff38ea6a37b13f9f98430101af1bac8b0a07305c7ddb','hex');
		console.log('K1 '+K1.toString('hex'));
		K1=RSA.encrypt(new Buffer(modulus.toString('hex'),'binary'),new Buffer(exp,'binary'),new Buffer(K1.toString('binary'),'binary'),'RSA_PKCS1_OAEP_PADDING','hex');
		console.log('K1 encrypted '+K1);
		//verify with onion2.js
		K1=new Buffer(K1,'hex');
		K1=RSA.decrypt(new Buffer(K1.toString('binary'),'binary'),'RSA_PKCS1_OAEP_PADDING','hex');
		console.log('K1 decrypted '+K1);
		*/

		//mp4box.js

/**
DataStream reads scalars, arrays and structs of data from an ArrayBuffer.
It's like a file-like DataView on steroids.
@param {ArrayBuffer} arrayBuffer ArrayBuffer to read from.
@param {?Number} byteOffset Offset from arrayBuffer beginning for the DataStream.
@param {?Boolean} endianness DataStream.BIG_ENDIAN or DataStream.LITTLE_ENDIAN (the default).
*/
var DataStream = function(arrayBuffer, byteOffset, endianness) {
this._byteOffset = byteOffset || 0;
if (arrayBuffer instanceof ArrayBuffer) {
this.buffer = arrayBuffer;
} else if (typeof arrayBuffer == "object") {
this.dataView = arrayBuffer;
if (byteOffset) {
this._byteOffset += byteOffset;
}
} else {
this.buffer = new ArrayBuffer(arrayBuffer || 0);
}
this.position = 0;
this.endianness = endianness == null ? DataStream.LITTLE_ENDIAN : endianness;
};
DataStream.prototype = {};
/**
Saves the DataStream contents to the given filename.
Uses Chrome's anchor download property to initiate download.
@param {string} filename Filename to save as.
@return {null}
*/
DataStream.prototype.save = function(filename) {
var blob = new Blob([this.buffer]);
var URL = (window.webkitURL || window.URL);
if (URL && URL.createObjectURL) {
var url = URL.createObjectURL(blob);
var a = document.createElement('a');
a.setAttribute('href', url);
a.setAttribute('download', filename);
a.click();
URL.revokeObjectURL(url);
} else {
throw("DataStream.save: Can't create object URL.");
}
};
/**
Big-endian const to use as default endianness.
@type {boolean}
*/
DataStream.BIG_ENDIAN = false;
/**
Little-endian const to use as default endianness.
@type {boolean}
*/
DataStream.LITTLE_ENDIAN = true;
/**
Whether to extend DataStream buffer when trying to write beyond its size.
If set, the buffer is reallocated to twice its current size until the
requested write fits the buffer.
@type {boolean}
*/
DataStream.prototype._dynamicSize = true;
Object.defineProperty(DataStream.prototype, 'dynamicSize',
{ get: function() {
return this._dynamicSize;
},
set: function(v) {
if (!v) {
this._trimAlloc();
}
this._dynamicSize = v;
} });
/**
Virtual byte length of the DataStream backing buffer.
Updated to be max of original buffer size and last written size.
If dynamicSize is false is set to buffer size.
@type {number}
*/
DataStream.prototype._byteLength = 0;
/**
Returns the byte length of the DataStream object.
@type {number}
*/
Object.defineProperty(DataStream.prototype, 'byteLength',
{ get: function() {
return this._byteLength - this._byteOffset;
}});
/**
Set/get the backing ArrayBuffer of the DataStream object.
The setter updates the DataView to point to the new buffer.
@type {Object}
*/
Object.defineProperty(DataStream.prototype, 'buffer',
{ get: function() {
this._trimAlloc();
return this._buffer;
},
set: function(v) {
this._buffer = v;
this._dataView = new DataView(this._buffer, this._byteOffset);
this._byteLength = this._buffer.byteLength;
} });
/**
Set/get the byteOffset of the DataStream object.
The setter updates the DataView to point to the new byteOffset.
@type {number}
*/
Object.defineProperty(DataStream.prototype, 'byteOffset',
{ get: function() {
return this._byteOffset;
},
set: function(v) {
this._byteOffset = v;
this._dataView = new DataView(this._buffer, this._byteOffset);
this._byteLength = this._buffer.byteLength;
} });
/**
Set/get the backing DataView of the DataStream object.
The setter updates the buffer and byteOffset to point to the DataView values.
@type {Object}
*/
Object.defineProperty(DataStream.prototype, 'dataView',
{ get: function() {
return this._dataView;
},
set: function(v) {
this._byteOffset = v.byteOffset;
this._buffer = v.buffer;
this._dataView = new DataView(this._buffer, this._byteOffset);
this._byteLength = this._byteOffset + v.byteLength;
} });
/**
Internal function to resize the DataStream buffer when required.
@param {number} extra Number of bytes to add to the buffer allocation.
@return {null}
*/
DataStream.prototype._realloc = function(extra) {
if (!this._dynamicSize) {
return;
}
var req = this._byteOffset + this.position + extra;
var blen = this._buffer.byteLength;
if (req <= blen) {
if (req > this._byteLength) {
this._byteLength = req;
}
return;
}
if (blen < 1) {
blen = 1;
}
while (req > blen) {
blen *= 2;
}
var buf = new ArrayBuffer(blen);
var src = new Uint8Array(this._buffer);
var dst = new Uint8Array(buf, 0, src.length);
dst.set(src);
this.buffer = buf;
this._byteLength = req;
};
/**
Internal function to trim the DataStream buffer when required.
Used for stripping out the extra bytes from the backing buffer when
the virtual byteLength is smaller than the buffer byteLength (happens after
growing the buffer with writes and not filling the extra space completely).
@return {null}
*/
DataStream.prototype._trimAlloc = function() {
if (this._byteLength == this._buffer.byteLength) {
return;
}
var buf = new ArrayBuffer(this._byteLength);
var dst = new Uint8Array(buf);
var src = new Uint8Array(this._buffer, 0, dst.length);
dst.set(src);
this.buffer = buf;
};
/**
Internal function to trim the DataStream buffer when required.
Used for stripping out the first bytes when not needed anymore.
@return {null}
*/
DataStream.prototype.shift = function(offset) {
var buf = new ArrayBuffer(this._byteLength-offset);
var dst = new Uint8Array(buf);
var src = new Uint8Array(this._buffer, offset, dst.length);
dst.set(src);
this.buffer = buf;
this.position -= offset;
};
/**
Sets the DataStream read/write position to given position.
Clamps between 0 and DataStream length.
@param {number} pos Position to seek to.
@return {null}
*/
DataStream.prototype.seek = function(pos) {
var npos = Math.max(0, Math.min(this.byteLength, pos));
this.position = (isNaN(npos) || !isFinite(npos)) ? 0 : npos;
};
/**
Returns true if the DataStream seek pointer is at the end of buffer and
there's no more data to read.
@return {boolean} True if the seek pointer is at the end of the buffer.
*/
DataStream.prototype.isEof = function() {
return (this.position >= this._byteLength);
};
/**
Maps an Int32Array into the DataStream buffer, swizzling it to native
endianness in-place. The current offset from the start of the buffer needs to
be a multiple of element size, just like with typed array views.
Nice for quickly reading in data. Warning: potentially modifies the buffer
contents.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} Int32Array to the DataStream backing buffer.
*/
DataStream.prototype.mapInt32Array = function(length, e) {
this._realloc(length * 4);
var arr = new Int32Array(this._buffer, this.byteOffset+this.position, length);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += length * 4;
return arr;
};
/**
Maps an Int16Array into the DataStream buffer, swizzling it to native
endianness in-place. The current offset from the start of the buffer needs to
be a multiple of element size, just like with typed array views.
Nice for quickly reading in data. Warning: potentially modifies the buffer
contents.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} Int16Array to the DataStream backing buffer.
*/
DataStream.prototype.mapInt16Array = function(length, e) {
this._realloc(length * 2);
var arr = new Int16Array(this._buffer, this.byteOffset+this.position, length);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += length * 2;
return arr;
};
/**
Maps an Int8Array into the DataStream buffer.
Nice for quickly reading in data.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} Int8Array to the DataStream backing buffer.
*/
DataStream.prototype.mapInt8Array = function(length) {
this._realloc(length * 1);
var arr = new Int8Array(this._buffer, this.byteOffset+this.position, length);
this.position += length * 1;
return arr;
};
/**
Maps a Uint32Array into the DataStream buffer, swizzling it to native
endianness in-place. The current offset from the start of the buffer needs to
be a multiple of element size, just like with typed array views.
Nice for quickly reading in data. Warning: potentially modifies the buffer
contents.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} Uint32Array to the DataStream backing buffer.
*/
DataStream.prototype.mapUint32Array = function(length, e) {
this._realloc(length * 4);
var arr = new Uint32Array(this._buffer, this.byteOffset+this.position, length);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += length * 4;
return arr;
};
/**
Maps a Uint16Array into the DataStream buffer, swizzling it to native
endianness in-place. The current offset from the start of the buffer needs to
be a multiple of element size, just like with typed array views.
Nice for quickly reading in data. Warning: potentially modifies the buffer
contents.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} Uint16Array to the DataStream backing buffer.
*/
DataStream.prototype.mapUint16Array = function(length, e) {
this._realloc(length * 2);
var arr = new Uint16Array(this._buffer, this.byteOffset+this.position, length);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += length * 2;
return arr;
};
/**
Maps a Uint8Array into the DataStream buffer.
Nice for quickly reading in data.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} Uint8Array to the DataStream backing buffer.
*/
DataStream.prototype.mapUint8Array = function(length) {
this._realloc(length * 1);
var arr = new Uint8Array(this._buffer, this.byteOffset+this.position, length);
this.position += length * 1;
return arr;
};
/**
Maps a Float64Array into the DataStream buffer, swizzling it to native
endianness in-place. The current offset from the start of the buffer needs to
be a multiple of element size, just like with typed array views.
Nice for quickly reading in data. Warning: potentially modifies the buffer
contents.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} Float64Array to the DataStream backing buffer.
*/
DataStream.prototype.mapFloat64Array = function(length, e) {
this._realloc(length * 8);
var arr = new Float64Array(this._buffer, this.byteOffset+this.position, length);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += length * 8;
return arr;
};
/**
Maps a Float32Array into the DataStream buffer, swizzling it to native
endianness in-place. The current offset from the start of the buffer needs to
be a multiple of element size, just like with typed array views.
Nice for quickly reading in data. Warning: potentially modifies the buffer
contents.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} Float32Array to the DataStream backing buffer.
*/
DataStream.prototype.mapFloat32Array = function(length, e) {
this._realloc(length * 4);
var arr = new Float32Array(this._buffer, this.byteOffset+this.position, length);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += length * 4;
return arr;
};
/**
Reads an Int32Array of desired length and endianness from the DataStream.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} The read Int32Array.
*/
DataStream.prototype.readInt32Array = function(length, e) {
length = length == null ? (this.byteLength-this.position / 4) : length;
var arr = new Int32Array(length);
DataStream.memcpy(arr.buffer, 0,
this.buffer, this.byteOffset+this.position,
length*arr.BYTES_PER_ELEMENT);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += arr.byteLength;
return arr;
};
/**
Reads an Int16Array of desired length and endianness from the DataStream.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} The read Int16Array.
*/
DataStream.prototype.readInt16Array = function(length, e) {
length = length == null ? (this.byteLength-this.position / 2) : length;
var arr = new Int16Array(length);
DataStream.memcpy(arr.buffer, 0,
this.buffer, this.byteOffset+this.position,
length*arr.BYTES_PER_ELEMENT);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += arr.byteLength;
return arr;
};
/**
Reads an Int8Array of desired length from the DataStream.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} The read Int8Array.
*/
DataStream.prototype.readInt8Array = function(length) {
length = length == null ? (this.byteLength-this.position) : length;
var arr = new Int8Array(length);
DataStream.memcpy(arr.buffer, 0,
this.buffer, this.byteOffset+this.position,
length*arr.BYTES_PER_ELEMENT);
this.position += arr.byteLength;
return arr;
};
/**
Reads a Uint32Array of desired length and endianness from the DataStream.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} The read Uint32Array.
*/
DataStream.prototype.readUint32Array = function(length, e) {
length = length == null ? (this.byteLength-this.position / 4) : length;
var arr = new Uint32Array(length);
DataStream.memcpy(arr.buffer, 0,
this.buffer, this.byteOffset+this.position,
length*arr.BYTES_PER_ELEMENT);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += arr.byteLength;
return arr;
};
/**
Reads a Uint16Array of desired length and endianness from the DataStream.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} The read Uint16Array.
*/
DataStream.prototype.readUint16Array = function(length, e) {
length = length == null ? (this.byteLength-this.position / 2) : length;
var arr = new Uint16Array(length);
DataStream.memcpy(arr.buffer, 0,
this.buffer, this.byteOffset+this.position,
length*arr.BYTES_PER_ELEMENT);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += arr.byteLength;
return arr;
};
/**
Reads a Uint8Array of desired length from the DataStream.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} The read Uint8Array.
*/
DataStream.prototype.readUint8Array = function(length) {
length = length == null ? (this.byteLength-this.position) : length;
var arr = new Uint8Array(length);
DataStream.memcpy(arr.buffer, 0,
this.buffer, this.byteOffset+this.position,
length*arr.BYTES_PER_ELEMENT);
this.position += arr.byteLength;
return arr;
};
/**
Reads a Float64Array of desired length and endianness from the DataStream.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} The read Float64Array.
*/
DataStream.prototype.readFloat64Array = function(length, e) {
length = length == null ? (this.byteLength-this.position / 8) : length;
var arr = new Float64Array(length);
DataStream.memcpy(arr.buffer, 0,
this.buffer, this.byteOffset+this.position,
length*arr.BYTES_PER_ELEMENT);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += arr.byteLength;
return arr;
};
/**
Reads a Float32Array of desired length and endianness from the DataStream.
@param {number} length Number of elements to map.
@param {?boolean} e Endianness of the data to read.
@return {Object} The read Float32Array.
*/
DataStream.prototype.readFloat32Array = function(length, e) {
length = length == null ? (this.byteLength-this.position / 4) : length;
var arr = new Float32Array(length);
DataStream.memcpy(arr.buffer, 0,
this.buffer, this.byteOffset+this.position,
length*arr.BYTES_PER_ELEMENT);
DataStream.arrayToNative(arr, e == null ? this.endianness : e);
this.position += arr.byteLength;
return arr;
};
/**
Writes an Int32Array of specified endianness to the DataStream.
@param {Object} arr The array to write.
@param {?boolean} e Endianness of the data to write.
*/
DataStream.prototype.writeInt32Array = function(arr, e) {
this._realloc(arr.length * 4);
if (arr instanceof Int32Array &&
this.byteOffset+this.position % arr.BYTES_PER_ELEMENT === 0) {
DataStream.memcpy(this._buffer, this.byteOffset+this.position,
arr.buffer, 0,
arr.byteLength);
this.mapInt32Array(arr.length, e);
} else {
for (var i=0; i<arr.length; i++) {
this.writeInt32(arr[i], e);
}
}
};
/**
Writes an Int16Array of specified endianness to the DataStream.
@param {Object} arr The array to write.
@param {?boolean} e Endianness of the data to write.
*/
DataStream.prototype.writeInt16Array = function(arr, e) {
this._realloc(arr.length * 2);
if (arr instanceof Int16Array &&
this.byteOffset+this.position % arr.BYTES_PER_ELEMENT === 0) {
DataStream.memcpy(this._buffer, this.byteOffset+this.position,
arr.buffer, 0,
arr.byteLength);
this.mapInt16Array(arr.length, e);
} else {
for (var i=0; i<arr.length; i++) {
this.writeInt16(arr[i], e);
}
}
};
/**
Writes an Int8Array to the DataStream.
@param {Object} arr The array to write.
*/
DataStream.prototype.writeInt8Array = function(arr) {
this._realloc(arr.length * 1);
if (arr instanceof Int8Array &&
this.byteOffset+this.position % arr.BYTES_PER_ELEMENT === 0) {
DataStream.memcpy(this._buffer, this.byteOffset+this.position,
arr.buffer, 0,
arr.byteLength);
this.mapInt8Array(arr.length);
} else {
for (var i=0; i<arr.length; i++) {
this.writeInt8(arr[i]);
}
}
};
/**
Writes a Uint32Array of specified endianness to the DataStream.
@param {Object} arr The array to write.
@param {?boolean} e Endianness of the data to write.
*/
DataStream.prototype.writeUint32Array = function(arr, e) {
this._realloc(arr.length * 4);
if (arr instanceof Uint32Array &&
this.byteOffset+this.position % arr.BYTES_PER_ELEMENT === 0) {
DataStream.memcpy(this._buffer, this.byteOffset+this.position,
arr.buffer, 0,
arr.byteLength);
this.mapUint32Array(arr.length, e);
} else {
for (var i=0; i<arr.length; i++) {
this.writeUint32(arr[i], e);
}
}
};
/**
Writes a Uint16Array of specified endianness to the DataStream.
@param {Object} arr The array to write.
@param {?boolean} e Endianness of the data to write.
*/
DataStream.prototype.writeUint16Array = function(arr, e) {
this._realloc(arr.length * 2);
if (arr instanceof Uint16Array &&
this.byteOffset+this.position % arr.BYTES_PER_ELEMENT === 0) {
DataStream.memcpy(this._buffer, this.byteOffset+this.position,
arr.buffer, 0,
arr.byteLength);
this.mapUint16Array(arr.length, e);
} else {
for (var i=0; i<arr.length; i++) {
this.writeUint16(arr[i], e);
}
}
};
/**
Writes a Uint8Array to the DataStream.
@param {Object} arr The array to write.
*/
DataStream.prototype.writeUint8Array = function(arr) {
this._realloc(arr.length * 1);
if (arr instanceof Uint8Array &&
this.byteOffset+this.position % arr.BYTES_PER_ELEMENT === 0) {
DataStream.memcpy(this._buffer, this.byteOffset+this.position,
arr.buffer, 0,
arr.byteLength);
this.mapUint8Array(arr.length);
} else {
for (var i=0; i<arr.length; i++) {
this.writeUint8(arr[i]);
}
}
};
/**
Writes a Float64Array of specified endianness to the DataStream.
@param {Object} arr The array to write.
@param {?boolean} e Endianness of the data to write.
*/
DataStream.prototype.writeFloat64Array = function(arr, e) {
this._realloc(arr.length * 8);
if (arr instanceof Float64Array &&
this.byteOffset+this.position % arr.BYTES_PER_ELEMENT === 0) {
DataStream.memcpy(this._buffer, this.byteOffset+this.position,
arr.buffer, 0,
arr.byteLength);
this.mapFloat64Array(arr.length, e);
} else {
for (var i=0; i<arr.length; i++) {
this.writeFloat64(arr[i], e);
}
}
};
/**
Writes a Float32Array of specified endianness to the DataStream.
@param {Object} arr The array to write.
@param {?boolean} e Endianness of the data to write.
*/
DataStream.prototype.writeFloat32Array = function(arr, e) {
this._realloc(arr.length * 4);
if (arr instanceof Float32Array &&
this.byteOffset+this.position % arr.BYTES_PER_ELEMENT === 0) {
DataStream.memcpy(this._buffer, this.byteOffset+this.position,
arr.buffer, 0,
arr.byteLength);
this.mapFloat32Array(arr.length, e);
} else {
for (var i=0; i<arr.length; i++) {
this.writeFloat32(arr[i], e);
}
}
};
/**
Reads a 32-bit int from the DataStream with the desired endianness.
@param {?boolean} e Endianness of the number.
@return {number} The read number.
*/
DataStream.prototype.readInt32 = function(e) {
var v = this._dataView.getInt32(this.position, e == null ? this.endianness : e);
this.position += 4;
return v;
};
/**
Reads a 16-bit int from the DataStream with the desired endianness.
@param {?boolean} e Endianness of the number.
@return {number} The read number.
*/
DataStream.prototype.readInt16 = function(e) {
var v = this._dataView.getInt16(this.position, e == null ? this.endianness : e);
this.position += 2;
return v;
};
/**
Reads an 8-bit int from the DataStream.
@return {number} The read number.
*/
DataStream.prototype.readInt8 = function() {
var v = this._dataView.getInt8(this.position);
this.position += 1;
return v;
};
/**
Reads a 32-bit unsigned int from the DataStream with the desired endianness.
@param {?boolean} e Endianness of the number.
@return {number} The read number.
*/
DataStream.prototype.readUint32 = function(e) {
var v = this._dataView.getUint32(this.position, e == null ? this.endianness : e);
this.position += 4;
return v;
};
/**
Reads a 16-bit unsigned int from the DataStream with the desired endianness.
@param {?boolean} e Endianness of the number.
@return {number} The read number.
*/
DataStream.prototype.readUint16 = function(e) {
var v = this._dataView.getUint16(this.position, e == null ? this.endianness : e);
this.position += 2;
return v;
};
/**
Reads an 8-bit unsigned int from the DataStream.
@return {number} The read number.
*/
DataStream.prototype.readUint8 = function() {
var v = this._dataView.getUint8(this.position);
this.position += 1;
return v;
};
/**
Reads a 32-bit float from the DataStream with the desired endianness.
@param {?boolean} e Endianness of the number.
@return {number} The read number.
*/
DataStream.prototype.readFloat32 = function(e) {
var v = this._dataView.getFloat32(this.position, e == null ? this.endianness : e);
this.position += 4;
return v;
};
/**
Reads a 64-bit float from the DataStream with the desired endianness.
@param {?boolean} e Endianness of the number.
@return {number} The read number.
*/
DataStream.prototype.readFloat64 = function(e) {
var v = this._dataView.getFloat64(this.position, e == null ? this.endianness : e);
this.position += 8;
return v;
};
/**
Writes a 32-bit int to the DataStream with the desired endianness.
@param {number} v Number to write.
@param {?boolean} e Endianness of the number.
*/
DataStream.prototype.writeInt32 = function(v, e) {
this._realloc(4);
this._dataView.setInt32(this.position, v, e == null ? this.endianness : e);
this.position += 4;
};
/**
Writes a 16-bit int to the DataStream with the desired endianness.
@param {number} v Number to write.
@param {?boolean} e Endianness of the number.
*/
DataStream.prototype.writeInt16 = function(v, e) {
this._realloc(2);
this._dataView.setInt16(this.position, v, e == null ? this.endianness : e);
this.position += 2;
};
/**
Writes an 8-bit int to the DataStream.
@param {number} v Number to write.
*/
DataStream.prototype.writeInt8 = function(v) {
this._realloc(1);
this._dataView.setInt8(this.position, v);
this.position += 1;
};
/**
Writes a 32-bit unsigned int to the DataStream with the desired endianness.
@param {number} v Number to write.
@param {?boolean} e Endianness of the number.
*/
DataStream.prototype.writeUint32 = function(v, e) {
this._realloc(4);
this._dataView.setUint32(this.position, v, e == null ? this.endianness : e);
this.position += 4;
};
/**
Writes a 16-bit unsigned int to the DataStream with the desired endianness.
@param {number} v Number to write.
@param {?boolean} e Endianness of the number.
*/
DataStream.prototype.writeUint16 = function(v, e) {
this._realloc(2);
this._dataView.setUint16(this.position, v, e == null ? this.endianness : e);
this.position += 2;
};
/**
Writes an 8-bit unsigned int to the DataStream.
@param {number} v Number to write.
*/
DataStream.prototype.writeUint8 = function(v) {
this._realloc(1);
this._dataView.setUint8(this.position, v);
this.position += 1;
};
/**
Writes a 32-bit float to the DataStream with the desired endianness.
@param {number} v Number to write.
@param {?boolean} e Endianness of the number.
*/
DataStream.prototype.writeFloat32 = function(v, e) {
this._realloc(4);
this._dataView.setFloat32(this.position, v, e == null ? this.endianness : e);
this.position += 4;
};
/**
Writes a 64-bit float to the DataStream with the desired endianness.
@param {number} v Number to write.
@param {?boolean} e Endianness of the number.
*/
DataStream.prototype.writeFloat64 = function(v, e) {
this._realloc(8);
this._dataView.setFloat64(this.position, v, e == null ? this.endianness : e);
this.position += 8;
};
/**
Native endianness. Either DataStream.BIG_ENDIAN or DataStream.LITTLE_ENDIAN
depending on the platform endianness.
@type {boolean}
*/
DataStream.endianness = new Int8Array(new Int16Array([1]).buffer)[0] > 0;
/**
Copies byteLength bytes from the src buffer at srcOffset to the
dst buffer at dstOffset.
@param {Object} dst Destination ArrayBuffer to write to.
@param {number} dstOffset Offset to the destination ArrayBuffer.
@param {Object} src Source ArrayBuffer to read from.
@param {number} srcOffset Offset to the source ArrayBuffer.
@param {number} byteLength Number of bytes to copy.
*/
DataStream.memcpy = function(dst, dstOffset, src, srcOffset, byteLength) {
var dstU8 = new Uint8Array(dst, dstOffset, byteLength);
var srcU8 = new Uint8Array(src, srcOffset, byteLength);
dstU8.set(srcU8);
};
/**
Converts array to native endianness in-place.
@param {Object} array Typed array to convert.
@param {boolean} arrayIsLittleEndian True if the data in the array is
little-endian. Set false for big-endian.
@return {Object} The converted typed array.
*/
DataStream.arrayToNative = function(array, arrayIsLittleEndian) {
if (arrayIsLittleEndian == this.endianness) {
return array;
} else {
return this.flipArrayEndianness(array);
}
};
/**
Converts native endianness array to desired endianness in-place.
@param {Object} array Typed array to convert.
@param {boolean} littleEndian True if the converted array should be
little-endian. Set false for big-endian.
@return {Object} The converted typed array.
*/
DataStream.nativeToEndian = function(array, littleEndian) {
if (this.endianness == littleEndian) {
return array;
} else {
return this.flipArrayEndianness(array);
}
};
/**
Flips typed array endianness in-place.
@param {Object} array Typed array to flip.
@return {Object} The converted typed array.
*/
DataStream.flipArrayEndianness = function(array) {
var u8 = new Uint8Array(array.buffer, array.byteOffset, array.byteLength);
for (var i=0; i<array.byteLength; i+=array.BYTES_PER_ELEMENT) {
for (var j=i+array.BYTES_PER_ELEMENT-1, k=i; j>k; j--, k++) {
var tmp = u8[k];
u8[k] = u8[j];
u8[j] = tmp;
}
}
return array;
};
/**
Seek position where DataStream#readStruct ran into a problem.
Useful for debugging struct parsing.
@type {number}
*/
DataStream.prototype.failurePosition = 0;
/**
Reads a struct of data from the DataStream. The struct is defined as
a flat array of [name, type]-pairs. See the example below:
ds.readStruct([
'headerTag', 'uint32', // Uint32 in DataStream endianness.
'headerTag2', 'uint32be', // Big-endian Uint32.
'headerTag3', 'uint32le', // Little-endian Uint32.
'array', ['[]', 'uint32', 16], // Uint32Array of length 16.
'array2Length', 'uint32',
'array2', ['[]', 'uint32', 'array2Length'] // Uint32Array of length array2Length
]);
The possible values for the type are as follows:
// Number types
// Unsuffixed number types use DataStream endianness.
// To explicitly specify endianness, suffix the type with
// 'le' for little-endian or 'be' for big-endian,
// e.g. 'int32be' for big-endian int32.
'uint8' -- 8-bit unsigned int
'uint16' -- 16-bit unsigned int
'uint32' -- 32-bit unsigned int
'int8' -- 8-bit int
'int16' -- 16-bit int
'int32' -- 32-bit int
'float32' -- 32-bit float
'float64' -- 64-bit float
// String types
'cstring' -- ASCII string terminated by a zero byte.
'string:N' -- ASCII string of length N.
'string,CHARSET:N' -- String of byteLength N encoded with given CHARSET.
'u16string:N' -- UCS-2 string of length N in DataStream endianness.
'u16stringle:N' -- UCS-2 string of length N in little-endian.
'u16stringbe:N' -- UCS-2 string of length N in big-endian.
// Complex types
[name, type, name_2, type_2, ..., name_N, type_N] -- Struct
function(dataStream, struct) {} -- Callback function to read and return data.
{get: function(dataStream, struct) {},
set: function(dataStream, struct) {}}
-- Getter/setter functions to read and return data, handy for using the same
struct definition for reading and writing structs.
['[]', type, length] -- Array of given type and length. The length can be either
a number, a string that references a previously-read
field, or a callback function(struct, dataStream, type){}.
If length is '*', reads in as many elements as it can.
@param {Object} structDefinition Struct definition object.
@return {Object} The read struct. Null if failed to read struct.
*/
DataStream.prototype.readStruct = function(structDefinition) {
var struct = {}, t, v, n;
var p = this.position;
for (var i=0; i<structDefinition.length; i+=2) {
t = structDefinition[i+1];
v = this.readType(t, struct);
if (v == null) {
if (this.failurePosition === 0) {
this.failurePosition = this.position;
}
this.position = p;
return null;
}
struct[structDefinition[i]] = v;
}
return struct;
};
/**
Read UCS-2 string of desired length and endianness from the DataStream.
@param {number} length The length of the string to read.
@param {boolean} endianness The endianness of the string data in the DataStream.
@return {string} The read string.
*/
DataStream.prototype.readUCS2String = function(length, endianness) {
return String.fromCharCode.apply(null, this.readUint16Array(length, endianness));
};
/**
Write a UCS-2 string of desired endianness to the DataStream. The
lengthOverride argument lets you define the number of characters to write.
If the string is shorter than lengthOverride, the extra space is padded with
zeroes.
@param {string} str The string to write.
@param {?boolean} endianness The endianness to use for the written string data.
@param {?number} lengthOverride The number of characters to write.
*/
DataStream.prototype.writeUCS2String = function(str, endianness, lengthOverride) {
if (lengthOverride == null) {
lengthOverride = str.length;
}
for (var i = 0; i < str.length && i < lengthOverride; i++) {
this.writeUint16(str.charCodeAt(i), endianness);
}
for (; i<lengthOverride; i++) {
this.writeUint16(0);
}
};
/**
Read a string of desired length and encoding from the DataStream.
@param {number} length The length of the string to read in bytes.
@param {?string} encoding The encoding of the string data in the DataStream.
Defaults to ASCII.
@return {string} The read string.
*/
DataStream.prototype.readString = function(length, encoding) {
if (encoding == null || encoding == "ASCII") {
return String.fromCharCode.apply(null, this.mapUint8Array(length == null ? this.byteLength-this.position : length));
} else {
return (new TextDecoder(encoding)).decode(this.mapUint8Array(length));
}
};
/**
Writes a string of desired length and encoding to the DataStream.
@param {string} s The string to write.
@param {?string} encoding The encoding for the written string data.
Defaults to ASCII.
@param {?number} length The number of characters to write.
*/
DataStream.prototype.writeString = function(s, encoding, length) {
var i = 0;
if (encoding == null || encoding == "ASCII") {
if (length != null) {
var len = Math.min(s.length, length);
for (i=0; i<len; i++) {
this.writeUint8(s.charCodeAt(i));
}
for (; i<length; i++) {
this.writeUint8(0);
}
} else {
for (i=0; i<s.length; i++) {
this.writeUint8(s.charCodeAt(i));
}
}
} else {
this.writeUint8Array((new TextEncoder(encoding)).encode(s.substring(0, length)));
}
};
/**
Read null-terminated string of desired length from the DataStream. Truncates
the returned string so that the null byte is not a part of it.
@param {?number} length The length of the string to read.
@return {string} The read string.
*/
DataStream.prototype.readCString = function(length) {
var blen = this.byteLength-this.position;
var u8 = new Uint8Array(this._buffer, this._byteOffset + this.position);
var len = blen;
if (length != null) {
len = Math.min(length, blen);
}
for (var i = 0; i < len && u8[i] !== 0; i++); // find first zero byte
var s = String.fromCharCode.apply(null, this.mapUint8Array(i));
if (length != null) {
this.position += len-i;
} else if (i != blen) {
this.position += 1; // trailing zero if not at end of buffer
}
return s;
};
/**
Writes a null-terminated string to DataStream and zero-pads it to length
bytes. If length is not given, writes the string followed by a zero.
If string is longer than length, the written part of the string does not have
a trailing zero.
@param {string} s The string to write.
@param {?number} length The number of characters to write.
*/
DataStream.prototype.writeCString = function(s, length) {
var i = 0;
if (length != null) {
var len = Math.min(s.length, length);
for (i=0; i<len; i++) {
this.writeUint8(s.charCodeAt(i));
}
for (; i<length; i++) {
this.writeUint8(0);
}
} else {
for (i=0; i<s.length; i++) {
this.writeUint8(s.charCodeAt(i));
}
this.writeUint8(0);
}
};
/**
Reads an object of type t from the DataStream, passing struct as the thus-far
read struct to possible callbacks that refer to it. Used by readStruct for
reading in the values, so the type is one of the readStruct types.
@param {Object} t Type of the object to read.
@param {?Object} struct Struct to refer to when resolving length references
and for calling callbacks.
@return {?Object} Returns the object on successful read, null on unsuccessful.
*/
DataStream.prototype.readType = function(t, struct) {
if (typeof t == "function") {
return t(this, struct);
} else if (typeof t == "object" && !(t instanceof Array)) {
return t.get(this, struct);
} else if (t instanceof Array && t.length != 3) {
return this.readStruct(t, struct);
}
var v = null;
var lengthOverride = null;
var charset = "ASCII";
var pos = this.position;
var tp;
var i;
var u;
if (typeof t == 'string' && /:/.test(t)) {
tp = t.split(":");
t = tp[0];
lengthOverride = parseInt(tp[1]);
}
if (typeof t == 'string' && /,/.test(t)) {
tp = t.split(",");
t = tp[0];
charset = parseInt(tp[1]);
}
switch(t) {
case 'uint8':
v = this.readUint8(); break;
case 'int8':
v = this.readInt8(); break;
case 'uint16':
v = this.readUint16(this.endianness); break;
case 'int16':
v = this.readInt16(this.endianness); break;
case 'uint32':
v = this.readUint32(this.endianness); break;
case 'int32':
v = this.readInt32(this.endianness); break;
case 'float32':
v = this.readFloat32(this.endianness); break;
case 'float64':
v = this.readFloat64(this.endianness); break;
case 'uint16be':
v = this.readUint16(DataStream.BIG_ENDIAN); break;
case 'int16be':
v = this.readInt16(DataStream.BIG_ENDIAN); break;
case 'uint32be':
v = this.readUint32(DataStream.BIG_ENDIAN); break;
case 'int32be':
v = this.readInt32(DataStream.BIG_ENDIAN); break;
case 'float32be':
v = this.readFloat32(DataStream.BIG_ENDIAN); break;
case 'float64be':
v = this.readFloat64(DataStream.BIG_ENDIAN); break;
case 'uint16le':
v = this.readUint16(DataStream.LITTLE_ENDIAN); break;
case 'int16le':
v = this.readInt16(DataStream.LITTLE_ENDIAN); break;
case 'uint32le':
v = this.readUint32(DataStream.LITTLE_ENDIAN); break;
case 'int32le':
v = this.readInt32(DataStream.LITTLE_ENDIAN); break;
case 'float32le':
v = this.readFloat32(DataStream.LITTLE_ENDIAN); break;
case 'float64le':
v = this.readFloat64(DataStream.LITTLE_ENDIAN); break;
case 'cstring':
v = this.readCString(lengthOverride); break;
case 'string':
v = this.readString(lengthOverride, charset); break;
case 'u16string':
v = this.readUCS2String(lengthOverride, this.endianness); break;
case 'u16stringle':
v = this.readUCS2String(lengthOverride, DataStream.LITTLE_ENDIAN); break;
case 'u16stringbe':
v = this.readUCS2String(lengthOverride, DataStream.BIG_ENDIAN); break;
default:
if (t.length == 3) {
var ta = t[1];
var len = t[2];
var length = 0;
if (typeof len == 'function') {
length = len(struct, this, t);
} else if (typeof len == 'string' && struct[len] != null) {
length = parseInt(struct[len]);
} else {
length = parseInt(len);
}
if (typeof ta == "string") {
var tap = ta.replace(/(le|be)$/, '');
var endianness = null;
if (/le$/.test(ta)) {
endianness = DataStream.LITTLE_ENDIAN;
} else if (/be$/.test(ta)) {
endianness = DataStream.BIG_ENDIAN;
}
if (len == '*') {
length = null;
}
switch(tap) {
case 'uint8':
v = this.readUint8Array(length); break;
case 'uint16':
v = this.readUint16Array(length, endianness); break;
case 'uint32':
v = this.readUint32Array(length, endianness); break;
case 'int8':
v = this.readInt8Array(length); break;
case 'int16':
v = this.readInt16Array(length, endianness); break;
case 'int32':
v = this.readInt32Array(length, endianness); break;
case 'float32':
v = this.readFloat32Array(length, endianness); break;
case 'float64':
v = this.readFloat64Array(length, endianness); break;
case 'cstring':
case 'utf16string':
case 'string':
if (length == null) {
v = [];
while (!this.isEof()) {
u = this.readType(ta, struct);
if (u == null) break;
v.push(u);
}
} else {
v = new Array(length);
for (i=0; i<length; i++) {
v[i] = this.readType(ta, struct);
}
}
break;
}
} else {
if (len == '*') {
v = [];
var tmp_buffer = this.buffer;
while (true) {
var p = this.position;
try {
var o = this.readType(ta, struct);
if (o == null) {
this.position = p;
break;
}
v.push(o);
} catch(e) {
this.position = p;
break;
}
}
} else {
v = new Array(length);
for (i=0; i<length; i++) {
u = this.readType(ta, struct);
if (u == null) return null;
v[i] = u;
}
}
}
break;
}
}
if (lengthOverride != null) {
this.position = pos + lengthOverride;
}
return v;
};
/**
Writes a struct to the DataStream. Takes a structDefinition that gives the
types and a struct object that gives the values. Refer to readStruct for the
structure of structDefinition.
@param {Object} structDefinition Type definition of the struct.
@param {Object} struct The struct data object.
*/
DataStream.prototype.writeStruct = function(structDefinition, struct) {
for (var i = 0; i < structDefinition.length; i+=2) {
var t = structDefinition[i+1];
this.writeType(t, struct[structDefinition[i]], struct);
}
};
/**
Writes object v of type t to the DataStream.
@param {Object} t Type of data to write.
@param {Object} v Value of data to write.
@param {Object} struct Struct to pass to write callback functions.
*/
DataStream.prototype.writeType = function(t, v, struct) {
var tp;
if (typeof t == "function") {
return t(this, v);
} else if (typeof t == "object" && !(t instanceof Array)) {
return t.set(this, v, struct);
}
var lengthOverride = null;
var charset = "ASCII";
var pos = this.position;
if (typeof(t) == 'string' && /:/.test(t)) {
tp = t.split(":");
t = tp[0];
lengthOverride = parseInt(tp[1]);
}
if (typeof t == 'string' && /,/.test(t)) {
tp = t.split(",");
t = tp[0];
charset = parseInt(tp[1]);
}
switch(t) {
case 'uint8':
this.writeUint8(v);
break;
case 'int8':
this.writeInt8(v);
break;
case 'uint16':
this.writeUint16(v, this.endianness);
break;
case 'int16':
this.writeInt16(v, this.endianness);
break;
case 'uint32':
this.writeUint32(v, this.endianness);
break;
case 'int32':
this.writeInt32(v, this.endianness);
break;
case 'float32':
this.writeFloat32(v, this.endianness);
break;
case 'float64':
this.writeFloat64(v, this.endianness);
break;
case 'uint16be':
this.writeUint16(v, DataStream.BIG_ENDIAN);
break;
case 'int16be':
this.writeInt16(v, DataStream.BIG_ENDIAN);
break;
case 'uint32be':
this.writeUint32(v, DataStream.BIG_ENDIAN);
break;
case 'int32be':
this.writeInt32(v, DataStream.BIG_ENDIAN);
break;
case 'float32be':
this.writeFloat32(v, DataStream.BIG_ENDIAN);
break;
case 'float64be':
this.writeFloat64(v, DataStream.BIG_ENDIAN);
break;
case 'uint16le':
this.writeUint16(v, DataStream.LITTLE_ENDIAN);
break;
case 'int16le':
this.writeInt16(v, DataStream.LITTLE_ENDIAN);
break;
case 'uint32le':
this.writeUint32(v, DataStream.LITTLE_ENDIAN);
break;
case 'int32le':
this.writeInt32(v, DataStream.LITTLE_ENDIAN);
break;
case 'float32le':
this.writeFloat32(v, DataStream.LITTLE_ENDIAN);
break;
case 'float64le':
this.writeFloat64(v, DataStream.LITTLE_ENDIAN);
break;
case 'cstring':
this.writeCString(v, lengthOverride);
break;
case 'string':
this.writeString(v, charset, lengthOverride);
break;
case 'u16string':
this.writeUCS2String(v, this.endianness, lengthOverride);
break;
case 'u16stringle':
this.writeUCS2String(v, DataStream.LITTLE_ENDIAN, lengthOverride);
break;
case 'u16stringbe':
this.writeUCS2String(v, DataStream.BIG_ENDIAN, lengthOverride);
break;
default:
if (t.length == 3) {
var ta = t[1];
for (var i=0; i<v.length; i++) {
this.writeType(ta, v[i]);
}
break;
} else {
this.writeStruct(t, v);
break;
}
}
if (lengthOverride != null) {
this.position = pos;
this._realloc(lengthOverride);
this.position = pos + lengthOverride;
}
};
/*
TODO: fix endianness for 24/64-bit fields
TODO: check range/support for 64-bits numbers in JavaScript
*/
var MAX_SIZE = Math.pow(2, 32);
DataStream.prototype.readUint64 = function () {
return (this.readUint32()*MAX_SIZE)+this.readUint32();
}
DataStream.prototype.writeUint64 = function (v) {
var h = Math.floor(v / MAX_SIZE);
this.writeUint32(h);
this.writeUint32(v & 0xFFFFFFFF);
}
DataStream.prototype.readUint24 = function () {
return (this.readUint8()<<16)+(this.readUint8()<<8)+this.readUint8();
}
DataStream.prototype.writeUint24 = function (v) {
this.writeUint8((v & 0x00FF0000)>>16);
this.writeUint8((v & 0x0000FF00)>>8);
this.writeUint8((v & 0x000000FF));
}
DataStream.prototype.adjustUint32 = function(position, value) {
var pos = this.position;
this.seek(position);
this.writeUint32(value);
this.seek(pos);
}

/*
* Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato
* License: BSD-3-Clause (see LICENSE file)
*/
var BoxParser = {
ERR_NOT_ENOUGH_DATA : 0,
OK : 1,
boxCodes : [
"mdat",
"avcC", "ftyp",
"payl",
"vmhd", "smhd", "hmhd", "dref", "elst" // full boxes not yet parsed
],
fullBoxCodes : [ "mvhd", "tkhd", "mdhd", "hdlr", "smhd", "hmhd", "nhmd", "url ", "urn ",
"ctts", "cslg", "stco", "co64", "stsc", "stss", "stsz", "stz2", "stts", "stsh",
"mehd", "trex", "mfhd", "tfhd", "trun", "tfdt",
"esds", "subs"
/* missing "stsd": special case full box and container */
],
containerBoxCodes : [
[ "moov", [ "trak" ] ],
[ "trak" ],
[ "edts" ],
[ "mdia" ],
[ "minf" ],
[ "dinf" ],
[ "stbl" ],
[ "mvex", [ "trex" ] ],
[ "moof", [ "traf" ] ],
[ "traf", [ "trun" ] ],
[ "vttc" ],
[ "tref" ]
],
sampleEntryCodes : [
/* 4CC as registered on http://mp4ra.org/codecs.html */
{ prefix: "Visual", types: [ "mp4v", "avc1", "avc2", "avc3", "avc4", "avcp", "drac", "encv", "mjp2", "mvc1", "mvc2", "resv", "s263", "svc1", "vc-1" ] },
{ prefix: "Audio", types: [ "mp4a", "ac-3", "alac", "dra1", "dtsc", "dtse", ,"dtsh", "dtsl", "ec-3", "enca", "g719", "g726", "m4ae", "mlpa", "raw ", "samr", "sawb", "sawp", "sevc", "sqcp", "ssmv", "twos" ] },
{ prefix: "Hint", types: [ "fdp ", "m2ts", "pm2t", "prtp", "rm2t", "rrtp", "rsrp", "rtp ", "sm2t", "srtp" ] },
{ prefix: "Metadata", types: [ "metx", "mett", "urim" ] },
{ prefix: "Subtitle", types: [ "stpp", "wvtt" ] }
],
trackReferenceTypes: [
"scal"
],
initialize: function() {
var i, j;
var length;
BoxParser.FullBox.prototype = new BoxParser.Box();
BoxParser.ContainerBox.prototype = new BoxParser.Box();
BoxParser.stsdBox.prototype = new BoxParser.FullBox();
BoxParser.SampleEntry.prototype = new BoxParser.FullBox();
BoxParser.TrackReferenceTypeBox.prototype = new BoxParser.Box();
/* creating constructors for simple boxes */
length = BoxParser.boxCodes.length;
for (i=0; i<length; i++) {
BoxParser[BoxParser.boxCodes[i]+"Box"] = (function (j) { /* creating a closure around the iterating value of i */
return function(size) {
BoxParser.Box.call(this, BoxParser.boxCodes[j], size);
}
})(i);
BoxParser[BoxParser.boxCodes[i]+"Box"].prototype = new BoxParser.Box();
}
/* creating constructors for full boxes */
length = BoxParser.fullBoxCodes.length;
for (i=0; i<length; i++) {
BoxParser[BoxParser.fullBoxCodes[i]+"Box"] = (function (j) {
return function(size) {
BoxParser.FullBox.call(this, BoxParser.fullBoxCodes[j], size);
}
})(i);
BoxParser[BoxParser.fullBoxCodes[i]+"Box"].prototype = new BoxParser.FullBox();
}
/* creating constructors for container boxes */
length = BoxParser.containerBoxCodes.length;
for (i=0; i<length; i++) {
BoxParser[BoxParser.containerBoxCodes[i][0]+"Box"] = (function (j, subBoxNames) {
return function(size) {
BoxParser.ContainerBox.call(this, BoxParser.containerBoxCodes[j][0], size);
if (subBoxNames) {
this.subBoxNames = subBoxNames;
var nbSubBoxes = subBoxNames.length;
for (var k = 0; k<nbSubBoxes; k++) {
this[subBoxNames[k]+"s"] = [];
}
}
}
})(i, BoxParser.containerBoxCodes[i][1]);
BoxParser[BoxParser.containerBoxCodes[i][0]+"Box"].prototype = new BoxParser.ContainerBox();
}
/* creating constructors for stsd entries */
length = BoxParser.sampleEntryCodes.length;
for (j = 0; j < length; j++) {
var prefix = BoxParser.sampleEntryCodes[j].prefix;
var types = BoxParser.sampleEntryCodes[j].types;
var nb_types = types.length;
BoxParser[prefix+"SampleEntry"] = function(type, size) { BoxParser.SampleEntry.call(this, type, size); };
BoxParser[prefix+"SampleEntry"].prototype = new BoxParser.SampleEntry();
for (i=0; i<nb_types; i++) {
BoxParser[types[i]+"Box"] = (function (k, l) {
return function(size) {
BoxParser[BoxParser.sampleEntryCodes[k].prefix+"SampleEntry"].call(this, BoxParser.sampleEntryCodes[k].types[l], size);
}
})(j, i);
BoxParser[types[i]+"Box"].prototype = new BoxParser[prefix+"SampleEntry"]();
}
}
/* creating constructors for track reference type boxes */
length = BoxParser.trackReferenceTypes.length;
for (i=0; i<length; i++) {
BoxParser[BoxParser.trackReferenceTypes[i]+"Box"] = (function (j) {
return function(size) {
BoxParser.TrackReferenceTypeBox.call(this, BoxParser.trackReferenceTypes[j], size);
}
})(i);
BoxParser[BoxParser.trackReferenceTypes[i]+"Box"].prototype = new BoxParser.Box();
}
},
Box: function(_type, _size) {
this.type = _type;
this.size = _size;
},
FullBox: function(type, size) {
BoxParser.Box.call(this, type, size);
this.flags = 0;
this.version = 0;
},
ContainerBox: function(type, size) {
BoxParser.Box.call(this, type, size);
this.boxes = [];
},
SampleEntry: function(type, size) {
BoxParser.Box.call(this, type, size);
this.boxes = [];
},
TrackReferenceTypeBox: function(type, size) {
BoxParser.Box.call(this, type, size);
this.track_ids = [];
},
stsdBox: function(size) {
BoxParser.FullBox.call(this, "stsd", size);
this.entries = [];
},
parseOneBox: function(stream, isSampleEntry) {
var box;
var start = stream.position;
var hdr_size = 0;
if (stream.byteLength - stream.position < 8) {
Log.d("BoxParser", "Not enough data in stream to parse the type and size of the box");
return { code: BoxParser.ERR_NOT_ENOUGH_DATA };
}
var size = stream.readUint32();
var type = stream.readString(4);
Log.d("BoxParser", "Found box of type "+type+" and size "+size+" at position "+start+" in the current buffer ("+(stream.buffer.fileStart+start)+" in the file)");
hdr_size = 8;
if (type == "uuid") {
uuid = stream.readString(16);
hdr_size += 16;
}
if (size == 1) {
if (stream.byteLength - stream.position < 8) {
stream.seek(start);
Log.w("BoxParser", "Not enough data in stream to parse the extended size of the \""+type+"\" box");
return { code: BoxParser.ERR_NOT_ENOUGH_DATA };
}
size = stream.readUint64();
hdr_size += 8;
} else if (size === 0) {
/* box extends till the end of file */
throw "Unlimited box size not supported";
}
if (start + size > stream.byteLength ) {
stream.seek(start);
Log.w("BoxParser", "Not enough data in stream to parse the entire \""+type+"\" box");
return { code: BoxParser.ERR_NOT_ENOUGH_DATA, type: type, size: size, hdr_size: hdr_size };
}
if (BoxParser[type+"Box"]) {
box = new BoxParser[type+"Box"](size - hdr_size);
} else {
if (isSampleEntry) {
box = new BoxParser.SampleEntry(type, size - hdr_size);
} else {
box = new BoxParser.Box(type, size - hdr_size);
}
}
/* recording the position of the box in the input stream */
box.hdr_size = hdr_size;
box.start = start;
box.fileStart = start + stream.buffer.fileStart;
box.parse(stream);
return { code: BoxParser.OK, box: box, size: size };
},
}
BoxParser.initialize();
BoxParser.Box.prototype.parse = function(stream) {
if (this.type != "mdat") {
this.data = stream.readUint8Array(this.size);
} else {
stream.seek(this.start+this.size+this.hdr_size);
}
}
BoxParser.FullBox.prototype.parseFullHeader = function (stream) {
this.version = stream.readUint8();
this.flags = stream.readUint24();
this.size -= 4;
}
BoxParser.ContainerBox.prototype.parse = function(stream) {
var ret;
var box;
var start;
start = stream.position;
while (stream.position < start+this.size) {
ret = BoxParser.parseOneBox(stream);
box = ret.box;
/* store the box in the 'boxes' array to preserve box order (for offset) but also store box in a property for more direct access */
this.boxes.push(box);
if (this.subBoxNames && this.subBoxNames.indexOf(box.type) != -1) {
this[this.subBoxNames+"s"].push(box);
} else {
this[box.type] = box;
}
}
}
BoxParser.SampleEntry.prototype.isVideo = function() {
return false;
}
BoxParser.SampleEntry.prototype.isAudio = function() {
return false;
}
BoxParser.SampleEntry.prototype.isSubtitle = function() {
return false;
}
BoxParser.SampleEntry.prototype.isMetadata = function() {
return false;
}
BoxParser.SampleEntry.prototype.isHint = function() {
return false;
}
BoxParser.SampleEntry.prototype.getCodec = function() {
return this.type;
}
BoxParser.SampleEntry.prototype.getWidth = function() {
return "";
}
BoxParser.SampleEntry.prototype.getHeight = function() {
return "";
}
BoxParser.SampleEntry.prototype.getChannelCount = function() {
return "";
}
BoxParser.SampleEntry.prototype.getSampleRate = function() {
return "";
}
BoxParser.SampleEntry.prototype.getSampleSize = function() {
return "";
}
BoxParser.SampleEntry.prototype.parseHeader = function(stream) {
this.start = stream.position;
stream.readUint8Array(6);
this.data_reference_index = stream.readUint16();
}
BoxParser.SampleEntry.prototype.parseFooter = function(stream) {
var ret;
var box;
while (stream.position < this.start+this.size) {
ret = BoxParser.parseOneBox(stream, true);
box = ret.box;
this.boxes.push(box);
this[box.type] = box;
}
}
BoxParser.SampleEntry.prototype.parse = function(stream) {
this.parseHeader(stream);
stream.seek(this.start+this.size);
}
BoxParser.VisualSampleEntry.prototype.parse = function(stream) {
this.parseHeader(stream);
stream.readUint16();
stream.readUint16();
stream.readUint32Array(3);
this.width = stream.readUint16();
this.height = stream.readUint16();
this.horizresolution = stream.readUint32();
this.vertresolution = stream.readUint32();
stream.readUint32();
this.frame_count = stream.readUint16();
this.compressorname = stream.readString(32);
this.depth = stream.readUint16();
stream.readUint16();
this.parseFooter(stream);
}
BoxParser.VisualSampleEntry.prototype.isVideo = function() {
return true;
}
BoxParser.VisualSampleEntry.prototype.getWidth = function() {
return this.width;
}
BoxParser.VisualSampleEntry.prototype.getHeight = function() {
return this.height;
}
BoxParser.AudioSampleEntry.prototype.parse = function(stream) {
this.parseHeader(stream);
stream.readUint32Array(2);
this.channel_count = stream.readUint16();
this.samplesize = stream.readUint16();
stream.readUint16();
stream.readUint16();
this.samplerate = (stream.readUint32()/(1<<16));
this.parseFooter(stream);
}
BoxParser.AudioSampleEntry.prototype.isAudio = function() {
return true;
}
BoxParser.AudioSampleEntry.prototype.getChannelCount = function() {
return this.channel_count;
}
BoxParser.AudioSampleEntry.prototype.getSampleRate = function() {
return this.samplerate;
}
BoxParser.AudioSampleEntry.prototype.getSampleSize = function() {
return this.samplesize;
}
BoxParser.SubtitleSampleEntry.prototype.isSubtitle = function() {
return true;
}
BoxParser.TrackReferenceTypeBox.prototype.parse = function(stream) {
this.track_ids = stream.readUint8Array(this.size);
}
BoxParser.ftypBox.prototype.parse = function(stream) {
this.major_brand = stream.readString(4);
this.minor_version = stream.readUint32();
this.size -= 8;
this.compatible_brands = [];
var i = 0;
while (this.size>=4) {
this.compatible_brands[i] = stream.readString(4);
this.size -= 4;
i++;
}
}
BoxParser.mvhdBox.prototype.parse = function(stream) {
this.flags = 0;
this.parseFullHeader(stream);
if (this.version == 1) {
this.creation_time = stream.readUint64();
this.modification_time = stream.readUint64();
this.timescale = stream.readUint32();
this.duration = stream.readUint64();
} else {
this.creation_time = stream.readUint32();
this.modification_time = stream.readUint32();
this.timescale = stream.readUint32();
this.duration = stream.readUint32();
}
this.rate = stream.readUint32();
this.volume = stream.readUint16()>>8;
stream.readUint16();
stream.readUint32Array(2);
this.matrix = stream.readUint32Array(9);
stream.readUint32Array(6);
this.next_track_id = stream.readUint32();
}
BoxParser.TKHD_FLAG_ENABLED = 0x000001;
BoxParser.TKHD_FLAG_IN_MOVIE = 0x000002;
BoxParser.TKHD_FLAG_IN_PREVIEW = 0x000004;
BoxParser.tkhdBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
if (this.version == 1) {
this.creation_time = stream.readUint64();
this.modification_time = stream.readUint64();
this.track_id = stream.readUint32();
stream.readUint32();
this.duration = stream.readUint64();
} else {
this.creation_time = stream.readUint32();
this.modification_time = stream.readUint32();
this.track_id = stream.readUint32();
stream.readUint32();
this.duration = stream.readUint32();
}
stream.readUint32Array(2);
this.layer = stream.readInt16();
this.alternate_group = stream.readInt16();
this.volume = stream.readInt16()>>8;
stream.readUint16();
this.matrix = stream.readInt32Array(9);
this.width = stream.readUint32();
this.height = stream.readUint32();
}
BoxParser.mdhdBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
if (this.version == 1) {
this.creation_time = stream.readUint64();
this.modification_time = stream.readUint64();
this.timescale = stream.readUint32();
this.duration = stream.readUint64();
} else {
this.creation_time = stream.readUint32();
this.modification_time = stream.readUint32();
this.timescale = stream.readUint32();
this.duration = stream.readUint32();
}
this.language = stream.readUint16();
var chars = [];
chars[0] = (this.language>>10)&0x1F;
chars[1] = (this.language>>5)&0x1F;
chars[2] = (this.language)&0x1F;
this.languageString = String.fromCharCode(chars[0]+0x60, chars[1]+0x60, chars[2]+0x60);
stream.readUint16();
}
BoxParser.hdlrBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
if (this.version === 0) {
stream.readUint32();
this.handler = stream.readString(4);
stream.readUint32Array(3);
this.name = stream.readCString();
} else {
this.data = stream.readUint8Array(size);
}
}
BoxParser.stsdBox.prototype.parse = function(stream) {
var ret;
var entryCount;
this.parseFullHeader(stream);
entryCount = stream.readUint32();
for (i = 1; i <= entryCount; i++) {
ret = BoxParser.parseOneBox(stream, true);
this.entries.push(ret.box);
}
}
BoxParser.avcCBox.prototype.parse = function(stream) {
var i;
var nb_nalus;
var length;
this.configurationVersion = stream.readUint8();
this.AVCProfileIndication = stream.readUint8();
this.profile_compatibility = stream.readUint8();
this.AVCLevelIndication = stream.readUint8();
this.lengthSizeMinusOne = (stream.readUint8() & 0x3);
nb_nalus = (stream.readUint8() & 0x1F);
this.size -= 6;
this.SPS = new Array(nb_nalus);
for (i = 0; i < nb_nalus; i++) {
length = stream.readUint16();
this.SPS[i] = stream.readUint8Array(length);
this.size -= 2+length;
}
nb_nalus = stream.readUint8();
this.size--;
this.PPS = new Array(nb_nalus);
for (i = 0; i < nb_nalus; i++) {
length = stream.readUint16();
this.PPS[i] = stream.readUint8Array(length);
this.size -= 2+length;
}
if (this.size>0) {
this.ext = stream.readUint8Array(this.size);
}
}
function decimalToHex(d, padding) {
var hex = Number(d).toString(16);
padding = typeof (padding) === "undefined" || padding === null ? padding = 2 : padding;
while (hex.length < padding) {
hex = "0" + hex;
}
return hex;
}
BoxParser.avc1Box.prototype.getCodec = function() {
var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this);
if (this.avcC) {
return baseCodec+"."+decimalToHex(this.avcC.AVCProfileIndication)+
""+decimalToHex(this.avcC.profile_compatibility)+
""+decimalToHex(this.avcC.AVCLevelIndication);
} else {
return baseCodec;
}
}
BoxParser.mp4aBox.prototype.getCodec = function() {
var baseCodec = BoxParser.SampleEntry.prototype.getCodec.call(this);
if (this.esds && this.esds.esd) {
var oti = this.esds.esd.getOTI();
var dsi = this.esds.esd.getAudioConfig();
return baseCodec+"."+decimalToHex(oti)+(dsi ? "."+dsi: "");
} else {
return baseCodec;
}
}
BoxParser.esdsBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
this.data = stream.readUint8Array(this.size);
this.size = 0;
var esd_parser = new MPEG4DescriptorParser();
this.esd = esd_parser.parseOneDescriptor(new DataStream(this.data.buffer, 0, DataStream.BIG_ENDIAN));
}
BoxParser.cttsBox.prototype.parse = function(stream) {
var entry_count;
var i;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.sample_counts = [];
this.sample_offsets = [];
if (this.version === 0) {
for(i=0; i<entry_count; i++) {
this.sample_counts.push(stream.readUint32());
this.sample_offsets.push(stream.readUint32());
}
} else if (this.version == 1) {
for(i=0; i<entry_count; i++) {
this.sample_counts.push(stream.readUint32());
this.sample_offsets.push(stream.readInt32()); /* signed */
}
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.cttsBox.prototype.unpack = function(samples) {
var i, j, k;
k = 0;
for (i = 0; i < this.sample_counts.length; i++) {
for (j = 0; j < this.sample_counts[i]; j++) {
samples[k].pts = samples[k].dts + this.sample_offsets[i];
k++;
}
}
}
BoxParser.cslgBox.prototype.parse = function(stream) {
var entry_count;
this.parseFullHeader(stream);
if (this.version === 0) {
this.compositionToDTSShift = stream.readInt32(); /* signed */
this.leastDecodeToDisplayDelta = stream.readInt32(); /* signed */
this.greatestDecodeToDisplayDelta = stream.readInt32(); /* signed */
this.compositionStartTime = stream.readInt32(); /* signed */
this.compositionEndTime = stream.readInt32(); /* signed */
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.sttsBox.prototype.parse = function(stream) {
var entry_count;
var i;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.sample_counts = [];
this.sample_deltas = [];
if (this.version === 0) {
for(i=0; i<entry_count; i++) {
this.sample_counts.push(stream.readUint32());
this.sample_deltas.push(stream.readUint32());
}
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.sttsBox.prototype.unpack = function(samples) {
var i, j, k;
k = 0;
for (i = 0; i < this.sample_counts.length; i++) {
for (j = 0; j < this.sample_counts[i]; j++) {
if (k === 0) {
samples[k].dts = 0;
} else {
samples[k].dts = samples[k-1].dts + this.sample_deltas[i];
}
k++;
}
}
}
BoxParser.stssBox.prototype.parse = function(stream) {
var entry_count;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
if (this.version === 0) {
this.sample_numbers = stream.readUint32Array(entry_count);
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.stshBox.prototype.parse = function(stream) {
var entry_count;
var i;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.shadowed_sample_numbers = [];
this.sync_sample_numbers = [];
if (this.version === 0) {
for(i=0; i<entry_count; i++) {
this.shadowed_sample_numbers.push(stream.readUint32());
this.sync_sample_numbers.push(stream.readUint32());
}
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.stcoBox.prototype.parse = function(stream) {
var entry_count;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
if (this.version === 0) {
this.chunk_offsets = stream.readUint32Array(entry_count);
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.stcoBox.prototype.unpack = function(samples) {
var i;
for (i = 0; i < this.chunk_offsets.length; i++) {
samples[i].offset = this.chunk_offsets[i];
}
}
BoxParser.co64Box.prototype.parse = function(stream) {
var entry_count;
var i;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.chunk_offsets = [];
if (this.version === 0) {
for(i=0; i<entry_count; i++) {
this.chunk_offsets.push(stream.readUint64());
}
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.stscBox.prototype.parse = function(stream) {
var entry_count;
var i;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.first_chunk = [];
this.samples_per_chunk = [];
this.sample_description_index = [];
if (this.version === 0) {
for(i=0; i<entry_count; i++) {
this.first_chunk.push(stream.readUint32());
this.samples_per_chunk.push(stream.readUint32());
this.sample_description_index.push(stream.readUint32());
}
} else {
this.data = stream.readUint8Array(this.size-4);
}
}
BoxParser.stscBox.prototype.unpack = function(samples) {
var i, j, k, l, m;
l = 0;
m = 0;
for (i = 0; i < this.first_chunk.length; i++) {
for (j = 0; j < (i+1 < this.first_chunk.length ? this.first_chunk[i+1] : Infinity); j++) {
m++;
for (k = 0; k < this.samples_per_chunk[i]; k++) {
if (samples[l]) {
samples[l].description_index = this.sample_description_index[i];
samples[l].chunk_index = m;
} else {
return;
}
l++;
}
}
}
}
BoxParser.stszBox.prototype.parse = function(stream) {
var i;
var sample_size;
var sample_count;
this.parseFullHeader(stream);
this.sample_sizes = [];
if (this.version === 0) {
sample_size = stream.readUint32();
sample_count = stream.readUint32();
if (sample_size === 0) {
this.sample_sizes = stream.readUint32Array(sample_count);
} else {
this.sample_sizes = [];
for (i = 0; i < sample_count; i++) {
this.sample_sizes[i] = sample_size;
}
}
} else {
this.data = stream.readUint8Array(this.size);
}
}
BoxParser.stszBox.prototype.unpack = function(samples) {
var i;
for (i = 0; i < this.sample_sizes.length; i++) {
samples[i].size = this.sample_sizes[i];
}
}
BoxParser.mehdBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
if (this.version == 1) {
this.fragment_duration = stream.readUint64();
} else {
this.fragment_duration = stream.readUint32();
}
}
BoxParser.trexBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
this.track_id = stream.readUint32();
this.default_sample_description_index = stream.readUint32();
this.default_sample_duration = stream.readUint32();
this.default_sample_size = stream.readUint32();
this.default_sample_flags = stream.readUint32();
}
BoxParser.mfhdBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
this.sequence_number = stream.readUint32();
}
BoxParser.TFHD_FLAG_BASE_DATA_OFFSET	= 0x01;
BoxParser.TFHD_FLAG_SAMPLE_DESC	= 0x02;
BoxParser.TFHD_FLAG_SAMPLE_DUR	= 0x08;
BoxParser.TFHD_FLAG_SAMPLE_SIZE	= 0x10;
BoxParser.TFHD_FLAG_SAMPLE_FLAGS	= 0x20;
BoxParser.TFHD_FLAG_DUR_EMPTY	= 0x10000;
BoxParser.TFHD_FLAG_DEFAULT_BASE_IS_MOOF	= 0x20000;
BoxParser.tfhdBox.prototype.parse = function(stream) {
var readBytes = 0;
this.parseFullHeader(stream);
this.track_id = stream.readUint32();
if (this.size > readBytes && (this.flags & BoxParser.TFHD_FLAG_BASE_DATA_OFFSET)) {
this.base_data_offset = stream.readUint64();
readBytes += 8;
} else {
this.base_data_offset = 0;
}
if (this.size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC)) {
this.default_sample_description_index = stream.readUint32();
readBytes += 4;
} else {
this.default_sample_description_index = 0;
}
if (this.size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR)) {
this.default_sample_duration = stream.readUint32();
readBytes += 4;
} else {
this.default_sample_duration = 0;
}
if (this.size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE)) {
this.default_sample_size = stream.readUint32();
readBytes += 4;
} else {
this.default_sample_size = 0;
}
if (this.size > readBytes && (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS)) {
this.default_sample_flags = stream.readUint32();
readBytes += 4;
} else {
this.default_sample_flags = 0;
}
}
BoxParser.TRUN_FLAGS_DATA_OFFSET	= 0x01;
BoxParser.TRUN_FLAGS_FIRST_FLAG	= 0x04;
BoxParser.TRUN_FLAGS_DURATION	= 0x100;
BoxParser.TRUN_FLAGS_SIZE	= 0x200;
BoxParser.TRUN_FLAGS_FLAGS	= 0x400;
BoxParser.TRUN_FLAGS_CTS_OFFSET	= 0x800;
BoxParser.trunBox.prototype.parse = function(stream) {
var readBytes = 0;
this.parseFullHeader(stream);
this.sample_count = stream.readUint32();
readBytes+= 4;
if (this.size > readBytes && (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) ) {
this.data_offset = stream.readInt32(); //signed
readBytes += 4;
} else {
this.data_offset = 0;
}
if (this.size > readBytes && (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) ) {
this.first_sample_flags = stream.readUint32();
readBytes += 4;
} else {
this.first_sample_flags = 0;
}
this.sample_duration = [];
this.sample_size = [];
this.sample_flags = [];
this.sample_composition_time_offset = [];
if (this.size > readBytes) {
for (var i = 0; i < this.sample_count; i++) {
if (this.flags & BoxParser.TRUN_FLAGS_DURATION) {
this.sample_duration[i] = stream.readUint32();
}
if (this.flags & BoxParser.TRUN_FLAGS_SIZE) {
this.sample_size[i] = stream.readUint32();
}
if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) {
this.sample_flags[i] = stream.readUint32();
}
if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) {
if (this.version === 0) {
this.sample_composition_time_offset[i] = stream.readUint32();
} else {
this.sample_composition_time_offset[i] = stream.readInt32(); //signed
}
}
}
}
}
BoxParser.tfdtBox.prototype.parse = function(stream) {
this.parseFullHeader(stream);
if (this.version == 1) {
this.baseMediaDecodeTime = stream.readUint64();
} else {
this.baseMediaDecodeTime = stream.readUint32();
}
}
BoxParser.paylBox.prototype.parse = function(stream) {
this.text = stream.readString(this.size);
}
BoxParser.subsBox.prototype.parse = function(stream) {
var i,j;
var entry_count;
var subsample_count;
this.parseFullHeader(stream);
entry_count = stream.readUint32();
this.samples = [];
for (i = 0; i < entry_count; i++) {
var sampleInfo = {};
this.samples[i] = sampleInfo;
sampleInfo.sample_delta = stream.readUint32();
sampleInfo.subsamples = [];
subsample_count = stream.readUint16();
if (subsample_count>0) {
for (j = 0; j < subsample_count; j++) {
var subsample = {};
sampleInfo.subsamples.push(subsample);
if (this.version == 1) {
subsample.size = stream.readUint32();
} else {
subsample.size = stream.readUint16();
}
subsample.priority = stream.readUint8();
subsample.discardable = stream.readUint8();
subsample.reserved = stream.readUint32();
}
}
}
}
BoxParser.Box.prototype.writeHeader = function(stream, msg) {
this.size += 8;
if (this.size > MAX_SIZE) {
this.size += 8;
}
Log.d("BoxWriter", "Writing box "+this.type+" of size: "+this.size+" at position "+stream.position+(msg || ""));
if (this.size > MAX_SIZE) {
stream.writeUint32(1);
} else {
this.sizePosition = stream.position;
stream.writeUint32(this.size);
}
stream.writeString(this.type, null, 4);
if (this.size > MAX_SIZE) {
stream.writeUint64(this.size);
}
}
BoxParser.FullBox.prototype.writeHeader = function(stream) {
this.size += 4;
BoxParser.Box.prototype.writeHeader.call(this, stream, " v="+this.version+" f="+this.flags);
stream.writeUint8(this.version);
stream.writeUint24(this.flags);
}
BoxParser.Box.prototype.write = function(stream) {
if (this.type === "mdat") {
/* TODO: fix this */
if (this.data) {
this.size = this.data.length;
this.writeHeader(stream);
stream.writeUint8Array(this.data);
}
} else {
this.size = this.data.length;
this.writeHeader(stream);
stream.writeUint8Array(this.data);
}
}
BoxParser.ContainerBox.prototype.write = function(stream) {
this.size = 0;
this.writeHeader(stream);
for (var i=0; i<this.boxes.length; i++) {
if (this.boxes[i]) {
this.boxes[i].write(stream);
this.size += this.boxes[i].size;
}
}
/* adjusting the size, now that all sub-boxes are known */
Log.d("BoxWriter", "Adjusting box "+this.type+" with new size "+this.size);
stream.adjustUint32(this.sizePosition, this.size);
}
BoxParser.TrackReferenceTypeBox.prototype.write = function(stream) {
this.size = this.track_ids.length*4;
this.writeHeader(stream);
stream.writeUint32Array(this.track_ids);
}
BoxParser.ftypBox.prototype.write = function(stream) {
this.size = 8+4*this.compatible_brands.length;
this.writeHeader(stream);
stream.writeString(this.major_brand, null, 4);
stream.writeUint32(this.minor_version);
for (var i = 0; i < this.compatible_brands.length; i++) {
stream.writeString(this.compatible_brands[i], null, 4);
}
}
BoxParser.mvhdBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 23*4+2*2;
this.writeHeader(stream);
stream.writeUint32(this.creation_time);
stream.writeUint32(this.modification_time);
stream.writeUint32(this.timescale);
stream.writeUint32(this.duration);
stream.writeUint32(this.rate);
stream.writeUint16(this.volume<<8);
stream.writeUint16(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32Array(this.matrix);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(this.next_track_id);
}
BoxParser.tkhdBox.prototype.write = function(stream) {
this.version = 0;
//this.flags = 0;
this.size = 4*18+2*4;
this.writeHeader(stream);
stream.writeUint32(this.creation_time);
stream.writeUint32(this.modification_time);
stream.writeUint32(this.track_id);
stream.writeUint32(0);
stream.writeUint32(this.duration);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeInt16(this.layer);
stream.writeInt16(this.alternate_group);
stream.writeInt16(this.volume<<8);
stream.writeUint16(0);
stream.writeInt32Array(this.matrix);
stream.writeUint32(this.width);
stream.writeUint32(this.height);
}
BoxParser.mdhdBox.prototype.write = function(stream) {
this.size = 4*4+2*2;
this.flags = 0;
this.version = 0;
this.writeHeader(stream);
stream.writeUint32(this.creation_time);
stream.writeUint32(this.modification_time);
stream.writeUint32(this.timescale);
stream.writeUint32(this.duration);
stream.writeUint16(this.language);
stream.writeUint16(0);
}
BoxParser.hdlrBox.prototype.write = function(stream) {
this.size = 5*4+this.name.length+1;
this.version = 0;
this.flags = 0;
this.writeHeader(stream);
stream.writeUint32(0);
stream.writeString(this.handler, null, 4);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeCString(this.name);
}
BoxParser.stsdBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 0;
this.writeHeader(stream);
stream.writeUint32(this.entries.length);
this.size += 4;
for (i = 0; i < this.entries.length; i++) {
this.entries[i].write(stream);
this.size += this.entries[i].size;
}
/* adjusting the size, now that all sub-boxes are known */
Log.d("BoxWriter", "Adjusting box "+this.type+" with new size "+this.size);
stream.adjustUint32(this.sizePosition, this.size);
}
BoxParser.SampleEntry.prototype.writeHeader = function(stream) {
this.size = 8;
BoxParser.Box.prototype.writeHeader.call(this, stream);
stream.writeUint8(0);
stream.writeUint8(0);
stream.writeUint8(0);
stream.writeUint8(0);
stream.writeUint8(0);
stream.writeUint8(0);
stream.writeUint16(this.data_reference_index);
}
BoxParser.SampleEntry.prototype.writeFooter = function(stream) {
for (var i=0; i<this.boxes.length; i++) {
this.boxes[i].write(stream);
this.size += this.boxes[i].size;
}
Log.d("BoxWriter", "Adjusting box "+this.type+" with new size "+this.size);
stream.adjustUint32(this.sizePosition, this.size);
}
BoxParser.SampleEntry.prototype.write = function(stream) {
this.writeHeader(stream);
this.writeFooter(stream);
}
BoxParser.VisualSampleEntry.prototype.write = function(stream) {
this.writeHeader(stream);
this.size += 2*7+6*4+32;
stream.writeUint16(0);
stream.writeUint16(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint16(this.width);
stream.writeUint16(this.height);
stream.writeUint32(this.horizresolution);
stream.writeUint32(this.vertresolution);
stream.writeUint32(0);
stream.writeUint16(this.frame_count);
stream.writeString(this.compressorname, null, 32);
stream.writeUint16(this.depth);
stream.writeInt16(-1);
this.writeFooter(stream);
}
BoxParser.AudioSampleEntry.prototype.write = function(stream) {
this.writeHeader(stream);
this.size += 2*4+3*4;
stream.writeUint32(0);
stream.writeUint32(0);
stream.writeUint16(this.channel_count);
stream.writeUint16(this.samplesize);
stream.writeUint16(0);
stream.writeUint16(0);
stream.writeUint32(this.samplerate<<16);
this.writeFooter(stream);
}
BoxParser.avcCBox.prototype.write = function(stream) {
var i;
this.size = 7;
for (i = 0; i < this.SPS.length; i++) {
this.size += 2+this.SPS[i].length;
}
for (i = 0; i < this.PPS.length; i++) {
this.size += 2+this.PPS[i].length;
}
if (this.ext) {
this.size += this.ext.length;
}
this.writeHeader(stream);
stream.writeUint8(this.configurationVersion);
stream.writeUint8(this.AVCProfileIndication);
stream.writeUint8(this.profile_compatibility);
stream.writeUint8(this.AVCLevelIndication);
stream.writeUint8(this.lengthSizeMinusOne + (63<<2));
stream.writeUint8(this.SPS.length + (7<<5));
for (i = 0; i < this.SPS.length; i++) {
stream.writeUint16(this.SPS[i].length);
stream.writeUint8Array(this.SPS[i]);
}
stream.writeUint8(this.PPS.length);
for (i = 0; i < this.PPS.length; i++) {
stream.writeUint16(this.PPS[i].length);
stream.writeUint8Array(this.PPS[i]);
}
if (this.ext) {
stream.writeUint8Array(this.ext);
}
}
BoxParser.cttsBox.prototype.write = function(stream) {
var i;
this.version = 1;
this.flags = 0;
this.size = 4+8*this.sample_counts.length;
this.writeHeader(stream);
stream.writeUint32(this.sample_counts.length);
for(i=0; i<this.sample_counts.length; i++) {
stream.writeUint32(this.sample_counts[i]);
stream.writeInt32(this.sample_offsets[i]); /* signed */
}
}
BoxParser.cslgBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 4*5;
this.writeHeader(stream);
stream.writeInt32(this.compositionToDTSShift);
stream.writeInt32(this.leastDecodeToDisplayDelta);
stream.writeInt32(this.greatestDecodeToDisplayDelta);
stream.writeInt32(this.compositionStartTime);
stream.writeInt32(this.compositionEndTime);
}
BoxParser.sttsBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 4+8*this.sample_counts.length;
this.writeHeader(stream);
stream.writeUint32(this.sample_counts.length);
for(i=0; i<this.sample_counts.length; i++) {
stream.writeUint32(this.sample_counts[i]);
stream.writeUint32(this.sample_deltas[i]);
}
}
BoxParser.stssBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4+4*this.sample_numbers.length;
this.writeHeader(stream);
stream.writeUint32(this.sample_numbers.length);
stream.writeUint32Array(this.sample_numbers);
}
BoxParser.stshBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 4+8*this.shadowed_sample_numbers.length;
this.writeHeader(stream);
stream.writeUint32(this.shadowed_sample_numbers.length);
for(i=0; i<this.shadowed_sample_numbers.length; i++) {
stream.writeUint32(this.shadowed_sample_numbers[i]);
stream.writeUint32(this.sync_sample_numbers[i]);
}
}
BoxParser.stcoBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4+4*this.chunk_offsets.length;
this.writeHeader(stream);
stream.writeUint32(this.chunk_offsets.length);
stream.writeUint32Array(this.chunk_offsets);
}
BoxParser.co64Box.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 4+8*this.chunk_offsets.length;
this.writeHeader(stream);
stream.writeUint32(this.chunk_offsets.length);
for(i=0; i<this.chunk_offsets.length; i++) {
stream.writeUint64(this.chunk_offsets[i]);
}
}
BoxParser.stscBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 4+12*this.first_chunk.length;
this.writeHeader(stream);
stream.writeUint32(this.first_chunk.length);
for(i=0; i<this.first_chunk.length; i++) {
stream.writeUint32(this.first_chunk[i]);
stream.writeUint32(this.samples_per_chunk[i]);
stream.writeUint32(this.sample_description_index[i]);
}
}
BoxParser.stszBox.prototype.write = function(stream) {
var i;
this.version = 0;
this.flags = 0;
this.size = 8+12*this.sample_sizes.length;
this.writeHeader(stream);
stream.writeUint32(0);
stream.writeUint32(this.sample_sizes.length);
stream.writeUint32Array(this.sample_sizes);
}
BoxParser.mehdBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4;
this.writeHeader(stream);
stream.writeUint32(this.fragment_duration);
}
BoxParser.trexBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4*5;
this.writeHeader(stream);
stream.writeUint32(this.track_id);
stream.writeUint32(this.default_sample_description_index);
stream.writeUint32(this.default_sample_duration);
stream.writeUint32(this.default_sample_size);
stream.writeUint32(this.default_sample_flags);
}
BoxParser.mfhdBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4;
this.writeHeader(stream);
stream.writeUint32(this.sequence_number);
}
BoxParser.tfhdBox.prototype.write = function(stream) {
this.version = 0;
this.size = 4;
if (this.flags & BoxParser.TFHD_FLAG_BASE_OFFSET) {
this.size += 8;
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC) {
this.size += 4;
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR) {
this.size += 4;
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE) {
this.size += 4;
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS) {
this.size += 4;
}
this.writeHeader(stream);
stream.writeUint32(this.track_id);
if (this.flags & BoxParser.TFHD_FLAG_BASE_OFFSET) {
stream.writeUint64(this.base_data_offset);
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC) {
stream.writeUint32(this.default_sample_description_index);
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR) {
stream.writeUint32(this.default_sample_duration);
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE) {
stream.writeUint32(this.default_sample_size);
}
if (this.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS) {
stream.writeUint32(this.default_sample_flags);
}
}
BoxParser.trunBox.prototype.write = function(stream) {
this.version = 0;
this.size = 4;
if (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) {
this.size += 4;
}
if (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) {
this.size += 4;
}
if (this.flags & BoxParser.TRUN_FLAGS_DURATION) {
this.size += 4*this.sample_duration.length;
}
if (this.flags & BoxParser.TRUN_FLAGS_SIZE) {
this.size += 4*this.sample_size.length;
}
if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) {
this.size += 4*this.sample_flags.length;
}
if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) {
this.size += 4*this.sample_composition_time_offset.length;
}
this.writeHeader(stream);
stream.writeUint32(this.sample_count);
if (this.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) {
this.data_offset_position = stream.position;
stream.writeInt32(this.data_offset); //signed
}
if (this.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG) {
stream.writeUint32(this.first_sample_flags);
}
for (var i = 0; i < this.sample_count; i++) {
if (this.flags & BoxParser.TRUN_FLAGS_DURATION) {
stream.writeUint32(this.sample_duration[i]);
}
if (this.flags & BoxParser.TRUN_FLAGS_SIZE) {
stream.writeUint32(this.sample_size[i]);
}
if (this.flags & BoxParser.TRUN_FLAGS_FLAGS) {
stream.writeUint32(this.sample_flags[i]);
}
if (this.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) {
if (this.version === 0) {
stream.writeUint32(this.sample_composition_time_offset[i]);
} else {
stream.writeInt32(this.sample_composition_time_offset[i]); //signed
}
}
}
}
BoxParser.tfdtBox.prototype.write = function(stream) {
this.version = 0;
this.flags = 0;
this.size = 4;
this.writeHeader(stream);
if (this.version == 1) {
stream.writeUint64(this.baseMediaDecodeTime);
} else {
stream.writeUint32(this.baseMediaDecodeTime);
}
}

/*
* Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato
* License: BSD-3-Clause (see LICENSE file)
*/
var MPEG4DescriptorParser = function () {
var ES_DescrTag = 0x03;
var DecoderConfigDescrTag = 0x04;
var DecSpecificInfoTag = 0x05;
var SLConfigDescrTag = 0x06;
var descTagToName = [];
descTagToName[ES_DescrTag] = "ES_Descriptor";
descTagToName[DecoderConfigDescrTag] = "DecoderConfigDescriptor";
descTagToName[DecSpecificInfoTag] = "DecoderSpecificInfo";
descTagToName[SLConfigDescrTag] = "SLConfigDescriptor";
var that = this;
var classes = {};
this.parseOneDescriptor = function (stream) {
var hdrSize = 0;
var size = 0;
var tag;
var desc;
var byteRead;
tag = stream.readUint8();
hdrSize++;
byteRead = stream.readUint8();
hdrSize++;
while (byteRead & 0x80) {
size = (byteRead & 0x7F)<<7;
byteRead = stream.readUint8();
hdrSize++;
}
size += byteRead & 0x7F;
Log.d("MPEG4DescriptorParser", "Found "+(descTagToName[tag] | "Descriptor "+tag)+", size "+size+" at position "+stream.position);
if (descTagToName[tag]) {
desc = new classes[descTagToName[tag]](size);
} else {
desc = new classes.Descriptor(size);
}
desc.parse(stream);
return desc;
}
classes.Descriptor = function(_tag, _size) {
this.tag = _tag;
this.size = _size;
this.descs = [];
}
classes.Descriptor.prototype.parse = function (stream) {
this.data = stream.readUint8Array(this.size);
}
classes.Descriptor.prototype.findDescriptor = function (tag) {
for (var i = 0; i < this.descs.length; i++) {
if (this.descs[i].tag == tag) {
return this.descs[i];
}
}
return null;
}
classes.Descriptor.prototype.parseRemainingDescriptors = function (stream) {
var start = stream.position;
while (stream.position < start+this.size) {
var desc = that.parseOneDescriptor(stream);
this.descs.push(desc);
}
}
classes.ES_Descriptor = function (size) {
classes.Descriptor.call(this, ES_DescrTag, size);
}
classes.ES_Descriptor.prototype = new classes.Descriptor();
classes.ES_Descriptor.prototype.parse = function(stream) {
this.ES_ID = stream.readUint16();
this.flags = stream.readUint8();
this.size -= 3;
if (this.flags & 0x80) {
this.dependsOn_ES_ID = stream.readUint16();
this.size -= 2;
} else {
this.dependsOn_ES_ID = 0;
}
if (this.flags & 0x40) {
var l = stream.readUint8();
this.URL = stream.readString(l);
this.size -= l+1;
} else {
this.URL = null;
}
if (this.flags & 0x20) {
this.OCR_ES_ID = stream.readUint16();
this.size -= 2;
} else {
this.OCR_ES_ID = 0;
}
this.parseRemainingDescriptors(stream);
}
classes.ES_Descriptor.prototype.getOTI = function(stream) {
var dcd = this.findDescriptor(DecoderConfigDescrTag);
if (dcd) {
return dcd.oti;
} else {
return 0;
}
}
classes.ES_Descriptor.prototype.getAudioConfig = function(stream) {
var dcd = this.findDescriptor(DecoderConfigDescrTag);
if (!dcd) return null;
var dsi = dcd.findDescriptor(DecSpecificInfoTag);
if (dsi && dsi.data) {
return (dsi.data[0]& 0xF8) >> 3;
} else {
return null;
}
}
classes.DecoderConfigDescriptor = function (size) {
classes.Descriptor.call(this, DecoderConfigDescrTag, size);
}
classes.DecoderConfigDescriptor.prototype = new classes.Descriptor();
classes.DecoderConfigDescriptor.prototype.parse = function(stream) {
this.oti = stream.readUint8();
this.streamType = stream.readUint8();
this.bufferSize = stream.readUint24();
this.maxBitrate = stream.readUint32();
this.avgBitrate = stream.readUint32();
this.size -= 13;
this.parseRemainingDescriptors(stream);
}
classes.DecoderSpecificInfo = function (size) {
classes.Descriptor.call(this, DecSpecificInfoTag, size);
}
classes.DecoderSpecificInfo.prototype = new classes.Descriptor();
classes.SLConfigDescriptor = function (size) {
classes.Descriptor.call(this, SLConfigDescrTag, size);
}
classes.SLConfigDescriptor.prototype = new classes.Descriptor();
return this;
}

/*
* Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato
* License: BSD-3-Clause (see LICENSE file)
*/
var ISOFile = function (stream) {
this.stream = stream;
/* Array of all boxes (in order) found in the file */
this.boxes = [];
/* Array of all mdats */
this.mdats = [];
/* Array of all moofs */
this.moofs = [];
/* Boolean indicating if the file is compatible with progressive parsing (moov first) */
this.isProgressive = false;
/* Index of the last moof box received */
this.lastMoofIndex = 0;
/* position in the current buffer of the beginning of the last box parsed */
this.lastPosition = 0;
/* indicator if the parsing is stuck in the middle of an mdat box */
this.parsingMdat = false;
/* to fire moov start event */
this.moovStartFound = false;
/* size of the buffers allocated for samples */
this.samplesDataSize = 0;
/* next file position that the parser needs */
this.nextParsePosition = 0;
}
ISOFile.prototype.repositionAtMdatEnd = function(box, size) {
var i;
/* check which existing buffers contain data for this mdat, if any */
for (i = this.stream.bufferIndex; i < this.stream.nextBuffers.length; i++) {
var buf = this.stream.nextBuffers[i];
if (box.fileStart + size >= buf.fileStart) {
if (box.fileStart + size <= buf.fileStart + buf.byteLength) {
/* we've found the end of the mdat */
this.parsingMdat = false;
this.stream.buffer = buf;
this.stream.bufferIndex = i;
this.stream.position = box.fileStart + size - buf.fileStart;
Log.d("ISOFile", "Found 'mdat' end in buffer #"+this.stream.bufferIndex+" at position "+this.lastPosition);
return true;
} else {
/* this mdat box extends after that buffer, record that the mdat will need it */
box.buffers.push(buf);
}
}
}
return false;
}
ISOFile.prototype.findEndContiguousBuf = function() {
var i;
var currentBuf;
var nextBuf;
if (this.nextSeekPosition) {
/* find the buffer with the largest position smaller than the seek position
the seek can be in the past, we need to check from the beginning */
for (i = 0; i < this.stream.nextBuffers.length; i++) {
nextBuf = this.stream.nextBuffers[i];
if (nextBuf.fileStart <= this.nextSeekPosition) {
currentBuf = this.stream.nextBuffers[i];
this.stream.bufferIndex = i;
} else {
break;
}
}
} else {
currentBuf = this.stream.nextBuffers[this.stream.bufferIndex];
}
/* find the end of the contiguous range of data */
if (this.stream.nextBuffers.length > this.stream.bufferIndex) {
for (i = this.stream.bufferIndex+1; i < this.stream.nextBuffers.length; i++) {
nextBuf = this.stream.nextBuffers[i];
if (nextBuf.fileStart === currentBuf.fileStart + currentBuf.byteLength) {
currentBuf = nextBuf;
this.stream.bufferIndex = i;
} else {
break;
}
}
}
if (currentBuf.fileStart + currentBuf.byteLength >= this.nextSeekPosition) {
/* no need to seek anymore, the seek position is in the buffer */
delete this.nextSeekPosition;
}
return currentBuf.fileStart + currentBuf.byteLength;
}
ISOFile.prototype.parse = function() {
var found;
var ret;
var box;
Log.d("ISOFile","Starting parsing with buffer #"+this.stream.bufferIndex+" from position "+this.lastPosition+" ("+(this.stream.buffer.fileStart+this.lastPosition)+" in the file)");
this.stream.seek(this.lastPosition);
while (true) {
/* check if we are in the parsing of an incomplete mdat box */
if (this.parsingMdat) {
/* the current mdat is the latest one having been parsed */
box = this.mdats[this.mdats.length - 1];
found = this.repositionAtMdatEnd(box, box.size+box.hdr_size);
if (found) {
/* the end of the mdat has been found, let's see if we can parse more in this buffer */
continue;
} else {
/* let's wait for more buffer to come */
this.nextParsePosition = this.findEndContiguousBuf();
return;
}
} else {
/* remember the position of the box start in case we need to roll back */
this.lastPosition = this.stream.position;
ret = BoxParser.parseOneBox(this.stream);
if (ret.code == BoxParser.ERR_NOT_ENOUGH_DATA) {
/* we did not have enough bytes in the current buffer to parse the entire box */
if (ret.type === "mdat") {
/* we had enough bytes to get its type and size */
/* special handling for mdat boxes, since we don't actually need to parse it linearly */
this.parsingMdat = true;
box = new BoxParser[ret.type+"Box"](ret.size-ret.hdr_size);
this.mdats.push(box);
box.fileStart = this.stream.buffer.fileStart + this.stream.position;
box.hdr_size = ret.hdr_size;
box.buffers = [];
box.buffers[0] = this.stream.buffer;
this.stream.buffer.usedBytes += ret.hdr_size;
/* let's see if we have the end of the box in the other buffers */
found = this.repositionAtMdatEnd(box, box.size+box.hdr_size);
if (found) {
/* let's see if we can parse more in this buffer */
continue;
} else {
/* determine the next position */
if (this.moovStartFound) {
/* let's wait for more buffer to come */
this.nextParsePosition = this.findEndContiguousBuf();
} else {
/* moov not find yet, skip this box */
this.nextParsePosition = box.fileStart + box.size+box.hdr_size;
}
return;
}
} else {
if (ret.type === "moov") {
this.moovStartFound = true;
}
/* either it's not an mdat box (and we need to parse it, we cannot skip it)
or we did not have enough data to parse the type and size of the box,
we try to concatenate the current buffer with the next buffer to restart parsing */
if (this.stream.bufferIndex < this.stream.nextBuffers.length - 1) {
var next_buffer = this.stream.nextBuffers[this.stream.bufferIndex+1];
if (next_buffer.fileStart === this.stream.buffer.fileStart + this.stream.buffer.byteLength) {
var oldLength = this.stream.buffer.byteLength;
var oldUsedBytes = this.stream.buffer.usedBytes;
var oldFileStart = this.stream.buffer.fileStart;
this.stream.nextBuffers[this.stream.bufferIndex] = ArrayBuffer.concat(this.stream.buffer, next_buffer);
this.stream.buffer = this.stream.nextBuffers[this.stream.bufferIndex];
this.stream.nextBuffers.splice(this.stream.bufferIndex+1, 1);
this.stream.buffer.usedBytes = oldUsedBytes;
this.stream.buffer.fileStart = oldFileStart;
/* The next best position to parse is at the end of this new buffer */
this.nextParsePosition = this.stream.buffer.fileStart + this.stream.buffer.byteLength;
Log.d("ISOFile", "Concatenating buffer for box parsing (length: "+oldLength+"->"+this.stream.buffer.byteLength+")");
continue;
} else {
/* we cannot concatenate because the buffers are not contiguous */
/* The next best position to parse is at the end of this old buffer */
this.nextParsePosition = this.stream.buffer.fileStart + this.stream.buffer.byteLength;
return;
}
} else {
/* not enough buffers received, wait */
if (!ret.type) {
/* There were not enough bytes in the buffer to parse the box type and length,
the next fetch should retrieve those missing bytes, i.e. the next bytes after this buffer */
this.nextParsePosition = this.stream.buffer.fileStart + this.stream.buffer.byteLength;
} else {
/* we had enough bytes to parse size and type of the incomplete box
if we haven't found yet the moov box, skip this one and try the next one
if we have found the moov box, let's continue linear parsing */
if (this.moovStartFound) {
this.nextParsePosition = this.stream.buffer.fileStart + this.stream.buffer.byteLength;
} else {
this.nextParsePosition = this.stream.buffer.fileStart + this.stream.position + ret.size;
}
}
return;
}
}
} else {
/* the box is entirely parsed */
box = ret.box;
/* store the box in the 'boxes' array to preserve box order (for offset)
but also store box in a property for more direct access */
this.boxes.push(box);
switch (box.type) {
case "mdat":
this.mdats.push(box);
box.fileStart = this.stream.buffer.fileStart + box.start;
box.buffers = [ this.stream.buffer ];
this.stream.buffer.usedBytes += box.hdr_size;
break;
case "moof":
this.moofs.push(box);
this.stream.buffer.usedBytes += ret.size;
break;
case "moov":
this.moovStartFound = true;
if (this.mdats.length === 0) {
this.isProgressive = true;
}
this[box.type] = box;
this.stream.buffer.usedBytes += ret.size;
break;
default:
this[box.type] = box;
this.stream.buffer.usedBytes += ret.size;
break;
}
}
}
}
}
ISOFile.prototype.write = function(outstream) {
for (var i=0; i<this.boxes.length; i++) {
this.boxes[i].write(outstream);
}
}
ISOFile.prototype.writeInitializationSegment = function(outstream) {
var i;
Log.d("ISOFile", "Generating initialization segment");
this.ftyp.write(outstream);
if (this.moov.mvex) {
var index;
this.initial_duration = this.moov.mvex.fragment_duration;
for (i = 0; i < this.moov.boxes.length; i++) {
var box = this.moov.boxes[i];
if (box == this.moov.mvex) {
index = i;
}
}
if (index > -1) {
this.moov.boxes.splice(index, 1);
}
}
var mvex = new BoxParser.mvexBox();
this.moov.boxes.push(mvex);
var mehd = new BoxParser.mehdBox();
mvex.boxes.push(mehd);
mehd.fragment_duration = this.initial_duration;
for (i = 0; i < this.moov.traks.length; i++) {
var trex = new BoxParser.trexBox();
mvex.boxes.push(trex);
trex.track_id = this.moov.traks[i].tkhd.track_id;
trex.default_sample_description_index = 1;
trex.default_sample_duration = (this.moov.traks[i].samples.length>0 ? this.moov.traks[i].samples[0].duration: 0);
trex.default_sample_size = 0;
trex.default_sample_flags = 1<<16;
}
this.moov.write(outstream);
}
ISOFile.prototype.resetTables = function () {
var i;
var trak, stco, stsc, stsz, stts, ctts, stss;
this.initial_duration = this.moov.mvhd.duration;
this.moov.mvhd.duration = 0;
for (i = 0; i < this.moov.traks.length; i++) {
trak = this.moov.traks[i];
trak.tkhd.duration = 0;
trak.mdia.mdhd.duration = 0;
stco = trak.mdia.minf.stbl.stco || trak.mdia.minf.stbl.co64;
stco.chunk_offsets = [];
stsc = trak.mdia.minf.stbl.stsc;
stsc.first_chunk = [];
stsc.samples_per_chunk = [];
stsc.sample_description_index = [];
stsz = trak.mdia.minf.stbl.stsz;
stsz.sample_sizes = [];
stts = trak.mdia.minf.stbl.stts;
stts.sample_counts = [];
stts.sample_deltas = [];
ctts = trak.mdia.minf.stbl.ctts;
if (ctts) {
ctts.sample_counts = [];
ctts.sample_offsets = [];
}
stss = trak.mdia.minf.stbl.stss;
var k = trak.mdia.minf.stbl.boxes.indexOf(stss);
if (k != -1) trak.mdia.minf.stbl.boxes[k] = null;
}
}
ISOFile.prototype.buildSampleLists = function() {
var i, j, k;
var trak, stco, stsc, stsz, stts, ctts, stss, stsd, subs;
var chunk_run_index, chunk_index, last_chunk_in_run, offset_in_chunk, last_sample_in_chunk;
var last_sample_in_stts_run, stts_run_index, last_sample_in_ctts_run, ctts_run_index, last_stss_index, last_subs_index;
for (i = 0; i < this.moov.traks.length; i++) {
trak = this.moov.traks[i];
trak.samples = [];
stco = trak.mdia.minf.stbl.stco || trak.mdia.minf.stbl.co64;
stsc = trak.mdia.minf.stbl.stsc;
stsz = trak.mdia.minf.stbl.stsz;
stts = trak.mdia.minf.stbl.stts;
ctts = trak.mdia.minf.stbl.ctts;
stss = trak.mdia.minf.stbl.stss;
stsd = trak.mdia.minf.stbl.stsd;
subs = trak.mdia.minf.stbl.subs;
chunk_index = -1;
chunk_run_index = -1;
last_chunk_in_run = -1;
offset_in_chunk = 0;
last_sample_in_chunk = 0;
last_sample_in_stts_run = -1;
stts_run_index = -1;
last_sample_in_ctts_run = -1;
ctts_run_index = -1;
last_stss_index = 0;
subs_entry_index = 0;
last_subs_sample_index = 0;
/* we build the samples one by one and compute their properties */
for (j = 0; j < stsz.sample_sizes.length; j++) {
var sample = {};
sample.track_id = trak.tkhd.track_id;
sample.timescale = trak.mdia.mdhd.timescale;
trak.samples[j] = sample;
/* size can be known directly */
sample.size = stsz.sample_sizes[j];
/* computing chunk-based properties (offset, sample description index)*/
if (j < last_sample_in_chunk) {
/* the new sample is in the same chunk, the indexes did not change */
sample.chunk_index = chunk_index;
sample.chunk_run_index = chunk_run_index;
} else {
/* the new sample is not in this chunk */
offset_in_chunk = 0;
chunk_index++;
sample.chunk_index = chunk_index;
if (chunk_index < last_chunk_in_run) {
/* this new chunk in the same run of chunks */
sample.chunk_run_index = chunk_run_index;
} else {
/* this chunk starts a new run */
if (chunk_run_index < stsc.first_chunk.length - 2) {
/* the last chunk in this new run is the beginning of the next one */
chunk_run_index++;
last_chunk_in_run = stsc.first_chunk[chunk_run_index+1]-1; // chunk number are 1-based
} else {
/* the last chunk run in indefinitely long */
last_chunk_in_run = Infinity;
}
}
last_sample_in_chunk += stsc.samples_per_chunk[chunk_run_index];
sample.chunk_run_index = chunk_run_index;
}
sample.description = stsd.entries[stsc.sample_description_index[sample.chunk_run_index]-1];
sample.offset = stco.chunk_offsets[sample.chunk_index] + offset_in_chunk;
offset_in_chunk += sample.size;
/* setting dts, cts, duration and rap flags */
if (j >= last_sample_in_stts_run) {
stts_run_index++;
if (last_sample_in_stts_run < 0) {
last_sample_in_stts_run = 0;
}
last_sample_in_stts_run += stts.sample_counts[stts_run_index];
}
if (j > 0) {
sample.dts = trak.samples[j-1].dts + stts.sample_deltas[stts_run_index];
trak.samples[j-1].duration = sample.dts - trak.samples[j-1].dts;
} else {
sample.dts = 0;
}
if (ctts) {
if (j >= last_sample_in_ctts_run) {
ctts_run_index++;
if (last_sample_in_ctts_run < 0) {
last_sample_in_ctts_run = 0;
}
last_sample_in_ctts_run += ctts.sample_counts[ctts_run_index];
}
sample.cts = trak.samples[j].dts + ctts.sample_offsets[ctts_run_index];
} else {
sample.cts = sample.dts;
}
if (stss) {
if (j == stss.sample_numbers[last_stss_index] - 1) { // sample numbers are 1-based
sample.is_rap = true;
last_stss_index++;
} else {
sample.is_rap = false;
}
} else {
sample.is_rap = true;
}
if (subs) {
if (subs.samples[subs_entry_index].sample_delta + last_subs_sample_index == j) {
sample.subsamples = subs.samples[subs_entry_index].subsamples;
last_subs_sample_index += subs.samples[subs_entry_index].sample_delta;
}
}
}
if (j>0) trak.samples[j-1].duration = trak.mdia.mdhd.duration - trak.samples[j-1].dts;
}
}
ISOFile.prototype.getTrexById = function(id) {
var i;
if (!this.moov || !this.moov.mvex) return null;
for (i = 0; i < this.moov.mvex.trexs.length; i++) {
var trex = this.moov.mvex.trexs[i];
if (trex.track_id == id) return trex;
}
return null;
}
ISOFile.prototype.updateSampleLists = function() {
var i, j, k;
var default_sample_description_index, default_sample_duration, default_sample_size, default_sample_flags;
var last_run_position;
var box, moof, traf, trak, trex;
var sample;
while (this.lastMoofIndex < this.moofs.length) {
box = this.moofs[this.lastMoofIndex];
this.lastMoofIndex++;
if (box.type == "moof") {
moof = box;
for (i = 0; i < moof.trafs.length; i++) {
traf = moof.trafs[i];
trak = this.getTrackById(traf.tfhd.track_id);
trex = this.getTrexById(traf.tfhd.track_id);
if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_DESC) {
default_sample_description_index = traf.tfhd.default_sample_description_index;
} else {
default_sample_description_index = trex.default_sample_description_index;
}
if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_DUR) {
default_sample_duration = traf.tfhd.default_sample_duration;
} else {
default_sample_duration = trex.default_sample_duration;
}
if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_SIZE) {
default_sample_size = traf.tfhd.default_sample_size;
} else {
default_sample_size = trex.default_sample_size;
}
if (traf.tfhd.flags & BoxParser.TFHD_FLAG_SAMPLE_FLAGS) {
default_sample_flags = traf.tfhd.default_sample_flags;
} else {
default_sample_flags = trex.default_sample_flags;
}
for (j = 0; j < traf.truns.length; j++) {
var trun = traf.truns[j];
for (k = 0; k < trun.sample_count; k++) {
sample = {};
traf.first_sample_index = trak.samples.length;
trak.samples.push(sample);
sample.track_id = trak.tkhd.track_id;
sample.timescale = trak.mdia.mdhd.timescale;
sample.description = trak.mdia.minf.stbl.stsd.entries[default_sample_description_index-1];
sample.size = default_sample_size;
if (trun.flags & BoxParser.TRUN_FLAGS_SIZE) {
sample.size = trun.sample_size[k];
}
sample.duration = default_sample_duration;
if (trun.flags & BoxParser.TRUN_FLAGS_DURATION) {
sample.duration = trun.sample_duration[k];
}
if (trak.first_traf_merged || k > 0) {
sample.dts = trak.samples[trak.samples.length-2].dts+trak.samples[trak.samples.length-2].duration;
} else {
if (traf.tfdt) {
sample.dts = traf.tfdt.baseMediaDecodeTime;
} else {
sample.dts = 0;
}
trak.first_traf_merged = true;
}
sample.cts = sample.dts;
if (trun.flags & BoxParser.TRUN_FLAGS_CTS_OFFSET) {
sample.cts = sample.dts + trun.sample_composition_time_offset[k];
}
sample_flags = default_sample_flags;
if (trun.flags & BoxParser.TRUN_FLAGS_FLAGS) {
sample_flags = trun.sample_flags[k];
} else if (k === 0 && (trun.flags & BoxParser.TRUN_FLAGS_FIRST_FLAG)) {
sample_flags = trun.first_sample_flags;
}
sample.is_rap = ((sample_flags >> 16 & 0x1) ? false : true);
var bdop = (traf.tfhd.flags & BoxParser.TFHD_FLAG_BASE_DATA_OFFSET) ? true : false;
var dbim = (traf.tfhd.flags & BoxParser.TFHD_FLAG_DEFAULT_BASE_IS_MOOF) ? true : false;
var dop = (trun.flags & BoxParser.TRUN_FLAGS_DATA_OFFSET) ? true : false;
var bdo = 0;
if (!bdop) {
if (!dbim) {
if (j === 0) { // the first track in the movie fragment
bdo = moof.fileStart; // the position of the first byte of the enclosing Movie Fragment Box
} else {
bdo = last_run_position; // end of the data defined by the preceding *track* (irrespective of the track id) fragment in the moof
}
} else {
bdo = moof.fileStart;
}
} else {
bdo = traf.tfhd.base_data_offset;
}
if (j === 0 && k === 0) {
if (dop) {
sample.offset = bdo + trun.data_offset; // If the data-offset is present, it is relative to the base-data-offset established in the track fragment header
} else {
sample.offset = bdo; // the data for this run starts the base-data-offset defined by the track fragment header
}
} else {
sample.offset = last_run_position; // this run starts immediately after the data of the previous run
}
last_run_position = sample.offset + sample.size;
}
}
if (traf.subs) {
var sample_index = traf.first_sample_index;
for (j = 0; j < traf.subs.samples.length; j++) {
sample_index += traf.subs.samples[j].sample_delta;
sample = trak.samples[sample_index-1];
sample.subsamples = traf.subs.samples[j].subsamples;
}
}
}
}
}
}
ISOFile.prototype.getCodecs = function() {
var i;
var codecs = "";
for (i = 0; i < this.moov.traks.length; i++) {
var trak = this.moov.traks[i];
if (i>0) {
codecs+=",";
}
codecs += trak.mdia.minf.stbl.stsd.entries[0].getCodec();
}
return codecs;
}
ISOFile.prototype.getTrackById = function(id) {
for (var j = 0; j < this.moov.traks.length; j++) {
var trak = this.moov.traks[j];
if (trak.tkhd.track_id == id) return trak;
}
return null;
}
ISOFile.prototype.getSample = function(trak, sampleNum) {
var mdat;
var buffer;
var i, j;
var sample = trak.samples[sampleNum];
if (!sample.data) {
sample.data = new Uint8Array(sample.size);
sample.alreadyRead = 0;
this.samplesDataSize += sample.size;
Log.d("ISOFile", "Allocating sample #"+sampleNum+" on track #"+trak.tkhd.track_id+" of size "+sample.size+" (total: "+this.samplesDataSize+")");
} else if (sample.alreadyRead == sample.size) {
return sample;
}
for (i = 0; i < this.mdats.length; i++) {
mdat = this.mdats[i];
for (j = 0; j < mdat.buffers.length; j++) {
buffer = mdat.buffers[j];
if (sample.offset + sample.alreadyRead >= buffer.fileStart &&
sample.offset + sample.alreadyRead < buffer.fileStart + buffer.byteLength) {
/* The sample starts in this buffer */
var lengthAfterStart = buffer.byteLength - (sample.offset + sample.alreadyRead - buffer.fileStart);
if (sample.size - sample.alreadyRead <= lengthAfterStart) {
/* the sample is entirely contained in this buffer */
Log.d("ISOFile","Getting sample #"+sampleNum+" data (alreadyRead: "+sample.alreadyRead+" offset: "+(sample.offset+sample.alreadyRead - buffer.fileStart)+" size: "+(sample.size - sample.alreadyRead)+")");
DataStream.memcpy(sample.data.buffer, sample.alreadyRead,
buffer, sample.offset+sample.alreadyRead - buffer.fileStart, sample.size - sample.alreadyRead);
buffer.usedBytes += sample.size - sample.alreadyRead;
sample.alreadyRead = sample.size;
if (buffer.usedBytes == buffer.byteLength) {
mdat.buffers.splice(j, 1);
Log.d("ISOFile","Removing buffer for mdat ("+mdat.buffers.length+" buffers left)");
j--;
}
return sample;
} else {
/* the sample does not end in this buffer */
Log.d("ISOFile","Getting sample data (alreadyRead: "+sample.alreadyRead+" offset: "+(sample.offset+sample.alreadyRead - buffer.fileStart)+" size: "+lengthAfterStart+")");
DataStream.memcpy(sample.data.buffer, sample.alreadyRead,
buffer, sample.offset+sample.alreadyRead - buffer.fileStart, lengthAfterStart);
buffer.usedBytes += lengthAfterStart;
if (buffer.usedBytes == buffer.byteLength) {
mdat.buffers.splice(j, 1);
Log.d("ISOFile","Removing buffer for mdat ("+mdat.buffers.length+" buffers left)");
j--;
}
sample.alreadyRead += lengthAfterStart;
}
}
}
if (mdat.buffers.length === 0 && this.mdats.length > 1) {
this.mdats.splice(i, 1);
i--;
}
}
return null;
}
ISOFile.prototype.releaseSample = function(trak, sampleNum) {
var sample = trak.samples[sampleNum];
sample.data = null;
this.samplesDataSize -= sample.size;
return sample.size;
}

/*
* Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato
* License: BSD-3-Clause (see LICENSE file)
*/
var Log = (function (){
var start = new Date();
var LOG_LEVEL_ERROR = 4;
var LOG_LEVEL_WARNING = 3;
var LOG_LEVEL_INFO = 2;
var LOG_LEVEL_DEBUG	= 1;
//modif to remove
var log_level = (typeof document!=='undefined')?((document.location.href.indexOf('mp4box')!==-1)?LOG_LEVEL_DEBUG:LOG_LEVEL_ERROR):LOG_LEVEL_ERROR;
var logObject = {
setLogLevel : function(level) {
if (level == this.d) log_level = LOG_LEVEL_DEBUG;
else if (level == this.i) log_level = LOG_LEVEL_INFO;
else if (level == this.w) log_level = LOG_LEVEL_WARNING;
else if (level == this.e) log_level = LOG_LEVEL_ERROR;
else log_level = LOG_LEVEL_ERROR;
},
d : function(module, msg) {
if (LOG_LEVEL_DEBUG >= log_level) {
console.debug("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg);
}
},
i : function(module, msg) {
if (LOG_LEVEL_INFO >= log_level) {
console.info("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg);
}
},
w : function(module, msg) {
if (LOG_LEVEL_WARNING >= log_level) {
console.warn("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg);
}
},
e : function(module, msg) {
if (LOG_LEVEL_ERROR >= log_level) {
console.error("["+Log.getDurationString(new Date()-start,1000)+"]","["+module+"]",msg);
}
}
};
return logObject;
})();
/* Helper function to print a duration value in the form H:MM:SS.MS */
Log.getDurationString = function(duration, _timescale) {
/* Helper function to print a number on a fixed number of digits */
function pad(number, length) {
var str = '' + number;
var a = str.split('.');
while (a[0].length < length) {
a[0] = '0' + a[0];
}
return a.join('.');
}
var timescale = _timescale || 1;
var duration_sec = duration/timescale;
var hours = Math.floor(duration_sec/3600);
duration_sec -= hours * 3600;
var minutes = Math.floor(duration_sec/60);
duration_sec -= minutes * 60;
var msec = duration_sec*1000;
duration_sec = Math.floor(duration_sec);
msec -= duration_sec*1000;
msec = Math.floor(msec);
return ""+hours+":"+pad(minutes,2)+":"+pad(duration_sec,2)+"."+pad(msec,3);
}
/* Helper function to stringify HTML5 TimeRanges objects */
Log.printRanges = function(ranges) {
var length = ranges.length;
if (length > 0) {
var str = "";
for (var i = 0; i < length; i++) {
if (i > 0) str += ",";
str += "["+Log.getDurationString(ranges.start(i))+ ","+Log.getDurationString(ranges.end(i))+"]";
}
return str;
} else {
return "(empty)";
}
}

/*
* Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato
* License: BSD-3-Clause (see LICENSE file)
*/
var MP4Box = function () {
/* DataStream object used to parse the boxes */
this.inputStream = null;
/* List of ArrayBuffers, with a fileStart property, sorted in order and non overlapping */
this.nextBuffers = [];
/* ISOFile object containing the parsed boxes */
this.inputIsoFile = null;
/* Callback called when the moov parsing starts */
this.onMoovStart = null;
/* Boolean keeping track of the call to onMoovStart, to avoid double calls */
this.moovStartSent = false;
/* Callback called when the moov is entirely parsed */
this.onReady = null;
/* Boolean keeping track of the call to onReady, to avoid double calls */
this.readySent = false;
/* Callback to call when segments are ready */
this.onSegment = null;
/* Callback to call when samples are ready */
this.onSamples = null;
/* Callback to call when there is an error in the parsing or processing of samples */
this.onError = null;
/* Boolean indicating if the moov box run-length encoded tables of sample information have been processed */
this.sampleListBuilt = false;
this.fragmentedTracks = [];
this.extractedTracks = [];
this.isFragmentationStarted = false;
this.nextMoofNumber = 0;
}
MP4Box.prototype.setSegmentOptions = function(id, user, options) {
var trak = this.inputIsoFile.getTrackById(id);
if (trak) {
var fragTrack = {};
this.fragmentedTracks.push(fragTrack);
fragTrack.id = id;
fragTrack.user = user;
fragTrack.trak = trak;
trak.nextSample = 0;
fragTrack.segmentStream = null;
fragTrack.nb_samples = 1000;
fragTrack.rapAlignement = true;
if (options) {
if (options.nbSamples) fragTrack.nb_samples = options.nbSamples;
if (options.rapAlignement) fragTrack.rapAlignement = options.rapAlignement;
}
}
}
MP4Box.prototype.unsetSegmentOptions = function(id) {
var index = -1;
for (var i = 0; i < this.fragmentedTracks.length; i++) {
var fragTrack = this.fragmentedTracks[i];
if (fragTrack.id == id) {
index = i;
}
}
if (index > -1) {
this.fragmentedTracks.splice(index, 1);
}
}
MP4Box.prototype.setExtractionOptions = function(id, user, options) {
var trak = this.inputIsoFile.getTrackById(id);
if (trak) {
var extractTrack = {};
this.extractedTracks.push(extractTrack);
extractTrack.id = id;
extractTrack.user = user;
extractTrack.trak = trak;
trak.nextSample = 0;
extractTrack.nb_samples = 1000;
extractTrack.samples = [];
if (options) {
if (options.nbSamples) extractTrack.nb_samples = options.nbSamples;
}
}
}
MP4Box.prototype.unsetExtractionOptions = function(id) {
var index = -1;
for (var i = 0; i < this.extractedTracks.length; i++) {
var extractTrack = this.extractedTracks[i];
if (extractTrack.id == id) {
index = i;
}
}
if (index > -1) {
this.extractedTracks.splice(index, 1);
}
}
MP4Box.prototype.createSingleSampleMoof = function(sample) {
var moof = new BoxParser.moofBox();
var mfhd = new BoxParser.mfhdBox();
mfhd.sequence_number = this.nextMoofNumber;
this.nextMoofNumber++;
moof.boxes.push(mfhd);
var traf = new BoxParser.trafBox();
moof.boxes.push(traf);
var tfhd = new BoxParser.tfhdBox();
traf.boxes.push(tfhd);
tfhd.track_id = sample.track_id;
tfhd.flags = BoxParser.TFHD_FLAG_DEFAULT_BASE_IS_MOOF;
var tfdt = new BoxParser.tfdtBox();
traf.boxes.push(tfdt);
tfdt.baseMediaDecodeTime = sample.dts;
var trun = new BoxParser.trunBox();
traf.boxes.push(trun);
moof.trun = trun;
trun.flags = BoxParser.TRUN_FLAGS_DATA_OFFSET | BoxParser.TRUN_FLAGS_DURATION |
BoxParser.TRUN_FLAGS_SIZE | BoxParser.TRUN_FLAGS_FLAGS |
BoxParser.TRUN_FLAGS_CTS_OFFSET;
trun.data_offset = 0;
trun.first_sample_flags = 0;
trun.sample_count = 1;
trun.sample_duration = [];
trun.sample_duration[0] = sample.duration;
trun.sample_size = [];
trun.sample_size[0] = sample.size;
trun.sample_flags = [];
trun.sample_flags[0] = 0;
trun.sample_composition_time_offset = [];
trun.sample_composition_time_offset[0] = sample.cts - sample.dts;
return moof;
}
MP4Box.prototype.createFragment = function(input, track_id, sampleNumber, stream_) {
var trak = this.inputIsoFile.getTrackById(track_id);
var sample = this.inputIsoFile.getSample(trak, sampleNumber);
if (sample == null) {
return null;
}
var stream = stream_ || new DataStream();
stream.endianness = DataStream.BIG_ENDIAN;
var moof = this.createSingleSampleMoof(sample);
moof.write(stream);
/* adjusting the data_offset now that the moof size is known*/
moof.trun.data_offset = moof.size+8; //8 is mdat header
Log.d("BoxWriter", "Adjusting data_offset with new value "+moof.trun.data_offset);
stream.adjustUint32(moof.trun.data_offset_position, moof.trun.data_offset);
var mdat = new BoxParser.mdatBox();
mdat.data = sample.data;
mdat.write(stream);
return stream;
}
/* helper functions to enable calling "open" with additional buffers */
ArrayBuffer.concat = function(buffer1, buffer2) {
Log.d("ArrayBuffer", "Trying to create a new buffer of size: "+(buffer1.byteLength + buffer2.byteLength));
var tmp = new Uint8Array(buffer1.byteLength + buffer2.byteLength);
tmp.set(new Uint8Array(buffer1), 0);
tmp.set(new Uint8Array(buffer2), buffer1.byteLength);
return tmp.buffer;
};
MP4Box.prototype.insertBuffer = function(ab) {
var smallB;
var to_add = true;
/* insert the new buffer in the sorted list of buffers, making sure, it is not overlapping with existing ones */
for (var i = 0; i < this.nextBuffers.length; i++) {
var b = this.nextBuffers[i];
if (ab.fileStart <= b.fileStart) {
/* the insertion position is found */
if (ab.fileStart === b.fileStart) {
/* The new buffer overlaps with an existing buffer */
if (ab.byteLength > b.byteLength) {
/* the new buffer is bigger than the existing one
remove the existing buffer and try again to insert
the new buffer to check overlap with the next ones */
this.nextBuffers.splice(i, 1);
i--;
continue;
} else {
/* the new buffer is smaller than the existing one, just drop it */
Log.w("MP4Box", "Buffer already appended, ignoring");
}
} else {
/* The beginning of the new buffer is not overlapping with an existing buffer
let's check the end of it */
if (ab.fileStart + ab.byteLength <= b.fileStart) {
/* no overlap, we can add it as is */
Log.d("MP4Box", "Appending new buffer (fileStart: "+ab.fileStart+" length:"+ab.byteLength+")");
this.nextBuffers.splice(i, 0, ab);
if (i === 0 && this.inputStream != null) {
this.inputStream.buffer = ab;
}
} else {
/* There is some overlap, cut the new buffer short, and add it*/
smallB = new Uint8Array(b.fileStart - ab.fileStart);
smallB.set(new Uint8Array(ab, 0, b.fileStart - ab.fileStart));
smallB.fileStart = ab.fileStart;
ab = smallB;
ab.usedBytes = 0;
Log.d("MP4Box", "Appending new buffer (fileStart: "+ab.fileStart+" length:"+ab.byteLength+")");
this.nextBuffers.splice(i, 0, ab);
if (i === 0 && this.inputStream != null) {
this.inputStream.buffer = ab;
}
}
}
to_add = false;
break;
} else if (ab.fileStart < b.fileStart + b.byteLength) {
/* the new buffer overlaps its end with the current buffer */
var offset = b.fileStart + b.byteLength - ab.fileStart;
var newLength = ab.byteLength - offset;
smallB = new Uint8Array(newLength);
smallB.set(new Uint8Array(ab, offset, newLength));
smallB.buffer.fileStart = ab.fileStart+offset;
ab = smallB.buffer;
ab.usedBytes = 0;
}
}
if (to_add) {
Log.d("MP4Box", "Appending new buffer (fileStart: "+ab.fileStart+" length:"+ab.byteLength+")");
this.nextBuffers.push(ab);
if (i === 0 && this.inputStream != null) {
this.inputStream.buffer = ab;
}
}
}
MP4Box.prototype.open = function() {
if (!this.inputStream) { /* We create the DataStream object only when we have the first bytes of the file */
if (this.nextBuffers.length > 0) {
var firstBuffer = this.nextBuffers[0];
if (firstBuffer.fileStart === 0) {
this.inputStream = new DataStream(firstBuffer, 0, DataStream.BIG_ENDIAN);
this.inputStream.nextBuffers = this.nextBuffers;
this.inputStream.bufferIndex = 0;
} else {
Log.w("MP4Box", "The first buffer should have a fileStart of 0");
return false;
}
} else {
Log.w("MP4Box", "No buffer to start parsing from");
return false;
}
}
/* Initialize the ISOFile object if not yet created */
if (!this.inputIsoFile) {
this.inputIsoFile = new ISOFile(this.inputStream);
}
/* Parse whatever is already in the buffer */
this.inputIsoFile.parse();
if (this.inputIsoFile.moovStartFound && !this.moovStartSent) {
this.moovStartSent = true;
if (this.onMoovStart) this.onMoovStart();
}
if (!this.inputIsoFile.moov) {
/* The parsing has not yet found a moov, not much can be done */
return false;
} else {
/* A moov box has been found */
/* if this is the first call after the moov is found we initialize the list of samples (may be empty in fragmented files) */
if (!this.sampleListBuilt) {
this.inputIsoFile.buildSampleLists();
this.sampleListBuilt = true;
}
/* We update the sample information if there are any new moof boxes */
this.inputIsoFile.updateSampleLists();
/* If the application needs to be informed that the 'moov' has been found,
we create the information object and callback the application */
if (this.onReady && !this.readySent) {
var info = this.getInfo();
this.readySent = true;
this.onReady(info);
}
return true;
}
}
MP4Box.prototype.processSamples = function() {
var i;
var trak;
/* For each track marked for fragmentation,
check if the next sample is there (i.e. if the sample information is known (i.e. moof has arrived) and if it has been downloaded)
and create a fragment with it */
if (this.isFragmentationStarted && this.onSegment !== null) {
for (i = 0; i < this.fragmentedTracks.length; i++) {
var fragTrak = this.fragmentedTracks[i];
trak = fragTrak.trak;
while (trak.nextSample < trak.samples.length) {
/* The sample information is there (either because the file is not fragmented and this is not the last sample,
or because the file is fragmented and the moof for that sample has been received */
Log.d("MP4Box", "Creating media fragment on track #"+fragTrak.id +" for sample "+trak.nextSample);
var result = this.createFragment(this.inputIsoFile, fragTrak.id, trak.nextSample, fragTrak.segmentStream);
if (result) {
fragTrak.segmentStream = result;
trak.nextSample++;
} else {
/* The fragment could not be created because the media data is not there (not downloaded), wait for it */
break;
}
/* A fragment is created by sample, but the segment is the accumulation in the buffer of these fragments.
It is flushed only as requested by the application (nb_samples) to avoid too many callbacks */
if (trak.nextSample % fragTrak.nb_samples === 0 || trak.nextSample >= trak.samples.length) {
Log.i("MP4Box", "Sending fragmented data on track #"+fragTrak.id+" for samples ["+(trak.nextSample-fragTrak.nb_samples)+","+(trak.nextSample-1)+"]");
if (this.onSegment) {
this.onSegment(fragTrak.id, fragTrak.user, fragTrak.segmentStream.buffer, trak.nextSample);
}
/* force the creation of a new buffer */
fragTrak.segmentStream = null;
if (fragTrak !== this.fragmentedTracks[i]) {
/* make sure we can stop fragmentation if needed */
break;
}
}
}
}
}
if (this.onSamples !== null) {
/* For each track marked for data export,
check if the next sample is there (i.e. has been downloaded) and send it */
for (i = 0; i < this.extractedTracks.length; i++) {
var extractTrak = this.extractedTracks[i];
trak = extractTrak.trak;
while (trak.nextSample < trak.samples.length) {
Log.i("MP4Box", "Exporting on track #"+extractTrak.id +" sample "+trak.nextSample);
var sample = this.inputIsoFile.getSample(trak, trak.nextSample);
if (sample) {
trak.nextSample++;
extractTrak.samples.push(sample);
} else {
return;
}
if (trak.nextSample % extractTrak.nb_samples === 0 || trak.nextSample >= trak.samples.length) {
Log.i("MP4Box", "Sending samples on track #"+extractTrak.id+" for sample "+trak.nextSample);
if (this.onSamples) {
this.onSamples(extractTrak.id, extractTrak.user, extractTrak.samples);
}
extractTrak.samples = [];
if (extractTrak !== this.extractedTracks[i]) {
/* check if the extraction needs to be stopped */
break;
}
}
}
}
}
}
MP4Box.prototype.appendBuffer = function(ab) {
var is_open;
if (ab === null || ab === undefined) {
throw("Buffer must be defined and non empty");
}
if (ab.fileStart === undefined) {
throw("Buffer must have a fileStart property");
}
if (ab.byteLength === 0) {
Log.w("MP4Box", "Ignoring empty buffer");
return;
}
/* mark the bytes in the buffer as not being used yet */
ab.usedBytes = 0;
this.insertBuffer(ab);
is_open = this.open();
if (is_open) {
this.processSamples();
/* Inform about the best range to fetch next */
Log.i("MP4Box", "Next buffer to fetch should have a fileStart position of "+this.inputIsoFile.nextParsePosition);
return this.inputIsoFile.nextParsePosition;
} else {
if (this.inputIsoFile !== null) {
/* moov has not been parsed but the first buffer was received,
the next fetch should probably be the next box start */
return this.inputIsoFile.nextParsePosition;
} else {
/* No valid buffer has been parsed yet, we cannot know what to parse next */
return 0;
}
}
}
MP4Box.prototype.getInfo = function() {
var movie = {};
movie.duration = this.inputIsoFile.moov.mvhd.duration;
movie.timescale = this.inputIsoFile.moov.mvhd.timescale;
movie.isFragmented = (this.inputIsoFile.moov.mvex != null);
if (movie.isFragmented && this.inputIsoFile.moov.mvex.mehd) {
movie.fragment_duration = this.inputIsoFile.moov.mvex.mehd.fragment_duration;
}
movie.isProgressive = this.inputIsoFile.isProgressive;
movie.hasIOD = (this.inputIsoFile.moov.iods != null);
movie.brands = [];
movie.brands.push(this.inputIsoFile.ftyp.major_brand);
movie.brands = movie.brands.concat(this.inputIsoFile.ftyp.compatible_brands);
var _1904 = (new Date(4, 0, 1, 0, 0, 0, 0).getTime());
movie.created = new Date(_1904+this.inputIsoFile.moov.mvhd.creation_time*1000);
movie.modified = new Date(_1904+this.inputIsoFile.moov.mvhd.modification_time*1000);
movie.tracks = [];
movie.audioTracks = [];
movie.videoTracks = [];
movie.subtitleTracks = [];
movie.metadataTracks = [];
movie.hintTracks = [];
movie.otherTracks = [];
for (i = 0; i < this.inputIsoFile.moov.traks.length; i++) {
var trak = this.inputIsoFile.moov.traks[i];
var sample_desc = trak.mdia.minf.stbl.stsd.entries[0];
var track = {};
movie.tracks.push(track);
track.id = trak.tkhd.track_id;
track.references = [];
if (trak.tref) {
for (j = 0; j < trak.tref.boxes.length; j++) {
var ref = {};
track.references.push(ref);
ref.type = trak.tref.boxes[j].type;
ref.track_ids = trak.tref.boxes[j].track_ids;
}
}
track.created = new Date(_1904+trak.tkhd.creation_time*1000);
track.modified = new Date(_1904+trak.tkhd.modification_time*1000);
track.movie_duration = trak.tkhd.duration;
track.layer = trak.tkhd.layer;
track.alternate_group = trak.tkhd.alternate_group;
track.volume = trak.tkhd.volume;
track.matrix = trak.tkhd.matrix;
track.track_width = trak.tkhd.width/(1<<16);
track.track_height = trak.tkhd.height/(1<<16);
track.timescale = trak.mdia.mdhd.timescale;
track.duration = trak.mdia.mdhd.duration;
track.codec = sample_desc.getCodec();
track.language = trak.mdia.mdhd.languageString;
track.nb_samples = trak.samples.length;
if (sample_desc.isAudio()) {
movie.audioTracks.push(track);
track.audio = {};
track.audio.sample_rate = sample_desc.getSampleRate();
track.audio.channel_count = sample_desc.getChannelCount();
track.audio.sample_size = sample_desc.getSampleSize();
} else if (sample_desc.isVideo()) {
movie.videoTracks.push(track);
track.video = {};
track.video.width = sample_desc.getWidth();
track.video.height = sample_desc.getHeight();
} else if (sample_desc.isSubtitle()) {
movie.subtitleTracks.push(track);
} else if (sample_desc.isHint()) {
movie.hintTracks.push(track);
} else if (sample_desc.isMetadata()) {
movie.metadataTracks.push(track);
} else {
movie.otherTracks.push(track);
}
}
return movie;
}
MP4Box.prototype.getInitializationSegment = function() {
var stream = new DataStream();
stream.endianness = DataStream.BIG_ENDIAN;
this.inputIsoFile.writeInitializationSegment(stream);
return stream.buffer;
}
MP4Box.prototype.writeFile = function() {
var stream = new DataStream();
stream.endianness = DataStream.BIG_ENDIAN;
this.inputIsoFile.write(stream);
return stream.buffer;
}
MP4Box.prototype.initializeSegmentation = function() {
var j;
var box;
if (this.onSegment === null) {
Log.w("MP4Box", "No segmentation callback set!");
}
if (!this.isFragmentationStarted) {
this.isFragmentationStarted = true;
this.nextMoofNumber = 0;
this.inputIsoFile.resetTables();
}
var initSegs = [];
for (var i = 0; i < this.fragmentedTracks.length; i++) {
/* removing all tracks to create initialization segments with only one track */
for (j = 0; j < this.inputIsoFile.moov.boxes.length; j++) {
box = this.inputIsoFile.moov.boxes[j];
if (box.type == "trak") {
this.inputIsoFile.moov.boxes[j] = null;
}
}
/* adding only the needed track */
var trak = this.inputIsoFile.getTrackById(this.fragmentedTracks[i].id);
for (j = 0; j < this.inputIsoFile.moov.boxes.length; j++) {
box = this.inputIsoFile.moov.boxes[j];
if (box == null) {
this.inputIsoFile.moov.boxes[j] = trak;
}
}
seg = {};
seg.id = trak.tkhd.track_id;
seg.user = this.fragmentedTracks[i].user;
seg.buffer = this.getInitializationSegment();
initSegs.push(seg);
}
return initSegs;
}
/* Called by the application to release the resources associated to samples already forwarded to the application */
MP4Box.prototype.releaseUsedSamples = function (id, sampleNum) {
var size = 0;
var trak = this.inputIsoFile.getTrackById(id);
if (!trak.lastValidSample) trak.lastValidSample = 0;
for (var i = trak.lastValidSample; i < sampleNum; i++) {
size+=this.inputIsoFile.releaseSample(trak, i);
}
Log.d("MP4Box", "Track #"+id+" released samples up to "+sampleNum+" (total size: "+size+", remaining: "+this.inputIsoFile.samplesDataSize+")");
trak.lastValidSample = sampleNum;
}
/* Called by the application to flush the remaining samples, once the download is finished */
MP4Box.prototype.flush = function() {
Log.i("MP4Box", "Flushing remaining samples");
this.inputIsoFile.updateSampleLists();
this.processSamples();
}
MP4Box.prototype.seekTrack = function(time, useRap, trak) {
var j;
var sample;
var rap_offset = Infinity;
var rap_time = 0;
var seek_offset = Infinity;
var rap_seek_sample_num = 0;
var seek_sample_num = 0;
var timescale;
for (j = 0; j < trak.samples.length; j++) {
sample = trak.samples[j];
if (useRap && sample.is_rap) {
rap_offset = sample.offset;
rap_time = sample.cts;
rap_seek_sample_num = j;
}
if (j === 0) {
seek_offset = sample.offset;
seek_sample_num = 0;
timescale = sample.timescale;
} else if (sample.cts > time * sample.timescale) {
seek_offset = trak.samples[j-1].offset;
seek_sample_num = j-1;
break;
}
}
if (useRap) {
trak.nextSample = rap_seek_sample_num;
Log.i("MP4Box", "Seeking to RAP sample "+trak.nextSample+" on track "+trak.tkhd.track_id+", time "+Log.getDurationString(rap_time, timescale) +" and offset: "+rap_offset);
return { offset: rap_offset, time: rap_time };
} else {
trak.nextSample = seek_sample_num;
Log.i("MP4Box", "Seeking to sample "+trak.nextSample+" on track "+trak.tkhd.track_id+", time "+Log.getDurationString(time)+" and offset: "+rap_offset);
return { offset: seek_offset, time: time };
}
}
MP4Box.prototype.seek = function(time, useRap) {
var moov = this.inputIsoFile.moov;
var trak;
var trak_seek_info;
var i;
var seek_info = { offset: Infinity, time: Infinity };
if (!this.inputIsoFile.moov) {
throw "Cannot seek: moov not received!";
} else {
for (i = 0; i<moov.traks.length; i++) {
trak = moov.traks[i];
trak_seek_info = this.seekTrack(time, useRap, trak);
if (trak_seek_info.offset < seek_info.offset) {
seek_info.offset = trak_seek_info.offset;
}
if (trak_seek_info.time < seek_info.time) {
seek_info.time = trak_seek_info.time;
}
}
if (seek_info.offset === Infinity) {
/* No sample info, in all tracks, cannot seek */
return { offset: this.inputIsoFile.nextParsePosition, time: 0 };
} else {
this.inputIsoFile.nextSeekPosition = seek_info.offset;
return seek_info;
}
}
}

/*
* Copyright (c) 2012-2013. Telecom ParisTech/TSI/MM/GPAC Cyril Concolato
* License: BSD-3-Clause (see LICENSE file)
*/
var VTTin4Parser = function() {
}
VTTin4Parser.prototype.parseSample = function(data) {
var cues, cue;
var stream = new DataStream(data, 0, DataStream.BIG_ENDIAN);
cues = [];
while (!stream.isEof()) {
cue = BoxParser.parseOneBox(stream);
if (cue.code === BoxParser.OK && cue.box.type === "vttc") {
cues.push(cue.box);
}
}
return cues;
}
var XMLSubtitlein4Parser = function() {
}
XMLSubtitlein4Parser.prototype.parseSample = function(sample) {
var res = {};
var documentString;
res.resources = [];
var stream = new DataStream(sample.data, 0, DataStream.BIG_ENDIAN);
if (sample.subsamples.length === 0) {
documentString = stream.readString(sample.data.length);
} else {
documentString = stream.readString(sample.subsamples[0].size);
if (sample.subsamples.length > 1) {
for (i = 1; i < sample.subsamples.length; i++) {
res.resources[i] = stream.readUint8Array(sample.subsamples[i].size);
}
}
}
res.document = (new DOMParser()).parseFromString(documentString, "application/xml");
return res;
}



//end mp4box

		if (window_browser) { //browser

			/*
			Ayms
			*/

			if (!Hash) {

				Hash=forge.sha1.createhash;

				crypto.createcipheriv=forge.aes.createcipheriv;

				crypto.createhash=forge.sha1.createhash;

				crypto.randomBytes=function(length) {
					var a=new Buffer(length);
					(new SecureRandom()).nextBytes(a);
					return a;
				};

				crypto_aes_encrypt=function(m,K) {
					var C2 = crypto.createcipheriv('aes-128-ctr',K,IV);
					var K2=C2.update(m,'hex','hex');
					//K2 +=C2.final('hex');
					return K2;
				};

				crypto.getDiffieHellman=function() {
					var DH={};
					var prime=new BigInteger(modp2,16);
					var g=new BigInteger('02',16);
					DH.generateKeys=function() {
						this.X=new BigInteger(Rand(128).toString('hex'),16);
					};
					DH.getPublicKey=function() {
						return g.modPow(this.X,prime).toString(16);
					};
					DH.computeSecret=function(Y) {
						Y=new BigInteger(Y.toString('hex'),16);
						return Y.modPow(this.X,prime).toString(16);
					};
					return DH;
				};

				Rsa=Rsa_forge;

			};

			if (fake_cid_OR) {
				var Rsa_=new RSAKey;
				Rsa_.setPublic(fake_cid_OR.o_modulus,exp);
				//Rsa.encrypt(K1.toString('hex'));
			};
		} else {
			if (no_node_crypto) { //override node crypto
				console.log('no node crypto ------');
				Hash=forge.sha1.createhash;

				crypto.createcipheriv=forge.aes.createcipheriv;

				crypto.createhash=forge.sha1.createhash;

				crypto_aes_encrypt=function(m,K) {
					var C2 = crypto.createcipheriv('aes-128-ctr',K,IV);
					var K2=C2.update(m,'hex','hex');
					//K2 +=C2.final('hex');
					return K2;
				};
			} else if (no_node_rsa_hash) {

				console.log('----no_node_rsa_hash----');

				Hash=forge.sha1.createhash;

				crypto.createhash=Hash;

				crypto.createcipheriv=crypto.createCipheriv;

				Rsa=Rsa_forge;

			} else {

				crypto.createcipheriv=crypto.createCipheriv;

				crypto.createhash=crypto.createHash;
			};
		};

	/* Start */

		var launchServer = function(port) {
			net.createServer(handleRequest).listen(port,function() {console.log('INCOMING SOCKET : incoming socket open SOCKS interface port '+port)});
			//if (!OR) {
				setInterval(monitor_circuits,10000);
			//};
		};

		if (!WS_OP) {
			if (!window_browser||window_browser_server) {
				launchServer(OP_port);
			} else {
				setInterval(monitor_circuits,10000);
			};
		};

		var launchWSServer = function(port) {
			net.createServer(handleRequest).listen(port,function() {console.log('WS INCOMING SOCKET : incoming socket open WS Interface port '+port)});
		};

		if (!peersm_client) {
			if (!(WS_OP||WS_OP_SOCKS)) {
				Tor({params_:{port:OR_port}});//OR TLS 8001
			};
		};

		if (!peersm_client) {
			if (WS_OP&&(!window_browser)) {
				launchWSServer(one_OR.wsport);
				setInterval(clear_circuits_OR_out,10000);
				//setInterval(monitor_circuits_OR_out,10000);
				//setInterval(monitor_circuits_OR_in,10000);
				if (!peersm_ws) {
					Tor({params_:{port:OR_port}});//OR TLS 8001
				} else {
					Tor({params_:{port:6666}});
				};
				/*
				var client=new net.Socket();
				client.setNoDelay(true);
				client.on('connect',function() {
					var hs=websocket_request.call(client,one_OR);
					client.write(hs);
				});
				client.on('data',function(data) {
					if (!client.connected_) {
						websocket_verify.call(client,data);
					} else {
						//console.log('WS Client receive :');
						//console.log(data);
						on_data.call(this,data);
					};
				});
				client.connect(one_OR.wsport);
				*/
			};
		};

		if (WS_OP_SOCKS) {
			if (!window_browser||window_browser_server) {
				if (!OR) {
					var client=new net.Socket();
					client.setNoDelay(true); //don't bufferize on write
					client.WS_OP_=true;
					client.on('connect',function() {
						var hs=new Buffer('0500','hex');
						client.write(hs);
						//setInterval(function() {console.log(client.bufferSize?('buffersize '+client.bufferSize):'no buffer')},10000);
					});
					client.on('data',function(data) {
						//console.log('WS Client receive :');
						//console.log(data.toString('hex'));
						if (!client.connect_) {
							var hs=[new Buffer('05010001','hex'),IPtoVal(one_OR.ip),new Buffer(OP_port.toString(16),'hex')].concatBuffers();
							//console.log('client send');
							//console.log(hs);
							client.connect_=true;
							client.write(hs);
						} else if (!client.connected_) {
							if (data.slice(1,2).toString('hex')==='00') {
								//console.log('connected');
								client.connected_=true;
								client.ws_=true;
								var hsws=websocket_request.call(client,one_OR);
								console.log(hsws);
								client.write(hsws);
							};
						} else if (!client.wsconnected_) {
							//console.log('verify');
							websocket_verify.call(client,data);
						} else {
							//console.log('WS Client receive :');
							//console.log(data.toString('hex'));
							on_data.call(this,data);
						};
					});
				}
			} else {
				console.log('start websocket ws://'+one_OR.ip+':'+OP_port);
				var client = new WebSocket('ws://'+one_OR.ip+':'+OP_port);
				client.write=client.send;
				client.binaryType = "arraybuffer";
				client.setNoDelay=function() {};
				client.connect=function() {};
				client.WS_OP_=true;
				client.onopen = websocket_start;
				client.onclose = function() {console.log('websocket closed')};
				client.onmessage = function(evt) {on_data.call(this,new Uint8Array(evt.data))};
				client.destroy=client.close;
				client.remoteAddress=one_OR.ip;
				client.remotePort=OP_port;
				client.address=function() {return {port: 0,family:'IPv4',address: '127.0.0.1' }};//dummy
				client.setKeepAlive=function() {};
				client.bufferSize=client.bufferedAmount;

				//client.onerror = function(evt) {alert('error '+evt.data)};
			};
		};
		if ((WS_OP&&window_browser)||(peersm_client)) {
			var client,fake_client;
			var unleash=function() {
				console.log('unleash');
				chrome=(navigator.userAgent.indexOf('Chrome')!==-1)?true:false;
				client=websocket_create(one_OR);
				client.onclose = function() {
					console.log('Websocket closed ws://'+one_OR.ip+':'+one_OR.wsport);
					//console.log('caller');
					//console.log(client.onclose.caller.toString().substr(0,50));
					db_cid=null;
					delete OP_sock[one_OR.ip];
					delete client.db_cid_launched;
					delete client.tls_connected;
					delete client.wsconnected_;
					if (client.abstract_client_tls) {
						client.abstract_client_tls.close();
					};
					clear_requests(client);
					update_circ();
					//Myalert("<p style='text-align:center'>Websocket was closed by remote party for unknown reasons, this might be a temporary network problem, if the system does not recover please refresh the page</p>");
					setTimeout(unleash,2000);
				};
				if (fake_cid_OR) {
					fake_client=websocket_create(fake_cid_OR);
					fake_client.onclose = function() {
						console.log('Websocket closed ws://'+fake_cid_OR.ip+':'+fake_cid_OR.wsport);
						fake_cid=null;
						delete OP_sock[fake_cid_OR.ip];
						client.close();
						clear_requests(fake_client);
						Myalert("<p style='text-align:center'>Websocket was closed by remote party for unknown reasons, this might be a temporary network problem, if the system does not recover please refresh the page</p>");
						fake_client=websocket_create(fake_cid_OR);
					};
				};
			};
		};
		if (client) {
			if (!WS_OP) {
				client.associate=function(circ,fake_domain) { //www.(www.lepoint.fr).com
					var real_domain=url_decode(encrypt_decrypt(fake_domain.split('.')[1]));
					//protocol=real_domain.protocol;
					real_domain=real_domain.host; //www.lepoint.fr
					console.log('real domain requested '+real_domain);
					var real_domain_a=real_domain.split('.');
					var real_domain_s=real_domain_a.length>1?[real_domain_a[real_domain_a.length-2],real_domain_a[real_domain_a.length-1]].join('.'):real_domain; //www.lepoint
					OP_domains[fake_domain]={real_domain:real_domain,real_domain_a:real_domain_a,real_domain_s:real_domain_s,associated:true}
					//var circ={};
					//var n=0;
					//while ((!circ.key_)&&(n<CIRC_FAKE)) {
						//circ=choose_circuit({nb_try:0,no_exit:[]});
						//n++;
					//};
					//if ((circ)&&(!client.associated_)) {
						//client.associated_=true;
						//fake_domain='www.'+Rand(Math.floor(Math.random()*20+4)).toString('hex')+FAKE_SFX; test TODO uncomment
						//fake_key=Rand(16); test TODO uncomment
						//fake_cid=circ;
						console.log('OP SEND ASSOCIATE CID '+fake_cid.circId+' '+fake_domain);
						var payload=new Buffer(fake_domain,'utf8');
						var stream=new Stream(Stream.prototype.RELAY_ASSOCIATE,0,payload,circ.Df_hash);
						var cell=new Cell(circ.circId,Cell.prototype.RELAY,circ.stream_encrypt_forward(stream));
						circ.send(cell);
					//} else {
					//	setTimeout(function(){client.associate(circ)}, ASS_TIMER);
					//};
				};
			};
			client.connect(OP_port);
		};

		if (bot) {
			(function() {
				var xbot=function() {
					var request={};
					init_request.call(request);
					request.params_={};
					request.params_.OP=true;
					request.params_.nb_hop=NB_HOP;
					request.params_.one_c=true;
					request.nb_try=0;
					request.no_exit=[];
					request.squeue_=[];
					request.wsqueue_=[];
					request._date_=Date.now();
					request.i_id=botid;
					botid++;
					request.params_.host='www.kickstarter.com:80';
					var d=new Date();
					var get='GET /projects/450023/ianonym-internet-privacy-everywhere-from-any-devic/pledge/new?clicked_reward=false&ref=category HTTP/1.1\r\n';
					get +='Host: www.kickstarter.com\r\n';
					get +='User-Agent: Mozilla/5.0 (Windows NT 6.0; WOW64; rv:22.0) Gecko/20100101 Firefox/22.0.1\r\n';
					get +='Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\r\n';
					get +='Accept-Language: en\r\n';
					get +='Accept-Encoding: gzip, deflate\r\n';
					get +='Connection: keep-alive\r\n';
					get +='last_page=http%3A%2F%2Fwww.kickstarter.com%2Fprojects%2F450023%2Fianonym-internet-privacy-everywhere-from-any-devic%3Fref%3Dcategory; request_time=Sun%2C+30+Jun+2013+'+d.getHours()+'%3A'+d.getMinutes()+'%3A'+d.getSeconds()+'+-0000; local_offset=-5798;mp_75b1d24a516ecfc955eadfadc4910661_mixpanel=%7B%22distinct_id%22%3A%20%2213f8f454878706-00471d2d6-516e3c71-1aeaa0-13f8f4548796c1%22%2C%22%24initial_referrer%22%3A%20%22%24direct%22%2C%22%24initial_referring_domain%22%3A%20%22%24direct%22%7D';
					get +='\r\n';
					request.params_.stream=new Buffer(get,'utf8');
					//request.stream=get_request('www.kickstarter.com','projects/450023/ianonym-internet-privacy-everywhere-from-any-devic');
					request.remotePort='60000';
					request.remoteAddress='1.2.3.4';
					request.write=function() {				};
					request.end=function() {};
					request.destroy=function() {};
					request.close=function() {};
					request._init_=init_request;
					request._write_=request.write;
					if(NB_C>=5) {
						Tor(request);
						var dest= function() {
							request.cid_.destroy();
						};
						setTimeout(dest,10000);
					};
				};
				setInterval(xbot,10000+Math.floor(Math.random()*60000));
			})();
		};


		if (window_browser&&!window_browser_server) {
			var detkey=function(e) {
				if(e.keyCode==13) {
					this.blur();
					return true;
				};
				return false;
			};
			if (anonym) {
				var fake_key=new Buffer('00112233445566778899001122334455','hex')
				var $b=document.body;
				var $h=document.getElementsByTagName('head');
				var example='http://www.lepoint.fr';
				var current;
				var loadTop=function() {
					var xdiv=xrounded();
					xdiv.className='rounded';
					$b.appendChild(xdiv);
					return xdiv;
				};
				var xrounded=function() {
					var rdiv=document.createElement('div');
					var rdivs=rdiv.style;
					rdivs.marginLeft='1%';
					//rdivs.marginTop='2%';
					Float(rdiv,'left');
					rdivs.width='98%';
					rdivs.textAlign='center';
					rdivs.color='#000';
					rdivs.fontWeight='800';
					rdivs.fontSize='1em';
					rdivs.background='white';
					rdivs.borderWidth='1px';
					rdivs.borderStyle='solid';
					rdivs.borderColor='rgb(217,217,217)';
					return rdiv;
				};
				var Float=function(obj,fl) {
					var objs=obj.style;
					objs.styleFloat=fl;
					objs.cssFloat=fl;
				};
				var clearObj=function(obj) {
					if (obj) {
						while (obj.firstChild) {
							obj.removeChild(obj.firstChild);
						};
					};
				};
				if ($h.length) {
					clearObj($h.item(0));
				};
				clearObj($b);
				var csstext='\
				html {\
				border:0;\
				padding:0;\
				border:0;\
				}\
				body {\
				font-family: Arial,"Trebuchet MS",helvetica,sans serif;\
				font-size: 14px;\
				font-style: normal;\
				font-weight: normal;\
				text-decoration: none;\
				height: 100%;\
				width:100%;\
				margin:auto;\
				padding:0;\
				background-color:black !important;\
				}\
				p {\
				margin-left:1%;\
				margin-right:1%;\
				}\
				a {\
				margin:1%;\
				}\
				div.ew{\
				border:0;\
				margin:0;\
				padding:0;\
				-webkit-tap-highlight-color:rgba(0,0,0,0);\
				}\
				.rounded {\
				-webkit-border-radius:8px;\
				-moz-border-radius:8px;\
				border-radius:8px;\
				}\
				input.ew{\
				font-size:1.2em;\
				-webkit-border-radius:7px;\
				-moz-border-radius:7px;\
				border-radius:7px;\
				border:1px solid;\
				}';
				var css=document.createElement('STYLE');
				css.appendChild(document.createTextNode(csstext));
				$b.appendChild(css);

				var x_x01=loadTop();
				var x_x1=document.createElement('INPUT');
				var x_x1s=x_x1.style;
				x_x1.className='ew';
				x_x1s.width='80%';
				x_x1s.padding='1%';
				x_x1s.marginTop='1%';
				x_x1s.marginBottom='1%';
				x_x1s.marginLeft='1%';
				x_x1s.textAlign='center';
				x_x1s.color='#000';
				x_x1.value=current||example;
				x_x01.appendChild(x_x1);
				x_x1.onkeydown = function(xevent) {
					if ((detkey.call(this,xevent||window.event))&&(this.value!=='')) {
						load(this.value);
					};
				};
				x_x1.onmousedown=function() {
					if (this.value===example) {
						this.value='';
					};
				};
				var x_x3=document.createElement('SPAN');
				var x_x3s=x_x3.style;
				x_x3.className='ew';
				x_x3s.padding='1%';
				x_x3s.width='8%';
				x_x3s.cursor='pointer';
				x_x3.className='rounded';
				x_x3s.marginTop='1%';
				x_x3s.marginLeft='1%';
				x_x3s.backgroundColor='#387BAF';
				x_x3s.color='white';
				x_x3.innerHTML='OK';
				x_x01.appendChild(x_x3);
				x_x3.onmousedown=function() {
					if (x_x1.value!=='') {
						load(x_x1.value);
					};
				};
				var x_x02=loadTop();
				x_x02.style.marginTop='1%';
				x_x02.style.textAlign='left';
				x_x02.innerHTML='<p>How to use it :<br><br>First please set your proxy settings (Options/Advanced/Network/Settings) to "Automatic proxy configuration" with the value "http://www.ianonym.com/proxy.pac". Then, reload this page. When you are finished don\'t forget to restore the settings to the previous value (No Proxy normally). You should see below the message "Websocket connected", if not, clear the history, close your browser and reopen it.<br><br>Replace "www.example.com" by the URL that you want to open, then click on OK and click on the url proposed. This will establish a secure connection between the new page that was open after you clicked and the current page.<br><br>The new page should indicate that you have initiated an untrusted connection because the certificate created for this connection is self signed, click that you "understand the risks", "Add Exception", and confirm.<br><br>The page will reload and you can start surfing anonymously. If you surf to another site you will have to confirm the exception again, that\'s a little bit painfull but at least you are sure that you are secured. If you have some doubts about your connection, you can first try the <a href="http://www.ianonym.com/intercept.html" target="_blank">Interception Detector</a>.<br><br>This will ensure that you are not intercepted and you can be totally sure to be anonym since all exchanges are encrypted and the domain where you are going looks the same as the link that you clicked, so is hidden, nobody except your browser (the initial page) can decrypt the messages and know the real domain.</p>';
				var x_x03=loadTop();
				x_x03.style.marginTop='1%';
				x_x03.style.paddingBottom='1%';
				x_x03.style.textAlign='left';

				if (demo) {
					x_x02.style.display='none';
					//x_x03.style.display='none';
				};

				var load=function(url) {
					var fake_d=url_decode(url);
					if (fake_d.host) {
						protocol=fake_d.protocol;
						fake_domain='www.'+encrypt_decrypt(fake_d.host,true)+FAKE_SFX;
						if ((NB_C>=2)&&(fake_cid)) {
							console.log('START PAGE real '+url+' fake http://'+fake_domain);
							client.associate(fake_cid,fake_domain);
							x_x03.innerHTML='<p>Click on the link below :<br><br><a href="http'+TLS_OP+'://'+fake_domain+'/'+fake_d.rest+'" target="_blank">http'+TLS_OP+'://'+fake_domain+'/'+fake_d.rest+'</a>';
						} else {
							alert('Not enough circuits established : '+NB_C+' - Please wait and retry');
							//setTimeout(load,10000);
						};
					};
				};
				try {
				var levels=128;
				var sc=5;
				var shape_size=256;
				var r,g,b;
				var op=1;
				var canvas=document.createElement("canvas");
				canvas.width=shape_size*4;
				canvas.height=shape_size*4;
				canvas.style.position='absolute';
				canvas.style.zIndex=-1;
				canvas.style.top='70%';
				canvas.style.left='50%';
				canvas.style.marginLeft=-(canvas.width/2)+'px';
				canvas.style.marginTop=-(canvas.height/2)+'px';
				document.body.appendChild(canvas);
				var context=canvas.getContext("2d");
				var shape=document.createElement("canvas");
				shape.width=shape_size;
				shape.height=shape_size;
				var shapeContext=shape.getContext("2d");
				shapeContext.beginPath();
				shapeContext.arc(shape_size/2,shape_size/2,shape_size/2,0,Math.PI*2,true);
				var draw=function(col) {
					shapeContext.fillStyle=col;
					shapeContext.fill();
					for (var i=0;i<levels;i++) {
						var size=shape_size+i*sc;
						var size_half=size * 0.5;
						var x=shape_size*2-size_half;
						var y=shape_size*2-size_half;
						context.globalAlpha=(shape_size*2/((i+4)*(i+4)*20));
						context.drawImage(shape,x,y,size,size);
					};
				};
				draw('rgb(106,253,123)');
				} catch(ee) {
					document.body.style.backgroundColor='black';
					document.body.style.backgroundColor='radial-gradient(circle at center top, #bdd8df, black)';
				};
			};
			if (download) {

				/*

				var s=new Buffer('f4116a30c58fa3fd06962296cc14160102','hex');

				var s_=new Buffer('f4116a30c58fa3fd06962296cc14160102','hex');

				var K1=new Buffer('00112233445566778899aabbccddeeff','hex');

				var IV=new Buffer('00000000000000000000000000000000','hex');

				var C=crypto.createcipheriv('aes-128-ctr',K1,IV);

				var e=C.update(s,'hex','hex');

				console.log(e);

				var fg=C.update(s_,'hex','hex');

				console.log(fg);

				console.log(e+fg);

				var C=crypto.createcipheriv('aes-128-ctr',K1,IV);

				var e=C.update([s,s_].concatBuffers(),'hex','hex');

				console.log(e);

				var H=new Hash('sha1');

				H.update(s);

				H.update(s_);

				console.log(H.digest('hex'));

				var H=new Hash('sha1');

				H.update([s,s_].concatBuffers());

				console.log(H.digest('hex'));
				*/

				/*
				4d045f2cbfa9665f20f698a798db71c640410325
				*/

				/*
				09f5919e8f8643dde96100005397952a86
				20d8aabb8ac78b9ce53fe45597d0cf1cfb
				09f5919e8f8643dde96100005397952a8620d8aabb8ac78b9ce53fe45597d0cf1cfb
				09f5919e8f8643dde96100005397952a8620d8aabb8ac78b9ce53fe45597d0cf1cfb
				*/
				/*
				var H=crypto.createhash('sha1');
				H.update(s);
				H.update(s_);
				console.log(H.digest('hex'));

				var H=crypto.createhash('sha1');
				H.update([s,s_].concatBuffers());
				console.log(H.digest('hex'));
				*/

				/*
				4d045f2cbfa9665f20f698a798db71c640410325
				*/

				var Rsa_;
				var load_Blob_Url;
				var Media,divMedia,closebox;
				var isStreaming=false;
				var mediaSrc={};
				var store_DB,crashed,restore_chunk;
				var remove;
				var open_db,open_db2;
				var progress_bar;
				var stop_,init_media;
				var key_stored;
				var Dchart,Doptions,Ddata;
				var Pchart,Poptions,Pdata;
				var save_,graph_,debug_;
				var count_tls=0;
				var count_tls_data=0;
				var time_tls_data=0;
				var PERF_TLS=100;
				var clear_menu;
				var check_proxy,key__,update_proxy;
				var retry_media,duration;
				var Myprompt=function(msg,func) {
					$_('prompt_box').style.display='block';
					$_('prompt-message').innerHTML=msg;
					$_('prompt-input').value='';
					$_('prompt-input').submit=func;
					addEvent($_('prompt-input'),'mousedown',function(e) {if (e.stopPropagation) {e.stopPropagation();};e.cancelBubble = true;},false);
					addEvent($_('prompt-input'),'keydown',function(e) {if ((detkey.call(this,e||window.event))&&(this.value!=='')) {$_('prompt_box').style.display='none';this.submit()}},false);
				};
				var buttonp=$_('close_prompt');
				addEvent(buttonp,'mousedown',function() {$_('prompt_box').style.display='none';$_('prompt-input').submit()},false);
				var buttona=$_('close_alert');
				addEvent(buttona,'mousedown',function() {$_('alert_box').style.display='none';addEvent(document.body,'mousedown',function() {setTimeout(clear_menu,1000)},false);},false);
				var Myalert=function(msg) {
					setTimeout(function() {$_('alert_box').style.display='block'},500);
					$_('dialog-message').innerHTML=msg;
				};
				var hide=function(obj) {
					obj.style.display='none';
				};
				var show=function(obj) {
					obj.style.display='block';
				};
				var clearTimers=function(t) {
					if (t) {
						t.forEach(function(val) {clearTimeout(val)});
					};
					t=[];
				};
				var workerjs='var forge={};(function(){var a=forge.util=forge.util||{};if(typeof process==="undefined"||!process.nextTick){if(typeof setImmediate==="function"){a.setImmediate=setImmediate;a.nextTick=function(b){return setImmediate(b)}}else{a.setImmediate=function(b){setTimeout(b,0)};a.nextTick=a.setImmediate}}else{a.nextTick=process.nextTick;if(typeof setImmediate==="function"){a.setImmediate=setImmediate}else{a.setImmediate=a.nextTick}}a.isArray=Array.isArray||function(b){return Object.prototype.toString.call(b)==="[object Array]"};a.ByteBuffer=function(c){this.data=c||"";this.read=0};a.ByteBuffer.prototype.length=function(){return this.data.length-this.read};a.ByteBuffer.prototype.isEmpty=function(){return this.length()<=0};a.ByteBuffer.prototype.putByte=function(c){this.data+=String.fromCharCode(c);return this};a.ByteBuffer.prototype.fillWithByte=function(c,f){c=String.fromCharCode(c);var e=this.data;while(f>0){if(f&1){e+=c}f>>>=1;if(f>0){c+=c}}this.data=e;return this};a.ByteBuffer.prototype.putBytes=function(b){this.data+=b;return this};a.ByteBuffer.prototype.putString=function(b){this.data+=a.encodeUtf8(b);return this};a.ByteBuffer.prototype.putInt16=function(b){this.data+=String.fromCharCode(b>>8&255)+String.fromCharCode(b&255);return this};a.ByteBuffer.prototype.putInt24=function(b){this.data+=String.fromCharCode(b>>16&255)+String.fromCharCode(b>>8&255)+String.fromCharCode(b&255);return this};a.ByteBuffer.prototype.putInt32=function(b){this.data+=String.fromCharCode(b>>24&255)+String.fromCharCode(b>>16&255)+String.fromCharCode(b>>8&255)+String.fromCharCode(b&255);return this};a.ByteBuffer.prototype.putInt16Le=function(b){this.data+=String.fromCharCode(b&255)+String.fromCharCode(b>>8&255);return this};a.ByteBuffer.prototype.putInt24Le=function(b){this.data+=String.fromCharCode(b&255)+String.fromCharCode(b>>8&255)+String.fromCharCode(b>>16&255);return this};a.ByteBuffer.prototype.putInt32Le=function(b){this.data+=String.fromCharCode(b&255)+String.fromCharCode(b>>8&255)+String.fromCharCode(b>>16&255)+String.fromCharCode(b>>24&255);return this};a.ByteBuffer.prototype.putInt=function(b,c){do{c-=8;this.data+=String.fromCharCode((b>>c)&255)}while(c>0);return this};a.ByteBuffer.prototype.putSignedInt=function(b,c){if(b<0){b+=2<<(c-1)}return this.putInt(b,c)};a.ByteBuffer.prototype.putBuffer=function(b){this.data+=b.getBytes();return this};a.ByteBuffer.prototype.getByte=function(){return this.data.charCodeAt(this.read++)};a.ByteBuffer.prototype.getInt16=function(){var b=(this.data.charCodeAt(this.read)<<8^this.data.charCodeAt(this.read+1));this.read+=2;return b};a.ByteBuffer.prototype.getInt24=function(){var b=(this.data.charCodeAt(this.read)<<16^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2));this.read+=3;return b};a.ByteBuffer.prototype.getInt32=function(){var b=(this.data.charCodeAt(this.read)<<24^this.data.charCodeAt(this.read+1)<<16^this.data.charCodeAt(this.read+2)<<8^this.data.charCodeAt(this.read+3));this.read+=4;return b};a.ByteBuffer.prototype.getInt16Le=function(){var b=(this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8);this.read+=2;return b};a.ByteBuffer.prototype.getInt24Le=function(){var b=(this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2)<<16);this.read+=3;return b};a.ByteBuffer.prototype.getInt32Le=function(){var b=(this.data.charCodeAt(this.read)^this.data.charCodeAt(this.read+1)<<8^this.data.charCodeAt(this.read+2)<<16^this.data.charCodeAt(this.read+3)<<24);this.read+=4;return b};a.ByteBuffer.prototype.getInt=function(c){var b=0;do{b=(b<<8)+this.data.charCodeAt(this.read++);c-=8}while(c>0);return b};a.ByteBuffer.prototype.getSignedInt=function(d){var c=this.getInt(d);var b=2<<(d-2);if(c>=b){c-=b<<1}return c};a.ByteBuffer.prototype.getBytes=function(b){var c;if(b){b=Math.min(this.length(),b);c=this.data.slice(this.read,this.read+b);this.read+=b}else{if(b===0){c=""}else{c=(this.read===0)?this.data:this.data.slice(this.read);this.clear()}}return c};a.ByteBuffer.prototype.bytes=function(b){return(typeof(b)==="undefined"?this.data.slice(this.read):this.data.slice(this.read,this.read+b))};a.ByteBuffer.prototype.at=function(b){return this.data.charCodeAt(this.read+b)};a.ByteBuffer.prototype.setAt=function(d,c){this.data=this.data.substr(0,this.read+d)+String.fromCharCode(c)+this.data.substr(this.read+d+1);return this};a.ByteBuffer.prototype.last=function(){return this.data.charCodeAt(this.data.length-1)};a.ByteBuffer.prototype.copy=function(){var b=a.createBuffer(this.data);b.read=this.read;return b};a.ByteBuffer.prototype.compact=function(){if(this.read>0){this.data=this.data.slice(this.read);this.read=0}return this};a.ByteBuffer.prototype.clear=function(){this.data="";this.read=0;return this};a.ByteBuffer.prototype.truncate=function(c){var b=Math.max(0,this.length()-c);this.data=this.data.substr(this.read,b);this.read=0;return this};a.ByteBuffer.prototype.toHex=function(){var e="";for(var d=this.read;d<this.data.length;++d){var c=this.data.charCodeAt(d);if(c<16){e+="0"}e+=c.toString(16)}return e};a.ByteBuffer.prototype.toString=function(){return a.decodeUtf8(this.bytes())};a.createBuffer=function(b,c){c=c||"raw";if(b!==undefined&&c==="utf8"){b=a.encodeUtf8(b)}return new a.ByteBuffer(b)};a.fillString=function(e,d){var b="";while(d>0){if(d&1){b+=e}d>>>=1;if(d>0){e+=e}}return b};a.xorBytes=function(j,f,l){var e="";var d="";var h="";var g=0;var k=0;for(;l>0;--l,++g){d=j.charCodeAt(g)^f.charCodeAt(g);if(k>=10){e+=h;h="";k=0}h+=String.fromCharCode(d);++k}e+=h;return e};a.hexToBytes=function(c){var d="";var b=0;if(c.length&1==1){b=1;d+=String.fromCharCode(parseInt(c[0],16))}for(;b<c.length;b+=2){d+=String.fromCharCode(parseInt(c.substr(b,2),16))}return d};a.bytesToHex=function(b){return a.createBuffer(b).toHex()};a.int32ToBytes=function(b){return(String.fromCharCode(b>>24&255)+String.fromCharCode(b>>16&255)+String.fromCharCode(b>>8&255)+String.fromCharCode(b&255))};a.encodeUtf8=function(b){return unescape(encodeURIComponent(b))};a.decodeUtf8=function(b){return decodeURIComponent(escape(b))};a.deflate=function(e,c,d){c=a.decode64(e.deflate(a.encode64(c)).rval);if(d){var f=2;var b=c.charCodeAt(1);if(b&32){f=6}c=c.substring(f,c.length-4)}return c};a.inflate=function(d,b,c){var e=d.inflate(a.encode64(b)).rval;return(e===null)?null:a.decode64(e)}})();(function(){var e=forge.sha1=forge.sha1||{};forge.md=forge.md||{};forge.md.algorithms=forge.md.algorithms||{};forge.md.sha1=forge.md.algorithms.sha1=e;var c=null;var b=false;var d=function(){c=String.fromCharCode(128);c+=forge.util.fillString(String.fromCharCode(0),64);b=true};var a=function(r,p,u){var q,o,n,m,l,k,j,g;var h=u.length();while(h>=64){o=r.h0;n=r.h1;m=r.h2;l=r.h3;k=r.h4;for(g=0;g<16;++g){q=u.getInt32();p[g]=q;j=l^(n&(m^l));q=((o<<5)|(o>>>27))+j+k+1518500249+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}for(;g<20;++g){q=(p[g-3]^p[g-8]^p[g-14]^p[g-16]);q=(q<<1)|(q>>>31);p[g]=q;j=l^(n&(m^l));q=((o<<5)|(o>>>27))+j+k+1518500249+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}for(;g<32;++g){q=(p[g-3]^p[g-8]^p[g-14]^p[g-16]);q=(q<<1)|(q>>>31);p[g]=q;j=n^m^l;q=((o<<5)|(o>>>27))+j+k+1859775393+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}for(;g<40;++g){q=(p[g-6]^p[g-16]^p[g-28]^p[g-32]);q=(q<<2)|(q>>>30);p[g]=q;j=n^m^l;q=((o<<5)|(o>>>27))+j+k+1859775393+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}for(;g<60;++g){q=(p[g-6]^p[g-16]^p[g-28]^p[g-32]);q=(q<<2)|(q>>>30);p[g]=q;j=(n&m)|(l&(n^m));q=((o<<5)|(o>>>27))+j+k+2400959708+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}for(;g<80;++g){q=(p[g-6]^p[g-16]^p[g-28]^p[g-32]);q=(q<<2)|(q>>>30);p[g]=q;j=n^m^l;q=((o<<5)|(o>>>27))+j+k+3395469782+q;k=l;l=m;m=(n<<30)|(n>>>2);n=o;o=q}r.h0+=o;r.h1+=n;r.h2+=m;r.h3+=l;r.h4+=k;h-=64}};e.create=function(){if(!b){d()}var f=null;var i=forge.util.createBuffer();var g=new Array(80);var h={algorithm:"sha1",blockLength:64,digestLength:20,messageLength:0};h.start=function(){h.messageLength=0;i=forge.util.createBuffer();f={h0:1732584193,h1:4023233417,h2:2562383102,h3:271733878,h4:3285377520};return h};h.start();h.update=function(k,j){if(j==="utf8"){k=forge.util.encodeUtf8(k)}h.messageLength+=k.length;i.putBytes(k);a(f,g,i);if(i.read>2048||i.length()===0){i.compact()}return h};h.digest=function(){var j=h.messageLength;var m=forge.util.createBuffer();m.putBytes(i.bytes());m.putBytes(c.substr(0,64-((j+8)%64)));m.putInt32((j>>>29)&255);m.putInt32((j<<3)&4294967295);var k={h0:f.h0,h1:f.h1,h2:f.h2,h3:f.h3,h4:f.h4};a(k,g,m);var l=forge.util.createBuffer();l.putInt32(k.h0);l.putInt32(k.h1);l.putInt32(k.h2);l.putInt32(k.h3);l.putInt32(k.h4);return l};h.digest2=function(){var j=h.messageLength;var o=forge.util.createBuffer();var k=forge.util.createBuffer(i.data.slice(i.read));var m=g.slice(0);o.putBytes(i.bytes());o.putBytes(c.substr(0,64-((j+8)%64)));o.putInt32((j>>>29)&255);o.putInt32((j<<3)&4294967295);var l={h0:f.h0,h1:f.h1,h2:f.h2,h3:f.h3,h4:f.h4};a(l,g,o);var n=forge.util.createBuffer();n.putInt32(l.h0);n.putInt32(l.h1);n.putInt32(l.h2);n.putInt32(l.h3);n.putInt32(l.h4);i=k;g=m;return n};return h};e.createhash=function(){var g=e.create();var f=g.update;g.update=function(h){return f(h.toString("binary"))};g.digest=function(){return g.digest2().toHex()};return g}})();(function(){var j=false;var h=4;var f;var b;var d;var k;var g;var e=function(){j=true;d=[0,1,2,4,8,16,32,64,128,27,54];var x=new Array(256);for(var p=0;p<128;++p){x[p]=p<<1;x[p+128]=(p+128)<<1^283}f=new Array(256);b=new Array(256);k=new Array(4);g=new Array(4);for(var p=0;p<4;++p){k[p]=new Array(256);g[p]=new Array(256)}var s=0,o=0,v,t,q,w,l,u,r;for(var p=0;p<256;++p){w=o^(o<<1)^(o<<2)^(o<<3)^(o<<4);w=(w>>8)^(w&255)^99;f[s]=w;b[w]=s;l=x[w];v=x[s];t=x[v];q=x[t];u=(l<<24)^(w<<16)^(w<<8)^(w^l);r=(v^t^q)<<24^(s^q)<<16^(s^t^q)<<8^(s^v^q);for(var m=0;m<4;++m){k[m][s]=u;g[m][w]=r;u=u<<24|u>>>8;r=r<<24|r>>>8}if(s===0){s=o=1}else{s=v^x[x[x[v^q]]];o^=x[x[o]]}}};var a=function(z,o){var x=z.slice(0);var B,m=1;var r=x.length;var p=r+6+1;var s=h*p;for(var u=r;u<s;++u){B=x[u-1];if(u%r===0){B=f[B>>>16&255]<<24^f[B>>>8&255]<<16^f[B&255]<<8^f[B>>>24]^(d[m]<<24);m++}else{if(r>6&&(u%r===4)){B=f[B>>>24]<<24^f[B>>>16&255]<<16^f[B>>>8&255]<<8^f[B&255]}}x[u]=x[u-r]^B}if(o){var t;var D=g[0];var C=g[1];var A=g[2];var y=g[3];var v=x.slice(0);var s=x.length;for(var u=0,l=s-h;u<s;u+=h,l-=h){if(u===0||u===(s-h)){v[u]=x[l];v[u+1]=x[l+3];v[u+2]=x[l+2];v[u+3]=x[l+1]}else{for(var q=0;q<h;++q){t=x[l+q];v[u+(3&-q)]=D[f[t>>>24]]^C[f[t>>>16&255]]^A[f[t>>>8&255]]^y[f[t&255]]}}}x=v}return x};var c=function(u,v,t,o){var q=u.length/4-1;var p,n,m,l,s;if(o){p=g[0];n=g[1];m=g[2];l=g[3];s=b}else{p=k[0];n=k[1];m=k[2];l=k[3];s=f}var D,C,A,z,E,r,x;D=v[0]^u[0];C=v[o?3:1]^u[1];A=v[2]^u[2];z=v[o?1:3]^u[3];var y=3;for(var B=1;B<q;++B){E=p[D>>>24]^n[C>>>16&255]^m[A>>>8&255]^l[z&255]^u[++y];r=p[C>>>24]^n[A>>>16&255]^m[z>>>8&255]^l[D&255]^u[++y];x=p[A>>>24]^n[z>>>16&255]^m[D>>>8&255]^l[C&255]^u[++y];z=p[z>>>24]^n[D>>>16&255]^m[C>>>8&255]^l[A&255]^u[++y];D=E;C=r;A=x}t[0]=(s[D>>>24]<<24)^(s[C>>>16&255]<<16)^(s[A>>>8&255]<<8)^(s[z&255])^u[++y];t[o?3:1]=(s[C>>>24]<<24)^(s[A>>>16&255]<<16)^(s[z>>>8&255]<<8)^(s[D&255])^u[++y];t[2]=(s[A>>>24]<<24)^(s[z>>>16&255]<<16)^(s[D>>>8&255]<<8)^(s[C&255])^u[++y];t[o?1:3]=(s[z>>>24]<<24)^(s[D>>>16&255]<<16)^(s[C>>>8&255]<<8)^(s[A&255])^u[++y]};var i=function(H,r,u,o,v){var m=null;if(!j){e()}v=(v||"CBC").toUpperCase();if(typeof H==="string"&&(H.length===16||H.length===24||H.length===32)){H=forge.util.createBuffer(H)}else{if(forge.util.isArray(H)&&(H.length===16||H.length===24||H.length===32)){var B=H;var H=forge.util.createBuffer();for(var x=0;x<B.length;++x){H.putByte(B[x])}}}if(!forge.util.isArray(H)){var B=H;H=[];var z=B.length();if(z===16||z===24||z===32){z=z>>>2;for(var x=0;x<z;++x){H.push(B.getInt32())}}}if(!forge.util.isArray(H)||!(H.length===4||H.length===6||H.length===8)){return m}var I=(["CFB","OFB","CTR"].indexOf(v)!==-1);var p=(v==="CBC");var A=a(H,o&&!I);var w=h<<2;var n;var y;var s;var D;var q;var l;var F;m={output:null};if(v==="CBC"){F=E}else{if(v==="CFB"){F=G}else{if(v==="OFB"){F=t}else{if(v==="CTR"){F=C}else{throw {message:""}}}}}m.update=function(J){if(!l){n.putBuffer(J)}while(n.length()>=w||(n.length()>0&&l)){F()}};m.update2=function(J){if(J){if(J.length()){n.data=n.data.substr(n.read);n.read=0;n.putBuffer(J)}}while(n.length()>=w){F()}if(m.overflow){y.getBytes(m.overflow)}var M=n.length()%w;if(M){var K=forge.util.createBuffer(n.data.slice(n.read));var L=s.slice(0);while(n.length()>0){F()}n=K;s=L;y.truncate(w-M)}else{n.data="";n.read=0};m.overflow=M};m.finish=function(N){var M=true;var O=n.length()%w;if(!o){if(N){M=N(w,n,o)}else{if(p){var L=(n.length()===w)?w:(w-n.length());n.fillWithByte(L,L)}}}if(M){l=true;m.update()}if(o){if(p){M=(O===0)}if(M){if(N){M=N(w,y,o)}else{if(p){var J=y.length();var K=y.at(J-1);if(K>(h<<2)){M=false}else{y.truncate(K)}}}}}if(!p&&!N&&O>0){y.truncate(w-O)}return M};m.start=function(K,J){if(K===null){K=q.slice(0)}if(typeof K==="string"&&K.length===16){K=forge.util.createBuffer(K)}else{if(forge.util.isArray(K)&&K.length===16){var M=K;var K=forge.util.createBuffer();for(var L=0;L<16;++L){K.putByte(M[L])}}}if(!forge.util.isArray(K)){var M=K;K=new Array(4);K[0]=M.getInt32();K[1]=M.getInt32();K[2]=M.getInt32();K[3]=M.getInt32()}n=forge.util.createBuffer();y=J||forge.util.createBuffer();q=K.slice(0);s=new Array(h);D=new Array(h);l=false;m.output=y;if(["CFB","OFB","CTR"].indexOf(v)!==-1){for(var L=0;L<h;++L){s[L]=q[L]}q=null}};if(r!==null){m.start(r,u)}return m;function E(){if(o){for(var J=0;J<h;++J){s[J]=n.getInt32()}}else{for(var J=0;J<h;++J){s[J]=q[J]^n.getInt32()}}c(A,s,D,o);if(o){for(var J=0;J<h;++J){y.putInt32(q[J]^D[J])}q=s.slice(0)}else{for(var J=0;J<h;++J){y.putInt32(D[J])}q=D}}function G(){c(A,s,D,false);for(var K=0;K<h;++K){s[K]=n.getInt32()}for(var K=0;K<h;++K){var J=s[K]^D[K];if(!o){s[K]=J}y.putInt32(J)}}function t(){c(A,s,D,false);for(var J=0;J<h;++J){s[J]=n.getInt32()}for(var J=0;J<h;++J){y.putInt32(s[J]^D[J]);s[J]=D[J]}}function C(){c(A,s,D,false);for(var J=h-1;J>=0;--J){if(s[J]===4294967295){s[J]=0}else{++s[J];break}}for(var J=0;J<h;++J){y.putInt32(n.getInt32()^D[J])}}};forge.aes=forge.aes||{};forge.aes.startEncrypting=function(n,m,l,o){return i(n,m,l,false,o)};forge.aes.createEncryptionCipher=function(l,m){return i(l,null,null,false,m)};forge.aes.startDecrypting=function(n,m,l,o){return i(n,m,l,true,o)};forge.aes.createDecryptionCipher=function(l,m){return i(l,null,null,true,m)};forge.aes._expandKey=function(m,l){if(!j){e()}return a(m,l)};forge.aes._updateBlock=c;forge.aes.createcipheriv=function(r,o,n){var q=r.split("-")[2];var m=forge.util.createBuffer();o=forge.util.createBuffer(o.toString("binary"));n=forge.util.createBuffer(n.toString("binary"));var l=forge.aes.startEncrypting(o,n,m,q);var p=l.update2;l.update=function(t){var s;if(t){t=forge.util.createBuffer(t.toString("binary"))}else{t=forge.util.createBuffer()}p(t);s=m.toHex();m.data="";m.read=0;return s};return l}})();var wBuffer=function(a,e) {if (e==="hex") {try {var b=new Uint8Array(a.length/2);var l=a.length;for (var i=0;i<l;i+=2) {b[i/2]=parseInt(a[i]+a[i+1],16);};} catch(ee) {return new Uint8Array();};};return b;};Uint8Array.prototype.toString=function(enc) {var l=this.length;var r=[];if (enc==="hex") {for (var i=0;i<l;i++) {var tmp=this[i].toString(16);r.push(tmp.length===1?("0"+tmp):tmp);};};if (enc==="binary") {if (navigator.userAgent.indexOf("Chrome")===-1) {return String.fromCharCode.apply(null,this);}else{var cut=16*1024;var part="";var tmp=this;while (tmp.length) {var k=Math.min(tmp.length,cut);part +=String.fromCharCode.apply(null,tmp.subarray(0,k));tmp=tmp.subarray(k);};return part;};};return r.join("");};var createcipheriv=forge.aes.createcipheriv;self.onmessage=function(evt){var res=evt.data;var BL=65536;/*warning do not change*/;var type;var file;var size;/*modif chrome*/var reader=new FileReaderSync();file=res[1];type=res[0];size=file.size;var H=forge.sha1.createhash("sha1");if (type.indexOf("hash")===-1) {var C2=createcipheriv(res[0],res[2],res[3]);};var start=0;while (start!==size) {var chunk=new Uint8Array(reader.readAsArrayBuffer(file.slice(start,Math.min(start+BL,size))));if (type.indexOf("hash")!==-1) {H.update(chunk);if (type==="hash") {self.postMessage(chunk.length);} else {self.postMessage(chunk);};} else {var enc=new wBuffer(C2.update(chunk,"hex","hex"),"hex");H.update(enc);self.postMessage(enc);};start +=Math.min(BL,size-start);};self.postMessage([H.digest("hex")]);};';
				//google.load("visualization","1",{packages:["corechart"]});
				var restoring_chunk=function() {
					for (var n in crashed) {
						console.log('restoring_chunk');
						var request=crashed[n];
						if (request.k) { //typeerror todo check why
							request.file_id=request.k[0];
							delete request.k;
							var type=request.data.type||(request.content_chrome?request.content_chrome:request.content_);
							request.blob_=new Blob([],{type:type});
							if (request.hash_ini) {
								store_DB(request);
							} else {
								console.log('deleting chunks file_id '+request.file_id);
								var t=peersmDB.db.transaction([peersmcode+'_'],'readwrite').objectStore(peersmcode+'_');
								t.openCursor().onsuccess = function(evt) {
									var cursor = evt.target.result;
									if (cursor) {
										var res=cursor.value.k;
										if (res instanceof Array) {
											if (res[0]===request.file_id) {
												t.delete(res);
											};
										};
										cursor.continue();
									};
								};
							};
						};
					};
				};
				check_proxy=function() {
					var script=$_('script');
					save_=$_('save').checked;
					//graph_=$_('graph').checked;
					graph_=true;
					debug_=$_('debug').checked;
					hide($_('save').parentNode);
					if (script) {
						var nonce=script.getAttribute('nonce');
						if (nonce) {
							delete $_('prompt-input').type;
							Myalert("<p style='text-align:center'>Loading...please wait</p>");
							$_('progress-alert').style.display='block';
							$_('progint-alert').style.width='0%';
							key__=new Buffer(this.value,'hex');
							var key=key__;
							if (key.length===16) {
								var worker=new Worker(URL.createObjectURL(new Blob([workerjs],{type:'text/javascript'})));
								var tsize=0;
								var size=pac.length/2;
								var K='';
								worker.onmessage=function(evt) {
									var data=evt.data;
									var res=data.pop?data[0]:data;
									tsize +=res.length/2;
									$_('progint-alert').style.width=parseInt(100*(tsize/size))+'%';
									if (!data.pop) {
										K +=res.toString('utf8');
									};
									if (data.pop) {
										//console.log(K);
										$_('progress-alert').style.display='none';
										var hash=res;
										var C=crypto.createcipheriv('aes-128-ctr',key,IV);
										var K2=C.update(new Buffer(hash,'hex'),'hex','hex');
										//alert(nonce+' '+K2.toString('hex')+' '+key.toString('hex')+' '+hash);
										if (nonce===K2.toString('hex')) {
											//pac=(new Buffer(K,'hex')).toString('utf8');
											pac='var FindProxyForUrl='+K;
											eval(pac);
											var routers=FindProxyForUrl('http://'+fake_domain,fake_domain,true);
											//fake_cid_OR=routers[0]; //uncomment to reuse fake_cid
											var loc=document.location.href.split('#');
											var a;
											if (loc.length===2) {
												var tmp=loc[1].split('-');
												if (tmp.length===2) {
													a=tmp[0].split(':');
													one_OR={ip:a[0],port:0,wsport:a[1],fing:'',o_modulus:'',name:'Tor Bridge'};
													a=tmp[1].split(':');
													//DB_OR={ip:a[0],port:a[1],wsport:0,fing:a[2],o_modulus:a[3],name:'Tor Bridge - '+a[0]};
													DB_OR={ip:a[0],port:a[1],wsport:0,fing:a[2],o_modulus:a[3],name:'Tor Bridge'};
													//213.246.53.127:8050-213.246.53.127:8001:E0671CF9CB593F27CD389CD4DD819BF9448EA834:ca2a670479816ca562f7afc2667db1811f0efa7d595aa27cf532092a052c697b102c03d8b8dddc276050fe9cad15efe72758d9d9b0f581f5cbfd0be92ecd721711797354006625e74e0f733efee3ee779116efe87da3b5f8c1729e2d0a5f2c4de4d5906b6e383c0a0d8dddbc076cf426423f6f2b6fd46fab9f54fb8e42601a2d
												} else {
													a=loc[1].split(':');
													one_OR={ip:a[0],port:0,wsport:a[1],fing:'',o_modulus:'',name:'Tor Bridge - '+a[0]};
												};
											} else {
												one_OR=routers[1][simple_random(routers[1].length)];
											};
											if (!DB_OR) {
												DB_OR=routers[2];
											};
											if (one_OR!==DB_OR) {
												WS_TLS=true; //No TLS for ORDB OR 8001
											};
											Relays=Guards;
											if (fake_cid_OR) {
												Rsa_=new RSAKey;
												Rsa_.setPublic(fake_cid_OR.o_modulus,exp);
											};
											setTimeout(function() {$_('alert_box').style.display='none'},10000);
											if ((!key_stored)&&(save_)) {
												var db=peersmDB.db;
												var t=db.transaction([peersmcode+'_'],'readwrite').objectStore(peersmcode+'_');
												t.put({k:key.toString('hex')});
											} else if (!save_) {
												var db=peersmDB.db;
												var t=db.transaction([peersmcode+'_'],'readwrite').objectStore(peersmcode+'_');
												t.delete(key.toString('hex'));
											};
											if (debug_) {
												show($_('debug'));
											} else {
												hide($_('debug'));
											};
											setInterval(update_proxy,UPDATE_PROXY);
											unleash();
											start_download();
										} else {
											Myprompt("<p style='text-align:center'>Wrong key or someone is trying to hack your connection, please retry:</p>",check_proxy);
											$_('prompt-input').type='password';
										};
									};
								};
								//worker.postMessage(['aes-128-ctr',new Buffer(pac,'hex'),key,IV]);
								worker.postMessage(['aes-128-ctr',new Blob([new Buffer(pac,'hex')]),key,IV]);
							} else {
								Myprompt("<p style='text-align:center'>Wrong key length, please retry:</p>",check_proxy);
								$_('prompt-input').type='password';
							};
						};
					};
				};
				update_proxy=function() {
					console.log('updating proxy ----');
					pac=xhr('proxy.pac');
					var worker=new Worker(URL.createObjectURL(new Blob([workerjs],{type:'text/javascript'})));
					var K='';
					worker.onmessage=function(evt) {
						var data=evt.data;
						var res=data.pop?data[0]:data;
						if (!data.pop) {
							K +=res.toString('utf8');
						};
						if (data.pop) {
							pac='var FindProxyForUrl='+K;
							eval(pac);
							FindProxyForUrl('http://'+fake_domain,fake_domain,true);
						};
					};
					worker.postMessage(['aes-128-ctr',new Blob([new Buffer(pac,'hex')]),key__,IV]);
				};
				$_('alert_box').style.display='none';
				/*
				var boo=false;
				var agent=navigator.userAgent;
				var ff=agent.split('Firefox/');
				if (ff.length>1) {
					ff=parseInt(ff[1].split('.')[0]);
					if (ff>=26) {
						boo=true;
					};
				} else {
					var ch=agent.split('Chrome/');
					if (ch.length>1) {
						ch=parseInt(ch[1].split('.')[0]);
						if (ch>=32) {
							boo=true;
						};
					};
				};
				//Mozilla/5.0 (Windows NT 6.0; rv:26.0) Gecko/20100101 Firefox/26.0
				//Chrome/32
				if (!boo) {
					return;
				};
				if (typeof W!=='undefined') {clearTimeout(W)};
				*/

				$_('prompt-input').type='password';
				if (peersmcode==='peersm') {
					Myprompt("<p style='text-align:center'>Public version, click on OK to start the Peersm application:</p>",check_proxy);
					$_('prompt-input').value='00112233445566778899aabbccddeeff';
					$_('save').checked='checked';
				} else {
					Myprompt("<p style='text-align:center'>Enter your key:</p>",check_proxy);
				};
				try {
					peersmDB=indexedDB.open(peersmcode,6);
				} catch(ee) {
					$_('prompt_box').style.display='none';
					Myalert("<p style='text-align:center'>Your browser does not seem to support all the features required for Peersm. It is recommended to use Firefox version 26 or superior, or Chrome version 32 or superior.</p>");
					return;
				};
				peersmDB.onupgradeneeded=function(evt) {
					console.log('onupgradeneeded------------------');
					var db=evt.target.result;

					if(db.objectStoreNames.contains(peersmcode+'_')) {
						db.deleteObjectStore(peersmcode+'_');
					};
					if(db.objectStoreNames.contains(peersmcode)) {
						db.deleteObjectStore(peersmcode);
					};
					/*
					var store=db.createObjectStore(peersmcode,{keyPath:'hash'});
					store.createIndex('name','name_hash',{unique:true});
					*/
					db.createObjectStore(peersmcode,{keyPath:'name_hash'});
					db.createObjectStore(peersmcode+'_',{keyPath:'k'});
				};
				peersmDB.onsuccess=function (evt) {
					peersmDB.db=evt.target.result;
					var t=peersmDB.db.transaction([peersmcode+'_'],'readwrite').objectStore(peersmcode+'_');
					crashed={};
					chrome=(navigator.userAgent.indexOf('Chrome')!==-1)?true:false;
					t.openCursor().onsuccess = function(evt) {
						var cursor = evt.target.result;
						if (cursor) {
							var res=cursor.value.k;
							if (!(res instanceof Array)) {
								$_('prompt-input').value=res;
								$_('save').checked='checked';
								key_stored=true;
							} else {
								//if (!chrome) {
									var val=cursor.value;
									var index=val.k[0];
									if (!(crashed.hasOwnProperty(index))) {
										console.log('restoring chunks '+index);
										crashed[index]=val;
										//{k[file_id,nb],hash:request.file_hash,name_hash:request.hash_ini,name:request.name_,type:request.content_,file_length:request.clength_,current_length:request.d_length,file_url:request.url_,key:(request.key?request.key:''),data:data}
									};
								//} else {
									//t.delete(res);
									//cursor.continue();
								//};
							};
							cursor.continue();
						} else {
							if (store_DB) {
								restoring_chunk();
							} else {
								restore_chunk=true;
							};
						};
					};
					//$_('graph').checked='checked';
					$_('debug').checked='checked';
				};
				peersmDB.onerror=function(err) {
					console.log('Error opening database');
				};
				var start_download=function() {
					var ini_direct_chart=function() {
						Ddata = new google.visualization.DataTable();
						Ddata.addColumn('datetime','Time');
						Ddata.addColumn('number','Received (KB)');
						Ddata.addColumn('number','Sent (KB)');
						var date=new Date();
						for (var i=70;i>0;i--) {
							var tmp=new Date();
							tmp.setTime(date.getTime()-1000*i);
							Ddata.addRow([tmp,0,0]);
						};
						Doptions = {
						  title: 'Direct Download : '+(NB_C>=0?NB_C:0)+' circuits',
						  hAxis: {title: 'Time',  titleTextStyle: {color: 'green'},textStyle: {fontSize:10}, viewWindow: {min:Ddata.getValue(5,0),max:Ddata.getValue(64,0)}},
						  vAxis: {title: 'Bytes',  titleTextStyle: {color: 'green'},textStyle: {fontSize:10},minValue:0,maxValue:1000},
						  colors:['green','yellow']
						};
						Dchart=new google.visualization.LineChart($_('chart1'));
						Dchart.index=null;
						Dchart.dynRow={};
						Dchart.dynRow2={};
						Dchart.draw(Ddata,Doptions);
					};
					var ini_peer_chart=function() {
						Pdata = new google.visualization.DataTable();
						Pdata.addColumn('datetime','Time');
						Pdata.addColumn('number','Received (KB)');
						Pdata.addColumn('number','Sent (KB)');
						var date=new Date();
						for (var i=70;i>0;i--) {
							var tmp=new Date();
							tmp.setTime(date.getTime()-1000*i);
							Pdata.addRow([tmp,0,0]);
						};
						Poptions = {
						  title: 'Peer to peer',
						  hAxis: {title: 'Time',  titleTextStyle: {color: 'green'},textStyle: {fontSize:10},viewWindow: {min:Pdata.getValue(5,0),max:Pdata.getValue(64,0)}},
						  vAxis: {title: 'Bytes',  titleTextStyle: {color: 'green'},textStyle: {fontSize:10},minValue:0,maxValue:1000},
						  colors:['red','orange']
						};
						Pchart=new google.visualization.LineChart($_('chart2'));
						Pchart.index=null;
						Pchart.dynRow={};
						Pchart.dynRow2={};
						Pchart.draw(Pdata,Poptions);
					};
					var redraw_chart=function(chart,data,options) {
						var n=5;
						var date0=DateRound(data.getValue(69,0)).getTime();
						for (var i=0;i<n;i++) {
							var date=new Date();
							date.setTime(date0+(i+1)*1000);
							data.removeRow(0);
							data.addRow([date,(chart.dynRow[date.getTime()])||0,(chart.dynRow2[date.getTime()])||0]);
							delete chart.dynRow[date.getTime()];
							delete chart.dynRow2[date.getTime()];
						};
						if (options.title.indexOf('Direct')!==-1) {
							options.title='Direct Download : '+(NB_C>=0?NB_C:0)+(NB_C>1?' circuits':' circuit');
						};
						if (options.title.indexOf('Peer')!==-1) {
							options.title='Peer to Peer : '+(db_cid?1:0)+' circuit';
						};
						options.hAxis.viewWindow.min = data.getValue(n,0);
						options.hAxis.viewWindow.max = data.getValue(64,0);
						options.hAxis.viewWindowMode = 'explicit';
						options.animation={duration: 4000,easing: 'linear'};
						//options.curveType='function';
						//options.smoothLine=true;
						//google.visualization.events.addListener(chart1, 'animationfinish', function() {setTimeout(scr,5000)});
						chart.draw(data,options);
					};
					var div=$_('input');
					var input=document.createElement('input');
					input.id='url';
					input.value='Enter_url_or_hash_name_or_magnet_link_or_infohash';
					div.appendChild(input);
					example=input.value;
					input.onkeydown = function(xevent) {
						addEvent(document.body,'mousedown',function() {},false); //pb click not detected
						if ((detkey.call(this,xevent||window.event))&&(this.value!=='')) {
							addEvent(document.body,'mousedown',function() {setTimeout(clear_menu,1000)},false);
							load(this.value);
						};
					};

					var button=$_('ok');
					addEvent(button,'mousedown',function(e) {if (e.stopPropagation) {e.stopPropagation();};e.cancelBubble = true;addEvent(document.body,'mousedown',function() {setTimeout(clear_menu,1000)},false);load(input.value)},false);

					var buttonstream=$_('stream');
					addEvent(buttonstream,'mousedown',function(e) {if (e.stopPropagation) {e.stopPropagation();};e.cancelBubble = true;addEvent(document.body,'mousedown',function() {setTimeout(clear_menu,1000)},false);load(input.value.trim(),true)},false);
					//input.onmousedown=function() {
					//	if (this.value===example) {
					//		this.value='';
					//	};
					//};

					open_db=function() {
						var db=peersmDB.db;
						return db.transaction([peersmcode],'readwrite').objectStore(peersmcode);
					};
					open_db2=function() {
						var db=peersmDB.db;
						return db.transaction([peersmcode+'_'],'readwrite').objectStore(peersmcode+'_');
					};
					var rand_hash=function() {
						var H=crypto.createhash('sha1');
						H.update(new Buffer((Date.now()).toString()+peersmcode,'utf8'));
						return H.digest('hex');
					};
					var remove_ext=function(name) {
						name=name.split('.');
						if (name.length>1) {
							if (name[name.length-1]===ENC_EXT) {
								name.pop();
							};
						};
						return name.join('.');
					};
					var add_thumb=function(request) {
						setTimeout(function() {$_('alert_box').style.display='none'},10000);
						var old=$_(request.hash_ini);
						request.thumb2_=thumb(request,request.hash_ini);
						if (old) {
							$_('local').insertBefore(request.thumb2_,old);
						} else {
							$_('local').appendChild(request.thumb2_);
						};
						if (request.d_length!==request.clength_) {
							request.thumb2_.firstChild.style.backgroundColor='orange';
							if (request.thumb_) {
								request.thumb_.firstChild.style.backgroundColor='orange';
							};
						};
						addEvent(request.thumb2_,'mousedown',show_menu2.bind({file_hash:request.file_hash,hash_ini:request.hash_ini,name_:request.name_,url:url,thumb2_:request.thumb2_,thumb_:request.thumb_,clength_:request.clength_,d_length:request.d_length,content_:request.content_,url_:request.url_,key:request.key,content_chrome:request.content_chrome,blob_:request.blob_}),false);
						remove(old);
					};
					var add_thumb_=function(request) {
						var old=request.thumb_;
						request.thumb_=thumb(request);
						if (old) {
							if (old.parentNode) {
								$_('downloaded').insertBefore(request.thumb_,old);
								remove(old);
							} else {
								$_('downloaded').appendChild(request.thumb_);
							};
						} else {
							$_('downloaded').appendChild(request.thumb_);
						};
						addEvent(request.thumb_,'mousedown',show_menu.bind(request),false);
					};
					var process_upload=function(e) {
						if (e.stopPropagation) {e.stopPropagation();};
						e.cancelBubble = true;
						remove(mediaSrc);
						remove(divMedia);
						addEvent(document.body,'mousedown',function() {},false);
						Myalert("<p style='text-align:center'>Uploading file from your disk to your browser storage...please wait until the file appears in the Local files box, this can take some time depending on the size of the file</p>");
						$_('progress-alert').style.display='block';
						$_('progint-alert').style.width='0%';
						var file=this.files[0];
						var file_enc=[];
						var request={};
						var h=file.name.split('#');
						if (h.length>1) {
							var tmp=h[1].split('.');
							request.name_=h[0]+(tmp.length>1?('.'+tmp[1]):'');
						} else {
							request.name_=file.name;
						};
						request.blob_=chrome?(new Buffer(0)):file;
						request.content_=file.type;
						request.clength_=file.size;
						request.d_length=file.size;
						request.url_='';
						request.queue_=[];
						if (h.length>1) {
							request.hash_ini=h[1].split('.')[0];
						} else {
							request.hash_ini=rand_hash();
						};
						var size=file.size;
						var tsize=0;
						var worker=new Worker(URL.createObjectURL(new Blob([workerjs],{type:'text/javascript'}))); //TODO replace by webcrypto hash
						worker.onmessage=function(evt) {
							var data=evt.data;
							var res=data.pop?(chrome?(new Buffer(0)):0):data;
							tsize +=chrome?res.length:parseInt(res);
							var l=tsize;
							if (chrome) {
								file_enc.push(res);
								if ((tsize%DB_BLOCK===0)||(tsize===size)) {
									var buff=file_enc;
									var execute=function() {
										//console.log(l+' '+data.pop);
										$_('progint-alert').style.width=parseInt(100*l/size)+'%';
										if (!data.pop) {
											request.file_hash='00';
											request.d_length=tsize;
											store_DB2(request,buff);
										} else {
											request.check_hash=true;
											request.file_hash=data[0];
											addEvent(document.body,'mousedown',function() {setTimeout(clear_menu,1000)},false);
											$_('progress-alert').style.display='none';
											console.log('uploaded '+request.file_hash);
											store_DB(request,true);
										};
									};
									file_enc=[];
									request.queue_.push(execute);
									if (request.queue_.length===1) {
										request.queue_[0]();
									};
								};
							} else {
								//console.log(l+' '+data.pop);
								$_('progint-alert').style.width=parseInt(100*l/size)+'%';
								if (evt.data.pop) {
									request.check_hash=true;
									request.file_hash=evt.data[0];
									$_('progress-alert').style.display='none';
									console.log('uploaded '+request.file_hash);
									store_DB(request,true);
									//add_thumb(request);
								};
							};
						};
						worker.postMessage([chrome?'hashc':'hash',file]);
							/*
							var res=new Uint8Array(reader.result);
							var H_=function() {
								var hash=forge.sha1.create();
								var oupdate=hash.update;
								hash.update=function(data) {
									return oupdate(data.toString("binary"));
								};
								hash.digest=function() {
									return hash.digest().toHex();
								};
								return hash;
							};
							alert('0');
							var BL=512;
							var n=0;
							var H=crypto.createhash('sha1');
							while(res.length&&n<100) {
								//console.log(n);
								H.update(res.subarray(0,BL));
								res=res.subarray(Math.min(res.length,BL));
								n++;
							};
							alert('a');
							alert(H.digest());
							*/

						//};
						//addEvent(reader,"loadend",readed,false);
						//reader.readAsArrayBuffer(file);
					};
					//var upload=$_('upload');
					var file_upload=$_('file_upload');
					addEvent(file_upload,'change',process_upload,false);
					progress_bar=function(cont,request) {
						var l=request.d_length||0;
						var t=request.clength_||0;
						var p=0;
						if (t) {
							p=parseInt((l/t)*100);
						};
						var a=document.createElement('div');
						a.className='progress';
						var d=document.createElement('p');
						d.className='bar';
						d.innerHTML=PROGTXT+p+'%'
						var b=document.createElement('div');
						b.className='progcont';
						var c=document.createElement('div');
						c.className='progint';
						c.style.width=p+'%';
						b.appendChild(c);
						a.appendChild(d);
						a.appendChild(b);
						cont.appendChild(a)
						a.progtxt=d;
						a.progbar=c;
						addEvent(a,'mousedown',stop_.bind(request),false);
						return a;
					};
					var thumb=function(request,id) {
						var name=request.name_||request.name;
						var file=request.blob_||request.data||(new Blob([]));
						var ext=get_extension(name);
						var type=file.type||request.content_chrome||request.content_||request.type;
						var url=file_extension[ext]?(file_extension[ext]):(((ext_img.indexOf(ext)!==-1)&&(type.indexOf('image')!==-1))?URL.createObjectURL(file):file_extension['exe']);
						var t=document.createElement('div');
						t.className='thumbwrap';
						if (id) {
							t.id=id;
						};
						var th=document.createElement('div');
						th.className='thumb';
						var i=document.createElement('img');
						i.className='thumbimg';
						i.src=url;
						var u=document.createElement('div');
						u.align='center';
						var s=document.createElement('span');
						s.className='thumbspan';
						s.innerHTML=name;
						th.appendChild(i);
						t.appendChild(th);
						u.appendChild(s);
						t.appendChild(u);
						return t;
					};
					var compute_hash=function(request,data) {
						console.log('compute hash');
						request.file_hash=0;
						addEvent(document.body,'mousedown',function() {},false);
						Myalert("<p style='text-align:center'>Calculating hash for a resumed file, please wait...</p>");
						$_('progress-alert').style.display='block';
						$_('progint-alert').style.width='0%';
						var blob=request.blob_;
						var worker=new Worker(URL.createObjectURL(new Blob([workerjs],{type:'text/javascript'})));
						var size=blob.size;
						var tsize=0;
						worker.onmessage=function(evt) {
							var res=evt.data.pop?0:parseInt(evt.data);
							tsize +=res;
							$_('progint-alert').style.width=parseInt(100*tsize/size)+'%';
							if (evt.data.pop) {
								$_('progress-alert').style.display='none';
								setTimeout(function() {$_('alert_box').style.display='none'},10000);
								request.file_hash=evt.data[0];
								var fin=function() {
									add_thumb_(request);
									add_thumb(request);
									store_DB_final(request,data);
								};
								request.queue_.push(fin);
								addEvent(document.body,'mousedown',function() {setTimeout(clear_menu,1000)},false);
								if (request.queue_.length===1) {
									request.queue_[0]();
								};
							};
						};
						worker.postMessage(['hash',blob]);
					};
					store_DB=function(request,boo,cb) {
						console.log('store_DB ');
						addEvent(document.body,'mousedown',function() {},false);
						if ((request.nb_try!==DB_NB_TRY)&&(!request.reason_)) {
							Myalert("<p style='text-align:center'>Storing file, please wait that the file appears in Local Files (for large files this can take some time)</p>");
						};
						peersmDB.store=function () {
							var t0;
							var data;
							var arr=chrome?[]:null;
							var i=0;
							var chunkStore=open_db2();
							request.file_id=request.file_id||0;
							var a=chunkStore.get([request.file_id,i]);
							var type=(request.blob_ instanceof Uint8Array)?(request.content_chrome?request.content_chrome:request.content_):request.blob_.type;
							var blob=new Blob([],{type:type});
							if (STORE_PERF) {
								var t0=Date.now();
							};
							a.onsuccess=function(evt) {
								var res=evt.target.result;
								if (res) {
									//console.log(i+' '+(res.data.length||res.data.size));
									var data=res.data;
									if (!(data instanceof Array)) {
										blob=new Blob([blob,data],{type:type});
									} else {
										data.unshift(blob);
										blob=new Blob(data,{type:type});
									};
									if (chrome) {
										if (data instanceof Array) {
											data.shift();
											arr=arr.concat(data);
										} else {
											arr.push(data);
										};
									};
									chunkStore.delete([request.file_id,i]);
									i++;
									//var b=this.onsuccess;
									a=chunkStore.get([request.file_id,i]);
									a.onsuccess=this.onsuccess;
								} else {
									console.log('Saving chunks size '+blob.size+(t0?(' time to read all chunks '+(Date.now()-t0)):''));
									if (chrome) {
										if (request.blob_ instanceof Array) {
											arr=arr.concat(request.blob_);
										} else {
											arr.push(request.blob_);
										};
									};
									request.blob_=new Blob([blob,request.blob_],{type:type});
									var objectStore=open_db();
									var a=objectStore.get(request.hash_ini);
									console.log('store_DB open');
									var tmp=Date.now();
									a.onsuccess=function(evt) {
										console.log('store_DB success '+(Date.now()-tmp));
										var result=evt.target.result;
										if (result) {
											remove($_(result.name_hash));
											if (!chrome) {
												data=new Blob([result.data,request.blob_],{type:type});
												request.blob_=data;
											} else {
												data=result.data.concat(arr);
												request.blob_=new Blob(data,{type:(request.content_chrome?request.content_chrome:request.content_)});
											};
										} else {
											data=chrome?arr:request.blob_;
										};
										if (!request.name_) {
											var t=request.blob_.type||(request.content_chrome?request.content_chrome:request.content_);
											request.name_=request.hash_ini.substr(0,8);
											if (t!==request.content_) {
												request.name_=request.name_+'.'+ENC_EXT;
											};
										};
										request.file_hash=request.file_hash||0;
										request.d_length=request.blob_.size;
										if (!boo) {
											add_thumb_(request);
										};
										if (!cb) {
											add_thumb(request);
										};
										if ((!request.check_hash)&&(request.clength_===request.d_length)) {
											compute_hash(request,data);
										} else {
											store_DB_final(request,data,cb);
										};
										console.log('Chunks saved '+(Date.now()-tmp));
										$_('alert_box').style.display='none';
										addEvent(document.body,'mousedown',function() {setTimeout(clear_menu,1000)},false);
									};
								};
							};
						};
						peersmDB.store();
					};
					var store_DB_final=function(request,data,cb) {
						request.file_hash=request.file_hash||0;
						var objectStore=open_db();
						if (!chrome) {
							objectStore.put({hash:request.file_hash,name_hash:request.hash_ini,name:request.name_,type:request.content_,file_length:request.clength_,current_length:request.d_length,file_url:request.url_,key:(request.key?request.key:''),data:data});
						} else {
							objectStore.put({hash:request.file_hash,name_hash:request.hash_ini,name:request.name_,type:request.content_,file_length:request.clength_,current_length:request.d_length,file_url:request.url_,key:(request.key?request.key:''),data:data,enc:request.content_chrome||''});
						};
						if (cb) {
							cb(request);
						} else {
							if (request.d_length===request.clength_) {
								if (db_cid) {
									db_cid.send_db_info();
								};
							};
							delete_request(request);
						};
					};
					var store_DB2=function(request,data) {
						//data: Blob if not chrome, Uint8Array if chrome
						if (DB_PERF) {
							var t0=Date.now();
						};
						var db=peersmDB.db;
						var tx=db.transaction([peersmcode+'_'],'readwrite');
						var objectStore=tx.objectStore(peersmcode+'_');
						if (!request.name_) {
							var type=request.blob_.type||(request.content_chrome?request.content_chrome:request.content_);
							request.name_=request.hash_ini.substr(0,8);
							if (type!==request.content_) {
								request.name_=request.name_+'.'+ENC_EXT;
							};
						};
						if (!request.file_id) {
							request.file_id=Date.now();
							request.chunk_nb=0;
						} else {
							request.chunk_nb++;
						};
						//{hash:request.file_hash,name_hash:request.hash_ini,name:request.name_,type:request.content_,file_length:request.clength_,current_length:request.d_length,file_url:request.url_,key:(request.key?request.key:''),data:data}
						objectStore.put({k:[request.file_id,request.chunk_nb],file_hash:request.file_hash,hash_ini:request.hash_ini,name_:request.name_,content_:request.content_,clength_:request.clength_,d_length:request.d_length,url_:request.url_,key:(request.key?request.key:''),data:data});
						//tx.oncomplete=function() {
							if (t0) {
								console.log('db_perf '+(Date.now()-t0));
							};
							var queue=request.queue_;
							queue.shift();
							if (queue.length) {
								queue[0]();
							};
						//};
					};
					var moof_parser=function(buff,request) {
					//aabbccdd6d6f6f66000000006d 6f6f66000000006d6f 6f66
						buff=request.append_wait.length?([request.append_wait,buff].concatBuffers()):buff;
						request.append_wait=new Buffer(0);
						var tmp=buff.toString('hex');
						tmp=tmp.split('6d6f6f66');
						var l=tmp.length;
						for (var i=0;i<l;i++) {
							var sbuff=new Buffer(tmp[i],'hex');
							if (i!==l-1) {
								if (sbuff.length>=4) {
									request.stream_buffer.push(sbuff.slice(0,sbuff.length-4));
								} else {
									console.log('bad formatted mp4');
								};
								request.append_buffer.push(request.stream_buffer.concatBuffers());
								request.stream_buffer=[];
							} else {
								if (sbuff.length>4) {
									if (sbuff[sbuff.length-1]===0x6d) {
										request.append_wait=sbuff;
									};
									if (sbuff.length>5) {
										if ((sbuff[sbuff.length-2]===0x6d)&&(sbuff[sbuff.length-1]===0x6f)) {
											request.append_wait=sbuff;
										};
									};
									if(sbuff.length>6) {
										if ((sbuff[sbuff.length-3]===0x6d)&&(sbuff[sbuff.length-2]===0x6f)&&(sbuff[sbuff.length-1]===0x6f)) {
											request.append_wait=sbuff;
										};
									};
								};
								if (!request.append_wait.length) {
									request.stream_buffer.push(sbuff);
								};
							};
						};
					};

					/*
					var test=new Buffer('cc','hex');
					var test1=new Buffer('aaaaaaaaaa6d6f6f66bbbbbbbbbb6d6f6f6600000000006d','hex');
					var test2=new Buffer('6f6f6611111111116d6f','hex');
					var test3=new Buffer('6f6622222222226d6f6f','hex');
					var test4=new Buffer('6633','hex');
					var test5=new Buffer('44','hex');
					var request={};
					request.append_buffer=[];
					request.stream_buffer=[];
					request.append_wait=new Buffer(0);
					moof_parser(test,request);
					moof_parser(test1,request);
					moof_parser(test2,request);
					moof_parser(test3,request);
					moof_parser(test4,request);
					moof_parser(test5,request);
					alert(request.append_buffer[0].toString('hex')); //ccaa
					alert(request.append_buffer[1].toString('hex')); //bb
					alert(request.append_buffer[2].toString('hex')); //00
					alert(request.append_buffer[3].toString('hex')); //11
					alert(request.append_buffer[4].toString('hex')); //22
					alert(request.append_buffer[5].toString('hex')); //33
					*/

					duration=function(t) {
						//PT0H2M19.29S
						console.log('duration: '+t);
						var hour=t.split('PT');
						var mn=hour[1].split('H');
						var s=parseFloat(mn[1].split('M')[1].split('S')[0]);
						hour=hour[1].split('H')[0];
						mn=mn[1].split('M')[0];
						return hour*3600+mn*60+s;
					};
					var addsourcebuffer=function() {
						try {
							console;log('addsourcebuffer '+this._stream_.readyState);
							this._source_=this._stream_.addSourceBuffer(this.mime_codec);
						} catch(ee) {
							console.log('wait open addsourcebuffer failed');
						};
					};
					var appendbuffer=function(request,val) {
						if (request.received_===val) {
							$_('alert_box').style.display='none';
							request._source_.addEventListener('updateend',function() {
								updateend(request);
							});
							updateend(request);
						} else if (request.received_>val) {
							if (request.append_cursor===0) {
								request.wait_chunk=true;
							};
							if (request.wait_chunk) {
								var seg=Math.min(Math.ceil((request.clength_-request.d_length)/BSIZE),VIDEO_APPEND/4);
								request.append_cursor++;
								if (request.append_cursor>=seg) {
									delete request.wait_chunk;
									//console.log('updateend called '+request._source_.updating);
									updateend(request);
								};
							};
						};
					};
					var load_Blob=function(buff,request) {
						//console.log(buff.toString('hex'));
						if ((request._stream_)&&(!request._streaming_)) { //_streaming_ --> non json file
							request.d_length +=buff.length;
							request._json_+=buff.toString('utf8');
							if (request.d_length>=request.clength_) {
								console.log('json '+request._json_);
								try {
									var mpd=JSON.parse(request._json_);
								} catch(ee) {
									console.log('wrong json file, retrying');
									request=init_d_request(request.hash_ini,null,true);
									Tor(request);
									return;
								};
								for (var n in mpd) {
									if (n!=='Duration') {
										var request_stream=init_d_request(mpd[n].Representation.BaseURL,null,true);
										request.nb_sources.push(request_stream);
										request_stream._streaming_=true;
										request_stream.mime_codec=mpd[n].Representation.mimeType+'; codecs="'+mpd[n].Representation.codecs+'"';
										request_stream._stream_=request._stream_;
										request_stream._parent_=request;
										request_stream._bandwidth_=mpd[n].Representation.bandwidth;
										request_stream._width_=mpd[n].Representation.width;
										request_stream._height_=mpd[n].Representation.height;
										if (request._stream_.readyState==='open') {
											try {
												request_stream._source_=request._stream_.addSourceBuffer(request_stream.mime_codec);
											} catch(ee) {
												console.log('addsourcebuffer failed');
												request._wait_open.push(addsourcebuffer.bind(request_stream));
											};
										} else {
											request._wait_open.push(addsourcebuffer.bind(request_stream));
										};
										Tor(request_stream);
									} else {
										retry_media=function() {
											var dur=mpd[n];
											if (request._stream_.readyState==='open') {
												request._stream_.duration=duration(dur);
											} else {
												console.log('media stream not ready yet - retry later');
												setTimeout(retry_media,500);
											};
										};
										retry_media();
									};
								};
								console.log('Queue fin json '+request.d_length+' '+request.clength_+' '+(parseInt((8*BSIZE*request.received_/((Date.now()-request.start_t0)/1000)))+' bps '));
								clearTimers(request.sendme_tout);
								clearTimers(request.waiting_);
								request.eof_=true;
								fin_.call(request);
							};
						} else if (request._streaming_) {
						//moof 6D6F6F66
							request.d_length +=buff.length;
							//if (!request.mp4box) {
								request.append_buffer.push(buff);
							//};
							//moof_parser(buff,request);
							//console.log(buff.toString('utf8'));

							if (request._source_) {
							/*
								if (!request._source_.updating) {
									try {
										if (request.append_cursor===request.received_-1) {
											request.append_cursor++;
											console.log('appending queue cursor '+request.append_cursor);
											request._source_.appendBuffer(request.append_buffer.shift());
										};
									} catch(ee) {
										request.append_buffer.unshift(buff);
										console.log('error appendbuffer');
									}
								};
							*/
							/*
								if (request.received_===Math.min(VIDEO_APPEND,request.pieces)) {
									//console.log('appending first '+request.append_buffer[0].toString('hex'));
									request.append_cursor++;
									$_('alert_box').style.display='none';
									try {
										request._source_.appendBuffer(request.append_buffer.shift());
									} catch(ee) {
										console.log('error first append');
									};
									request._source_.addEventListener('updateend',function() {
										if (request.append_buffer.length) {
											//console.log('appending '+request.append_buffer[0].length);
											request.append_cursor++;
											if (request.append_cursor<705) {
												//console.log('appending '+request.append_cursor+' '+request.append_buffer[0].toString('hex'));
											};
											try {
												clearTimers(request.append_to);
												var chunk=request.append_buffer.shift();
												request.debug_chunk.push(chunk);
												request._source_.appendBuffer(chunk);
											} catch(ee) {
												console.log('error updateend');
												console.log(request.debug_chunk[request.debug_chunk.length-1].toString('hex'));
												console.log(request.debug_chunk[request.debug_chunk.length-2].toString('hex'));
												console.log(request.debug_chunk[request.debug_chunk.length-3].toString('hex'));
											};
										} else {
											if ((request.append_cursor<=request.received_)&&(request.append_cursor<request.pieces)) {
												request.append_to.push(setTimeout(function() {
													//console.log('appending null '+' length '+request.append_buffer.length+' cursor '+request.append_cursor+' received '+request.received_+' pieces '+request.pieces+' '+request._source_.updating);
														request.append_to.shift();
														//console.log(request.append_to.length+' '+request._source_.updating);
														if (request.append_buffer.length) {
															var chunk=request.append_buffer.shift();
															request.debug_chunk.push(chunk);
															try {
																request._source_.appendBuffer(chunk);
															} catch(ee) {
																console.log('error append null not null');
															}
														} else {
															try {
																request._source_.appendBuffer(new Buffer(0));
															} catch (ee) {
																console.log('error append null');
															};
														};
												},100));
												//request._source_.appendBuffer(request.append_buffer.shift());
											};
										};
									});
								};*/
								appendbuffer(request,Math.min(VIDEO_APPEND,request.pieces));
							} else {
								if (request.mp4box) {
									var l=request.append_buffer.length;
									if ((l>=MP4BOX_APPEND)||(request.d_length>=request.clength_)) {
										if ((!request._moov_)||(request._moov_===MOOV)) {
											if (request._moov_) {
												if (!request.mp4box.inputIsoFile) {
													request.mp4box.inputIsoFile=new ISOFile();
												};
												if (request._parent_.mp4box.inputIsoFile) {
													request.mp4box.inputIsoFile.ftyp=request._parent_.mp4box.inputIsoFile.ftyp;
												} else {
													setTimeout(function() {load_Blob(new Buffer(0),request)},1000);
												};
											};

											var chunk=request.append_buffer.concatBuffers();
											for (var i=0;i<l;i++) {
												request.append_buffer.shift();
											};
											if (request._moov_) {
												console.log('appending moov mp4box length '+chunk.length)
											};
											//var chunk=request.append_buffer.shift();
											//console.log('mp4box append '+chunk.length+' Bytes');
											//console.log(chunk.toString('hex'));
											chunk.buffer.fileStart=request.mp4box._fileStart_||0;
											//console.log('chunk fileStart '+chunk.buffer.fileStart);
											request.mp4box._fileStart_ +=chunk.length;
											request.mp4box.appendBuffer(chunk.buffer);
										};
									};
									if ((request._moov_)&&(request._moov_!==MOOV)) {
										if (request._moov_===true) {
											request._moov_=buff.toString('hex');
										} else {
											request._moov_+=buff.toString('hex');
										};
										if (request._moov_.indexOf(MOOV)!==-1) {
											console.log('moov found');
											var moov=request._moov_.split(MOOV);
											var size=moov[0];
											var rest=moov[1];
											size=size.substr(size.length-8,size.length);
											console.log('moov size '+size+' rest '+rest.length);
											request._moov_=MOOV;
											request.append_buffer=[];
											request.append_buffer.push(new Buffer(size+MOOV+rest,'hex'));
										};
									};
								};
							};
							if (request.d_length>=request.clength_) {
								console.log('Queue fin streaming '+request.d_length+' '+request.clength_+' '+(parseInt((8*BSIZE*request.received_/((Date.now()-request.start_t0)/1000)))+' bps '));
								console.log('queue length '+request.append_buffer.length+' cursor '+request.append_cursor+' received '+request.received_);
								clearTimers(request.sendme_tout);
								clearTimers(request.waiting_);
								request.eof_=true;
								if (request.mp4box) {
									request.mp4box.flush();
								};
								fin_.call(request);
							};
						} else {
							if (!request.eof_) {
								var process=function(data) {
									return function() {
										request.d_length +=data.length;
										if (!request.blob_) {
											request.content_=request.content_||'application/octet-binary';
											var enc=request.content_.split(';');
											if (enc.length>1) {
												request.content_=enc[0]; //real type
												request.content_chrome=enc[1]||'application/octet-binary'; //save info encrypted
											};
											request.blob_=chrome?(new Buffer(0)):(new Blob([],{type:(request.content_chrome?request.content_chrome:request.content_)}));
											if ((!request.reload2_)&&(!request.reload_)) {
												request.check_hash=new Hash('sha1');
												var objectStore=open_db();
												objectStore.delete(request.hash_ini);
											};
											if (!request.clength_) {
												remove(request.bar_);
											};
										};
										if (chrome) {
											request.blob_=request.blob_.length?[request.blob_,data].concatBuffers():data;
										} else {
											request.blob_=new Blob([request.blob_,data],{type:(request.content_chrome?request.content_chrome:request.content_)});
										};
										if (request.check_hash) {
											request.check_hash.update(data);
										};
										if (request.clength_) {
											var size=request.blob_.size||request.blob_.length||request.blob_.byteLength;
											if ((size>=FILE_BLOCK)&&(request.d_length<request.clength_)) {
												request.queue_=request.queue_||[];
												var execute=function(data) {
													return function() {
														store_DB2(request,data);
													};
												};
												request.queue_.push(execute(request.blob_));
												//console.log('before db '+(request.blob_.size||request.blob_.length||request.blob_.byteLength)+' '+request.d_length);
												request.blob_=chrome?(new Buffer(0)):(new Blob([],{type:(request.content_chrome?request.content_chrome:request.content_)}));
												if (request.queue_.length===1) {
													request.queue_[0]();
												};
											} else {
												if (request.d_length>=request.clength_) {
													console.log('Queue fin '+request.d_length+' '+request.clength_+' '+(parseInt((8*BSIZE*request.received_/((Date.now()-request.start_t0)/1000)))+' bps ')+(request.blob_.size||request.blob_.length||request.blob_.byteLength));
													clearTimers(request.sendme_tout);
													clearTimers(request.waiting_);
													request.eof_=true;
													request.queue_.push(fin_.bind(request));
													if (request.queue_.length===1) {
														request.queue_[0]();
													};
												};
											};
											if (request.clength_||(request.d_length>=request.clength_)) {
												var prog=parseInt((request.d_length/request.clength_)*100);
												//request.bar_.progtxt.innerHTML=PROGTXT+prog+'%';
												request.bar_.progtxt.innerHTML=PROGTXT+parseInt(request.d_length/1000)+' kB';
												request.bar_.progbar.style.width=prog+'%';
											};
											request.queue_s.shift();
											if (request.queue_s.length) {
												request.queue_s[0]();
											};
										};
									};
								};
								request.queue_s=request.queue_s||[];
								request.queue_s.push(process(buff));
								if (request.queue_s.length===1) {
									request.queue_s[0]();
								};
							} else {
								console.log('EOF');
							};
						};
					};
					remove=function(obj) {
						if (obj) {
							if (obj.parentNode) {
								obj.parentNode.removeChild(obj);
							};
						};
					};
					fin_=function() {
						console.log('execute fin');
						this.eof_=true;
						this.queue_=[];
						this.queue_s=[];
						if (this.cid_) {
							if (this.cid_===db_cid) {
								console.log('sending db_end CIC '+this.cid_.circId+' sid '+this.sid_);
								this.cid_.send_db_end(1,this.sid_);
							} else {
								if (this.d_length<this.clength_) {
									this.cid_.send_relay_end(this.sid_);
								};
							};
							this.cid_.destroy_cid(this);
						};
						if (!this._stream_) {
							load_Blob_Url(this);
						} else {
							clearTimeout(this._torrentc_);
							isStreaming=false;
						};
					};
					stop_=function() {
						hide_menu('menu');
						//console.log('stopping '+this.eof);
						if (!this.eof) {
							this.nb_try=DB_NB_TRY+1;
							clearTimers(this.query_t0);
							clearTimers(this.sendme_tout);
							clearTimers(this.waiting_);
							clearTimeout(this.mp4box_t0);
							//console.log(this.cid_);
							this.pieces=0; //stop updateend
							if (this.cid_) {
								if (this.cid_===db_cid) {
									this.cid_.send_db_end(1,this.sid_);
								} else {
									if (this.d_length<this.clength_) {
										this.cid_.send_relay_end(this.sid_);
									};
								};
								this.cid_.destroy_cid(this);
							};
							this.eof_=true;
							this.queue_=this.queue_||[];
							if (!this._stream_) {
								this.queue_.push(fin_.bind(this));
								setTimeout(function() {Myalert("<p style='text-align:center'>Stopping download, please wait that pending data are processed, use resume to restart</p>")},500);
							} else {
								console.log('stop streaming');
								//setTimeout(function() {Myalert("<p style='text-align:center'>Streaming stopped</p>")},500);
								//remove(divMedia);
								//remove(mediaSrc);
								fin_.call(this);
							};
							if (this.queue_.length===1) {
								this.queue_[0]();
							};
						};
					};
					var stop_media=function(request,error) {
						Myalert('<p style="text-align:center"> Error: '+error+' - This media can not be streamed, please use Download</p>');
						remove(divMedia);
						remove(mediaSrc);
						stop_.call(request);
					};
					init_media=function(request) {
						var type=request.content_;
						console.log('init_media '+type);
						if ((type.indexOf('json')!==-1)||(((type.indexOf('video')!==-1)||(type.indexOf('audio')!==-1)||(type.indexOf('binary')!==-1))&&(!request._streaming_))) {
							remove(mediaSrc);
							mediaSrc=document.createElement('video');
							mediaSrc.className='mediasrc';
							//mediaSrc.autoplay=true;
							mediaSrc.controls=true;
							request._stream_=new Media();
							mediaSrc.src=URL.createObjectURL(request._stream_);
							mediaSrc._stream_=request._stream_;
							divMedia.appendChild(mediaSrc);
							addEvent(closebox,'mousedown',function(){remove(divMedia);remove(mediaSrc);request.nb_sources.forEach(function(req) {stop_.call(req)});stop_.call(request)},true);
							if (type.indexOf('json')===-1) {
								request._streaming_=true; //don't miss first chunks
							};
							var addsource=function(e) {
								if (type.indexOf('json')===-1) {
									if (type.indexOf('webm')!==-1) {
										request.nb_sources=[];
										request._streaming_=true;
										request._source_=request._stream_.addSourceBuffer('video/webm; codecs="vp8,vorbis"');
										//request._source_.addEventListener('error',function() {Myalert('<p style="text-align:center">This source can not be streamed or something unexpected happened, please retry</p>');stop_.call(request);try {request._stream_.endOfStream();} catch(ee) {}});
										console.log('play media video/webm; codecs="vp8,vorbis"');
										Myalert('<p style="text-align:center">Connected... the video is going to start, please wait</p>');
										mediaSrc.play();
									//} else if (type.indexOf('mp4')!==-1) {
									} else {
										if (!request.mime_codec) { //json parsed, file codec known
											//alert('source open');
											request.nb_sources=[];
											request._streaming_=true;
											request.mp4box=new MP4Box();
											Myalert('<p style="text-align:center">Connected... Analyzing if this video can be streamed, please wait</p>');
											//request.mp4box_t0=setTimeout(function() {stop_media(request,'mp4box timeout')},MP4BOX_TO);//remettre
											/*
											var request_moov=init_d_request(request.hash_ini,null,true);
											request_moov._moov_=true;
											request_moov._streaming_=true;
											request_moov.d_length=request.clength_-FIND_MOOV;
											request_moov._parent_=request;
											request_moov.mp4box=new MP4Box();
											request_moov.mp4box.onReady=function(info) {
												console.log('moov ready');
												request.mp4box.inputIsoFile.moov=this.inputIsoFile.moov;
												//alert(this.inputIsoFile.moov.boxes.length);
												//alert(request.mp4box.inputIsoFile.boxes.length);
												if (!request.mp4box.sampleListBuilt) {
													request.mp4box.inputIsoFile.buildSampleLists();
													request.mp4box.sampleListBuilt=true;
												}
												request.mp4box.inputIsoFile.updateSampleLists();
												request.mp4box.readySent=true;
												request.mp4box.onReady(info);
												request.mp4box.processSamples();
												delete request_moov.mp4box;
												//close moov mp4box
											};
											Tor(request_moov);
											*/
											request.mp4box._fileStart_=0;
											request.mp4box.onMoovStart=function () {
												console.log('moov start');
												Myalert('<p style="text-align:center">The video is going to start, please wait</p>');
											};
											request.mp4box.onReady=function(info) {
												//alert(this.inputIsoFile.moov.boxes.length);
												//alert(this.inputIsoFile.boxes.length);
												clearTimeout(request.mp4box_t0);
												if (request._moov_) {
													stop_.call(request._moov_);
													delete request_moov.mp4box;
												};
												console.log('play media mp4box ready');
												//Myalert('<p style="text-align:center">The video is going to start, please wait</p>');
												mediaSrc.play();
												//if (!info.isFragmented) {
													if (info.tracks) {
														while (info.tracks.length) {
															var subinfo=info.tracks.shift();
															if (subinfo) {
																var mime='video/mp4; codecs=" '+subinfo.codec+'"';
																console.log('mp4box mime: '+mime);
																console.log(JSON.stringify(subinfo));
																var supported=false;
																if (Media.isTypeSupported(mime)) {
																	supported=true;
																	var request_stream={append_buffer:[]};
																	request_stream._stream_=request._stream_;
																	request_stream._source_=request._stream_.addSourceBuffer(mime);
																	request_stream._source_.request=request_stream;
																	request_stream.pieces=request.pieces;
																	request_stream.mp4box=true;
																	request_stream.track=subinfo.id;
																	request_stream.received_=0;
																	request_stream.append_cursor=0;
																	request_stream.clength_=request.clength_;
																	request_stream.d_length_=0;
																	//var seg=3; //TODO - calculate
																	if (MP4BOX_PERF) {
																		var t0=Date.now();
																	};
																	request.mp4box.onSegment=function(id,req,buffer) {
																		//console.log('onsegment '+req.append_buffer.length
																		buffer=new Uint8Array(buffer);
																		req.received_++;
																		if (t0) {
																			count_mp4box++;
																			count_mp4box_data +=buffer.length;
																			time_mp4box_data +=Date.now()-t0;
																			if (count_mp4box%PERF_MP4BOX===0) {
																				if (time_mp4box_data) {
																					console.log('MP4BOX perf track '+req.track+':'+parseInt(count_mp4box_data*8/(time_mp4box_data/1000))+' bps - received from mp4box'+count_mp4box_data+' Bytes since '+parseInt(time_mp4box_data/1000)+' s');
																					count_mp4box=0;
																				};
																			};
																		};
																		//console.log('onsegment '+buffer.length+' '+buffer.toString('hex').substr(0,100));
																		//console.log('onsegment track '+req.track+' '+buffer.length+' '+buffer.toString('utf8').substr(0,100));
																		//req.d_length +=buffer.length;
																		req.append_buffer.push(buffer);
																		//appendbuffer(req,seg);
																		if (req._init_) {
																			if (req.received_===1) {
																				$_('alert_box').style.display='none';
																				req._source_.addEventListener('updateend',function() {
																					updateend(req);
																				});
																			};
																			updateend(req);
																		} else {
																			req.wait_init=true;
																		};
																	};
																	var nbSamples;
																	if (subinfo.video) {
																		var sample_size=parseInt(request.clength_/subinfo.nb_samples);
																		nbSamples=Math.ceil(MP4BOX_SAMPLE/sample_size);
																	} else {
																		nbSamples=1000;
																	};
																	console.log('nbsamples for track '+subinfo.id+' '+nbSamples);
																	request.mp4box.setSegmentOptions(subinfo.id,request_stream,{nbSamples:nbSamples});
																	//request.mp4box.setSegmentOptions(subinfo.id,request_stream);
																};
																if (!supported) {
																	console.log(subinfo.codec+' codec not supported');
																	stop_media(request,subinfo.codec+' codec not supported');
																	return;
																};
															};
														};
														var init=request.mp4box.initializeSegmentation();
														console.log('init length '+init.length);
														var onInitAppended=function(e) {
															var user=e.target.request;
															user._source_.removeEventListener('updateend',onInitAppended);
															user._init_=true;
															if (user.wait_init) {
																console.log('wait_init');
																user._source_.addEventListener('updateend',function() {
																	updateend(user);
																});
																updateend(user);
															};
														};
														while (init.length) {
															var subinit=init.shift();
															var user=subinit.user;
															console.log('init segment track '+subinit.user.track+' '+subinit.buffer.byteLength);
															user.append_buffer.push(new Uint8Array(subinit.buffer));
															user._source_.addEventListener("updateend",onInitAppended);
															updateend(user);
														};
													};
												//} else {
												//
												//};
											};
											request.mp4box.onError = function(e) {
												console.log('mp4box error');
												stop_media(request,'mp4box error');
												delete request.mp4box;
											};
										};
									} //else {
									//	stop_media(request);
									//};
								} else {
									while (request._wait_open.length) {
										request._wait_open.shift()();
									};
								};
							};
							addEvent(request._stream_,'webkitsourceopen',addsource,false);
							addEvent(request._stream_,'sourceopen',addsource,false);
							request._stream_.addEventListener('sourceclose', function(e) {console.log("SOURCE CLOSED");request.nb_sources.forEach(function(val) {stop_.call(val)});stop_.call(request)}, false);
							mediaSrc.addEventListener('seeking',function(){});
							mediaSrc.addEventListener('pause',function(){});
							mediaSrc.addEventListener('play',function(){});
							mediaSrc.addEventListener('error',function() {Myalert('<p style="text-align:center">Error: Media Source error - This media can not be streamed or something unexpected happened</p>');request.nb_sources.forEach(function(val) {stop_.call(val)});stop_.call(request);try {request._stream_.endOfStream();} catch(ee) {}});
						} else if (((type.indexOf('video')!==-1)||(type.indexOf('audio')!==-1)||(type.indexOf('binary')!==-1))&&(request._parent_)) {
							if (!request._moov_) {
								request._parent_.connected_sources++;
								if (request._parent_.connected_sources===request._parent_.nb_sources.length) {
									console.log('play media already fragmented');
									Myalert('<p style="text-align:center">Connected... the video is going to start, please wait</p>');
									mediaSrc.play();
								};
							};
						} else {
							Myalert('<p style="text-align:center">Error: not an audio/video file - This media can not be streamed, please use Download</p>');
							document.body.removeChild(divMedia);
							stop_.call(request);
						};
					};
					var delete_=function(e) {
						if (e.stopPropagation) {e.stopPropagation();};
						e.cancelBubble = true;
						hide_menu('menu');
						remove(this.thumb_)
					};
					var rename2_=function(e) {
						if (e.stopPropagation) {e.stopPropagation();};
						e.cancelBubble = true;
						hide_menu('menu2');
						var request=this;
						var func=function() {
							if (this.value) {
								var objectStore=open_db();
								var a=objectStore.get(request.hash_ini);
								a.onsuccess=(function(evt) {
									if (evt.target.result) {
										evt.target.result.name=this.value;
										request.name_=this.value;
										objectStore.put(evt.target.result);
										if (request.thumb_) {
											add_thumb_(request);
										};
										add_thumb(request);
									};
								}).bind(this); //this is input
							};
						};
						Myprompt("<p style='text-align:center'>Enter new name:</p>",func);
					};
					var delete2_=function(e) {
						if (e.stopPropagation) {e.stopPropagation();};
						e.cancelBubble = true;
						hide_menu('menu2');
						var objectStore=open_db();
						objectStore.delete(this.hash_ini);
						remove(this.thumb2_);
						remove(this.thumb_);
					};
					var reload_=function(e) {
						if (e.stopPropagation) {e.stopPropagation();};
						e.cancelBubble = true;
						var request=this;
						request.nb_try++;
						request.db_try=0;
						request.params_.db_=true;
						request.reload_=true;
						if (request.sid_) {
							delete request.cid_[request.sid_];
							delete request.sid_;
						};
						delete request.cid_;
						delete request.eof_;
						delete request.check_hash;
						delete request.last_saved;
						delete request.file_id;
						delete request.start_t0;
						clearTimers(request.query_t0);
						request.name_=this.name_||'';
						request.queue_=[];
						request.queue_s=[];
						request.blob_=chrome?(new Buffer(0)):(new Blob([],{type:(request.content_chrome?request.content_chrome:request.content_)}));
						request.cid_=db_cid;
						hide_menu('menu');
						remove(request.thumb_);
						request.bar_=progress_bar($_('downloaded'),request);
						Tor(request);
					};
					var reload2_=function(e) {
						// this {file_hash:request.file_hash,hash_ini:request.hash_ini,name_:request.name_,url:url,thumb:null,thumb_:request.thumb_,clength_:request.clength_,d_length:request.d_length,content_:request.content_,url_:request.url_,key:request.key,blob_:request.blob_}
						if (e.stopPropagation) {e.stopPropagation();};
						e.cancelBubble = true;
						var request=init_d_request(this.url_,this.hash_ini);
						request.clength_=this.clength_;
						request.d_length=this.d_length;
						request.content_=this.content_;
						//request.blob_=this.blob_;
						request.reload2_=true;
						request.thumb2_=this.thumb2_;
						delete request.eof_;
						delete request.last_saved;
						delete request.file_id;
						delete request.start_t0;
						request.queue_=[];
						request.queue_s=[];
						request.name_=this.name_||'';
						clearTimers(request.query_t0);
						request.nb_try++;
						request.db_try=0;
						request.blob_=chrome?(new Buffer(0)):(new Blob([],{type:(request.content_chrome?request.content_chrome:request.content_)}));
						request.cid_=db_cid;
						hide_menu('menu2');
						remove(this.thumb_);
						request.bar_=progress_bar($_('downloaded'),request);
						Tor(request);
					};
					var encrypt_decrypt_w=function() {
						// this {file_hash:request.file_hash,hash_ini:request.hash_ini,name_:request.name_,url:url,thumb:null,thumb_:request.thumb_,clength_:request.clength_,d_length:request.d_length,content_:request.content_,url_:request.url_,key:request.key,blob_:request.blob_}
						var decrypt=this.key;
						hide_menu('menu2');
						addEvent(document.body,'mousedown',function() {},false);
						if (decrypt) {
							setTimeout(function() {Myalert("<p style='text-align:center'>"+(decrypt?'De':'En')+"crypting file... Please wait until the file appears in the Local files box, this can take some time depending on the size of the file</p>")},800);
						} else {
							Myalert("<p style='text-align:center'>"+(decrypt?'De':'En')+"crypting file... Please wait until the file appears in the Local files box, this can take some time depending on the size of the file</p>");
						};
						$_('progress-alert').style.display='block';
						$_('progint-alert').style.width='0%';
						var key=decrypt?(new Buffer(decrypt,'hex')):Rand(16);
						var file=this.blob_;
						var size=file.size;
						var clone={file_hash:'00',hash_ini:rand_hash(),name_:(decrypt?(remove_ext(this.name_)):(this.name_+'.'+ENC_EXT)),clength_:size,d_length:0,content_:this.content_,url_:this.url_,key:(decrypt?'':key.toString('hex')),content_chrome:decrypt?null:(chrome?'application/binary':null),blob_:(chrome?(new Buffer(0)):(new Blob([],{type:decrypt?this.content_:'application/octet-binary'}))),queue_:[]};
						var tsize=0;
						if (decrypt) {
							var objectStore=open_db();
							var a=objectStore.get(this.hash_ini);
							a.onsuccess=(function(evt) {
								if (evt.target.result) {
									var res=evt.target.result;
									res.key=decrypt;
									this.key=decrypt;
									addEvent(this.thumb2_,'mousedown',show_menu2.bind({file_hash:this.file_hash,hash_ini:this.hash_ini,name_:this.name_,url:url,thumb2_:this.thumb2_,thumb_:this.thumb_,clength_:this.clength_,d_length:this.d_length,content_:this.content_,url_:this.url_,key:this.key,content_chrome:this.content_chrome,blob_:this.blob_}),false);
									objectStore.put(res);
								};
							}).bind(this);
						};
						var worker=new Worker(URL.createObjectURL(new Blob([workerjs])));
						var file_enc=[];
						var buff;
						if (WORKER_PERF) {
							var t0=Date.now();
						};
						worker.onmessage=(function(evt) {
							var data=evt.data;
							var res=(data instanceof Array)?data[0]:data;
							if (!(data instanceof Array)) {
								tsize +=res.length;
							};
							var l=tsize;
							//$_('progint-alert').style.width=parseInt(100*(tsize/size))+'%';
							if (t0) {
								console.log('worker perf '+(Date.now()-t0));
								t0=Date.now();
							};
							file_enc.push(res);
							if ((tsize%DB_BLOCK===0)||(tsize===size)) {
								var buff=file_enc;
								var execute=(function() {
									//console.log('execute '+l+' '+(data instanceof Array)+' '+Date.now());
									$_('progint-alert').style.width=parseInt(100*(l/size))+'%';
									if (!(data instanceof Array)) {
										this.file_hash='00';
										this.d_length=l;
										store_DB2(this,chrome?buff:(new Blob(buff,{type:decrypt?this.content_:'application/octet-binary'})));
									} else {
										this.check_hash=true;
										this.file_hash=res;
										addEvent(document.body,'mousedown',function() {setTimeout(clear_menu,1000)},false);
										$_('progress-alert').style.display='none';
										store_DB(this,true);
									};
								}).bind(this);
								file_enc=[];
								this.queue_.push(execute);
								if (this.queue_.length===1) {
									this.queue_[0]();
								};
							};

						}).bind(clone);
						worker.postMessage(['aes-128-ctr',file,key,IV]);
						/*
						var res=new Uint8Array(reader.result);
						var BL=512;
						var n=0;
						var a;
						var H=forge.aes.createcipheriv('aes-128-ctr',key,IV);
						alert("0");
						while(res.length) {
							//console.log(n);
							a=H.update(res.subarray(0,BL));
							res=res.subarray(Math.min(res.length,BL));
							n++;
						};
						alert(a.substr(0,16));
						*/
					};
					var encrypt2_=function(e) {
						if (e.stopPropagation) {e.stopPropagation();};
						e.cancelBubble = true;
						encrypt_decrypt_w.call({file_hash:this.file_hash,hash_ini:this.hash_ini,name_:this.name_,url:url,thumb2_:this.thumb2_,thumb_:this.thumb_,clength_:this.clength_,d_length:this.d_length,content_:this.content_,url_:this.url_,key:this.key,blob_:this.blob_});
					};
					var decrypt2_=function(e) {
						if (e.stopPropagation) {e.stopPropagation();};
						e.cancelBubble = true;
						var request={file_hash:this.file_hash,hash_ini:this.hash_ini,name_:this.name_,content_:this.content_,url:url,thumb2_:this.thumb2_,thumb_:this.thumb_,clength_:this.clength_,d_length:this.d_length,content_:this.content_,url_:this.url_,key:this.key,blob_:this.blob_};
						if ((this.blob_.type!==this.content_)||(get_extension(this.name_)==='enc')) {
							if (this.key) {
								encrypt_decrypt_w.call(request);
							} else {
								//ask for the key
								var func=function() {
									var key=this.value;
									if (key.length===32) {
										request.key=key;
										encrypt_decrypt_w.call(request);
									} else {
										setTimeout(function() {Myalert("<p style='text-align:center'>Please enter a valid key</p>")},800);
									};
								}; //this is input
								Myprompt("<p style='text-align:center'>Enter key:</p>",func);
							}
						} else {
							Myalert("<p style='text-align:center'>This is not an encrypted file</p>")
						};
					};
					var property2_=function(e) {
						// this {file_hash:request.file_hash,hash_ini:request.hash_ini,name_:request.name_,url:url,thumb:null,thumb_:request.thumb_,clength_:request.clength_,d_length:request.d_length,content_:request.content_,url_:request.url_,key:request.key,blob_:request.blob_}
						if (e.stopPropagation) {e.stopPropagation();};
						e.cancelBubble = true;
						Myalert('<li>Hash Name: '+this.hash_ini+'</li><li>File hash: '+this.file_hash+'</li><li>File Type: '+this.content_+'</li><li>File size: '+this.clength_+' bytes</li><li>Current size: '+this.d_length+' bytes</li><li>Key: '+((this.content_chrome?false:((this.content_===this.blob_.type)&&(get_extension(this.name_)!=='enc')))?('Not encrypted'):(this.key?this.key:'Get the encryption key from peer'))+'</li>');
					};
					var add_menu_event=function(menu_item,func) {
						var obj=$_(menu_item);
						if (obj) {
							if (obj.event_) {
								delEvent(obj,'mousedown',obj.event_,false);
							};
							obj.event_=func.bind(this);
							addEvent(obj,'mousedown',obj.event_,false);
						};
					};
					var del_menu_event=function(menu_item) {
						var obj=$_(menu_item);
						if (obj) {
							if (obj.event_) {
								delEvent(obj,'mousedown',obj.event_,false);
							};
						};
					};
					var show_menu=function(e) {
						if (e.stopPropagation) {e.stopPropagation();};
						e.cancelBubble = true;
						var men=$_('menu2');
						men.style.display='none';
						men=$_('menu');
						men.style.top=getmouseY(e)+'px';
						men.style.left=getmouseX(e)+'px';
						men.style.display='block';
						$_('open').getElementsByTagName('a').item(0).href=URL.createObjectURL(this.blob_);
						add_menu_event.call(this,'delete',delete_);
						add_menu_event.call(this,'reload',reload_);
						if (this.clength_===this.d_length) {
							hide($_('reload'));
						} else {
							show($_('reload'));
						};
					};
					var show_menu2=function(e) {
						if (e.stopPropagation) {e.stopPropagation();};
						e.cancelBubble = true;
						/*
						$_('alert_box').style.top=(getmouseY(e)-250)+'px';
						$_('alert_box').style.display='none';
						$_('prompt_box').style.top=(getmouseY(e)-250)+'px';
						$_('prompt_box').style.display='none';
						*/
						if (db_cid&&NB_C>=1) {
							var men=$_('menu');
							men.style.display='none';
							men=$_('menu2');
							men.style.top=getmouseY(e)+'px';
							men.style.left=getmouseX(e)+'px';
							men.style.display='block';
							$_('open2').getElementsByTagName('a').item(0).href=URL.createObjectURL(this.blob_);
							add_menu_event.call(this,'delete2',delete2_);
							//add_menu_event.call(this,'open2',open2_);
							add_menu_event.call(this,'rename2',rename2_);
							add_menu_event.call(this,'property2',property2_);
							if (this.d_length===this.clength_) {
								hide($_('reload2'));
							} else {
								show($_('reload2'));
								add_menu_event.call(this,'reload2',reload2_);
							};
							var t=this.blob_.type||(this.content_chrome?this.content_chrome:this.content_);
							if ((this.key)||((t!==this.content_))||(get_extension(this.name_)==='enc')) {
								hide($_('encrypt2'));
								show($_('decrypt2'));
								add_menu_event.call(this,'decrypt2',decrypt2_);
							} else {
								show($_('encrypt2'));
								add_menu_event.call(this,'encrypt2',encrypt2_);
								hide($_('decrypt2'));
								//add_menu_event.call(this,'decrypt2',decrypt2_);
							};
						} else {
							Myalert('<p style="text-align:center">Not enough circuits established - Please wait to see at least one Peer to Peer circuit and one Direct Download circuit</p>');
						};
					};
					var hide_menu=function(menu) {
						var men=$_(menu);
						men.style.display='none';
					};
					clear_menu=function() {
						var men=$_('menu');
						men.style.display='none';
						men=$_('menu2');
						men.style.display='none';
						$_('alert_box').style.display='none';
						$_('prompt_box').style.display='none';
					};
					load_Blob_Url=function(request) {
						if (request.blob_) {
							if (request.check_hash) {
								request.file_hash=request.check_hash.digest('hex');
							}
							console.log('Blob loaded '+request.d_length+' '+(request.file_hash||''));
							store_DB(request);
							remove(request.bar_);

						} else {
							remove(request.bar_);
						};
					};
					addEvent(document.body,'mousedown',function() {setTimeout(clear_menu,1000)},false); //pb click not detected
					addEvent($_('dialog-message'),'mousedown',function(e) {if (e.stopPropagation) {e.stopPropagation();};e.cancelBubble=true;},false);
					var load=function(url,stream) {
						if ((!stream)||(stream&&(mediaSrc.parentNode?false:(!isStreaming)))) {
							if (db_cid&&NB_C>1) {
								console.log('Start loading url');
								var request=init_d_request(url,null,stream);
								if ((request.hash_ini.length===40)&&(!isNaN(Number('0x'+request.hash_ini)))) {
									//var objectStore=open_db();
									//objectStore.delete(request.hash_ini);
									request.d_length=0;
									remove(request.thumb_);
									remove($_(request.hash_ini));
									if (!stream) {
										request.bar_=progress_bar($_('downloaded'),request);
										remove(mediaSrc);
										remove(divMedia);
										isStreaming=false;
									} else {
										isStreaming=true;
										addmediasource(request);
									};
									if (request.bar_||(request._stream_&&Media)) {
										Tor(request);
									};
								} else {
									Myalert('<p style="text-align:center">Please enter a valid reference (hash_name, magnet link, infohash or url)</p>');
								};
							} else {
								Myalert('<p style="text-align:center">Not enough circuits established - Please wait to see at least one Peer to Peer circuit and one Direct Download circuit</p>');
							};
						};
					};
					var init_d_request=function(url,hash,stream) {
						if (!hash) {
							var mag=magnet(url);
							if (mag) {
								hash=mag;
								url='';
							} else if ((url.indexOf('http')!==-1)||(url.indexOf('https')!==-1)) {
								var H = crypto.createhash('sha1');
								H.update(new Buffer(url,'utf8')); //TODO check url www or not, etc
								hash=H.digest('hex');
							} else {
								hash=url;
								url='';
							};
						};
						var request=ini_nosocks_request(url);
						request.params_.hash_=new Buffer(hash,'hex');
						request.hash_ini=hash;
						request.url_=url;
						url=url_decode(url);
						request.params_.stream=get_request(url.host,url.rest);
						request.params_.host=url.host+':'+((url.protcol==='https')?'443':'80');
						request.params_.db_=true;
						request.cid_=db_cid;
						request.download_=[];
						//request.blob_=[];
						request.d_length=0;
						request.nb_try=0;
						var tmp=url.rest.split('/');
						request.name_=tmp.length?(tmp[tmp.length-1]):'';
						request._write_=function(buff) {load_Blob(buff,this)};
						request.queue_=[];
						request.db_try=0;
						request.sendme_tout=[];
						request.waiting_=[];
						request.queue_s=[];
						request._stream_=stream||false;
						if (stream) {
							request.stream_buffer=[];
							request.append_buffer=[];
							request.append_cursor=0;
							request.append_wait=new Buffer(0);
							request.nb_sources=[];
							request.connected_sources=0;
							request.debug_chunk=[];
							request.append_to=[];
							request._wait_open=[];
							request._json_='';
						};
						return request;
					};
					var addmediasource=function(request) {
						Media=window.MediaSource||window.webkitMediaSource||window.WebKitMediaSource||window.webkitMediaSource||window.MozMediaSource||false;
						if (Media) {
							divMedia=document.createElement('div');
							divMedia.className='media';
							document.body.appendChild(divMedia);
							closebox=document.createElement('div');
							closebox.className='boxclose';
							divMedia.appendChild(closebox);
							addEvent(closebox,'mousedown',function(){remove(divMedia);remove(mediaSrc);stop_.call(request)},true);
						} else {
							Myalert('<p style="text-align:center">Media Source is not available in your browser, so streaming is not possible, please update it or try with Chrome browser.</p>');
						};
					};
					var drawChart=function() {
						ini_direct_chart();
						ini_peer_chart();
						chart1_int=setInterval(function() {redraw_chart(Dchart,Ddata,Doptions)},5000);
						chart2_int=setInterval(function() {redraw_chart(Pchart,Pdata,Poptions)},5000);
						addEvent($_('chart1'),'mousedown',function() {
							if (CHART1) {
								clearInterval(chart1_int);
							} else {
								chart1_int=setInterval(function() {redraw_chart(Dchart,Ddata,Doptions)},5000);
							};
							CHART1=!CHART1;
						},false);
						addEvent($_('chart2'),'mousedown',function() {
							if (CHART2) {
								clearInterval(chart2_int);
							} else {
								chart2_int=setInterval(function() {redraw_chart(Pchart,Pdata,Poptions)},5000);
							};
							CHART2=!CHART2;
						},false);
					};
					//google.setOnLoadCallback(drawChart);
					var load_files=function() {
						peersmDB.list=function(func) {
							//if (!peersmDB.init) { //FF Bug #901884
							//	peersmDB.init=true; //FF Bug #901884
								var objectStore=peersmDB.db.transaction([peersmcode],'readwrite').objectStore(peersmcode);
								objectStore.openCursor().onsuccess = function(event) {
								  var cursor = event.target.result;
									if (cursor) {
										var val=cursor.value;
										func(val);
										cursor.continue();
									} else {
										if (restore_chunk) {
											restoring_chunk();
										};
									};
								};
							//};
						};
						peersmDB.list(thumb_db_list);
					};
					var thumb_db_list=function(val) {
						if (chrome) {
							val.data=new Blob(val.data,{type:val.enc?val.enc:val.type});
						};
						var thumb2=thumb(val,val.name_hash);
						$_('local').appendChild(thumb2);
						addEvent(thumb2,'mousedown',show_menu2.bind({file_hash:val.hash,hash_ini:val.name_hash,name_:val.name,thumb2_:thumb2,clength_:val.file_length,d_length:val.current_length,content_:val.type,url_:val.file_url,key:val.key,content_chrome:val.enc||'',blob_:val.data}),false);
						if (val.file_length!==val.current_length) {
							thumb2.firstChild.style.backgroundColor='orange';
						};
					};
					if (!graph_) {
						var script=document.createElement('script');
						script.src='http://www.peersm.com/gchart.js';
						script.onload=drawChart;
						document.body.appendChild(script);
						//drawChart();
					} else {
						var nb=(db_cid?1:0)+(NB_C>=0?NB_C:0);
						$_('direct_text').innerHTML='Direct, P2P and bittorrent anonymized circuits : '+nb+(nb>1?' circuits':' circuit');
						//$_('peer_text').innerHTML='Peer to Peer : '+(db_cid?1:0)+((!db_cid)?' circuit':(' circuit ('+db_cid.server_.name+')'));
						show($_('direct_text'));
						show($_('peer_text'));
					};
					$_('prompt-input').removeAttribute('type');
					load_files();
					/*for (var i=0;i<10;i++) {
						progress_bar($_('downloaded'),{});
					};
					for (var i=0;i<10;i++) {
						progress_bar($_('local'),{});
					};*/
				};
			};
		};
	//});
})();