emailjs-tcp-socket
Version:
This shim brings the W3C Raw Socket API to node.js and Chromium. Its purpose is to enable apps to use the same api in Firefox OS, Chrome OS, and on the server.
429 lines (357 loc) • 38.2 kB
JavaScript
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
var _ramda = require('ramda');
var _timeout = require('./timeout');
var _timeout2 = _interopRequireDefault(_timeout);
var _tlsUtils = require('./tls-utils');
var _tlsUtils2 = _interopRequireDefault(_tlsUtils);
var _workerUtils = require('./worker-utils');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
var TCPSocket = function () {
_createClass(TCPSocket, null, [{
key: 'open',
value: function open(host, port) {
var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
return new TCPSocket({ host: host, port: port, options: options });
}
}]);
function TCPSocket(_ref) {
var _this = this;
var host = _ref.host,
port = _ref.port,
options = _ref.options;
_classCallCheck(this, TCPSocket);
this.host = host;
this.port = port;
this.ssl = false;
this.bufferedAmount = 0;
this.readyState = 'connecting';
this.binaryType = (0, _ramda.propOr)('arraybuffer', 'binaryType')(options);
if (this.binaryType !== 'arraybuffer') {
throw new Error('Only arraybuffers are supported!');
}
this._ca = options.ca;
this._useTLS = (0, _ramda.propOr)(false, 'useSecureTransport')(options);
this._useSTARTTLS = false;
this._socketId = 0;
this._useLegacySocket = false;
this._useForgeTls = false;
// handles writes during starttls handshake, chrome socket only
this._startTlsBuffer = [];
this._startTlsHandshakeInProgress = false;
chrome.runtime.getPlatformInfo(function (platformInfo) {
if (platformInfo.os.indexOf('cordova') !== -1) {
// chrome.sockets.tcp.secure is not functional on cordova
// https://github.com/MobileChromeApps/mobile-chrome-apps/issues/269
_this._useLegacySocket = false;
_this._useForgeTls = true;
} else {
_this._useLegacySocket = true;
_this._useForgeTls = false;
}
if (_this._useLegacySocket) {
_this._createLegacySocket();
} else {
_this._createSocket();
}
});
}
/**
* Creates a socket using the deprecated chrome.socket API
*/
_createClass(TCPSocket, [{
key: '_createLegacySocket',
value: function _createLegacySocket() {
var _this2 = this;
chrome.socket.create('tcp', {}, function (createInfo) {
_this2._socketId = createInfo.socketId;
chrome.socket.connect(_this2._socketId, _this2.host, _this2.port, function (result) {
if (result !== 0) {
_this2.readyState = 'closed';
_this2._emit('error', chrome.runtime.lastError);
return;
}
_this2._onSocketConnected();
});
});
}
/**
* Creates a socket using chrome.sockets.tcp
*/
}, {
key: '_createSocket',
value: function _createSocket() {
var _this3 = this;
chrome.sockets.tcp.create({}, function (createInfo) {
_this3._socketId = createInfo.socketId;
// register for data events on the socket before connecting
chrome.sockets.tcp.onReceive.addListener(function (readInfo) {
if (readInfo.socketId === _this3._socketId) {
// process the data available on the socket
_this3._onData(readInfo.data);
}
});
// register for data error on the socket before connecting
chrome.sockets.tcp.onReceiveError.addListener(function (readInfo) {
if (readInfo.socketId === _this3._socketId) {
// socket closed remotely or broken
_this3.close();
}
});
chrome.sockets.tcp.setPaused(_this3._socketId, true, function () {
chrome.sockets.tcp.connect(_this3._socketId, _this3.host, _this3.port, function (result) {
if (result < 0) {
_this3.readyState = 'closed';
_this3._emit('error', chrome.runtime.lastError);
return;
}
_this3._onSocketConnected();
});
});
});
}
/**
* Invoked once a socket has been connected:
* - Kicks off TLS handshake, if necessary
* - Starts reading from legacy socket, if necessary
*/
}, {
key: '_onSocketConnected',
value: function _onSocketConnected() {
var _this4 = this;
var read = function read() {
if (_this4._useLegacySocket) {
// the tls handshake is done let's start reading from the legacy socket
_this4._readLegacySocket();
_this4._emit('open');
} else {
chrome.sockets.tcp.setPaused(_this4._socketId, false, function () {
_this4._emit('open');
});
}
};
if (!this._useTLS) {
return read();
}
// do an immediate TLS handshake if this._useTLS === true
this._upgradeToSecure(function () {
read();
});
}
/**
* Handles the rough edges for differences between chrome.socket and chrome.sockets.tcp
* for upgrading to a TLS connection with or without forge
*/
}, {
key: '_upgradeToSecure',
value: function _upgradeToSecure() {
var _this5 = this;
var callback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
// invoked after chrome.socket.secure or chrome.sockets.tcp.secure have been upgraded
var onUpgraded = function onUpgraded(tlsResult) {
if (tlsResult !== 0) {
_this5._emit('error', new Error('TLS handshake failed. Reason: ' + chrome.runtime.lastError.message));
_this5.close();
return;
}
_this5.ssl = true;
// empty the buffer
while (_this5._startTlsBuffer.length) {
_this5.send(_this5._startTlsBuffer.shift());
}
callback();
};
if (!this._useLegacySocket && this.readyState !== 'open') {
// use chrome.sockets.tcp.secure for TLS, not for STARTTLS!
// use forge only for STARTTLS
this._useForgeTls = false;
chrome.sockets.tcp.secure(this._socketId, onUpgraded);
} else if (this._useLegacySocket) {
chrome.socket.secure(this._socketId, onUpgraded);
} else if (this._useForgeTls) {
// setup the forge tls client or webworker as tls fallback
(0, _tlsUtils2.default)(this);
callback();
}
}
}, {
key: 'upgradeToSecure',
value: function upgradeToSecure() {
var _this6 = this;
if (this.ssl || this._useSTARTTLS) {
return;
}
this._useSTARTTLS = true;
this._upgradeToSecure(function () {
if (_this6._useLegacySocket) {
_this6._readLegacySocket(); // tls handshake is done, restart reading
}
});
}
/**
* Reads from a legacy chrome.socket.
*/
}, {
key: '_readLegacySocket',
value: function _readLegacySocket() {
var _this7 = this;
if (this._socketId === 0) {
// the socket is closed. omit read and stop further reads
return;
}
// don't read from chrome.socket if we have chrome.socket.secure a handshake in progress!
if ((this._useSTARTTLS || this._useTLS) && !this.ssl) {
return;
}
chrome.socket.read(this._socketId, function (readInfo) {
// socket closed remotely or broken
if (readInfo.resultCode <= 0) {
_this7._socketId = 0;
_this7.close();
return;
}
// process the data available on the socket
_this7._onData(readInfo.data);
// Queue the next read.
// If a STARTTLS handshake might be upcoming, postpone this onto
// the task queue so the IMAP client has a chance to call upgradeToSecure;
// without this, we might eat the beginning of the handshake.
// If we are already secure, just call it (for performance).
if (_this7.ssl) {
_this7._readLegacySocket();
} else {
(0, _timeout2.default)(function () {
return _this7._readLegacySocket();
});
}
});
}
/**
* Invoked when data has been read from the socket. Handles cases when to feed
* the data available on the socket to forge.
*
* @param {ArrayBuffer} buffer The binary data read from the socket
*/
}, {
key: '_onData',
value: function _onData(buffer) {
if ((this._useTLS || this._useSTARTTLS) && this._useForgeTls) {
// feed the data to the tls client
if (this._tlsWorker) {
this._tlsWorker.postMessage((0, _workerUtils.createMessage)(_workerUtils.EVENT_INBOUND, buffer), [buffer]);
} else {
this._tls.processInbound(buffer);
}
} else {
// emit data event
this._emit('data', buffer);
}
}
/**
* Closes the socket
* @return {[type]} [description]
*/
}, {
key: 'close',
value: function close() {
this.readyState = 'closing';
if (this._socketId !== 0) {
if (this._useLegacySocket) {
// close legacy socket
chrome.socket.disconnect(this._socketId);
chrome.socket.destroy(this._socketId);
} else {
// close socket
chrome.sockets.tcp.disconnect(this._socketId);
}
this._socketId = 0;
}
// terminate the tls worker
if (this._tlsWorker) {
this._tlsWorker.terminate();
this._tlsWorker = undefined;
}
this._emit('close');
}
}, {
key: 'send',
value: function send(buffer) {
if (!this._useForgeTls && this._useSTARTTLS && !this.ssl) {
// buffer the unprepared data until chrome.socket(s.tcp) handshake is done
this._startTlsBuffer.push(buffer);
} else if (this._useForgeTls && (this._useTLS || this._useSTARTTLS)) {
// give buffer to forge to be prepared for tls
if (this._tlsWorker) {
this._tlsWorker.postMessage((0, _workerUtils.createMessage)(_workerUtils.EVENT_OUTBOUND, buffer), [buffer]);
} else {
this._tls.prepareOutbound(buffer);
}
} else {
// send the arraybuffer
this._send(buffer);
}
}
}, {
key: '_send',
value: function _send(data) {
var _this8 = this;
if (this._socketId === 0) {
// the socket is closed.
return;
}
if (this._useLegacySocket) {
chrome.socket.write(this._socketId, data, function (writeInfo) {
if (writeInfo.bytesWritten < 0 && _this8._socketId !== 0) {
// if the socket is already 0, it has already been closed. no need to alert then...
_this8._emit('error', new Error('Could not write ' + data.byteLength + ' bytes to socket ' + _this8._socketId + '. Chrome error code: ' + writeInfo.bytesWritten));
_this8._socketId = 0;
_this8.close();
return;
}
_this8._emit('drain');
});
} else {
chrome.sockets.tcp.send(this._socketId, data, function (sendInfo) {
if (sendInfo.bytesSent < 0 && _this8._socketId !== 0) {
// if the socket is already 0, it has already been closed. no need to alert then...
_this8._emit('error', new Error('Could not write ' + data.byteLength + ' bytes to socket ' + _this8._socketId + '. Chrome error code: ' + sendInfo.bytesSent));
_this8.close();
return;
}
_this8._emit('drain');
});
}
}
}, {
key: '_emit',
value: function _emit(type, data) {
var target = this;
switch (type) {
case 'open':
this.readyState = 'open';
this.onopen && this.onopen({ target: target, type: type, data: data });
break;
case 'error':
this.onerror && this.onerror({ target: target, type: type, data: data });
break;
case 'data':
this.ondata && this.ondata({ target: target, type: type, data: data });
break;
case 'drain':
this.ondrain && this.ondrain({ target: target, type: type, data: data });
break;
case 'close':
this.readyState = 'closed';
this.onclose && this.onclose({ target: target, type: type, data: data });
break;
}
}
}]);
return TCPSocket;
}();
exports.default = TCPSocket;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy9jaHJvbWUtc29ja2V0LmpzIl0sIm5hbWVzIjpbIlRDUFNvY2tldCIsImhvc3QiLCJwb3J0Iiwib3B0aW9ucyIsInNzbCIsImJ1ZmZlcmVkQW1vdW50IiwicmVhZHlTdGF0ZSIsImJpbmFyeVR5cGUiLCJFcnJvciIsIl9jYSIsImNhIiwiX3VzZVRMUyIsIl91c2VTVEFSVFRMUyIsIl9zb2NrZXRJZCIsIl91c2VMZWdhY3lTb2NrZXQiLCJfdXNlRm9yZ2VUbHMiLCJfc3RhcnRUbHNCdWZmZXIiLCJfc3RhcnRUbHNIYW5kc2hha2VJblByb2dyZXNzIiwiY2hyb21lIiwicnVudGltZSIsImdldFBsYXRmb3JtSW5mbyIsInBsYXRmb3JtSW5mbyIsIm9zIiwiaW5kZXhPZiIsIl9jcmVhdGVMZWdhY3lTb2NrZXQiLCJfY3JlYXRlU29ja2V0Iiwic29ja2V0IiwiY3JlYXRlIiwiY3JlYXRlSW5mbyIsInNvY2tldElkIiwiY29ubmVjdCIsInJlc3VsdCIsIl9lbWl0IiwibGFzdEVycm9yIiwiX29uU29ja2V0Q29ubmVjdGVkIiwic29ja2V0cyIsInRjcCIsIm9uUmVjZWl2ZSIsImFkZExpc3RlbmVyIiwicmVhZEluZm8iLCJfb25EYXRhIiwiZGF0YSIsIm9uUmVjZWl2ZUVycm9yIiwiY2xvc2UiLCJzZXRQYXVzZWQiLCJyZWFkIiwiX3JlYWRMZWdhY3lTb2NrZXQiLCJfdXBncmFkZVRvU2VjdXJlIiwiY2FsbGJhY2siLCJvblVwZ3JhZGVkIiwidGxzUmVzdWx0IiwibWVzc2FnZSIsImxlbmd0aCIsInNlbmQiLCJzaGlmdCIsInNlY3VyZSIsInJlc3VsdENvZGUiLCJidWZmZXIiLCJfdGxzV29ya2VyIiwicG9zdE1lc3NhZ2UiLCJfdGxzIiwicHJvY2Vzc0luYm91bmQiLCJkaXNjb25uZWN0IiwiZGVzdHJveSIsInRlcm1pbmF0ZSIsInVuZGVmaW5lZCIsInB1c2giLCJwcmVwYXJlT3V0Ym91bmQiLCJfc2VuZCIsIndyaXRlIiwid3JpdGVJbmZvIiwiYnl0ZXNXcml0dGVuIiwiYnl0ZUxlbmd0aCIsInNlbmRJbmZvIiwiYnl0ZXNTZW50IiwidHlwZSIsInRhcmdldCIsIm9ub3BlbiIsIm9uZXJyb3IiLCJvbmRhdGEiLCJvbmRyYWluIiwib25jbG9zZSJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFBQTs7QUFDQTs7OztBQUNBOzs7O0FBQ0E7Ozs7OztJQUtxQkEsUzs7O3lCQUNOQyxJLEVBQU1DLEksRUFBb0I7QUFBQSxVQUFkQyxPQUFjLHVFQUFKLEVBQUk7O0FBQ3JDLGFBQU8sSUFBSUgsU0FBSixDQUFjLEVBQUVDLFVBQUYsRUFBUUMsVUFBUixFQUFjQyxnQkFBZCxFQUFkLENBQVA7QUFDRDs7O0FBRUQsMkJBQXNDO0FBQUE7O0FBQUEsUUFBdkJGLElBQXVCLFFBQXZCQSxJQUF1QjtBQUFBLFFBQWpCQyxJQUFpQixRQUFqQkEsSUFBaUI7QUFBQSxRQUFYQyxPQUFXLFFBQVhBLE9BQVc7O0FBQUE7O0FBQ3BDLFNBQUtGLElBQUwsR0FBWUEsSUFBWjtBQUNBLFNBQUtDLElBQUwsR0FBWUEsSUFBWjtBQUNBLFNBQUtFLEdBQUwsR0FBVyxLQUFYO0FBQ0EsU0FBS0MsY0FBTCxHQUFzQixDQUF0QjtBQUNBLFNBQUtDLFVBQUwsR0FBa0IsWUFBbEI7QUFDQSxTQUFLQyxVQUFMLEdBQWtCLG1CQUFPLGFBQVAsRUFBc0IsWUFBdEIsRUFBb0NKLE9BQXBDLENBQWxCOztBQUVBLFFBQUksS0FBS0ksVUFBTCxLQUFvQixhQUF4QixFQUF1QztBQUNyQyxZQUFNLElBQUlDLEtBQUosQ0FBVSxrQ0FBVixDQUFOO0FBQ0Q7O0FBRUQsU0FBS0MsR0FBTCxHQUFXTixRQUFRTyxFQUFuQjtBQUNBLFNBQUtDLE9BQUwsR0FBZSxtQkFBTyxLQUFQLEVBQWMsb0JBQWQsRUFBb0NSLE9BQXBDLENBQWY7QUFDQSxTQUFLUyxZQUFMLEdBQW9CLEtBQXBCO0FBQ0EsU0FBS0MsU0FBTCxHQUFpQixDQUFqQjtBQUNBLFNBQUtDLGdCQUFMLEdBQXdCLEtBQXhCO0FBQ0EsU0FBS0MsWUFBTCxH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Q0UsZUFBS25DLFVBQUwsR0FBa0IsUUFBbEI7QUFDQSxlQUFLMkUsT0FBTCxJQUFnQixLQUFLQSxPQUFMLENBQWEsRUFBRUwsY0FBRixFQUFVRCxVQUFWLEVBQWdCbEMsVUFBaEIsRUFBYixDQUFoQjtBQUNBO0FBakJKO0FBbUJEOzs7Ozs7a0JBbFZrQnpDLFMiLCJmaWxlIjoiY2hyb21lLXNvY2tldC5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHByb3BPciB9IGZyb20gJ3JhbWRhJ1xuaW1wb3J0IHNjaGVkdWxlSW5OZXh0RXZlbnRMb29wIGZyb20gJy4vdGltZW91dCdcbmltcG9ydCBjcmVhdGVUbHMgZnJvbSAnLi90bHMtdXRpbHMnXG5pbXBvcnQge1xuICBFVkVOVF9JTkJPVU5ELCBFVkVOVF9PVVRCT1VORCxcbiAgY3JlYXRlTWVzc2FnZVxufSBmcm9tICcuL3dvcmtlci11dGlscydcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgVENQU29ja2V0IHtcbiAgc3RhdGljIG9wZW4gKGhvc3QsIHBvcnQsIG9wdGlvbnMgPSB7fSkge1xuICAgIHJldHVybiBuZXcgVENQU29ja2V0KHsgaG9zdCwgcG9ydCwgb3B0aW9ucyB9KVxuICB9XG5cbiAgY29uc3RydWN0b3IgKHsgaG9zdCwgcG9ydCwgb3B0aW9ucyB9KSB7XG4gICAgdGhpcy5ob3N0ID0gaG9zdFxuICAgIHRoaXMucG9ydCA9IHBvcnRcbiAgICB0aGlzLnNzbCA9IGZhbHNlXG4gICAgdGhpcy5idWZmZXJlZEFtb3VudCA9IDBcbiAgICB0aGlzLnJlYWR5U3RhdGUgPSAnY29ubmVjdGluZydcbiAgICB0aGlzLmJpbmFyeVR5cGUgPSBwcm9wT3IoJ2FycmF5YnVmZmVyJywgJ2JpbmFyeVR5cGUnKShvcHRpb25zKVxuXG4gICAgaWYgKHRoaXMuYmluYXJ5VHlwZSAhPT0gJ2FycmF5YnVmZmVyJykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdPbmx5IGFycmF5YnVmZmVycyBhcmUgc3VwcG9ydGVkIScpXG4gICAgfVxuXG4gICAgdGhpcy5fY2EgPSBvcHRpb25zLmNhXG4gICAgdGhpcy5fdXNlVExTID0gcHJvcE9yKGZhbHNlLCAndXNlU2VjdXJlVHJhbnNwb3J0Jykob3B0aW9ucylcbiAgICB0aGlzLl91c2VTVEFSVFRMUyA9IGZhbHNlXG4gICAgdGhpcy5fc29ja2V0SWQgPSAwXG4gICAgdGhpcy5fdXNlTGVnYWN5U29ja2V0ID0gZmFsc2VcbiAgICB0aGlzLl91c2VGb3JnZVRscyA9IGZhbHNlXG5cbiAgICAvLyBoYW5kbGVzIHdyaXRlcyBkdXJpbmcgc3RhcnR0bHMgaGFuZHNoYWtlLCBjaHJvbWUgc29ja2V0IG9ubHlcbiAgICB0aGlzLl9zdGFydFRsc0J1ZmZlciA9IFtdXG4gICAgdGhpcy5fc3RhcnRUbHNIYW5kc2hha2VJblByb2dyZXNzID0gZmFsc2VcblxuICAgIGNocm9tZS5ydW50aW1lLmdldFBsYXRmb3JtSW5mbyhwbGF0Zm9ybUluZm8gPT4ge1xuICAgICAgaWYgKHBsYXRmb3JtSW5mby5vcy5pbmRleE9mKCdjb3Jkb3ZhJykgIT09IC0xKSB7XG4gICAgICAgIC8vIGNocm9tZS5zb2NrZXRzLnRjcC5zZWN1cmUgaXMgbm90IGZ1bmN0aW9uYWwgb24gY29yZG92YVxuICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vTW9iaWxlQ2hyb21lQXBwcy9tb2JpbGUtY2hyb21lLWFwcHMvaXNzdWVzLzI2OVxuICAgICAgICB0aGlzLl91c2VMZWdhY3lTb2NrZXQgPSBmYWxzZVxuICAgICAgICB0aGlzLl91c2VGb3JnZVRscyA9IHRydWVcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuX3VzZUxlZ2FjeVNvY2tldCA9IHRydWVcbiAgICAgICAgdGhpcy5fdXNlRm9yZ2VUbHMgPSBmYWxzZVxuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5fdXNlTGVnYWN5U29ja2V0KSB7XG4gICAgICAgIHRoaXMuX2NyZWF0ZUxlZ2FjeVNvY2tldCgpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLl9jcmVhdGVTb2NrZXQoKVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQ3JlYXRlcyBhIHNvY2tldCB1c2luZyB0aGUgZGVwcmVjYXRlZCBjaHJvbWUuc29ja2V0IEFQSVxuICAgKi9cbiAgX2NyZWF0ZUxlZ2FjeVNvY2tldCAoKSB7XG4gICAgY2hyb21lLnNvY2tldC5jcmVhdGUoJ3RjcCcsIHt9LCBjcmVhdGVJbmZvID0+IHtcbiAgICAgIHRoaXMuX3NvY2tldElkID0gY3JlYXRlSW5mby5zb2NrZXRJZFxuXG4gICAgICBjaHJvbWUuc29ja2V0LmNvbm5lY3QodGhpcy5fc29ja2V0SWQsIHRoaXMuaG9zdCwgdGhpcy5wb3J0LCByZXN1bHQgPT4ge1xuICAgICAgICBpZiAocmVzdWx0ICE9PSAwKSB7XG4gICAgICAgICAgdGhpcy5yZWFkeVN0YXRlID0gJ2Nsb3NlZCdcbiAgICAgICAgICB0aGlzLl9lbWl0KCdlcnJvcicsIGNocm9tZS5ydW50aW1lLmxhc3RFcnJvcilcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuX29uU29ja2V0Q29ubmVjdGVkKClcbiAgICAgIH0pXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBDcmVhdGVzIGEgc29ja2V0IHVzaW5nIGNocm9tZS5zb2NrZXRzLnRjcFxuICAgKi9cbiAgX2NyZWF0ZVNvY2tldCAoKSB7XG4gICAgY2hyb21lLnNvY2tldHMudGNwLmNyZWF0ZSh7fSwgY3JlYXRlSW5mbyA9PiB7XG4gICAgICB0aGlzLl9zb2NrZXRJZCA9IGNyZWF0ZUluZm8uc29ja2V0SWRcblxuICAgICAgLy8gcmVnaXN0ZXIgZm9yIGRhdGEgZXZlbnRzIG9uIHRoZSBzb2NrZXQgYmVmb3JlIGNvbm5lY3RpbmdcbiAgICAgIGNocm9tZS5zb2NrZXRzLnRjcC5vblJlY2VpdmUuYWRkTGlzdGVuZXIocmVhZEluZm8gPT4ge1xuICAgICAgICBpZiAocmVhZEluZm8uc29ja2V0SWQgPT09IHRoaXMuX3NvY2tldElkKSB7XG4gICAgICAgICAgLy8gcHJvY2VzcyB0aGUgZGF0YSBhdmFpbGFibGUgb24gdGhlIHNvY2tldFxuICAgICAgICAgIHRoaXMuX29uRGF0YShyZWFkSW5mby5kYXRhKVxuICAgICAgICB9XG4gICAgICB9KVxuXG4gICAgICAvLyByZWdpc3RlciBmb3IgZGF0YSBlcnJvciBvbiB0aGUgc29ja2V0IGJlZm9yZSBjb25uZWN0aW5nXG4gICAgICBjaHJvbWUuc29ja2V0cy50Y3Aub25SZWNlaXZlRXJyb3IuYWRkTGlzdGVuZXIocmVhZEluZm8gPT4ge1xuICAgICAgICBpZiAocmVhZEluZm8uc29ja2V0SWQgPT09IHRoaXMuX3NvY2tldElkKSB7XG4gICAgICAgICAgLy8gc29ja2V0IGNsb3NlZCByZW1vdGVseSBvciBicm9rZW5cbiAgICAgICAgICB0aGlzLmNsb3NlKClcbiAgICAgICAgfVxuICAgICAgfSlcblxuICAgICAgY2hyb21lLnNvY2tldHMudGNwLnNldFBhdXNlZCh0aGlzLl9zb2NrZXRJZCwgdHJ1ZSwgKCkgPT4ge1xuICAgICAgICBjaHJvbWUuc29ja2V0cy50Y3AuY29ubmVjdCh0aGlzLl9zb2NrZXRJZCwgdGhpcy5ob3N0LCB0aGlzLnBvcnQsIHJlc3VsdCA9PiB7XG4gICAgICAgICAgaWYgKHJlc3VsdCA8IDApIHtcbiAgICAgICAgICAgIHRoaXMucmVhZHlTdGF0ZSA9ICdjbG9zZWQnXG4gICAgICAgICAgICB0aGlzLl9lbWl0KCdlcnJvcicsIGNocm9tZS5ydW50aW1lLmxhc3RFcnJvcilcbiAgICAgICAgICAgIHJldHVyblxuICAgICAgICAgIH1cblxuICAgICAgICAgIHRoaXMuX29uU29ja2V0Q29ubmVjdGVkKClcbiAgICAgICAgfSlcbiAgICAgIH0pXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBJbnZva2VkIG9uY2UgYSBzb2NrZXQgaGFzIGJlZW4gY29ubmVjdGVkOlxuICAgKiAtIEtpY2tzIG9mZiBUTFMgaGFuZHNoYWtlLCBpZiBuZWNlc3NhcnlcbiAgICogLSBTdGFydHMgcmVhZGluZyBmcm9tIGxlZ2FjeSBzb2NrZXQsIGlmIG5lY2Vzc2FyeVxuICAgKi9cbiAgX29uU29ja2V0Q29ubmVjdGVkICgpIHtcbiAgICBjb25zdCByZWFkID0gKCkgPT4ge1xuICAgICAgaWYgKHRoaXMuX3VzZUxlZ2FjeVNvY2tldCkge1xuICAgICAgICAvLyB0aGUgdGxzIGhhbmRzaGFrZSBpcyBkb25lIGxldCdzIHN0YXJ0IHJlYWRpbmcgZnJvbSB0aGUgbGVnYWN5IHNvY2tldFxuICAgICAgICB0aGlzLl9yZWFkTGVnYWN5U29ja2V0KClcbiAgICAgICAgdGhpcy5fZW1pdCgnb3BlbicpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBjaHJvbWUuc29ja2V0cy50Y3Auc2V0UGF1c2VkKHRoaXMuX3NvY2tldElkLCBmYWxzZSwgKCkgPT4ge1xuICAgICAgICAgIHRoaXMuX2VtaXQoJ29wZW4nKVxuICAgICAgICB9KVxuICAgICAgfVxuICAgIH1cblxuICAgIGlmICghdGhpcy5fdXNlVExTKSB7XG4gICAgICByZXR1cm4gcmVhZCgpXG4gICAgfVxuXG4gICAgLy8gZG8gYW4gaW1tZWRpYXRlIFRMUyBoYW5kc2hha2UgaWYgdGhpcy5fdXNlVExTID09PSB0cnVlXG4gICAgdGhpcy5fdXBncmFkZVRvU2VjdXJlKCgpID0+IHsgcmVhZCgpIH0pXG4gIH1cblxuICAvKipcbiAgICogSGFuZGxlcyB0aGUgcm91Z2ggZWRnZXMgZm9yIGRpZmZlcmVuY2VzIGJldHdlZW4gY2hyb21lLnNvY2tldCBhbmQgY2hyb21lLnNvY2tldHMudGNwXG4gICAqIGZvciB1cGdyYWRpbmcgdG8gYSBUTFMgY29ubmVjdGlvbiB3aXRoIG9yIHdpdGhvdXQgZm9yZ2VcbiAgICovXG4gIF91cGdyYWRlVG9TZWN1cmUgKGNhbGxiYWNrID0gKCkgPT4ge30pIHtcbiAgICAvLyBpbnZva2VkIGFmdGVyIGNocm9tZS5zb2NrZXQuc2VjdXJlIG9yIGNocm9tZS5zb2NrZXRzLnRjcC5zZWN1cmUgaGF2ZSBiZWVuIHVwZ3JhZGVkXG4gICAgY29uc3Qgb25VcGdyYWRlZCA9IHRsc1Jlc3VsdCA9PiB7XG4gICAgICBpZiAodGxzUmVzdWx0ICE9PSAwKSB7XG4gICAgICAgIHRoaXMuX2VtaXQoJ2Vycm9yJywgbmV3IEVycm9yKCdUTFMgaGFuZHNoYWtlIGZhaWxlZC4gUmVhc29uOiAnICsgY2hyb21lLnJ1bnRpbWUubGFzdEVycm9yLm1lc3NhZ2UpKVxuICAgICAgICB0aGlzLmNsb3NlKClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIHRoaXMuc3NsID0gdHJ1ZVxuXG4gICAgICAvLyBlbXB0eSB0aGUgYnVmZmVyXG4gICAgICB3aGlsZSAodGhpcy5fc3RhcnRUbHNCdWZmZXIubGVuZ3RoKSB7XG4gICAgICAgIHRoaXMuc2VuZCh0aGlzLl9zdGFydFRsc0J1ZmZlci5zaGlmdCgpKVxuICAgICAgfVxuXG4gICAgICBjYWxsYmFjaygpXG4gICAgfVxuXG4gICAgaWYgKCF0aGlzLl91c2VMZWdhY3lTb2NrZXQgJiYgdGhpcy5yZWFkeVN0YXRlICE9PSAnb3BlbicpIHtcbiAgICAgIC8vIHVzZSBjaHJvbWUuc29ja2V0cy50Y3Auc2VjdXJlIGZvciBUTFMsIG5vdCBmb3IgU1RBUlRUTFMhXG4gICAgICAvLyB1c2UgZm9yZ2Ugb25seSBmb3IgU1RBUlRUTFNcbiAgICAgIHRoaXMuX3VzZUZvcmdlVGxzID0gZmFsc2VcbiAgICAgIGNocm9tZS5zb2NrZXRzLnRjcC5zZWN1cmUodGhpcy5fc29ja2V0SWQsIG9uVXBncmFkZWQpXG4gICAgfSBlbHNlIGlmICh0aGlzLl91c2VMZWdhY3lTb2NrZXQpIHtcbiAgICAgIGNocm9tZS5zb2NrZXQuc2VjdXJlKHRoaXMuX3NvY2tldElkLCBvblVwZ3JhZGVkKVxuICAgIH0gZWxzZSBpZiAodGhpcy5fdXNlRm9yZ2VUbHMpIHtcbiAgICAgIC8vIHNldHVwIHRoZSBmb3JnZSB0bHMgY2xpZW50IG9yIHdlYndvcmtlciBhcyB0bHMgZmFsbGJhY2tcbiAgICAgIGNyZWF0ZVRscyh0aGlzKVxuICAgICAgY2FsbGJhY2soKVxuICAgIH1cbiAgfVxuXG4gIHVwZ3JhZGVUb1NlY3VyZSAoKSB7XG4gICAgaWYgKHRoaXMuc3NsIHx8IHRoaXMuX3VzZVNUQVJUVExTKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICB0aGlzLl91c2VTVEFSVFRMUyA9IHRydWVcbiAgICB0aGlzLl91cGdyYWRlVG9TZWN1cmUoKCkgPT4ge1xuICAgICAgaWYgKHRoaXMuX3VzZUxlZ2FjeVNvY2tldCkge1xuICAgICAgICB0aGlzLl9yZWFkTGVnYWN5U29ja2V0KCkgLy8gdGxzIGhhbmRzaGFrZSBpcyBkb25lLCByZXN0YXJ0IHJlYWRpbmdcbiAgICAgIH1cbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIFJlYWRzIGZyb20gYSBsZWdhY3kgY2hyb21lLnNvY2tldC5cbiAgICovXG4gIF9yZWFkTGVnYWN5U29ja2V0ICgpIHtcbiAgICBpZiAodGhpcy5fc29ja2V0SWQgPT09IDApIHtcbiAgICAgIC8vIHRoZSBzb2NrZXQgaXMgY2xvc2VkLiBvbWl0IHJlYWQgYW5kIHN0b3AgZnVydGhlciByZWFkc1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgLy8gZG9uJ3QgcmVhZCBmcm9tIGNocm9tZS5zb2NrZXQgaWYgd2UgaGF2ZSBjaHJvbWUuc29ja2V0LnNlY3VyZSBhIGhhbmRzaGFrZSBpbiBwcm9ncmVzcyFcbiAgICBpZiAoKHRoaXMuX3VzZVNUQVJUVExTIHx8IHRoaXMuX3VzZVRMUykgJiYgIXRoaXMuc3NsKSB7XG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBjaHJvbWUuc29ja2V0LnJlYWQodGhpcy5fc29ja2V0SWQsIHJlYWRJbmZvID0+IHtcbiAgICAgIC8vIHNvY2tldCBjbG9zZWQgcmVtb3RlbHkgb3IgYnJva2VuXG4gICAgICBpZiAocmVhZEluZm8ucmVzdWx0Q29kZSA8PSAwKSB7XG4gICAgICAgIHRoaXMuX3NvY2tldElkID0gMFxuICAgICAgICB0aGlzLmNsb3NlKClcbiAgICAgICAgcmV0dXJuXG4gICAgICB9XG5cbiAgICAgIC8vIHByb2Nlc3MgdGhlIGRhdGEgYXZhaWxhYmxlIG9uIHRoZSBzb2NrZXRcbiAgICAgIHRoaXMuX29uRGF0YShyZWFkSW5mby5kYXRhKVxuXG4gICAgICAvLyBRdWV1ZSB0aGUgbmV4dCByZWFkLlxuICAgICAgLy8gSWYgYSBTVEFSVFRMUyBoYW5kc2hha2UgbWlnaHQgYmUgdXBjb21pbmcsIHBvc3Rwb25lIHRoaXMgb250b1xuICAgICAgLy8gdGhlIHRhc2sgcXVldWUgc28gdGhlIElNQVAgY2xpZW50IGhhcyBhIGNoYW5jZSB0byBjYWxsIHVwZ3JhZGVUb1NlY3VyZTtcbiAgICAgIC8vIHdpdGhvdXQgdGhpcywgd2UgbWlnaHQgZWF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIGhhbmRzaGFrZS5cbiAgICAgIC8vIElmIHdlIGFyZSBhbHJlYWR5IHNlY3VyZSwganVzdCBjYWxsIGl0IChmb3IgcGVyZm9ybWFuY2UpLlxuICAgICAgaWYgKHRoaXMuc3NsKSB7XG4gICAgICAgIHRoaXMuX3JlYWRMZWdhY3lTb2NrZXQoKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgc2NoZWR1bGVJbk5leHRFdmVudExvb3AoKCkgPT4gdGhpcy5fcmVhZExlZ2FjeVNvY2tldCgpKVxuICAgICAgfVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogSW52b2tlZCB3aGVuIGRhdGEgaGFzIGJlZW4gcmVhZCBmcm9tIHRoZSBzb2NrZXQuIEhhbmRsZXMgY2FzZXMgd2hlbiB0byBmZWVkXG4gICAqIHRoZSBkYXRhIGF2YWlsYWJsZSBvbiB0aGUgc29ja2V0IHRvIGZvcmdlLlxuICAgKlxuICAgKiBAcGFyYW0ge0FycmF5QnVmZmVyfSBidWZmZXIgVGhlIGJpbmFyeSBkYXRhIHJlYWQgZnJvbSB0aGUgc29ja2V0XG4gICAqL1xuICBfb25EYXRhIChidWZmZXIpIHtcbiAgICBpZiAoKHRoaXMuX3VzZVRMUyB8fCB0aGlzLl91c2VTVEFSVFRMUykgJiYgdGhpcy5fdXNlRm9yZ2VUbHMpIHtcbiAgICAgIC8vIGZlZWQgdGhlIGRhdGEgdG8gdGhlIHRscyBjbGllbnRcbiAgICAgIGlmICh0aGlzLl90bHNXb3JrZXIpIHtcbiAgICAgICAgdGhpcy5fdGxzV29ya2VyLnBvc3RNZXNzYWdlKGNyZWF0ZU1lc3NhZ2UoRVZFTlRfSU5CT1VORCwgYnVmZmVyKSwgW2J1ZmZlcl0pXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLl90bHMucHJvY2Vzc0luYm91bmQoYnVmZmVyKVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBlbWl0IGRhdGEgZXZlbnRcbiAgICAgIHRoaXMuX2VtaXQoJ2RhdGEnLCBidWZmZXIpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIENsb3NlcyB0aGUgc29ja2V0XG4gICAqIEByZXR1cm4ge1t0eXBlXX0gW2Rlc2NyaXB0aW9uXVxuICAgKi9cbiAgY2xvc2UgKCkge1xuICAgIHRoaXMucmVhZHlTdGF0ZSA9ICdjbG9zaW5nJ1xuXG4gICAgaWYgKHRoaXMuX3NvY2tldElkICE9PSAwKSB7XG4gICAgICBpZiAodGhpcy5fdXNlTGVnYWN5U29ja2V0KSB7XG4gICAgICAgIC8vIGNsb3NlIGxlZ2FjeSBzb2NrZXRcbiAgICAgICAgY2hyb21lLnNvY2tldC5kaXNjb25uZWN0KHRoaXMuX3NvY2tldElkKVxuICAgICAgICBjaHJvbWUuc29ja2V0LmRlc3Ryb3kodGhpcy5fc29ja2V0SWQpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICAvLyBjbG9zZSBzb2NrZXRcbiAgICAgICAgY2hyb21lLnNvY2tldHMudGNwLmRpc2Nvbm5lY3QodGhpcy5fc29ja2V0SWQpXG4gICAgICB9XG5cbiAgICAgIHRoaXMuX3NvY2tldElkID0gMFxuICAgIH1cblxuICAgIC8vIHRlcm1pbmF0ZSB0aGUgdGxzIHdvcmtlclxuICAgIGlmICh0aGlzLl90bHNXb3JrZXIpIHtcbiAgICAgIHRoaXMuX3Rsc1dvcmtlci50ZXJtaW5hdGUoKVxuICAgICAgdGhpcy5fdGxzV29ya2VyID0gdW5kZWZpbmVkXG4gICAgfVxuXG4gICAgdGhpcy5fZW1pdCgnY2xvc2UnKVxuICB9XG5cbiAgc2VuZCAoYnVmZmVyKSB7XG4gICAgaWYgKCF0aGlzLl91c2VGb3JnZVRscyAmJiB0aGlzLl91c2VTVEFSVFRMUyAmJiAhdGhpcy5zc2wpIHtcbiAgICAgIC8vIGJ1ZmZlciB0aGUgdW5wcmVwYXJlZCBkYXRhIHVudGlsIGNocm9tZS5zb2NrZXQocy50Y3ApIGhhbmRzaGFrZSBpcyBkb25lXG4gICAgICB0aGlzLl9zdGFydFRsc0J1ZmZlci5wdXNoKGJ1ZmZlcilcbiAgICB9IGVsc2UgaWYgKHRoaXMuX3VzZUZvcmdlVGxzICYmICh0aGlzLl91c2VUTFMgfHwgdGhpcy5fdXNlU1RBUlRUTFMpKSB7XG4gICAgICAvLyBnaXZlIGJ1ZmZlciB0byBmb3JnZSB0byBiZSBwcmVwYXJlZCBmb3IgdGxzXG4gICAgICBpZiAodGhpcy5fdGxzV29ya2VyKSB7XG4gICAgICAgIHRoaXMuX3Rsc1dvcmtlci5wb3N0TWVzc2FnZShjcmVhdGVNZXNzYWdlKEVWRU5UX09VVEJPVU5ELCBidWZmZXIpLCBbYnVmZmVyXSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHRoaXMuX3Rscy5wcmVwYXJlT3V0Ym91bmQoYnVmZmVyKVxuICAgICAgfVxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBzZW5kIHRoZSBhcnJheWJ1ZmZlclxuICAgICAgdGhpcy5fc2VuZChidWZmZXIpXG4gICAgfVxuICB9XG5cbiAgX3NlbmQgKGRhdGEpIHtcbiAgICBpZiAodGhpcy5fc29ja2V0SWQgPT09IDApIHtcbiAgICAgIC8vIHRoZSBzb2NrZXQgaXMgY2xvc2VkLlxuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgaWYgKHRoaXMuX3VzZUxlZ2FjeVNvY2tldCkge1xuICAgICAgY2hyb21lLnNvY2tldC53cml0ZSh0aGlzLl9zb2NrZXRJZCwgZGF0YSwgd3JpdGVJbmZvID0+IHtcbiAgICAgICAgaWYgKHdyaXRlSW5mby5ieXRlc1dyaXR0ZW4gPCAwICYmIHRoaXMuX3NvY2tldElkICE9PSAwKSB7XG4gICAgICAgICAgLy8gaWYgdGhlIHNvY2tldCBpcyBhbHJlYWR5IDAsIGl0IGhhcyBhbHJlYWR5IGJlZW4gY2xvc2VkLiBubyBuZWVkIHRvIGFsZXJ0IHRoZW4uLi5cbiAgICAgICAgICB0aGlzLl9lbWl0KCdlcnJvcicsIG5ldyBFcnJvcignQ291bGQgbm90IHdyaXRlICcgKyBkYXRhLmJ5dGVMZW5ndGggKyAnIGJ5dGVzIHRvIHNvY2tldCAnICsgdGhpcy5fc29ja2V0SWQgKyAnLiBDaHJvbWUgZXJyb3IgY29kZTogJyArIHdyaXRlSW5mby5ieXRlc1dyaXR0ZW4pKVxuICAgICAgICAgIHRoaXMuX3NvY2tldElkID0gMFxuICAgICAgICAgIHRoaXMuY2xvc2UoKVxuXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl9lbWl0KCdkcmFpbicpXG4gICAgICB9KVxuICAgIH0gZWxzZSB7XG4gICAgICBjaHJvbWUuc29ja2V0cy50Y3Auc2VuZCh0aGlzLl9zb2NrZXRJZCwgZGF0YSwgc2VuZEluZm8gPT4ge1xuICAgICAgICBpZiAoc2VuZEluZm8uYnl0ZXNTZW50IDwgMCAmJiB0aGlzLl9zb2NrZXRJZCAhPT0gMCkge1xuICAgICAgICAgIC8vIGlmIHRoZSBzb2NrZXQgaXMgYWxyZWFkeSAwLCBpdCBoYXMgYWxyZWFkeSBiZWVuIGNsb3NlZC4gbm8gbmVlZCB0byBhbGVydCB0aGVuLi4uXG4gICAgICAgICAgdGhpcy5fZW1pdCgnZXJyb3InLCBuZXcgRXJyb3IoJ0NvdWxkIG5vdCB3cml0ZSAnICsgZGF0YS5ieXRlTGVuZ3RoICsgJyBieXRlcyB0byBzb2NrZXQgJyArIHRoaXMuX3NvY2tldElkICsgJy4gQ2hyb21lIGVycm9yIGNvZGU6ICcgKyBzZW5kSW5mby5ieXRlc1NlbnQpKVxuICAgICAgICAgIHRoaXMuY2xvc2UoKVxuXG4gICAgICAgICAgcmV0dXJuXG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLl9lbWl0KCdkcmFpbicpXG4gICAgICB9KVxuICAgIH1cbiAgfVxuXG4gIF9lbWl0ICh0eXBlLCBkYXRhKSB7XG4gICAgY29uc3QgdGFyZ2V0ID0gdGhpc1xuICAgIHN3aXRjaCAodHlwZSkge1xuICAgICAgY2FzZSAnb3Blbic6XG4gICAgICAgIHRoaXMucmVhZHlTdGF0ZSA9ICdvcGVuJ1xuICAgICAgICB0aGlzLm9ub3BlbiAmJiB0aGlzLm9ub3Blbih7IHRhcmdldCwgdHlwZSwgZGF0YSB9KVxuICAgICAgICBicmVha1xuICAgICAgY2FzZSAnZXJyb3InOlxuICAgICAgICB0aGlzLm9uZXJyb3IgJiYgdGhpcy5vbmVycm9yKHsgdGFyZ2V0LCB0eXBlLCBkYXRhIH0pXG4gICAgICAgIGJyZWFrXG4gICAgICBjYXNlICdkYXRhJzpcbiAgICAgICAgdGhpcy5vbmRhdGEgJiYgdGhpcy5vbmRhdGEoeyB0YXJnZXQsIHR5cGUsIGRhdGEgfSlcbiAgICAgICAgYnJlYWtcbiAgICAgIGNhc2UgJ2RyYWluJzpcbiAgICAgICAgdGhpcy5vbmRyYWluICYmIHRoaXMub25kcmFpbih7IHRhcmdldCwgdHlwZSwgZGF0YSB9KVxuICAgICAgICBicmVha1xuICAgICAgY2FzZSAnY2xvc2UnOlxuICAgICAgICB0aGlzLnJlYWR5U3RhdGUgPSAnY2xvc2VkJ1xuICAgICAgICB0aGlzLm9uY2xvc2UgJiYgdGhpcy5vbmNsb3NlKHsgdGFyZ2V0LCB0eXBlLCBkYXRhIH0pXG4gICAgICAgIGJyZWFrXG4gICAgfVxuICB9XG59XG4iXX0=