UNPKG

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.

220 lines (184 loc) 21.4 kB
'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 _nodeForge = require('node-forge'); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var TlsClient = function () { function TlsClient() { var _this = this; _classCallCheck(this, TlsClient); this.open = false; this._outboundBuffer = []; this._tls = _nodeForge.tls.createConnection({ server: false, verify: function verify(connection, verified, depth, certs) { if (!(certs && certs[0])) { return false; } if (!_this.verifyCertificate(certs[0], _this._host)) { return false; } /* * Please see the readme for an explanation of the behavior without a native TLS stack! */ // without a pinned certificate, we'll just accept the connection and notify the upper layer if (!_this._ca) { // notify the upper layer of the new cert _this.tlscert(_nodeForge.pki.certificateToPem(certs[0])); // succeed only if this.tlscert is implemented (otherwise forge catches the error) return true; } // if we have a pinned certificate, things get a little more complicated: // - leaf certificates pin the host directly, e.g. for self-signed certificates // - we also allow intermediate certificates, for providers that are able to sign their own certs. // detect if this is a certificate used for signing by testing if the common name different from the hostname. // also, an intermediate cert has no SANs, at least none that match the hostname. if (!_this.verifyCertificate(_this._ca, _this._host)) { // verify certificate through a valid certificate chain return _this._ca.verify(certs[0]); } // verify certificate through host certificate pinning var fpPinned = _nodeForge.pki.getPublicKeyFingerprint(_this._ca.publicKey, { encoding: 'hex' }); var fpRemote = _nodeForge.pki.getPublicKeyFingerprint(certs[0].publicKey, { encoding: 'hex' }); // check if cert fingerprints match if (fpPinned === fpRemote) { return true; } // notify the upper layer of the new cert _this.tlscert(_nodeForge.pki.certificateToPem(certs[0])); // fail when fingerprint does not match return false; }, connected: function connected(connection) { if (!connection) { _this.tlserror('Unable to connect'); _this.tlsclose(); return; } // tls connection open _this.open = true; _this.tlsopen(); // empty the buffer while (_this._outboundBuffer.length) { _this.prepareOutbound(_this._outboundBuffer.shift()); } }, tlsDataReady: function tlsDataReady(connection) { return _this.tlsoutbound(s2a(connection.tlsData.getBytes())); }, dataReady: function dataReady(connection) { return _this.tlsinbound(s2a(connection.data.getBytes())); }, closed: function closed() { return _this.tlsclose(); }, error: function error(connection, _error) { _this.tlserror(_error.message); _this.tlsclose(); } }); } _createClass(TlsClient, [{ key: 'configure', value: function configure(options) { this._host = options.host; if (options.ca) { this._ca = _nodeForge.pki.certificateFromPem(options.ca); } } }, { key: 'prepareOutbound', value: function prepareOutbound(buffer) { if (!this.open) { this._outboundBuffer.push(buffer); return; } this._tls.prepare(a2s(buffer)); } }, { key: 'processInbound', value: function processInbound(buffer) { this._tls.process(a2s(buffer)); } }, { key: 'handshake', value: function handshake() { this._tls.handshake(); } /** * Verifies a host name by the Common Name or Subject Alternative Names * Expose as a method of TlsClient for testing purposes * * @param {Object} cert A forge certificate object * @param {String} host The host name, e.g. imap.gmail.com * @return {Boolean} true, if host name matches certificate, otherwise false */ }, { key: 'verifyCertificate', value: function verifyCertificate(cert, host) { var _this2 = this; var entries = void 0; var subjectAltName = cert.getExtension({ name: 'subjectAltName' }); var cn = cert.subject.getField('CN'); // If subjectAltName is present then it must be used and Common Name must be discarded // http://tools.ietf.org/html/rfc2818#section-3.1 // So we check subjectAltName first and if it does not exist then revert back to Common Name if (subjectAltName && subjectAltName.altNames && subjectAltName.altNames.length) { entries = subjectAltName.altNames.map(function (entry) { return entry.value; }); } else if (cn && cn.value) { entries = [cn.value]; } else { return false; } // find matches for hostname and if any are found return true, otherwise returns false return !!entries.filter(function (sanEntry) { return _this2.compareServername(host.toLowerCase(), sanEntry.toLowerCase()); }).length; } /** * Compares servername with a subjectAltName entry. Returns true if these values match. * * Wildcard usage in certificate hostnames is very limited, the only valid usage * form is "*.domain" and not "*sub.domain" or "sub.*.domain" so we only have to check * if the entry starts with "*." when comparing against a wildcard hostname. If "*" is used * in invalid places, then treat it as a string and not as a wildcard. * * @param {String} servername Hostname to check * @param {String} sanEntry subjectAltName entry to check against * @returns {Boolean} Returns true if hostname matches entry from SAN */ }, { key: 'compareServername', value: function compareServername() { var servername = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; var sanEntry = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; // if the entry name does not include a wildcard, then expect exact match if (sanEntry.substr(0, 2) !== '*.') { return sanEntry === servername; } // otherwise ignore the first subdomain return servername.split('.').slice(1).join('.') === sanEntry.substr(2); } }]); return TlsClient; }(); exports.default = TlsClient; var a2s = function a2s(arr) { return String.fromCharCode.apply(null, new Uint8Array(arr)); }; var s2a = function s2a(str) { return new Uint8Array(str.split('').map(function (char) { return char.charCodeAt(0); })).buffer; }; //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uL3NyYy90bHMuanMiXSwibmFtZXMiOlsiVGxzQ2xpZW50Iiwib3BlbiIsIl9vdXRib3VuZEJ1ZmZlciIsIl90bHMiLCJjcmVhdGVDb25uZWN0aW9uIiwic2VydmVyIiwidmVyaWZ5IiwiY29ubmVjdGlvbiIsInZlcmlmaWVkIiwiZGVwdGgiLCJjZXJ0cyIsInZlcmlmeUNlcnRpZmljYXRlIiwiX2hvc3QiLCJfY2EiLCJ0bHNjZXJ0IiwiY2VydGlmaWNhdGVUb1BlbSIsImZwUGlubmVkIiwiZ2V0UHVibGljS2V5RmluZ2VycHJpbnQiLCJwdWJsaWNLZXkiLCJlbmNvZGluZyIsImZwUmVtb3RlIiwiY29ubmVjdGVkIiwidGxzZXJyb3IiLCJ0bHNjbG9zZSIsInRsc29wZW4iLCJsZW5ndGgiLCJwcmVwYXJlT3V0Ym91bmQiLCJzaGlmdCIsInRsc0RhdGFSZWFkeSIsInRsc291dGJvdW5kIiwiczJhIiwidGxzRGF0YSIsImdldEJ5dGVzIiwiZGF0YVJlYWR5IiwidGxzaW5ib3VuZCIsImRhdGEiLCJjbG9zZWQiLCJlcnJvciIsIm1lc3NhZ2UiLCJvcHRpb25zIiwiaG9zdCIsImNhIiwiY2VydGlmaWNhdGVGcm9tUGVtIiwiYnVmZmVyIiwicHVzaCIsInByZXBhcmUiLCJhMnMiLCJwcm9jZXNzIiwiaGFuZHNoYWtlIiwiY2VydCIsImVudHJpZXMiLCJzdWJqZWN0QWx0TmFtZSIsImdldEV4dGVuc2lvbiIsIm5hbWUiLCJjbiIsInN1YmplY3QiLCJnZXRGaWVsZCIsImFsdE5hbWVzIiwibWFwIiwiZW50cnkiLCJ2YWx1ZSIsImZpbHRlciIsImNvbXBhcmVTZXJ2ZXJuYW1lIiwidG9Mb3dlckNhc2UiLCJzYW5FbnRyeSIsInNlcnZlcm5hbWUiLCJzdWJzdHIiLCJzcGxpdCIsInNsaWNlIiwiam9pbiIsIlN0cmluZyIsImZyb21DaGFyQ29kZSIsImFwcGx5IiwiVWludDhBcnJheSIsImFyciIsInN0ciIsImNoYXIiLCJjaGFyQ29kZUF0Il0sIm1hcHBpbmdzIjoi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Q1YsUUFBUVcsTUFBUixDQUFlO0FBQUEsZUFBWSxPQUFLQyxpQkFBTCxDQUF1QnRCLEtBQUt1QixXQUFMLEVBQXZCLEVBQTJDQyxTQUFTRCxXQUFULEVBQTNDLENBQVo7QUFBQSxPQUFmLEVBQStGdEMsTUFBeEc7QUFDRDs7QUFFRDs7Ozs7Ozs7Ozs7Ozs7O3dDQVltRDtBQUFBLFVBQWhDd0MsVUFBZ0MsdUVBQW5CLEVBQW1CO0FBQUEsVUFBZkQsUUFBZSx1RUFBSixFQUFJOztBQUNqRDtBQUNBLFVBQUlBLFNBQVNFLE1BQVQsQ0FBZ0IsQ0FBaEIsRUFBbUIsQ0FBbkIsTUFBMEIsSUFBOUIsRUFBb0M7QUFDbEMsZUFBT0YsYUFBYUMsVUFBcEI7QUFDRDs7QUFFRDtBQUNBLGFBQU9BLFdBQVdFLEtBQVgsQ0FBaUIsR0FBakIsRUFBc0JDLEtBQXRCLENBQTRCLENBQTVCLEVBQStCQyxJQUEvQixDQUFvQyxHQUFwQyxNQUE2Q0wsU0FBU0UsTUFBVCxDQUFnQixDQUFoQixDQUFwRDtBQUNEOzs7Ozs7a0JBbEtrQmxFLFM7OztBQXFLckIsSUFBTThDLE1BQU0sU0FBTkEsR0FBTTtBQUFBLFNBQU93QixPQUFPQyxZQUFQLENBQW9CQyxLQUFwQixDQUEwQixJQUExQixFQUFnQyxJQUFJQyxVQUFKLENBQWVDLEdBQWYsQ0FBaEMsQ0FBUDtBQUFBLENBQVo7QUFDQSxJQUFNNUMsTUFBTSxTQUFOQSxHQUFNO0FBQUEsU0FBTyxJQUFJMkMsVUFBSixDQUFlRSxJQUFJUixLQUFKLENBQVUsRUFBVixFQUFjVCxHQUFkLENBQWtCO0FBQUEsV0FBUWtCLEtBQUtDLFVBQUwsQ0FBZ0IsQ0FBaEIsQ0FBUjtBQUFBLEdBQWxCLENBQWYsRUFBOERsQyxNQUFyRTtBQUFBLENBQVoiLCJmaWxlIjoidGxzLmpzIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgdGxzLCBwa2kgfSBmcm9tICdub2RlLWZvcmdlJ1xuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBUbHNDbGllbnQge1xuICBjb25zdHJ1Y3RvciAoKSB7XG4gICAgdGhpcy5vcGVuID0gZmFsc2VcbiAgICB0aGlzLl9vdXRib3VuZEJ1ZmZlciA9IFtdXG5cbiAgICB0aGlzLl90bHMgPSB0bHMuY3JlYXRlQ29ubmVjdGlvbih7XG4gICAgICBzZXJ2ZXI6IGZhbHNlLFxuICAgICAgdmVyaWZ5OiAoY29ubmVjdGlvbiwgdmVyaWZpZWQsIGRlcHRoLCBjZXJ0cykgPT4ge1xuICAgICAgICBpZiAoIShjZXJ0cyAmJiBjZXJ0c1swXSkpIHtcbiAgICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgICAgfVxuXG4gICAgICAgIGlmICghdGhpcy52ZXJpZnlDZXJ0aWZpY2F0ZShjZXJ0c1swXSwgdGhpcy5faG9zdCkpIHtcbiAgICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgICAgfVxuXG4gICAgICAgIC8qXG4gICAgICAgICAqIFBsZWFzZSBzZWUgdGhlIHJlYWRtZSBmb3IgYW4gZXhwbGFuYXRpb24gb2YgdGhlIGJlaGF2aW9yIHdpdGhvdXQgYSBuYXRpdmUgVExTIHN0YWNrIVxuICAgICAgICAgKi9cblxuICAgICAgICAvLyB3aXRob3V0IGEgcGlubmVkIGNlcnRpZmljYXRlLCB3ZSdsbCBqdXN0IGFjY2VwdCB0aGUgY29ubmVjdGlvbiBhbmQgbm90aWZ5IHRoZSB1cHBlciBsYXllclxuICAgICAgICBpZiAoIXRoaXMuX2NhKSB7XG4gICAgICAgICAgLy8gbm90aWZ5IHRoZSB1cHBlciBsYXllciBvZiB0aGUgbmV3IGNlcnRcbiAgICAgICAgICB0aGlzLnRsc2NlcnQocGtpLmNlcnRpZmljYXRlVG9QZW0oY2VydHNbMF0pKVxuICAgICAgICAgIC8vIHN1Y2NlZWQgb25seSBpZiB0aGlzLnRsc2NlcnQgaXMgaW1wbGVtZW50ZWQgKG90aGVyd2lzZSBmb3JnZSBjYXRjaGVzIHRoZSBlcnJvcilcbiAgICAgICAgICByZXR1cm4gdHJ1ZVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gaWYgd2UgaGF2ZSBhIHBpbm5lZCBjZXJ0aWZpY2F0ZSwgdGhpbmdzIGdldCBhIGxpdHRsZSBtb3JlIGNvbXBsaWNhdGVkOlxuICAgICAgICAvLyAtIGxlYWYgY2VydGlmaWNhdGVzIHBpbiB0aGUgaG9zdCBkaXJlY3RseSwgZS5nLiBmb3Igc2VsZi1zaWduZWQgY2VydGlmaWNhdGVzXG4gICAgICAgIC8vIC0gd2UgYWxzbyBhbGxvdyBpbnRlcm1lZGlhdGUgY2VydGlmaWNhdGVzLCBmb3IgcHJvdmlkZXJzIHRoYXQgYXJlIGFibGUgdG8gc2lnbiB0aGVpciBvd24gY2VydHMuXG5cbiAgICAgICAgLy8gZGV0ZWN0IGlmIHRoaXMgaXMgYSBjZXJ0aWZpY2F0ZSB1c2VkIGZvciBzaWduaW5nIGJ5IHRlc3RpbmcgaWYgdGhlIGNvbW1vbiBuYW1lIGRpZmZlcmVudCBmcm9tIHRoZSBob3N0bmFtZS5cbiAgICAgICAgLy8gYWxzbywgYW4gaW50ZXJtZWRpYXRlIGNlcnQgaGFzIG5vIFNBTnMsIGF0IGxlYXN0IG5vbmUgdGhhdCBtYXRjaCB0aGUgaG9zdG5hbWUuXG4gICAgICAgIGlmICghdGhpcy52ZXJpZnlDZXJ0aWZpY2F0ZSh0aGlzLl9jYSwgdGhpcy5faG9zdCkpIHtcbiAgICAgICAgICAvLyB2ZXJpZnkgY2VydGlmaWNhdGUgdGhyb3VnaCBhIHZhbGlkIGNlcnRpZmljYXRlIGNoYWluXG4gICAgICAgICAgcmV0dXJuIHRoaXMuX2NhLnZlcmlmeShjZXJ0c1swXSlcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHZlcmlmeSBjZXJ0aWZpY2F0ZSB0aHJvdWdoIGhvc3QgY2VydGlmaWNhdGUgcGlubmluZ1xuICAgICAgICB2YXIgZnBQaW5uZWQgPSBwa2kuZ2V0UHVibGljS2V5RmluZ2VycHJpbnQodGhpcy5fY2EucHVibGljS2V5LCB7XG4gICAgICAgICAgZW5jb2Rpbmc6ICdoZXgnXG4gICAgICAgIH0pXG4gICAgICAgIHZhciBmcFJlbW90ZSA9IHBraS5nZXRQdWJsaWNLZXlGaW5nZXJwcmludChjZXJ0c1swXS5wdWJsaWNLZXksIHtcbiAgICAgICAgICBlbmNvZGluZzogJ2hleCdcbiAgICAgICAgfSlcblxuICAgICAgICAvLyBjaGVjayBpZiBjZXJ0IGZpbmdlcnByaW50cyBtYXRjaFxuICAgICAgICBpZiAoZnBQaW5uZWQgPT09IGZwUmVtb3RlKSB7XG4gICAgICAgICAgcmV0dXJuIHRydWVcbiAgICAgICAgfVxuXG4gICAgICAgIC8vIG5vdGlmeSB0aGUgdXBwZXIgbGF5ZXIgb2YgdGhlIG5ldyBjZXJ0XG4gICAgICAgIHRoaXMudGxzY2VydChwa2kuY2VydGlmaWNhdGVUb1BlbShjZXJ0c1swXSkpXG4gICAgICAgIC8vIGZhaWwgd2hlbiBmaW5nZXJwcmludCBkb2VzIG5vdCBtYXRjaFxuICAgICAgICByZXR1cm4gZmFsc2VcbiAgICAgIH0sXG4gICAgICBjb25uZWN0ZWQ6IChjb25uZWN0aW9uKSA9PiB7XG4gICAgICAgIGlmICghY29ubmVjdGlvbikge1xuICAgICAgICAgIHRoaXMudGxzZXJyb3IoJ1VuYWJsZSB0byBjb25uZWN0JylcbiAgICAgICAgICB0aGlzLnRsc2Nsb3NlKClcbiAgICAgICAgICByZXR1cm5cbiAgICAgICAgfVxuXG4gICAgICAgIC8vIHRscyBjb25uZWN0aW9uIG9wZW5cbiAgICAgICAgdGhpcy5vcGVuID0gdHJ1ZVxuXG4gICAgICAgIHRoaXMudGxzb3BlbigpXG5cbiAgICAgICAgLy8gZW1wdHkgdGhlIGJ1ZmZlclxuICAgICAgICB3aGlsZSAodGhpcy5fb3V0Ym91bmRCdWZmZXIubGVuZ3RoKSB7XG4gICAgICAgICAgdGhpcy5wcmVwYXJlT3V0Ym91bmQodGhpcy5fb3V0Ym91bmRCdWZmZXIuc2hpZnQoKSlcbiAgICAgICAgfVxuICAgICAgfSxcbiAgICAgIHRsc0RhdGFSZWFkeTogKGNvbm5lY3Rpb24pID0+IHRoaXMudGxzb3V0Ym91bmQoczJhKGNvbm5lY3Rpb24udGxzRGF0YS5nZXRCeXRlcygpKSksXG4gICAgICBkYXRhUmVhZHk6IChjb25uZWN0aW9uKSA9PiB0aGlzLnRsc2luYm91bmQoczJhKGNvbm5lY3Rpb24uZGF0YS5nZXRCeXRlcygpKSksXG4gICAgICBjbG9zZWQ6ICgpID0+IHRoaXMudGxzY2xvc2UoKSxcbiAgICAgIGVycm9yOiAoY29ubmVjdGlvbiwgZXJyb3IpID0+IHtcbiAgICAgICAgdGhpcy50bHNlcnJvcihlcnJvci5tZXNzYWdlKVxuICAgICAgICB0aGlzLnRsc2Nsb3NlKClcbiAgICAgIH1cbiAgICB9KVxuICB9XG5cbiAgY29uZmlndXJlIChvcHRpb25zKSB7XG4gICAgdGhpcy5faG9zdCA9IG9wdGlvbnMuaG9zdFxuICAgIGlmIChvcHRpb25zLmNhKSB7XG4gICAgICB0aGlzLl9jYSA9IHBraS5jZXJ0aWZpY2F0ZUZyb21QZW0ob3B0aW9ucy5jYSlcbiAgICB9XG4gIH1cblxuICBwcmVwYXJlT3V0Ym91bmQgKGJ1ZmZlcikge1xuICAgIGlmICghdGhpcy5vcGVuKSB7XG4gICAgICB0aGlzLl9vdXRib3VuZEJ1ZmZlci5wdXNoKGJ1ZmZlcilcbiAgICAgIHJldHVyblxuICAgIH1cblxuICAgIHRoaXMuX3Rscy5wcmVwYXJlKGEycyhidWZmZXIpKVxuICB9XG5cbiAgcHJvY2Vzc0luYm91bmQgKGJ1ZmZlcikge1xuICAgIHRoaXMuX3Rscy5wcm9jZXNzKGEycyhidWZmZXIpKVxuICB9XG5cbiAgaGFuZHNoYWtlICgpIHtcbiAgICB0aGlzLl90bHMuaGFuZHNoYWtlKClcbiAgfVxuXG4gIC8qKlxuICAgKiBWZXJpZmllcyBhIGhvc3QgbmFtZSBieSB0aGUgQ29tbW9uIE5hbWUgb3IgU3ViamVjdCBBbHRlcm5hdGl2ZSBOYW1lc1xuICAgKiBFeHBvc2UgYXMgYSBtZXRob2Qgb2YgVGxzQ2xpZW50IGZvciB0ZXN0aW5nIHB1cnBvc2VzXG4gICAqXG4gICAqIEBwYXJhbSB7T2JqZWN0fSBjZXJ0IEEgZm9yZ2UgY2VydGlmaWNhdGUgb2JqZWN0XG4gICAqIEBwYXJhbSB7U3RyaW5nfSBob3N0IFRoZSBob3N0IG5hbWUsIGUuZy4gaW1hcC5nbWFpbC5jb21cbiAgICogQHJldHVybiB7Qm9vbGVhbn0gdHJ1ZSwgaWYgaG9zdCBuYW1lIG1hdGNoZXMgY2VydGlmaWNhdGUsIG90aGVyd2lzZSBmYWxzZVxuICAgKi9cbiAgdmVyaWZ5Q2VydGlmaWNhdGUgKGNlcnQsIGhvc3QpIHtcbiAgICBsZXQgZW50cmllc1xuXG4gICAgY29uc3Qgc3ViamVjdEFsdE5hbWUgPSBjZXJ0LmdldEV4dGVuc2lvbih7XG4gICAgICBuYW1lOiAnc3ViamVjdEFsdE5hbWUnXG4gICAgfSlcblxuICAgIGNvbnN0IGNuID0gY2VydC5zdWJqZWN0LmdldEZpZWxkKCdDTicpXG5cbiAgICAvLyBJZiBzdWJqZWN0QWx0TmFtZSBpcyBwcmVzZW50IHRoZW4gaXQgbXVzdCBiZSB1c2VkIGFuZCBDb21tb24gTmFtZSBtdXN0IGJlIGRpc2NhcmRlZFxuICAgIC8vIGh0dHA6Ly90b29scy5pZXRmLm9yZy9odG1sL3JmYzI4MTgjc2VjdGlvbi0zLjFcbiAgICAvLyBTbyB3ZSBjaGVjayBzdWJqZWN0QWx0TmFtZSBmaXJzdCBhbmQgaWYgaXQgZG9lcyBub3QgZXhpc3QgdGhlbiByZXZlcnQgYmFjayB0byBDb21tb24gTmFtZVxuICAgIGlmIChzdWJqZWN0QWx0TmFtZSAmJiBzdWJqZWN0QWx0TmFtZS5hbHROYW1lcyAmJiBzdWJqZWN0QWx0TmFtZS5hbHROYW1lcy5sZW5ndGgpIHtcbiAgICAgIGVudHJpZXMgPSBzdWJqZWN0QWx0TmFtZS5hbHROYW1lcy5tYXAoZnVuY3Rpb24gKGVudHJ5KSB7XG4gICAgICAgIHJldHVybiBlbnRyeS52YWx1ZVxuICAgICAgfSlcbiAgICB9IGVsc2UgaWYgKGNuICYmIGNuLnZhbHVlKSB7XG4gICAgICBlbnRyaWVzID0gW2NuLnZhbHVlXVxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICAvLyBmaW5kIG1hdGNoZXMgZm9yIGhvc3RuYW1lIGFuZCBpZiBhbnkgYXJlIGZvdW5kIHJldHVybiB0cnVlLCBvdGhlcndpc2UgcmV0dXJucyBmYWxzZVxuICAgIHJldHVybiAhIWVudHJpZXMuZmlsdGVyKHNhbkVudHJ5ID0+IHRoaXMuY29tcGFyZVNlcnZlcm5hbWUoaG9zdC50b0xvd2VyQ2FzZSgpLCBzYW5FbnRyeS50b0xvd2VyQ2FzZSgpKSkubGVuZ3RoXG4gIH1cblxuICAvKipcbiAgICogQ29tcGFyZXMgc2VydmVybmFtZSB3aXRoIGEgc3ViamVjdEFsdE5hbWUgZW50cnkuIFJldHVybnMgdHJ1ZSBpZiB0aGVzZSB2YWx1ZXMgbWF0Y2guXG4gICAqXG4gICAqIFdpbGRjYXJkIHVzYWdlIGluIGNlcnRpZmljYXRlIGhvc3RuYW1lcyBpcyB2ZXJ5IGxpbWl0ZWQsIHRoZSBvbmx5IHZhbGlkIHVzYWdlXG4gICAqIGZvcm0gaXMgXCIqLmRvbWFpblwiIGFuZCBub3QgXCIqc3ViLmRvbWFpblwiIG9yIFwic3ViLiouZG9tYWluXCIgc28gd2Ugb25seSBoYXZlIHRvIGNoZWNrXG4gICAqIGlmIHRoZSBlbnRyeSBzdGFydHMgd2l0aCBcIiouXCIgd2hlbiBjb21wYXJpbmcgYWdhaW5zdCBhIHdpbGRjYXJkIGhvc3RuYW1lLiBJZiBcIipcIiBpcyB1c2VkXG4gICAqIGluIGludmFsaWQgcGxhY2VzLCB0aGVuIHRyZWF0IGl0IGFzIGEgc3RyaW5nIGFuZCBub3QgYXMgYSB3aWxkY2FyZC5cbiAgICpcbiAgICogQHBhcmFtIHtTdHJpbmd9IHNlcnZlcm5hbWUgSG9zdG5hbWUgdG8gY2hlY2tcbiAgICogQHBhcmFtIHtTdHJpbmd9IHNhbkVudHJ5IHN1YmplY3RBbHROYW1lIGVudHJ5IHRvIGNoZWNrIGFnYWluc3RcbiAgICogQHJldHVybnMge0Jvb2xlYW59IFJldHVybnMgdHJ1ZSBpZiBob3N0bmFtZSBtYXRjaGVzIGVudHJ5IGZyb20gU0FOXG4gICAqL1xuICBjb21wYXJlU2VydmVybmFtZSAoc2VydmVybmFtZSA9ICcnLCBzYW5FbnRyeSA9ICcnKSB7XG4gICAgLy8gaWYgdGhlIGVudHJ5IG5hbWUgZG9lcyBub3QgaW5jbHVkZSBhIHdpbGRjYXJkLCB0aGVuIGV4cGVjdCBleGFjdCBtYXRjaFxuICAgIGlmIChzYW5FbnRyeS5zdWJzdHIoMCwgMikgIT09ICcqLicpIHtcbiAgICAgIHJldHVybiBzYW5FbnRyeSA9PT0gc2VydmVybmFtZVxuICAgIH1cblxuICAgIC8vIG90aGVyd2lzZSBpZ25vcmUgdGhlIGZpcnN0IHN1YmRvbWFpblxuICAgIHJldHVybiBzZXJ2ZXJuYW1lLnNwbGl0KCcuJykuc2xpY2UoMSkuam9pbignLicpID09PSBzYW5FbnRyeS5zdWJzdHIoMilcbiAgfVxufVxuXG5jb25zdCBhMnMgPSBhcnIgPT4gU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShudWxsLCBuZXcgVWludDhBcnJheShhcnIpKVxuY29uc3QgczJhID0gc3RyID0+IG5ldyBVaW50OEFycmF5KHN0ci5zcGxpdCgnJykubWFwKGNoYXIgPT4gY2hhci5jaGFyQ29kZUF0KDApKSkuYnVmZmVyXG4iXX0=