<commit_before>var https = require('https');
var http = require('http');
var net = require('net');
var url = require('url');
var mime = require('mime');
var extend = require('util')._extend;
var util = require('../util');
var WhistleTransform = util.WhistleTransform;
var SpeedTransform = util.SpeedTransform;
var STATUS_CODE = http.STATUS_CODES || {};
var rules = require('../rules');

function readResRules(rules, callback) {
	var count = 4;
	var result = {};
	function handleCallback() {
		--count <= 0 && callback(result);
	}

	util.parseRuleToJson(rules.resHeaders, function(headers) {
		if (headers) {
			result.headers = headers;
		}
		handleCallback();
	});

	['resBody', 'prependRes', 'appendRes'].forEach(function(key) {
		util.getRuleValue(rules[key], function(value) {
			if (value != null) {
				if (key == 'resBody') {
					result.body = value;
				} else if (key == 'prependRes') {
					result.top = value;
				} else {
					result.bottom = value;
				}
			}
			handleCallback();
		});
	});
}

module.exports = function(req, res, next) {
	var config = this.config;
	var self = this;
	var responsed, newResHeaders;

	req.request = function(options) {
		options = options || req.options;
		req.realUrl = res.realUrl = options.href;

		var now = Date.now();
		var proxyUrl;
		if (util.isProxyProtocol(options.protocol)) {
			proxyUrl = util.rule.getMatcher(req.rules.rule);
			options = url.parse(req.fullUrl);
		}

		rules.resolveHost(options.localDNS && net.isIP(options.host) ? options.host :
			(proxyUrl && util.replaceProtocol(proxyUrl, util.getProtocol(req.fullUrl)) || options.href),
				function(err, ip) {
			req.dnsTime = Date.now() - now;
			req.hostIp = ip;

			if (err) {
				util.drain(req, function() {
					  res.response(util.wrapGatewayError('DNS Lookup Failed\r\n' + util.getErrorStack(err)));
					});
				return;
			}

			var isHttps = options.protocol == 'https:';
			var headers = req.headers;
			if (proxyUrl) {
				var proxyOptions = url.parse(proxyUrl);
				proxyOptions.host = ip;
				var isSocks = proxyOptions.protocol == 'socks:';
				if (isSocks || isHttps) {
					util.connect({
						isSocks: isSocks,
						host: ip,
						port: proxyOptions.port,
						isHttps: isHttps,
						url: req.fullUrl,
						auth: proxyOptions.auth,
						headers: {
							host: headers.host,
							'proxy-connection': 'keep-alive',
							'user-agent': headers['user-agent']
						}
					}, function(err, socket) {
						if (err) {
							util.drain(req, function() {
								  res.response(util.wrapGatewayError('Failed to connect to the proxy server: '
										  + proxyUrl + '; IP: ' + ip
										  + ' \r\n' + util.getErrorStack(err)));
								});
							return;
						}
						options.createConnection = function() {
							return socket;
						};
						request(options);
					});
					return;
				}

				options.port = proxyOptions.port || config.port; //http proxy
			}

			options.host = ip;//设置ip
			if ((options.port || 80) == config.port && util.isLocalAddress(options.host)) {
				util.drain(req, function() {
					res.response(util.wrapResponse({
						statusCode: 302,
						headers: {
							location: 'http://' + config.localUIHost + (options.path || '')
						}
					}));
				});
			} else {
				options.agent = isHttps ? config.httpsAgent : config.httpAgent;
				request(options);
			}

			function request(options) {
				options.headers = headers;
				options.method = req.method;
				options.rejectUnauthorized = false;
				if (!proxyUrl) {
					headers.host = options.hostname + (options.port ? ':' + options.port : '');
				}
				headers.Host = headers.host;//tencent的服务器不识别小写的host
				options.hostname = null; //防止自动dns
				var client = (isHttps ? https : http)
											.request(options, res.response)
												.on('error', function(err) {
													util.drain(req, function() {
														res.response(util.wrapGatewayError(util.getErrorStack(err)));
													});
												});
				req.pipe(client);

			}

		}, req.filter.host);
	};

	res.response = function(_res) {
		if (responsed) {
			return;
		}
		responsed = true;
		if (_res.realUrl) {
			req.realUrl = res.realUrl = _res.realUrl;
		}
		var headers = res.headers = _res.headers;
		res.trailers = _res.trailers;
		res.statusCode = _res.statusCode = _res.statusCode || 0;

		var newCookies = newResHeaders && newResHeaders['set-cookie'];
		if (newCookies) {
			var cookies = res.headers['set-cookie'];
			if (Array.isArray(cookies)) {
				var newNameMap = {};
				newCookies.forEach(function(cookie) {
					var index = cookie.indexOf('=');
					var name = index == -1 ? name : cookie.substring(0, index);
					newNameMap[name] = 1;
				});
				cookies.forEach(function(cookie) {
					var index = cookie.indexOf('=');
					var name = index == -1 ? name : cookie.substring(0, index);
					if (!newNameMap[name]) {
						newCookies.push(cookie);
					}
				});
			}
			res.headers['set-cookie'] = newCookies;
			delete newResHeaders['set-cookie'];
		}
		extend(res.headers, newResHeaders);
		if (!res.headers.pragma) {
			delete res.headers.pragma;
		}
		if (headers.location) {
			//nodejs的url只支持ascii，对非ascii的字符要encodeURIComponent，否则传到浏览器是乱码
			headers.location = util.encodeNonAsciiChar(headers.location);
		}
		res.src(_res);
		res.writeHead(_res.statusCode, _res.headers);
		res.trailers && res.addTrailers(res.trailers);
	};

	util.parseRuleToJson(req.rules.res,
			function(data) {
		if (req.rules.head && req.rules.head.res) {
			data = extend(req.rules.head.res, data);
		}

		util.readInjectFiles(data, function(data) {
			readResRules(req.rules, function(result) {
				data = extend(data || {}, result);
				var cache = util.getMatcherValue(req.rules.cache);
				var maxAge = parseInt(cache, 10);
				var noCache = /^(?:no|no-cache|no-store)$/i.test(cache) || maxAge < 0;
				if (maxAge > 0 || noCache) {
					util.setHeaders(data, {
						'cache-control': noCache ? (/^no-store$/i.test(cache) ? 'no-store' : 'no-cache') : 'max-age=' + maxAge,
						'expires': new Date(Date.now() + (noCache ? -60000000 : maxAge)).toGMTString(),
						'pragma': noCache ? 'no-cache' : ''
					});
				}

				var type = util.getMatcherValue(req.rules.resType);
				if (type) {
					type = type.indexOf('/') != -1 ? type : mime.lookup(type, type);
					util.setHeader(data, 'content-type', type);
				}

				var resDelay = util.getMatcherValue(req.rules.resDelay);
				resDelay = resDelay && parseInt(resDelay, 10);
				if (resDelay > 0) {
					data.delay = resDelay;
				}

				var resSpeed = util.getMatcherValue(req.rules.resSpeed);
				resSpeed = resSpeed && parseFloat(resSpeed);
				if (resSpeed > 0) {
					data.speed = resSpeed;
				}

				var statusCode = util.getMatcherValue(req.rules.statusCode);
				if (statusCode) {
					data.statusCode = statusCode;
					data.body = data.body || STATUS_CODE[statusCode];
				} else {
					var redirect = util.getMatcherValue(req.rules.redirect);
					if (redirect) {
						data.statusCode = 302;
						util.setHeader(data, 'location', redirect);
					}
				}

				util.parseRuleToJson(req.rules.resCookies,
						function(cookies) {
					var list = cookies && Object.keys(cookies);
					var curCookies = data.headers && data.headers['set-cookie'];
					if (!Array.isArray(curCookies)) {
						curCookies = curCookies ? [curCookies + ''] : [];
					}

					if (list && list.length) {
						var result = {};
						list.forEach(function(name) {
							var cookie = cookies[name];
							name = util.encodeURIComponent(name);
							if (cookie == null) {
								result[name] = null;
							} else if (typeof cookie == 'object') {
								var attrs = [];
								var value = cookie.value;
								if (value != null) {
									attrs.push(value ? util.encodeURIComponent(value) : value);
								}
								var maxAge = cookie.maxAge && parseInt(cookie.maxAge, 10);
								if (Number.isInteger(maxAge)) {
									attrs.push('expires=' + new Date(Date.now() + maxAge * 1000).toGMTString());
									attrs.push('max-age=' + maxAge);
								}
								cookie.secure && attrs.push('secure');
								cookie.domain && attrs.push(cookie.domain);
								cookie.httpOnly && attrs.push('httpOnly');
								result[name] = attrs.join('; ');
							} else {
								result[name] = util.encodeURIComponent(cookie);
							}
						});

						curCookies.forEach(function(cookie) {
							var index = cookie.indexOf('=');
							var name = index == -1 ? name : cookie.substring(0, index);
							if (!(name in result)) {
								result[name] = index == -1 ? null : cookie.substring(index + 1);
							}
						});

						cookies = Object.keys(result).map(function(name) {
							var value = result[name];
							return name + (value == null ? '' : '=' + value);
						});
						util.setHeader(data, 'set-cookie', cookies);
					}

					if (data.headers) {
						data.headers = newResHeaders = util.lowerCaseify(data.headers);
					}

					if (data.statusCode) {
						req.hostIp = '127.0.0.1';
						util.drain(req, function() {
							res.response(util.wrapResponse(data));
						});
						return;
					}

					if (data.speed || data.delay) {
						res.add(new SpeedTransform(data));
					}

					delete data.headers;
					delete data.speed;
					delete data.delay;

					if (!util.isEmptyObject(data)) {
						res.addZipTransform(new WhistleTransform(data));
					}
					next();
				});

			});
		});
	});
};<commit_msg>cookies<commit_after>var https = require('https');
var http = require('http');
var net = require('net');
var url = require('url');
var mime = require('mime');
var extend = require('util')._extend;
var util = require('../util');
var WhistleTransform = util.WhistleTransform;
var SpeedTransform = util.SpeedTransform;
var STATUS_CODE = http.STATUS_CODES || {};
var rules = require('../rules');

function readResRules(rules, callback) {
	var count = 4;
	var result = {};
	function handleCallback() {
		--count <= 0 && callback(result);
	}

	util.parseRuleToJson(rules.resHeaders, function(headers) {
		if (headers) {
			result.headers = headers;
		}
		handleCallback();
	});

	['resBody', 'prependRes', 'appendRes'].forEach(function(key) {
		util.getRuleValue(rules[key], function(value) {
			if (value != null) {
				if (key == 'resBody') {
					result.body = value;
				} else if (key == 'prependRes') {
					result.top = value;
				} else {
					result.bottom = value;
				}
			}
			handleCallback();
		});
	});
}

module.exports = function(req, res, next) {
	var config = this.config;
	var self = this;
	var responsed, newResHeaders;

	req.request = function(options) {
		options = options || req.options;
		req.realUrl = res.realUrl = options.href;

		var now = Date.now();
		var proxyUrl;
		if (util.isProxyProtocol(options.protocol)) {
			proxyUrl = util.rule.getMatcher(req.rules.rule);
			options = url.parse(req.fullUrl);
		}

		rules.resolveHost(options.localDNS && net.isIP(options.host) ? options.host :
			(proxyUrl && util.replaceProtocol(proxyUrl, util.getProtocol(req.fullUrl)) || options.href),
				function(err, ip) {
			req.dnsTime = Date.now() - now;
			req.hostIp = ip;

			if (err) {
				util.drain(req, function() {
					  res.response(util.wrapGatewayError('DNS Lookup Failed\r\n' + util.getErrorStack(err)));
					});
				return;
			}

			var isHttps = options.protocol == 'https:';
			var headers = req.headers;
			if (proxyUrl) {
				var proxyOptions = url.parse(proxyUrl);
				proxyOptions.host = ip;
				var isSocks = proxyOptions.protocol == 'socks:';
				if (isSocks || isHttps) {
					util.connect({
						isSocks: isSocks,
						host: ip,
						port: proxyOptions.port,
						isHttps: isHttps,
						url: req.fullUrl,
						auth: proxyOptions.auth,
						headers: {
							host: headers.host,
							'proxy-connection': 'keep-alive',
							'user-agent': headers['user-agent']
						}
					}, function(err, socket) {
						if (err) {
							util.drain(req, function() {
								  res.response(util.wrapGatewayError('Failed to connect to the proxy server: '
										  + proxyUrl + '; IP: ' + ip
										  + ' \r\n' + util.getErrorStack(err)));
								});
							return;
						}
						options.createConnection = function() {
							return socket;
						};
						request(options);
					});
					return;
				}

				options.port = proxyOptions.port || config.port; //http proxy
			}

			options.host = ip;//设置ip
			if ((options.port || 80) == config.port && util.isLocalAddress(options.host)) {
				util.drain(req, function() {
					res.response(util.wrapResponse({
						statusCode: 302,
						headers: {
							location: 'http://' + config.localUIHost + (options.path || '')
						}
					}));
				});
			} else {
				options.agent = isHttps ? config.httpsAgent : config.httpAgent;
				request(options);
			}

			function request(options) {
				options.headers = headers;
				options.method = req.method;
				options.rejectUnauthorized = false;
				if (!proxyUrl) {
					headers.host = options.hostname + (options.port ? ':' + options.port : '');
				}
				headers.Host = headers.host;//tencent的服务器不识别小写的host
				options.hostname = null; //防止自动dns
				var client = (isHttps ? https : http)
											.request(options, res.response)
												.on('error', function(err) {
													util.drain(req, function() {
														res.response(util.wrapGatewayError(util.getErrorStack(err)));
													});
												});
				req.pipe(client);

			}

		}, req.filter.host);
	};

	res.response = function(_res) {
		if (responsed) {
			return;
		}
		responsed = true;
		if (_res.realUrl) {
			req.realUrl = res.realUrl = _res.realUrl;
		}
		var headers = res.headers = _res.headers;
		res.trailers = _res.trailers;
		res.statusCode = _res.statusCode = _res.statusCode || 0;

		var newCookies = newResHeaders && newResHeaders['set-cookie'];
		if (newCookies) {
			var cookies = res.headers['set-cookie'];
			if (Array.isArray(cookies)) {
				var newNameMap = {};
				newCookies.forEach(function(cookie) {
					var index = cookie.indexOf('=');
					var name = index == -1 ? name : cookie.substring(0, index);
					newNameMap[name] = 1;
				});
				cookies.forEach(function(cookie) {
					var index = cookie.indexOf('=');
					var name = index == -1 ? name : cookie.substring(0, index);
					if (!newNameMap[name]) {
						newCookies.push(cookie);
					}
				});
			}
			res.headers['set-cookie'] = newCookies;
			delete newResHeaders['set-cookie'];
		}
		extend(res.headers, newResHeaders);
		if (!res.headers.pragma) {
			delete res.headers.pragma;
		}
		if (headers.location) {
			//nodejs的url只支持ascii，对非ascii的字符要encodeURIComponent，否则传到浏览器是乱码
			headers.location = util.encodeNonAsciiChar(headers.location);
		}
		res.src(_res);
		res.writeHead(_res.statusCode, _res.headers);
		res.trailers && res.addTrailers(res.trailers);
	};

	util.parseRuleToJson(req.rules.res,
			function(data) {
		if (req.rules.head && req.rules.head.res) {
			data = extend(req.rules.head.res, data);
		}

		util.readInjectFiles(data, function(data) {
			readResRules(req.rules, function(result) {
				data = extend(data || {}, result);
				var cache = util.getMatcherValue(req.rules.cache);
				var maxAge = parseInt(cache, 10);
				var noCache = /^(?:no|no-cache|no-store)$/i.test(cache) || maxAge < 0;
				if (maxAge > 0 || noCache) {
					util.setHeaders(data, {
						'cache-control': noCache ? (/^no-store$/i.test(cache) ? 'no-store' : 'no-cache') : 'max-age=' + maxAge,
						'expires': new Date(Date.now() + (noCache ? -60000000 : maxAge)).toGMTString(),
						'pragma': noCache ? 'no-cache' : ''
					});
				}

				var type = util.getMatcherValue(req.rules.resType);
				if (type) {
					type = type.indexOf('/') != -1 ? type : mime.lookup(type, type);
					util.setHeader(data, 'content-type', type);
				}

				var resDelay = util.getMatcherValue(req.rules.resDelay);
				resDelay = resDelay && parseInt(resDelay, 10);
				if (resDelay > 0) {
					data.delay = resDelay;
				}

				var resSpeed = util.getMatcherValue(req.rules.resSpeed);
				resSpeed = resSpeed && parseFloat(resSpeed);
				if (resSpeed > 0) {
					data.speed = resSpeed;
				}

				var statusCode = util.getMatcherValue(req.rules.statusCode);
				if (statusCode) {
					data.statusCode = statusCode;
					data.body = data.body || STATUS_CODE[statusCode];
				} else {
					var redirect = util.getMatcherValue(req.rules.redirect);
					if (redirect) {
						data.statusCode = 302;
						util.setHeader(data, 'location', redirect);
					}
				}

				util.parseRuleToJson(req.rules.resCookies,
						function(cookies) {
					var list = cookies && Object.keys(cookies);
					var curCookies = data.headers && data.headers['set-cookie'];
					if (!Array.isArray(curCookies)) {
						curCookies = curCookies ? [curCookies + ''] : [];
					}

					if (list && list.length) {
						var result = {};
						curCookies.forEach(function(cookie) {
							var index = cookie.indexOf('=');
							if (index == -1) {
								result[cookie] = null;
							} else {
								result[cookie.substring(0, index)] = cookie.substring(index + 1);
							}
						});

						list.forEach(function(name) {
							var cookie = cookies[name];
							name = util.encodeURIComponent(name);
							if (!cookie || typeof cookie != 'object') {
								result[name] = cookie ? util.encodeURIComponent(cookie) : cookie;
							} else {
								var attrs = [];
								var value = cookie.value;
								attrs.push(value ? util.encodeURIComponent(value) : (value == null ? '' : value));
								var maxAge = cookie.maxAge && parseInt(cookie.maxAge, 10);
								if (Number.isInteger(maxAge)) {
									attrs.push('expires=' + new Date(Date.now() + maxAge * 1000).toGMTString());
									attrs.push('max-age=' + maxAge);
								}
								cookie.secure && attrs.push('secure');
								cookie.domain && attrs.push(cookie.domain);
								cookie.httpOnly && attrs.push('httpOnly');
								result[name] = attrs.join('; ');
							}
						});

						cookies = Object.keys(result).map(function(name) {
							var value = result[name];
							return name + (value == null ? '' : '=' + value);
						});
						util.setHeader(data, 'set-cookie', cookies);
					}

					if (data.headers) {
						data.headers = newResHeaders = util.lowerCaseify(data.headers);
					}

					if (data.statusCode) {
						req.hostIp = '127.0.0.1';
						util.drain(req, function() {
							res.response(util.wrapResponse(data));
						});
						return;
					}

					if (data.speed || data.delay) {
						res.add(new SpeedTransform(data));
					}

					delete data.headers;
					delete data.speed;
					delete data.delay;

					if (!util.isEmptyObject(data)) {
						res.addZipTransform(new WhistleTransform(data));
					}
					next();
				});

			});
		});
	});
};<|endoftext|>


<commit_before>'use strict'

// Setup
const JsAudioExports = require('./lib/jsaudio')
const JsAudio = JsAudioExports.JsAudioNative
const JsPaStream = JsAudioExports.JsPaStream

const formats = {
  paFloat32: 1,
  paInt32: 2,
  paInt24: 4,
  paInt16: 8,
  paInt8: 16,
  paUInt8: 32,
  paCustomFormat: 65536,
  paNonInterleaved: 2147483648
}
let streamOpts = {
  input: {
    device: 1,
    channelCount: 2,
    sampleFormat: formats.paFloat32,
    suggestedLatency: 0.003
  },
  output: {
    device: 3,
    channelCount: 2,
    sampleFormat: formats.paFloat32,
    suggestedLatency: 0.003
  },
  sampleRate: 48000,
  framesPerBuffer: 0,
  streamFlags: 0
}

let stream = new JsPaStream()
streamOpts.stream = stream
// Exports
module.exports = JsAudio

let buffer
setTimeout(() => {
  let framesToWrite = 0
  let framesToRead = 0
  // let phase = 0
  // let phaseIncrement = (2 * Math.PI * 80) / 48000.0
  let readBuffer = new Float32Array()
  while (1) {
    framesToRead = JsAudio.getStreamReadAvailable(stream)
    if (framesToRead) {
      let tempBuffer = new Float32Array(framesToRead * 2)
      JsAudio.readStream(stream, tempBuffer)

      let copyBuffer = new Float32Array(readBuffer.length + tempBuffer.length)
      copyBuffer.set(readBuffer)
      copyBuffer.set(tempBuffer, readBuffer.length)
      readBuffer = new Float32Array(copyBuffer)
    }
    framesToWrite = JsAudio.getStreamWriteAvailable(stream)
    if (framesToWrite && readBuffer.length > 0) {
      buffer = new Float32Array(readBuffer.buffer, 0, framesToWrite * 2)

      JsAudio.writeStream(stream, buffer)

      readBuffer = new Float32Array(
        readBuffer.buffer,
        framesToWrite * 2 * Float32Array.BYTES_PER_ELEMENT
      )
    }
  }
}, 1000)
<commit_msg>Fix streaming example in index I broke<commit_after>'use strict'

// Setup
const JsAudioExports = require('./lib/jsaudio')
const JsAudio = JsAudioExports.JsAudioNative
const JsPaStream = JsAudioExports.JsPaStream

// Exports
module.exports = JsAudioExports

JsAudio.initialize()

const formats = {
  paFloat32: 1,
  paInt32: 2,
  paInt24: 4,
  paInt16: 8,
  paInt8: 16,
  paUInt8: 32,
  paCustomFormat: 65536,
  paNonInterleaved: 2147483648
}
let streamOpts = {
  input: {
    device: JsAudio.getDefaultInputDevice(),
    channelCount: 2,
    sampleFormat: formats.paFloat32,
    suggestedLatency: 0.003
  },
  output: {
    device: JsAudio.getDefaultOutputDevice(),
    channelCount: 2,
    sampleFormat: formats.paFloat32,
    suggestedLatency: 0.003
  },
  sampleRate: 48000,
  framesPerBuffer: 0,
  streamFlags: 0
}

let stream = new JsPaStream()
streamOpts.stream = stream
JsAudio.openStream(streamOpts)
JsAudio.startStream(stream)

let buffer
setTimeout(() => {
  let framesToWrite = 0
  let framesToRead = 0
  // let phase = 0
  // let phaseIncrement = (2 * Math.PI * 80) / 48000.0
  let readBuffer = new Float32Array()
  while (1) {
    framesToRead = JsAudio.getStreamReadAvailable(stream)
    if (framesToRead) {
      let tempBuffer = new Float32Array(framesToRead * 2)
      JsAudio.readStream(stream, tempBuffer)

      let copyBuffer = new Float32Array(readBuffer.length + tempBuffer.length)
      copyBuffer.set(readBuffer)
      copyBuffer.set(tempBuffer, readBuffer.length)
      readBuffer = new Float32Array(copyBuffer)
    }
    framesToWrite = JsAudio.getStreamWriteAvailable(stream)
    if (framesToWrite && readBuffer.length > 0) {
      buffer = new Float32Array(readBuffer.buffer, 0, framesToWrite * 2)

      JsAudio.writeStream(stream, buffer)

      readBuffer = new Float32Array(
        readBuffer.buffer,
        framesToWrite * 2 * Float32Array.BYTES_PER_ELEMENT
      )
    }
  }
}, 1000)
<|endoftext|>


<commit_before>var fs = require('fs')
var path = require('path')
var child = require('child_process')

var series = require('run-series')

function generateAppBasename (opts) {
  return path.basename(opts.app, '.app')
}

function generateAppFrameworksPath (opts) {
  return path.join(opts.app, 'Contents', 'Frameworks')
}

function generateHelperAppPath (opts, opt, suffix, callback) {
  var appFrameworksPath = generateAppFrameworksPath(opts)
  var appBasename = generateAppBasename(opts)
  if (opts[opt]) {
    // Use helper path if specified
    if (fs.existsSync(opts['helper-path'])) return opts['helper-path']
    else return callback(new Error('Specified Electron Helper not found.'))
  } else {
    var helperPath
    if (fs.existsSync(helperPath = path.join(appFrameworksPath, appBasename + ' Helper' + (suffix || '') + '.app'))) {
      // Try to look for helper named after app (assume renamed)
      return helperPath
    } else if (fs.existsSync(helperPath = path.join(appFrameworksPath,
        'Electron Helper' + (suffix || '') + '.app'))) {
      // Try to look for helper by default
      return helperPath
    } else {
      // Helper not found
      callback(new Error('Electron Helper' + (suffix || '') + ' not found.'))
      return null
    }
  }
}

function generateHelperAppExecutablePath (opts, helperPath, suffix, callback) {
  var appBasename = generateAppBasename(opts)
  var helperExecutablePath
  if (fs.existsSync(helperExecutablePath = path.join(helperPath, 'Contents', 'MacOS', appBasename + ' Helper' + (suffix || '')))) {
    // Try to look for helper named after app (assume renamed)
    return helperExecutablePath
  } else if (fs.existsSync(helperExecutablePath = path.join(helperPath, 'Contents', 'MacOS', 'Electron Helper' + (suffix || '')))) {
    // Try to look for helper by default
    return helperExecutablePath
  } else {
    // Helper not found
    callback(new Error('Electron Helper' + (suffix || '') + ' executable not found.'))
    return null
  }
}

function signApplication (opts, callback) {
  var operations = []
  var appFrameworksPath = generateAppFrameworksPath(opts)

  var childPaths
  if (opts.platform === 'mas') {
    childPaths = [
      path.join(appFrameworksPath, 'Electron Framework.framework', 'Versions', 'A', 'Electron Framework'),
      path.join(appFrameworksPath, 'Electron Framework.framework')
    ]
  } else if (opts.platform === 'darwin') {
    childPaths = [
      path.join(appFrameworksPath, 'Electron Framework.framework', 'Versions', 'A', 'Electron Framework'),
      path.join(appFrameworksPath, 'Electron Framework.framework'),
      path.join(appFrameworksPath, 'Mantle.framework', 'Versions', 'A', 'Mantle'),
      path.join(appFrameworksPath, 'Mantle.framework'),
      path.join(appFrameworksPath, 'ReactiveCocoa.framework', 'Versions', 'A', 'ReactiveCocoa'),
      path.join(appFrameworksPath, 'ReactiveCocoa.framework'),
      path.join(appFrameworksPath, 'Squirrel.framework', 'Versions', 'A', 'Squirrel'),
      path.join(appFrameworksPath, 'Squirrel.framework')
    ]
  }

  var helperPath = generateHelperAppPath(opts, 'helper-path', null, callback)
  if (helperPath) {
    var helperExecutablePath = generateHelperAppExecutablePath(opts, helperPath, null, callback)
    if (helperExecutablePath) childPaths.unshift(helperExecutablePath, helperPath)
    else return callback(new Error('Missing Electron Helper, stopped.'))
  }

  var helperEHPath = generateHelperAppPath(opts, 'helper-eh-path', ' EH', callback)
  if (helperEHPath) {
    var helperEHExecutablePath = generateHelperAppExecutablePath(opts, helperEHPath, ' EH', callback)
    if (helperEHExecutablePath) childPaths.unshift(helperEHExecutablePath, helperEHPath)
    else return callback(new Error('Missing Electron Helper EH, stopped.'))
  }

  var helperNPPath = generateHelperAppPath(opts, 'helper-np-path', ' NP', callback)
  if (helperNPPath) {
    var helperNPExecutablePath = generateHelperAppExecutablePath(opts, helperNPPath, ' NP', callback)
    if (helperNPExecutablePath) childPaths.unshift(helperNPExecutablePath, helperNPPath)
    else return callback(new Error('Missing Electron Helper NP, stopped.'))
  }

  if (opts.entitlements) {
    if (opts.platform === 'mas') {
      // Sign with entitlements
      childPaths.forEach(function (path) {
        operations.push(function (cb) {
          child.exec('codesign -f -s "' + opts.identity + '" -fv \ '
            + '--entitlements "' + opts['entitlements-inherit'] + '" \ '
            + '"' + path + '"'
          , cb)
        })
      })
      operations.push(function (cb) {
        child.exec('codesign -f -s "' + opts.identity + '" -fv \ '
          + '--entitlements "' + opts.entitlements + '" \ '
          + '"' + opts.app + '"'
        , cb)
      })
    } else if (opts.platform === 'darwin') {
      // TODO: Signing darwin builds with entitlements
    }
  } else {
    // Otherwise normally
    childPaths.forEach(function (path) {
      operations.push(function (cb) {
        child.exec('codesign -f -s "' + opts.identity + '" -fv \ '
          + '"' + path + '"'
        , cb)
      })
    })
    operations.push(function (cb) {
      child.exec('codesign -f -s "' + opts.identity + '" -fv \ '
        + '"' + opts.app + '"'
      , cb)
    })
  }

  // Lastly verify codesign
  operations.push(function (cb) {
    child.exec('codesign -v --verbose=4 \ '
      + '"' + opts.app + '"'
    , cb)
  })
  if (opts.entitlements) {
    // Check entitlements
    operations.push(function (cb) {
      child.exec('codesign -d --entitlements - \ '
        + '"' + opts.app + '"'
      , function (err, stdout, stderr) {
        if (err) return cb(err)
        if (!stdout) return cb(new Error('Entitlements failed to be signed.'))
        cb()
      })
    })
  }
  series(operations, function (err) {
    if (err) return callback(err)
    callback()
  })
}

module.exports = function sign (opts, cb) {
  if (!opts.app) return cb(new Error('Path to aplication must be specified.'))
  if (!fs.existsSync(opts.app)) return cb(new Error('Application not found.'))
  if (!cb) cb = function () {}

  // Match platform if none is provided
  if (!opts.platform) {
    var appFrameworksPath = generateAppFrameworksPath(opts)
    if (!fs.existsSync(path.join(appFrameworksPath, 'Mantle.framework'))
        && !fs.existsSync(path.join(appFrameworksPath, 'ReactiveCocoa.framework'))
        && !fs.existsSync(path.join(appFrameworksPath, 'Squirrel.framework'))) {
      // These frameworks do not exist in an Mac App Store version
      opts.platform = 'mas'
    } else {
      opts.platform = 'darwin'
    }
  }
  if (opts.platform === 'mas') {
    // To sign apps for Mac App Store, an entitlements file is required,
    // especially for app sandboxing (as well some other services).
    // Fallback entitlements for sandboxing by default:
    // Note this may cause troubles while running an signed app due to
    // missing keys special to the project.
    // Further reading: https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html
    if (!opts.entitlements) opts.entitlements = path.join(__dirname, 'mas.default.plist')
    if (!opts['entitlements-inherit']) opts['entitlements-inherit'] = path.join(__dirname, 'mas.inherit.default.plist')
  } else if (opts.platform === 'darwin') {
    // Not necessary to have entitlements for non Mac App Store distribution
    if (opts.entitlements) return cb(new Error('Unable to sign for darwin platform with entitlements.'))
  } else {
    return cb(new Error('Only platform darwin and mas are supported.'))
  }
  series([
    function (cb) {
      // Checking identity with series for async execution of child process
      if (!opts.identity) {
        child.exec('security find-identity', function (err, stdout, stderr) {
          if (err) return cb(new Error('Error in finding an identity.'))
          var lines = stdout.split('\n')
          var location
          for (var i = 0, l = lines.length; i < l && !opts.identity; i++) {
            var line = lines[i]
            if (opts.platform === 'mas') {
              location = line.indexOf('3rd Party Mac Developer Application')
              if (location >= 0) {
                opts.identity = line.substring(location, line.length - 1)
                break
              }
            } else if (opts.platform === 'darwin') {
              location = line.indexOf('Developer ID Application')
              if (location >= 0) {
                opts.identity = line.substring(location, line.length - 1)
                break
              }
            }
          }
          if (!opts.identity) cb(new Error('No identity found for signing.'))
          cb()
        })
      } else cb()
    }
  ], function (err) {
    if (err) return cb(err)
    return signApplication(opts, cb)
  })
}
<commit_msg>Sign libnode.dylib<commit_after>var fs = require('fs')
var path = require('path')
var child = require('child_process')

var series = require('run-series')

function generateAppBasename (opts) {
  return path.basename(opts.app, '.app')
}

function generateAppFrameworksPath (opts) {
  return path.join(opts.app, 'Contents', 'Frameworks')
}

function generateHelperAppPath (opts, opt, suffix, callback) {
  var appFrameworksPath = generateAppFrameworksPath(opts)
  var appBasename = generateAppBasename(opts)
  if (opts[opt]) {
    // Use helper path if specified
    if (fs.existsSync(opts['helper-path'])) return opts['helper-path']
    else return callback(new Error('Specified Electron Helper not found.'))
  } else {
    var helperPath
    if (fs.existsSync(helperPath = path.join(appFrameworksPath, appBasename + ' Helper' + (suffix || '') + '.app'))) {
      // Try to look for helper named after app (assume renamed)
      return helperPath
    } else if (fs.existsSync(helperPath = path.join(appFrameworksPath,
        'Electron Helper' + (suffix || '') + '.app'))) {
      // Try to look for helper by default
      return helperPath
    } else {
      // Helper not found
      callback(new Error('Electron Helper' + (suffix || '') + ' not found.'))
      return null
    }
  }
}

function generateHelperAppExecutablePath (opts, helperPath, suffix, callback) {
  var appBasename = generateAppBasename(opts)
  var helperExecutablePath
  if (fs.existsSync(helperExecutablePath = path.join(helperPath, 'Contents', 'MacOS', appBasename + ' Helper' + (suffix || '')))) {
    // Try to look for helper named after app (assume renamed)
    return helperExecutablePath
  } else if (fs.existsSync(helperExecutablePath = path.join(helperPath, 'Contents', 'MacOS', 'Electron Helper' + (suffix || '')))) {
    // Try to look for helper by default
    return helperExecutablePath
  } else {
    // Helper not found
    callback(new Error('Electron Helper' + (suffix || '') + ' executable not found.'))
    return null
  }
}

function signApplication (opts, callback) {
  var operations = []
  var appFrameworksPath = generateAppFrameworksPath(opts)

  var childPaths
  if (opts.platform === 'mas') {
    childPaths = [
      path.join(appFrameworksPath, 'Electron Framework.framework', 'Libraries', 'libnode.dylib'),
      path.join(appFrameworksPath, 'Electron Framework.framework', 'Versions', 'A', 'Electron Framework'),
      path.join(appFrameworksPath, 'Electron Framework.framework')
    ]
  } else if (opts.platform === 'darwin') {
    childPaths = [
      path.join(appFrameworksPath, 'Electron Framework.framework', 'Libraries', 'libnode.dylib'),
      path.join(appFrameworksPath, 'Electron Framework.framework', 'Versions', 'A', 'Electron Framework'),
      path.join(appFrameworksPath, 'Electron Framework.framework'),
      path.join(appFrameworksPath, 'Mantle.framework', 'Versions', 'A', 'Mantle'),
      path.join(appFrameworksPath, 'Mantle.framework'),
      path.join(appFrameworksPath, 'ReactiveCocoa.framework', 'Versions', 'A', 'ReactiveCocoa'),
      path.join(appFrameworksPath, 'ReactiveCocoa.framework'),
      path.join(appFrameworksPath, 'Squirrel.framework', 'Versions', 'A', 'Squirrel'),
      path.join(appFrameworksPath, 'Squirrel.framework')
    ]
  }

  var helperPath = generateHelperAppPath(opts, 'helper-path', null, callback)
  if (helperPath) {
    var helperExecutablePath = generateHelperAppExecutablePath(opts, helperPath, null, callback)
    if (helperExecutablePath) childPaths.unshift(helperExecutablePath, helperPath)
    else return callback(new Error('Missing Electron Helper, stopped.'))
  }

  var helperEHPath = generateHelperAppPath(opts, 'helper-eh-path', ' EH', callback)
  if (helperEHPath) {
    var helperEHExecutablePath = generateHelperAppExecutablePath(opts, helperEHPath, ' EH', callback)
    if (helperEHExecutablePath) childPaths.unshift(helperEHExecutablePath, helperEHPath)
    else return callback(new Error('Missing Electron Helper EH, stopped.'))
  }

  var helperNPPath = generateHelperAppPath(opts, 'helper-np-path', ' NP', callback)
  if (helperNPPath) {
    var helperNPExecutablePath = generateHelperAppExecutablePath(opts, helperNPPath, ' NP', callback)
    if (helperNPExecutablePath) childPaths.unshift(helperNPExecutablePath, helperNPPath)
    else return callback(new Error('Missing Electron Helper NP, stopped.'))
  }

  if (opts.entitlements) {
    if (opts.platform === 'mas') {
      // Sign with entitlements
      childPaths.forEach(function (path) {
        operations.push(function (cb) {
          child.exec('codesign -f -s "' + opts.identity + '" -fv \ '
            + '--entitlements "' + opts['entitlements-inherit'] + '" \ '
            + '"' + path + '"'
          , cb)
        })
      })
      operations.push(function (cb) {
        child.exec('codesign -f -s "' + opts.identity + '" -fv \ '
          + '--entitlements "' + opts.entitlements + '" \ '
          + '"' + opts.app + '"'
        , cb)
      })
    } else if (opts.platform === 'darwin') {
      // TODO: Signing darwin builds with entitlements
    }
  } else {
    // Otherwise normally
    childPaths.forEach(function (path) {
      operations.push(function (cb) {
        child.exec('codesign -f -s "' + opts.identity + '" -fv \ '
          + '"' + path + '"'
        , cb)
      })
    })
    operations.push(function (cb) {
      child.exec('codesign -f -s "' + opts.identity + '" -fv \ '
        + '"' + opts.app + '"'
      , cb)
    })
  }

  // Lastly verify codesign
  operations.push(function (cb) {
    child.exec('codesign -v --verbose=4 \ '
      + '"' + opts.app + '"'
    , cb)
  })
  if (opts.entitlements) {
    // Check entitlements
    operations.push(function (cb) {
      child.exec('codesign -d --entitlements - \ '
        + '"' + opts.app + '"'
      , function (err, stdout, stderr) {
        if (err) return cb(err)
        if (!stdout) return cb(new Error('Entitlements failed to be signed.'))
        cb()
      })
    })
  }
  series(operations, function (err) {
    if (err) return callback(err)
    callback()
  })
}

module.exports = function sign (opts, cb) {
  if (!opts.app) return cb(new Error('Path to aplication must be specified.'))
  if (!fs.existsSync(opts.app)) return cb(new Error('Application not found.'))
  if (!cb) cb = function () {}

  // Match platform if none is provided
  if (!opts.platform) {
    var appFrameworksPath = generateAppFrameworksPath(opts)
    if (!fs.existsSync(path.join(appFrameworksPath, 'Mantle.framework'))
        && !fs.existsSync(path.join(appFrameworksPath, 'ReactiveCocoa.framework'))
        && !fs.existsSync(path.join(appFrameworksPath, 'Squirrel.framework'))) {
      // These frameworks do not exist in an Mac App Store version
      opts.platform = 'mas'
    } else {
      opts.platform = 'darwin'
    }
  }
  if (opts.platform === 'mas') {
    // To sign apps for Mac App Store, an entitlements file is required,
    // especially for app sandboxing (as well some other services).
    // Fallback entitlements for sandboxing by default:
    // Note this may cause troubles while running an signed app due to
    // missing keys special to the project.
    // Further reading: https://developer.apple.com/library/mac/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/EnablingAppSandbox.html
    if (!opts.entitlements) opts.entitlements = path.join(__dirname, 'mas.default.plist')
    if (!opts['entitlements-inherit']) opts['entitlements-inherit'] = path.join(__dirname, 'mas.inherit.default.plist')
  } else if (opts.platform === 'darwin') {
    // Not necessary to have entitlements for non Mac App Store distribution
    if (opts.entitlements) return cb(new Error('Unable to sign for darwin platform with entitlements.'))
  } else {
    return cb(new Error('Only platform darwin and mas are supported.'))
  }
  series([
    function (cb) {
      // Checking identity with series for async execution of child process
      if (!opts.identity) {
        child.exec('security find-identity', function (err, stdout, stderr) {
          if (err) return cb(new Error('Error in finding an identity.'))
          var lines = stdout.split('\n')
          var location
          for (var i = 0, l = lines.length; i < l && !opts.identity; i++) {
            var line = lines[i]
            if (opts.platform === 'mas') {
              location = line.indexOf('3rd Party Mac Developer Application')
              if (location >= 0) {
                opts.identity = line.substring(location, line.length - 1)
                break
              }
            } else if (opts.platform === 'darwin') {
              location = line.indexOf('Developer ID Application')
              if (location >= 0) {
                opts.identity = line.substring(location, line.length - 1)
                break
              }
            }
          }
          if (!opts.identity) cb(new Error('No identity found for signing.'))
          cb()
        })
      } else cb()
    }
  ], function (err) {
    if (err) return cb(err)
    return signApplication(opts, cb)
  })
}
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

const Suite = require('mocha/lib/suite');
const Test = require('mocha/lib/test');
const scenario = require('../scenario');
const escapeRe = require('escape-string-regexp');

const addDataContext = require('../data/context');

/**
 * Codecept-style interface:
 *
 * Feature('login')
 * Scenario('login as regular user', (I) {
 *   I.fillField();
 *   I.click()
 *   I.see('Hello, '+data.login);
 * });
 *
 * @param {Suite} suite Root suite.
 */
module.exports = function (suite) {
  const suites = [suite];
  suite.timeout(0);
  let afterAllHooks;
  let afterEachHooks;
  let afterAllHooksAreLoaded;
  let afterEachHooksAreLoaded;

  suite.on('pre-require', (context, file, mocha) => {
    const common = require('mocha/lib/interfaces/common')(suites, context);

    const addScenario = function (title, opts, fn, data) {
      const suite = suites[0];

      if (typeof opts === 'function' && !fn) {
        fn = opts;
        opts = {};
      }
      /**
       * load hooks from arrays to suite to prevent reordering
       *
       */
      if (!afterAllHooksAreLoaded) {
        afterAllHooks.forEach((hook) => {
          suites[0].afterAll(hook[0], hook[1]);
        });
        afterAllHooksAreLoaded = true;
      }
      if (!afterEachHooksAreLoaded) {
        afterEachHooks.forEach((hook) => {
          suites[0].afterEach(hook[0], hook[1]);
        });
        afterEachHooksAreLoaded = true;
      }
      if (suite.pending) {
        fn = null;
      }
      const test = new Test(title, fn);
      test.fullTitle = () => `${suite.title}: ${title}`;
      test.file = file;
      test.async = true;
      test.timeout(0);
      test.inject = {};

      suite.addTest(scenario.test(test));
      if (opts.retries) test.retries(opts.retries);
      if (opts.timeout) test.timeout(opts.timeout);
      test.opts = opts;

      return test;
    };

    // create dispatcher

    context.BeforeAll = common.before;
    context.AfterAll = common.after;

    context.run = mocha.options.delay && common.runWithSuite(suite);
    /**
     * Describe a "suite" with the given `title`
     * and callback `fn` containing nested suites
     * and/or tests.
     */

    context.Feature = function (title, opts) {
      if (suites.length > 1) {
        suites.shift();
      }

      afterAllHooks = [];
      afterEachHooks = [];
      afterAllHooksAreLoaded = false;
      afterEachHooksAreLoaded = false;

      const suite = Suite.create(suites[0], title);
      if (!opts) opts = {};
      suite.timeout(0);

      if (opts.retries) suite.retries(opts.retries);
      if (opts.timeout) suite.timeout(opts.timeout);

      suite.file = file;
      suite.fullTitle = () => `${suite.title}:`;
      suites.unshift(suite);
      suite.beforeEach('codeceptjs.before', () => scenario.setup(suite));
      afterEachHooks.push(['finalize codeceptjs', () => scenario.teardown(suite)]);

      suite.beforeAll('codeceptjs.beforeSuite', () => scenario.suiteSetup(suite));
      afterAllHooks.push(['codeceptjs.afterSuite', () => scenario.suiteTeardown(suite)]);

      return suite;
    };

    context.BeforeSuite = function (fn) {
      suites[0].beforeAll('BeforeSuite', scenario.injected(fn, suites[0], 'beforeSuite'));
    };

    context.AfterSuite = function (fn) {
      afterAllHooks.unshift(['AfterSuite', scenario.injected(fn, suites[0], 'afterSuite')]);
    };

    context.Background = context.Before = function (fn) {
      suites[0].beforeEach('Before', scenario.injected(fn, suites[0], 'before'));
    };

    context.After = function (fn) {
      afterEachHooks.unshift(['After', scenario.injected(fn, suites[0], 'after')]);
    };

    /**
     * Describe a specification or test-case
     * with the given `title` and callback `fn`
     * acting as a thunk.
     */
    context.Scenario = addScenario;
    /**
     * Exclusive test-case.
     */
    context.Scenario.only = function (title, opts, fn, data) {
      const reString = `^${escapeRe(`${suites[0].title}: ${title}`.replace(/( \| {.+})?$/g, ''))}`;
      mocha.grep(new RegExp(reString));
      return addScenario(title, opts, fn, data);
    };

    /**
     * Pending test case.
     */
    context.xScenario = context.Scenario.skip = function (title) {
      context.Scenario(title, {});
    };

    addDataContext(context);
  });
};
<commit_msg>add injectDependencies method (#922)<commit_after>/**
 * Module dependencies.
 */

const Suite = require('mocha/lib/suite');
const Test = require('mocha/lib/test');
const scenario = require('../scenario');
const escapeRe = require('escape-string-regexp');

const addDataContext = require('../data/context');

/**
 * Codecept-style interface:
 *
 * Feature('login')
 * Scenario('login as regular user', (I) {
 *   I.fillField();
 *   I.click()
 *   I.see('Hello, '+data.login);
 * });
 *
 * @param {Suite} suite Root suite.
 */
module.exports = function (suite) {
  const suites = [suite];
  suite.timeout(0);
  let afterAllHooks;
  let afterEachHooks;
  let afterAllHooksAreLoaded;
  let afterEachHooksAreLoaded;

  suite.on('pre-require', (context, file, mocha) => {
    const common = require('mocha/lib/interfaces/common')(suites, context);

    const addScenario = function (title, opts, fn, data) {
      const suite = suites[0];

      if (typeof opts === 'function' && !fn) {
        fn = opts;
        opts = {};
      }
      /**
       * load hooks from arrays to suite to prevent reordering
       *
       */
      if (!afterAllHooksAreLoaded) {
        afterAllHooks.forEach((hook) => {
          suites[0].afterAll(hook[0], hook[1]);
        });
        afterAllHooksAreLoaded = true;
      }
      if (!afterEachHooksAreLoaded) {
        afterEachHooks.forEach((hook) => {
          suites[0].afterEach(hook[0], hook[1]);
        });
        afterEachHooksAreLoaded = true;
      }
      if (suite.pending) {
        fn = null;
      }
      const test = new Test(title, fn);
      test.fullTitle = () => `${suite.title}: ${title}`;
      test.injectDependencies = (deps) => {
        Object.keys(deps).forEach((key) => {
          test.inject[key] = deps[key];
        });
      }
      test.file = file;
      test.async = true;
      test.timeout(0);
      test.inject = {};

      suite.addTest(scenario.test(test));
      if (opts.retries) test.retries(opts.retries);
      if (opts.timeout) test.timeout(opts.timeout);
      test.opts = opts;

      return test;
    };

    // create dispatcher

    context.BeforeAll = common.before;
    context.AfterAll = common.after;

    context.run = mocha.options.delay && common.runWithSuite(suite);
    /**
     * Describe a "suite" with the given `title`
     * and callback `fn` containing nested suites
     * and/or tests.
     */

    context.Feature = function (title, opts) {
      if (suites.length > 1) {
        suites.shift();
      }

      afterAllHooks = [];
      afterEachHooks = [];
      afterAllHooksAreLoaded = false;
      afterEachHooksAreLoaded = false;

      const suite = Suite.create(suites[0], title);
      if (!opts) opts = {};
      suite.timeout(0);

      if (opts.retries) suite.retries(opts.retries);
      if (opts.timeout) suite.timeout(opts.timeout);

      suite.file = file;
      suite.fullTitle = () => `${suite.title}:`;
      suites.unshift(suite);
      suite.beforeEach('codeceptjs.before', () => scenario.setup(suite));
      afterEachHooks.push(['finalize codeceptjs', () => scenario.teardown(suite)]);

      suite.beforeAll('codeceptjs.beforeSuite', () => scenario.suiteSetup(suite));
      afterAllHooks.push(['codeceptjs.afterSuite', () => scenario.suiteTeardown(suite)]);

      return suite;
    };

    context.BeforeSuite = function (fn) {
      suites[0].beforeAll('BeforeSuite', scenario.injected(fn, suites[0], 'beforeSuite'));
    };

    context.AfterSuite = function (fn) {
      afterAllHooks.unshift(['AfterSuite', scenario.injected(fn, suites[0], 'afterSuite')]);
    };

    context.Background = context.Before = function (fn) {
      suites[0].beforeEach('Before', scenario.injected(fn, suites[0], 'before'));
    };

    context.After = function (fn) {
      afterEachHooks.unshift(['After', scenario.injected(fn, suites[0], 'after')]);
    };

    /**
     * Describe a specification or test-case
     * with the given `title` and callback `fn`
     * acting as a thunk.
     */
    context.Scenario = addScenario;
    /**
     * Exclusive test-case.
     */
    context.Scenario.only = function (title, opts, fn, data) {
      const reString = `^${escapeRe(`${suites[0].title}: ${title}`.replace(/( \| {.+})?$/g, ''))}`;
      mocha.grep(new RegExp(reString));
      return addScenario(title, opts, fn, data);
    };

    /**
     * Pending test case.
     */
    context.xScenario = context.Scenario.skip = function (title) {
      context.Scenario(title, {});
    };

    addDataContext(context);
  });
};
<|endoftext|>


<commit_before>'use strict';
var got = require('got');
var querystring = require('querystring');

module.exports = function (email, token, cb) {
	if (typeof email !== 'string' && email.indexOf('@') !== -1) {
		throw new Error('`email` required');
	}

	if (typeof token === 'function') {
		cb = token;
		token = null;
	}

	var headers = {
		'User-Agent': 'https://github.com/sindresorhus/github-username'
	};

	if (token) {
		headers['Authorization'] = 'token ' + token;
	}

	var qs = querystring.stringify({
		q: email + ' in:email'
	});

	got.get('https://api.github.com/search/users?' + qs, {
		headers: headers
	}, function (err, data) {
		if (err) {
			cb(err);
			return;
		}

		data = JSON.parse(data);

		if (data.total_count === 0) {
			cb(new Error('Couldn\'t find a username for the supplied email'));
			return;
		}

		cb(null, data.items[0].login);
	});
};
<commit_msg>lowercase the header as it's allowed<commit_after>'use strict';
var got = require('got');
var querystring = require('querystring');

module.exports = function (email, token, cb) {
	if (typeof email !== 'string' && email.indexOf('@') !== -1) {
		throw new Error('`email` required');
	}

	if (typeof token === 'function') {
		cb = token;
		token = null;
	}

	var headers = {
		'user-agent': 'https://github.com/sindresorhus/github-username'
	};

	if (token) {
		headers['Authorization'] = 'token ' + token;
	}

	var qs = querystring.stringify({
		q: email + ' in:email'
	});

	got.get('https://api.github.com/search/users?' + qs, {
		headers: headers
	}, function (err, data) {
		if (err) {
			cb(err);
			return;
		}

		data = JSON.parse(data);

		if (data.total_count === 0) {
			cb(new Error('Couldn\'t find a username for the supplied email'));
			return;
		}

		cb(null, data.items[0].login);
	});
};
<|endoftext|>


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

'use strict';

module.exports.load = function () {
  let stdin;
  if (process.stdin.isTTY) return;

  try {
    stdin = require('fs').readFileSync('/dev/stdin').toString();

  } catch(e) {
    if (e.code != 'EAGAIN') throw e;
    else return;
  };

  if(stdin) process.argv.push(stdin.trim());

};

<commit_msg>(fix): Replaces readFileSync by readSync to avoid ENXIO exception<commit_after>#!/usr/bin/env node

const fs = require('fs');

'use strict';

module.exports.load = () => {
  if (process.stdin.isTTY) return;

  const BUFSIZE = 65536;
  let nbytes = 0;
  let chunks = [];
  let buffer = '';

  while(true) {
    try {
      buffer = Buffer.alloc(BUFSIZE);
      nbytes = fs.readSync(0, buffer, 0, BUFSIZE, null);
    }
    catch (e) {
      if (e.code != 'EAGAIN') throw e;
    };

    if (nbytes === 0) break;
     chunks.push(buffer.slice(0, nbytes));
  };

  const stdin = Buffer.concat(chunks).toString();
  if (stdin) process.argv.push(stdin.trim());

};

<|endoftext|>


<commit_before>// index.js
'use strict'

var chalk = require('chalk')
var shells = require('shelljs')

module.exports = function (filesList, dest) {
  console.log(chalk.blue('\n= Begin copying files'))

  // if single file or directory, create an array
  if (! filesList.forEach) {
  	filesList = [filesList];
  }

  filesList.forEach(function (file) {
    console.log(chalk.green(' - Copying file or directory:'), chalk.red(file))

		// https://github.com/shelljs/shelljs#javascript
    shells.cp('-R', file, dest)
		//	cpy(file, userPath);
  })

  console.log(chalk.blue('\n= End copying files'))
}
<commit_msg>Fix style<commit_after>// index.js
'use strict'

var chalk = require('chalk')
var shells = require('shelljs')

module.exports = function (filesList, dest) {
  console.log(chalk.blue('\n= Begin copying files'))

  // if single file or directory, create an array
  if (!filesList.forEach) {
    filesList = [filesList]
  }

  filesList.forEach(function (file) {
    console.log(chalk.green(' - Copying file or directory:'), chalk.red(file))

		// https://github.com/shelljs/shelljs#javascript
    shells.cp('-R', file, dest)
		//	cpy(file, userPath);
  })

  console.log(chalk.blue('\n= End copying files'))
}
<|endoftext|>


<commit_before>exports = module.exports = PauseQueue;

function PauseQueue (worker, concurrency) {

	if (typeof concurrency === 'undefined') { concurrency = 1; }

	var pending = 0,
		queue = {
			tasks : [],
			concurrency : concurrency,
			drain : null,
			nothing : null,
			paused : false,

			add : function (task, priority, callback) {
				queue.tasks[priority ? "unshift" : "push"]({
					data : task,
					callback : (typeof callback === 'function') ? callback : null
				});

				process.nextTick(queue.run);
			},

            push : function(task, callback) {
                queue.add(task, false, callback);
            },

            unshift : function(task, callback) {
                queue.add(task, true, callback);
            },

			run : function () {
				if (pending == 0 && queue.pausedCb) { queue.pausedCb(); delete queue.pausedCb; };

				if (!queue.paused && pending < queue.concurrency && queue.tasks.length) {
					var task = queue.tasks.shift();
					pending += 1;

					var next = function () {
						pending -= 1;

						if (task.callback) {
							task.callback.apply(task, arguments);
						}

						if (queue.drain && queue.tasks.length + pending === 0) { queue.drain(); }
						queue.run();
					};

					worker(task.data, next);
				}

				if (queue.nothing && queue.tasks.length + pending === 0) { queue.nothing(); }

			},

			pause : function (cb) {
				queue.paused = true;
                queue.pausedCb = cb;
				queue.run();
			},

			resume : function () {
				queue.paused = false;
				queue.run();
			},

			length : function () {
				return queue.tasks.length;
			}
		};

	return queue;
}
<commit_msg>Fixed indentation<commit_after>exports = module.exports = PauseQueue;

function PauseQueue (worker, concurrency) {

	if (typeof concurrency === 'undefined') { concurrency = 1; }

	var pending = 0,
		queue = {
			tasks : [],
			concurrency : concurrency,
			drain : null,
			nothing : null,
			paused : false,

			add : function (task, priority, callback) {
				queue.tasks[priority ? "unshift" : "push"]({
					data : task,
					callback : (typeof callback === 'function') ? callback : null
				});

				process.nextTick(queue.run);
			},

			push : function(task, callback) {
				queue.add(task, false, callback);
			},

			unshift : function(task, callback) {
				queue.add(task, true, callback);
			},

			run : function () {
				if (pending == 0 && queue.pausedCb) { queue.pausedCb(); delete queue.pausedCb; };

				if (!queue.paused && pending < queue.concurrency && queue.tasks.length) {
					var task = queue.tasks.shift();
					pending += 1;

					var next = function () {
						pending -= 1;

						if (task.callback) {
							task.callback.apply(task, arguments);
						}

						if (queue.drain && queue.tasks.length + pending === 0) { queue.drain(); }
						queue.run();
					};

					worker(task.data, next);
				}

				if (queue.nothing && queue.tasks.length + pending === 0) { queue.nothing(); }

			},

			pause : function (cb) {
				queue.paused = true;
                queue.pausedCb = cb;
				queue.run();
			},

			resume : function () {
				queue.paused = false;
				queue.run();
			},

			length : function () {
				return queue.tasks.length;
			}
		};

	return queue;
}
<|endoftext|>


<commit_before>var checker   = require('ember-cli-version-checker');
var clone     = require('clone');
var path      = require('path');
var resolve   = require('resolve');

module.exports = {
  name: 'ember-cli-babel',

  shouldSetupRegistryInIncluded: function() {
    return !checker.isAbove(this, '0.2.0');
  },

  setupPreprocessorRegistry: function(type, registry) {
    var addon = this;

    registry.add('js', {
      name: 'ember-cli-babel',
      ext: 'js',
      toTree: function(tree) {
        return require('broccoli-babel-transpiler')(tree, getBabelOptions(addon));
      }
    });
  },

  shouldIncludePolyfill: function() {
    var options = getAddonOptions(this);
    return options.includePolyfill === true;
  },

  importPolyfill: function(app) {
    app.import('vendor/browser-polyfill.js');
  },

  treeFor: function(name) {
    if (name !== 'vendor') { return; }

    // Find babel-core's browser polyfill and use its directory as our vendor tree
    var transpilerRoot = path.dirname(resolve.sync('broccoli-babel-transpiler'));
    var polyfillDir = path.dirname(resolve.sync('babel-core/browser-polyfill', { basedir: transpilerRoot }));

    return this.treeGenerator(polyfillDir);
  },

  included: function(app) {
    this._super.included.apply(this, arguments);
    this.app = app;

    if (this.shouldSetupRegistryInIncluded()) {
      this.setupPreprocessorRegistry('parent', app.registry);
    }

    if (this.shouldIncludePolyfill()) {
      this.importPolyfill(app);
    }
  }
};

function getAddonOptions(addonContext) {
  var baseOptions = (addonContext.parent && addonContext.parent.options) || (addonContext.app && addonContext.app.options);
  return baseOptions && baseOptions.babel || {};
}

function getBabelOptions(addonContext) {
  var options = clone(getAddonOptions(addonContext));

  // Ensure modules aren't compiled unless explicitly set to compile
  options.blacklist = options.blacklist || ['es6.modules'];

  // do not enable non-standard transforms
  if (!('nonStandard' in options)) {
    options.nonStandard = false;
  }

  // Don't include the `includePolyfill` flag, since Babel doesn't care
  delete options.includePolyfill;

  if (options.compileModules === true) {
    if (options.blacklist.indexOf('es6.modules') >= 0) {
      options.blacklist.splice(options.blacklist.indexOf('es6.modules'), 1);
    }

    delete options.compileModules;
  } else {
    if (options.blacklist.indexOf('es6.modules') < 0) {
      options.blacklist.push('es6.modules');
    }
  }

  // Ember-CLI inserts its own 'use strict' directive
  options.blacklist.push('useStrict');
  options.highlightCode = false;

  return options;
}
<commit_msg>Prepend vendor import<commit_after>var checker   = require('ember-cli-version-checker');
var clone     = require('clone');
var path      = require('path');
var resolve   = require('resolve');

module.exports = {
  name: 'ember-cli-babel',

  shouldSetupRegistryInIncluded: function() {
    return !checker.isAbove(this, '0.2.0');
  },

  setupPreprocessorRegistry: function(type, registry) {
    var addon = this;

    registry.add('js', {
      name: 'ember-cli-babel',
      ext: 'js',
      toTree: function(tree) {
        return require('broccoli-babel-transpiler')(tree, getBabelOptions(addon));
      }
    });
  },

  shouldIncludePolyfill: function() {
    var options = getAddonOptions(this);
    return options.includePolyfill === true;
  },

  importPolyfill: function(app) {
    app.import('vendor/browser-polyfill.js', { prepend: true });
  },

  treeFor: function(name) {
    if (name !== 'vendor') { return; }

    // Find babel-core's browser polyfill and use its directory as our vendor tree
    var transpilerRoot = path.dirname(resolve.sync('broccoli-babel-transpiler'));
    var polyfillDir = path.dirname(resolve.sync('babel-core/browser-polyfill', { basedir: transpilerRoot }));

    return this.treeGenerator(polyfillDir);
  },

  included: function(app) {
    this._super.included.apply(this, arguments);
    this.app = app;

    if (this.shouldSetupRegistryInIncluded()) {
      this.setupPreprocessorRegistry('parent', app.registry);
    }

    if (this.shouldIncludePolyfill()) {
      this.importPolyfill(app);
    }
  }
};

function getAddonOptions(addonContext) {
  var baseOptions = (addonContext.parent && addonContext.parent.options) || (addonContext.app && addonContext.app.options);
  return baseOptions && baseOptions.babel || {};
}

function getBabelOptions(addonContext) {
  var options = clone(getAddonOptions(addonContext));

  // Ensure modules aren't compiled unless explicitly set to compile
  options.blacklist = options.blacklist || ['es6.modules'];

  // do not enable non-standard transforms
  if (!('nonStandard' in options)) {
    options.nonStandard = false;
  }

  // Don't include the `includePolyfill` flag, since Babel doesn't care
  delete options.includePolyfill;

  if (options.compileModules === true) {
    if (options.blacklist.indexOf('es6.modules') >= 0) {
      options.blacklist.splice(options.blacklist.indexOf('es6.modules'), 1);
    }

    delete options.compileModules;
  } else {
    if (options.blacklist.indexOf('es6.modules') < 0) {
      options.blacklist.push('es6.modules');
    }
  }

  // Ember-CLI inserts its own 'use strict' directive
  options.blacklist.push('useStrict');
  options.highlightCode = false;

  return options;
}
<|endoftext|>


<commit_before>/*eslint no-invalid-this: 0*/
/*
 * Maintains a lookup of connected IRC clients. These connections are transient
 * and may be closed for a variety of reasons.
 */
"use strict";
const stats = require("../config/stats");
const log = require("../logging").get("ClientPool");
const Promise = require("bluebird");
const QueuePool = require("../util/QueuePool");

const RECONNECT_CLIENT_QUEUE_SIZE = 50;

function ClientPool(ircBridge) {
    this._ircBridge = ircBridge;
    // The list of bot clients on servers (not specific users)
    this._botClients = {
        // server_domain: BridgedClient
    };

    // list of virtual users on servers
    this._virtualClients = {
        // server_domain: {
        //    nicks: {
        //      <nickname>: BridgedClient
        //    },
        //    userIds: {
        //      <user_id>: BridgedClient
        //    }
        // }
    }
    // map of numbers of connected clients on each server
    // Counting these is quite expensive because we have to
    // ignore entries where the value is undefined. Instead,
    // just keep track of how many we have.
    this._virtualClientCounts = {
        // server_domain: number
    };
    this._reconnectQueue = new QueuePool(
        RECONNECT_CLIENT_QUEUE_SIZE,
        (item) => {
            log.info(`Reconnecting client. ${this._reconnectQueue.waitingItems} left.`);
            return this._reconnectClient(item);
        }
    );
}

ClientPool.prototype.killAllClients = function() {
    let domainList = Object.keys(this._virtualClients);
    let clients = [];
    domainList.forEach((domain) => {
        clients = clients.concat(
            Object.keys(this._virtualClients[domain].nicks).map(
                (nick) => this._virtualClients[domain].nicks[nick]
            )
        );

        clients = clients.concat(
            Object.keys(this._virtualClients[domain].userIds).map(
                (userId) => this._virtualClients[domain].userIds[userId]
            )
        );

        clients.push(this._botClients[domain]);
    });

    clients = clients.filter((c) => Boolean(c));

    return Promise.all(
        clients.map(
            (client) => client.kill()
        )
    );
}

ClientPool.prototype.setBot = function(server, client) {
    this._botClients[server.domain] = client;
};

ClientPool.prototype.getBot = function(server) {
    return this._botClients[server.domain];
};

ClientPool.prototype.createIrcClient = function(ircClientConfig, matrixUser, isBot) {
    var bridgedClient = this._ircBridge.createBridgedClient(
        ircClientConfig, matrixUser, isBot
    );
    var server = bridgedClient.server;

    if (this._virtualClients[server.domain] === undefined) {
        this._virtualClients[server.domain] = {
            nicks: Object.create(null),
            userIds: Object.create(null)
        };
        this._virtualClientCounts[server.domain] = 0;
    }
    if (isBot) {
        this._botClients[server.domain] = bridgedClient;
    }

    // add event listeners
    bridgedClient.on("client-connected", this._onClientConnected.bind(this));
    bridgedClient.on("client-disconnected", this._onClientDisconnected.bind(this));
    bridgedClient.on("nick-change", this._onNickChange.bind(this));
    bridgedClient.on("join-error", this._onJoinError.bind(this));
    bridgedClient.on("irc-names", this._onNames.bind(this));

    // store the bridged client immediately in the pool even though it isn't
    // connected yet, else we could spawn 2 clients for a single user if this
    // function is called quickly.
    this._virtualClients[server.domain].userIds[bridgedClient.userId] = bridgedClient;
    this._virtualClientCounts[server.domain] = this._virtualClientCounts[server.domain] + 1;

    // Does this server have a max clients limit? If so, check if the limit is
    // reached and start cycling based on oldest time.
    this._checkClientLimit(server);
    return bridgedClient;
};

ClientPool.prototype.getBridgedClientByUserId = function(server, userId) {
    if (!this._virtualClients[server.domain]) {
        return undefined;
    }
    var cli = this._virtualClients[server.domain].userIds[userId];
    if (!cli || cli.isDead()) {
        return undefined;
    }
    return cli;
};

ClientPool.prototype.getBridgedClientByNick = function(server, nick) {
    var bot = this.getBot(server);
    if (bot && bot.nick === nick) {
        return bot;
    }

    if (!this._virtualClients[server.domain]) {
        return undefined;
    }
    var cli = this._virtualClients[server.domain].nicks[nick];
    if (!cli || cli.isDead()) {
        return undefined;
    }
    return cli;
};

ClientPool.prototype.getBridgedClientsForUserId = function(userId) {
    var domainList = Object.keys(this._virtualClients);
    var clientList = [];
    domainList.forEach((domain) => {
        var cli = this._virtualClients[domain].userIds[userId];
        if (cli && !cli.isDead()) {
            clientList.push(cli);
        }
    });
    return clientList;
};

ClientPool.prototype._checkClientLimit = function(server) {
    if (server.getMaxClients() === 0) {
        return;
    }

    var numConnections = this._getNumberOfConnections(server);
    this._sendConnectionMetric(server);

    if (numConnections < server.getMaxClients()) {
        // under the limit, we're good for now.
        log.debug(
            "%s active connections on %s",
            numConnections, server.domain
        );
        return;
    }

    log.debug(
        "%s active connections on %s (limit %s)",
        numConnections, server.domain, server.getMaxClients()
    );

    // find the oldest client to kill.
    var oldest = null;
    Object.keys(this._virtualClients[server.domain].nicks).forEach((nick) => {
        var client = this._virtualClients[server.domain].nicks[nick];
        if (!client) {
            // possible since undefined/null values can be present from culled entries
            return;
        }
        if (client.isBot) {
            return; // don't ever kick the bot off.
        }
        if (oldest === null) {
            oldest = client;
            return;
        }
        if (client.getLastActionTs() < oldest.getLastActionTs()) {
            oldest = client;
        }
    });
    if (!oldest) {
        return;
    }
    // disconnect and remove mappings.
    this._removeBridgedClient(oldest);
    oldest.disconnect("Client limit exceeded: " + server.getMaxClients()).then(
    function() {
        log.info("Client limit exceeded: Disconnected %s on %s.",
            oldest.nick, oldest.server.domain);
    },
    function(e) {
        log.error("Error when disconnecting %s on server %s: %s",
            oldest.nick, oldest.server.domain, JSON.stringify(e));
    });
};

ClientPool.prototype._getNumberOfConnections = function(server) {
    if (!server || !this._virtualClients[server.domain]) { return 0; }
    return this._virtualClientCounts[server.domain];
};

ClientPool.prototype.countTotalConnections = function() {
    var count = 0;

    Object.keys(this._virtualClients).forEach((domain) => {
        let server = this._ircBridge.getServer(domain);
        count += this._getNumberOfConnections(server);
    });

    return count;
};

ClientPool.prototype._sendConnectionMetric = function(server) {
    stats.ircClients(server.domain, this._getNumberOfConnections(server));
};

ClientPool.prototype._removeBridgedClient = function(bridgedClient) {
    var server = bridgedClient.server;
    this._virtualClients[server.domain].userIds[bridgedClient.userId] = undefined;
    this._virtualClients[server.domain].nicks[bridgedClient.nick] = undefined;
    this._virtualClientCounts[server.domain] = this._virtualClientCounts[server.domain] - 1;

    if (bridgedClient.isBot) {
        this._botClients[server.domain] = undefined;
    }
};

ClientPool.prototype._onClientConnected = function(bridgedClient) {
    var server = bridgedClient.server;
    var oldNick = bridgedClient.nick;
    var actualNick = bridgedClient.unsafeClient.nick;

    // assign a nick to this client
    this._virtualClients[server.domain].nicks[actualNick] = bridgedClient;

    // informative logging
    if (oldNick !== actualNick) {
        log.debug("Connected with nick '%s' instead of desired nick '%s'",
            actualNick, oldNick);
    }
};

ClientPool.prototype._onClientDisconnected = function(bridgedClient) {
    this._removeBridgedClient(bridgedClient);
    this._sendConnectionMetric(bridgedClient.server);

    if (bridgedClient.explicitDisconnect) {
        // don't reconnect users which explicitly disconnected e.g. client
        // cycling, idle timeouts, leaving rooms, etc.
        return;
    }
    // Reconnect this user
    // change the client config to use the current nick rather than the desired nick. This
    // makes sure that the client attempts to reconnect with the *SAME* nick, and also draws
    // from the latest !nick change, as the client config here may be very very old.
    var cliConfig = bridgedClient.getClientConfig();
    cliConfig.setDesiredNick(bridgedClient.nick);

    var cli = this.createIrcClient(
        cliConfig, bridgedClient.matrixUser, bridgedClient.isBot
    );
    var chanList = bridgedClient.chanList;
    // remove ref to the disconnected client so it can be GC'd. If we don't do this,
    // the timeout below holds it in a closure, preventing it from being GC'd.
    bridgedClient = undefined;
    this._reconnectQueue.enqueue(cli._id, {
        cli: cli,
        chanList: chanList,
    });
};

ClientPool.prototype._reconnectClient = function(cliChan) {
    const cli = cliChan.cli;
    const chanList = cliChan.chanList;
    return cli.connect().then(() => {
        log.info(
            "<%s> Reconnected %s@%s", cli._id, cli.nick, cli.server.domain
        );
        if (chanList.length > 0) {
            log.info("<%s> Rejoining %s channels", cli._id, chanList.length);
            chanList.forEach(function(c) {
                cli.joinChannel(c);
            });
        }
        this._sendConnectionMetric(cli.server);
    }, function(e) {
        log.error(
            "<%s> Failed to reconnect %s@%s", cli._id, cli.nick, cli.server.domain
        );
    });
}

ClientPool.prototype._onNickChange = function(bridgedClient, oldNick, newNick) {
    this._virtualClients[bridgedClient.server.domain].nicks[oldNick] = undefined;
    this._virtualClients[bridgedClient.server.domain].nicks[newNick] = bridgedClient;
};

ClientPool.prototype._onJoinError = Promise.coroutine(function*(bridgedClient, chan, err) {
    var errorsThatShouldKick = [
        "err_bannedfromchan", // they aren't allowed in channels they are banned on.
        "err_inviteonlychan", // they aren't allowed in invite only channels
        "err_channelisfull", // they aren't allowed in if the channel is full
        "err_badchannelkey", // they aren't allowed in channels with a bad key
        "err_needreggednick", // they aren't allowed in +r channels if they haven't authed
    ];
    if (errorsThatShouldKick.indexOf(err) === -1) {
        return;
    }
    if (!bridgedClient.userId || bridgedClient.isBot) {
        return; // the bot itself can get these join errors
    }
    // TODO: this is a bit evil, no one in their right mind would expect
    // the client pool to be kicking matrix users from a room :(
    log.info(`Kicking ${bridgedClient.userId} from room due to ${err}`);
    let matrixRooms = yield this._ircBridge.getStore().getMatrixRoomsForChannel(
        bridgedClient.server, chan
    );
    let promises = matrixRooms.map((room) => {
        return this._ircBridge.getAppServiceBridge().getIntent().kick(
            room.getId(), bridgedClient.userId, `IRC error on ${chan}: ${err}`
        );
    });
    yield Promise.all(promises);
});

ClientPool.prototype._onNames = Promise.coroutine(function*(bridgedClient, chan, names) {
    let mls = this._ircBridge.memberListSyncers[bridgedClient.server.domain];
    if (!mls) {
        return;
    }
    yield mls.updateIrcMemberList(chan, names);
});

module.exports = ClientPool;
<commit_msg>Don't attempt to reconnect client without channels.<commit_after>/*eslint no-invalid-this: 0*/
/*
 * Maintains a lookup of connected IRC clients. These connections are transient
 * and may be closed for a variety of reasons.
 */
"use strict";
const stats = require("../config/stats");
const log = require("../logging").get("ClientPool");
const Promise = require("bluebird");
const QueuePool = require("../util/QueuePool");

const RECONNECT_CLIENT_QUEUE_SIZE = 50;

function ClientPool(ircBridge) {
    this._ircBridge = ircBridge;
    // The list of bot clients on servers (not specific users)
    this._botClients = {
        // server_domain: BridgedClient
    };

    // list of virtual users on servers
    this._virtualClients = {
        // server_domain: {
        //    nicks: {
        //      <nickname>: BridgedClient
        //    },
        //    userIds: {
        //      <user_id>: BridgedClient
        //    }
        // }
    }
    // map of numbers of connected clients on each server
    // Counting these is quite expensive because we have to
    // ignore entries where the value is undefined. Instead,
    // just keep track of how many we have.
    this._virtualClientCounts = {
        // server_domain: number
    };
    this._reconnectQueue = new QueuePool(
        RECONNECT_CLIENT_QUEUE_SIZE,
        (item) => {
            log.info(`Reconnecting client. ${this._reconnectQueue.waitingItems} left.`);
            return this._reconnectClient(item);
        }
    );
}

ClientPool.prototype.killAllClients = function() {
    let domainList = Object.keys(this._virtualClients);
    let clients = [];
    domainList.forEach((domain) => {
        clients = clients.concat(
            Object.keys(this._virtualClients[domain].nicks).map(
                (nick) => this._virtualClients[domain].nicks[nick]
            )
        );

        clients = clients.concat(
            Object.keys(this._virtualClients[domain].userIds).map(
                (userId) => this._virtualClients[domain].userIds[userId]
            )
        );

        clients.push(this._botClients[domain]);
    });

    clients = clients.filter((c) => Boolean(c));

    return Promise.all(
        clients.map(
            (client) => client.kill()
        )
    );
}

ClientPool.prototype.setBot = function(server, client) {
    this._botClients[server.domain] = client;
};

ClientPool.prototype.getBot = function(server) {
    return this._botClients[server.domain];
};

ClientPool.prototype.createIrcClient = function(ircClientConfig, matrixUser, isBot) {
    var bridgedClient = this._ircBridge.createBridgedClient(
        ircClientConfig, matrixUser, isBot
    );
    var server = bridgedClient.server;

    if (this._virtualClients[server.domain] === undefined) {
        this._virtualClients[server.domain] = {
            nicks: Object.create(null),
            userIds: Object.create(null)
        };
        this._virtualClientCounts[server.domain] = 0;
    }
    if (isBot) {
        this._botClients[server.domain] = bridgedClient;
    }

    // add event listeners
    bridgedClient.on("client-connected", this._onClientConnected.bind(this));
    bridgedClient.on("client-disconnected", this._onClientDisconnected.bind(this));
    bridgedClient.on("nick-change", this._onNickChange.bind(this));
    bridgedClient.on("join-error", this._onJoinError.bind(this));
    bridgedClient.on("irc-names", this._onNames.bind(this));

    // store the bridged client immediately in the pool even though it isn't
    // connected yet, else we could spawn 2 clients for a single user if this
    // function is called quickly.
    this._virtualClients[server.domain].userIds[bridgedClient.userId] = bridgedClient;
    this._virtualClientCounts[server.domain] = this._virtualClientCounts[server.domain] + 1;

    // Does this server have a max clients limit? If so, check if the limit is
    // reached and start cycling based on oldest time.
    this._checkClientLimit(server);
    return bridgedClient;
};

ClientPool.prototype.getBridgedClientByUserId = function(server, userId) {
    if (!this._virtualClients[server.domain]) {
        return undefined;
    }
    var cli = this._virtualClients[server.domain].userIds[userId];
    if (!cli || cli.isDead()) {
        return undefined;
    }
    return cli;
};

ClientPool.prototype.getBridgedClientByNick = function(server, nick) {
    var bot = this.getBot(server);
    if (bot && bot.nick === nick) {
        return bot;
    }

    if (!this._virtualClients[server.domain]) {
        return undefined;
    }
    var cli = this._virtualClients[server.domain].nicks[nick];
    if (!cli || cli.isDead()) {
        return undefined;
    }
    return cli;
};

ClientPool.prototype.getBridgedClientsForUserId = function(userId) {
    var domainList = Object.keys(this._virtualClients);
    var clientList = [];
    domainList.forEach((domain) => {
        var cli = this._virtualClients[domain].userIds[userId];
        if (cli && !cli.isDead()) {
            clientList.push(cli);
        }
    });
    return clientList;
};

ClientPool.prototype._checkClientLimit = function(server) {
    if (server.getMaxClients() === 0) {
        return;
    }

    var numConnections = this._getNumberOfConnections(server);
    this._sendConnectionMetric(server);

    if (numConnections < server.getMaxClients()) {
        // under the limit, we're good for now.
        log.debug(
            "%s active connections on %s",
            numConnections, server.domain
        );
        return;
    }

    log.debug(
        "%s active connections on %s (limit %s)",
        numConnections, server.domain, server.getMaxClients()
    );

    // find the oldest client to kill.
    var oldest = null;
    Object.keys(this._virtualClients[server.domain].nicks).forEach((nick) => {
        var client = this._virtualClients[server.domain].nicks[nick];
        if (!client) {
            // possible since undefined/null values can be present from culled entries
            return;
        }
        if (client.isBot) {
            return; // don't ever kick the bot off.
        }
        if (oldest === null) {
            oldest = client;
            return;
        }
        if (client.getLastActionTs() < oldest.getLastActionTs()) {
            oldest = client;
        }
    });
    if (!oldest) {
        return;
    }
    // disconnect and remove mappings.
    this._removeBridgedClient(oldest);
    oldest.disconnect("Client limit exceeded: " + server.getMaxClients()).then(
    function() {
        log.info("Client limit exceeded: Disconnected %s on %s.",
            oldest.nick, oldest.server.domain);
    },
    function(e) {
        log.error("Error when disconnecting %s on server %s: %s",
            oldest.nick, oldest.server.domain, JSON.stringify(e));
    });
};

ClientPool.prototype._getNumberOfConnections = function(server) {
    if (!server || !this._virtualClients[server.domain]) { return 0; }
    return this._virtualClientCounts[server.domain];
};

ClientPool.prototype.countTotalConnections = function() {
    var count = 0;

    Object.keys(this._virtualClients).forEach((domain) => {
        let server = this._ircBridge.getServer(domain);
        count += this._getNumberOfConnections(server);
    });

    return count;
};

ClientPool.prototype._sendConnectionMetric = function(server) {
    stats.ircClients(server.domain, this._getNumberOfConnections(server));
};

ClientPool.prototype._removeBridgedClient = function(bridgedClient) {
    var server = bridgedClient.server;
    this._virtualClients[server.domain].userIds[bridgedClient.userId] = undefined;
    this._virtualClients[server.domain].nicks[bridgedClient.nick] = undefined;
    this._virtualClientCounts[server.domain] = this._virtualClientCounts[server.domain] - 1;

    if (bridgedClient.isBot) {
        this._botClients[server.domain] = undefined;
    }
};

ClientPool.prototype._onClientConnected = function(bridgedClient) {
    var server = bridgedClient.server;
    var oldNick = bridgedClient.nick;
    var actualNick = bridgedClient.unsafeClient.nick;

    // assign a nick to this client
    this._virtualClients[server.domain].nicks[actualNick] = bridgedClient;

    // informative logging
    if (oldNick !== actualNick) {
        log.debug("Connected with nick '%s' instead of desired nick '%s'",
            actualNick, oldNick);
    }
};

ClientPool.prototype._onClientDisconnected = function(bridgedClient) {
    this._removeBridgedClient(bridgedClient);
    this._sendConnectionMetric(bridgedClient.server);

    if (bridgedClient.explicitDisconnect) {
        // don't reconnect users which explicitly disconnected e.g. client
        // cycling, idle timeouts, leaving rooms, etc.
        return;
    }
    // Reconnect this user
    // change the client config to use the current nick rather than the desired nick. This
    // makes sure that the client attempts to reconnect with the *SAME* nick, and also draws
    // from the latest !nick change, as the client config here may be very very old.
    var cliConfig = bridgedClient.getClientConfig();
    cliConfig.setDesiredNick(bridgedClient.nick);

    var cli = this.createIrcClient(
        cliConfig, bridgedClient.matrixUser, bridgedClient.isBot
    );
    var chanList = bridgedClient.chanList;
    // remove ref to the disconnected client so it can be GC'd. If we don't do this,
    // the timeout below holds it in a closure, preventing it from being GC'd.
    bridgedClient = undefined;

    if(chanList.length === 0) {
        log.info(`Dropping ${cli._id} ${cli.nick} because they are not joined to any channels`);
        return;
    }

    this._reconnectQueue.enqueue(cli._id, {
        cli: cli,
        chanList: chanList,
    });
};

ClientPool.prototype._reconnectClient = function(cliChan) {
    const cli = cliChan.cli;
    const chanList = cliChan.chanList;
    return cli.connect().then(() => {
        log.info(
            "<%s> Reconnected %s@%s", cli._id, cli.nick, cli.server.domain
        );
        log.info("<%s> Rejoining %s channels", cli._id, chanList.length);
        chanList.forEach(function(c) {
            cli.joinChannel(c);
        });
        this._sendConnectionMetric(cli.server);
    }, function(e) {
        log.error(
            "<%s> Failed to reconnect %s@%s", cli._id, cli.nick, cli.server.domain
        );
    });
}

ClientPool.prototype._onNickChange = function(bridgedClient, oldNick, newNick) {
    this._virtualClients[bridgedClient.server.domain].nicks[oldNick] = undefined;
    this._virtualClients[bridgedClient.server.domain].nicks[newNick] = bridgedClient;
};

ClientPool.prototype._onJoinError = Promise.coroutine(function*(bridgedClient, chan, err) {
    var errorsThatShouldKick = [
        "err_bannedfromchan", // they aren't allowed in channels they are banned on.
        "err_inviteonlychan", // they aren't allowed in invite only channels
        "err_channelisfull", // they aren't allowed in if the channel is full
        "err_badchannelkey", // they aren't allowed in channels with a bad key
        "err_needreggednick", // they aren't allowed in +r channels if they haven't authed
    ];
    if (errorsThatShouldKick.indexOf(err) === -1) {
        return;
    }
    if (!bridgedClient.userId || bridgedClient.isBot) {
        return; // the bot itself can get these join errors
    }
    // TODO: this is a bit evil, no one in their right mind would expect
    // the client pool to be kicking matrix users from a room :(
    log.info(`Kicking ${bridgedClient.userId} from room due to ${err}`);
    let matrixRooms = yield this._ircBridge.getStore().getMatrixRoomsForChannel(
        bridgedClient.server, chan
    );
    let promises = matrixRooms.map((room) => {
        return this._ircBridge.getAppServiceBridge().getIntent().kick(
            room.getId(), bridgedClient.userId, `IRC error on ${chan}: ${err}`
        );
    });
    yield Promise.all(promises);
});

ClientPool.prototype._onNames = Promise.coroutine(function*(bridgedClient, chan, names) {
    let mls = this._ircBridge.memberListSyncers[bridgedClient.server.domain];
    if (!mls) {
        return;
    }
    yield mls.updateIrcMemberList(chan, names);
});

module.exports = ClientPool;
<|endoftext|>


<commit_before>/*!
 * hex-color-regex <https://github.com/regexps/hex-color-regex>
 *
 * Copyright (c) 2015 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk)
 * Released under the MIT license.
 */

'use strict'

module.exports = function hexColorRegex (opts) {
  opts = opts && typeof opts === 'object' ? opts : {}

  return opts.strict ? /^#([a-f0-9]{6}|[a-f0-9]{3})\b$/i : /#([a-f0-9]{6}|[a-f0-9]{3})\b/gi
}
<commit_msg>add alpha channel support to regex<commit_after>/*!
 * hex-color-regex <https://github.com/regexps/hex-color-regex>
 *
 * Copyright (c) 2015 Charlike Mike Reagent <@tunnckoCore> (http://www.tunnckocore.tk)
 * Released under the MIT license.
 */

'use strict'

module.exports = function hexColorRegex (opts) {
  opts = opts && typeof opts === 'object' ? opts : {}

  return opts.strict ? /^#([a-f0-9]{3,4}|[a-f0-9]{4}(?:[a-f0-9]{2}){1,2})\b$/i : /#([a-f0-9]{3}|[a-f0-9]{4}(?:[a-f0-9]{2}){0,2})\b/gi
}
<|endoftext|>