UNPKG

matrix-react-sdk

Version:
386 lines (374 loc) 58.1 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.DecryptionFailureTracker = exports.DecryptionFailure = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _bloomFilters = require("bloom-filters"); var _matrix = require("matrix-js-sdk/src/matrix"); var _cryptoApi = require("matrix-js-sdk/src/crypto-api"); var _PosthogAnalytics = require("./PosthogAnalytics"); var _crypto = require("./utils/crypto"); var _DecryptionFailureTracker; /* Copyright 2024 New Vector Ltd. Copyright 2018-2021 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; } function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; } /** The key that we use to store the `reportedEvents` bloom filter in localstorage */ const DECRYPTION_FAILURE_STORAGE_KEY = "mx_decryption_failure_event_ids"; class DecryptionFailure { constructor(failedEventId, errorCode, /** * The time that we failed to decrypt the event. If we failed to decrypt * multiple times, this will be the time of the first failure. */ ts, /** * Is the sender on a different server from us? */ isFederated, /** * Was the failed event ever visible to the user? */ wasVisibleToUser, /** * Has the user verified their own cross-signing identity, as of the most * recent decryption attempt for this event? */ userTrustsOwnIdentity) { /** * The time between our initial failure to decrypt and our successful * decryption (if we managed to decrypt). */ (0, _defineProperty2.default)(this, "timeToDecryptMillis", void 0); this.failedEventId = failedEventId; this.errorCode = errorCode; this.ts = ts; this.isFederated = isFederated; this.wasVisibleToUser = wasVisibleToUser; this.userTrustsOwnIdentity = userTrustsOwnIdentity; } } /** Properties associated with decryption errors, for classifying the error. */ exports.DecryptionFailure = DecryptionFailure; class DecryptionFailureTracker { /** * Create a new DecryptionFailureTracker. * * Call `start(client)` to start the tracker. The tracker will listen for * decryption events on the client and track decryption failures, and will * automatically stop tracking when the client logs out. * * @param {function} fn The tracking function, which will be called when failures * are tracked. The function should have a signature `(trackedErrorCode, rawError, properties) => {...}`, * where `errorCode` matches the output of `errorCodeMapFn`, `rawError` is the original * error (that is, the input to `errorCodeMapFn`), and `properties` is a map of the * error properties for classifying the error. * * @param {function} errorCodeMapFn The function used to map decryption failure reason codes to the * `trackedErrorCode`. * * @param {boolean} checkReportedEvents Check if we have already reported an event. * Defaults to `true`. This is only used for tests, to avoid possible false positives from * the Bloom filter. This should be set to `false` for all tests except for those * that specifically test the `reportedEvents` functionality. */ constructor(fn, errorCodeMapFn, checkReportedEvents = true) { /** Map of event IDs to `DecryptionFailure` items. * * Every `CHECK_INTERVAL_MS`, this map is checked for failures that happened > * `MAXIMUM_LATE_DECRYPTION_PERIOD` ago (considered undecryptable), or * decryptions that took > `GRACE_PERIOD_MS` (considered late decryptions). * * Any such events are then reported via the `TrackingFn`. */ (0, _defineProperty2.default)(this, "failures", new Map()); /** Set of event IDs that have been visible to the user. * * This will only contain events that are not already in `reportedEvents`. */ (0, _defineProperty2.default)(this, "visibleEvents", new Set()); /** Bloom filter tracking event IDs of failures that were reported previously */ (0, _defineProperty2.default)(this, "reportedEvents", new _bloomFilters.ScalableBloomFilter()); /** Set to an interval ID when `start` is called */ (0, _defineProperty2.default)(this, "checkInterval", null); (0, _defineProperty2.default)(this, "trackInterval", null); /** Properties that will be added to all reported events (mainly reporting * information about the Matrix client). */ (0, _defineProperty2.default)(this, "baseProperties", {}); /** The user's domain (homeserver name). */ (0, _defineProperty2.default)(this, "userDomain", void 0); /** Whether the user has verified their own cross-signing keys. */ (0, _defineProperty2.default)(this, "userTrustsOwnIdentity", undefined); /** Whether we are currently checking our own verification status. */ (0, _defineProperty2.default)(this, "checkingVerificationStatus", false); /** Whether we should retry checking our own verification status after we're * done our current check. i.e. we got notified that our keys changed while * we were already checking, so the result could be out of date. */ (0, _defineProperty2.default)(this, "retryVerificationStatus", false); this.fn = fn; this.errorCodeMapFn = errorCodeMapFn; this.checkReportedEvents = checkReportedEvents; if (!fn || typeof fn !== "function") { throw new Error("DecryptionFailureTracker requires tracking function"); } if (typeof errorCodeMapFn !== "function") { throw new Error("DecryptionFailureTracker second constructor argument should be a function"); } } static get instance() { return DecryptionFailureTracker.internalInstance; } loadReportedEvents() { const storedFailures = localStorage.getItem(DECRYPTION_FAILURE_STORAGE_KEY); if (storedFailures) { this.reportedEvents = _bloomFilters.ScalableBloomFilter.fromJSON(JSON.parse(storedFailures)); } else { this.reportedEvents = new _bloomFilters.ScalableBloomFilter(); } } saveReportedEvents() { localStorage.setItem(DECRYPTION_FAILURE_STORAGE_KEY, JSON.stringify(this.reportedEvents.saveAsJSON())); } /** Callback for when an event is decrypted. * * This function is called by our `MatrixEventEvent.Decrypted` event * handler after a decryption attempt on an event, whether the decryption * is successful or not. * * @param e the event that was decrypted * * @param nowTs the current timestamp */ eventDecrypted(e, nowTs) { // for now we only track megolm decryption failures if (e.getWireContent().algorithm != _crypto.MEGOLM_ENCRYPTION_ALGORITHM) { return; } const errCode = e.decryptionFailureReason; if (errCode === null) { // Could be an event in the failures, remove it this.removeDecryptionFailuresForEvent(e, nowTs); return; } const eventId = e.getId(); // if it's already reported, we don't need to do anything if (this.reportedEvents.has(eventId) && this.checkReportedEvents) { return; } // if we already have a record of this event, use the previously-recorded timestamp const failure = this.failures.get(eventId); const ts = failure ? failure.ts : nowTs; const sender = e.getSender(); const senderDomain = sender?.replace(/^.*?:/, ""); let isFederated; if (this.userDomain !== undefined && senderDomain !== undefined) { isFederated = this.userDomain !== senderDomain; } const wasVisibleToUser = this.visibleEvents.has(eventId); this.failures.set(eventId, new DecryptionFailure(eventId, errCode, ts, isFederated, wasVisibleToUser, this.userTrustsOwnIdentity)); } addVisibleEvent(e) { const eventId = e.getId(); // if it's already reported, we don't need to do anything if (this.reportedEvents.has(eventId) && this.checkReportedEvents) { return; } // if we've already marked the event as a failure, mark it as visible // in the failure object const failure = this.failures.get(eventId); if (failure) { failure.wasVisibleToUser = true; } this.visibleEvents.add(eventId); } removeDecryptionFailuresForEvent(e, nowTs) { const eventId = e.getId(); const failure = this.failures.get(eventId); if (failure) { this.failures.delete(eventId); const timeToDecryptMillis = nowTs - failure.ts; if (timeToDecryptMillis < DecryptionFailureTracker.GRACE_PERIOD_MS) { // the event decrypted on time, so we don't need to report it return; } else if (timeToDecryptMillis <= DecryptionFailureTracker.MAXIMUM_LATE_DECRYPTION_PERIOD) { // The event is a late decryption, so store the time it took. // If the time to decrypt is longer than // MAXIMUM_LATE_DECRYPTION_PERIOD, we consider the event as // undecryptable, and leave timeToDecryptMillis undefined failure.timeToDecryptMillis = timeToDecryptMillis; } this.reportFailure(failure); } } async handleKeysChanged(client) { if (this.checkingVerificationStatus) { // Flag that we'll need to do another check once the current check completes. this.retryVerificationStatus = true; return; } this.checkingVerificationStatus = true; try { do { this.retryVerificationStatus = false; this.userTrustsOwnIdentity = (await client.getCrypto().getUserVerificationStatus(client.getUserId())).isCrossSigningVerified(); } while (this.retryVerificationStatus); } finally { this.checkingVerificationStatus = false; } } /** * Start checking for and tracking failures. */ async start(client) { this.loadReportedEvents(); await this.calculateClientProperties(client); this.registerHandlers(client); this.checkInterval = window.setInterval(() => this.checkFailures(Date.now()), DecryptionFailureTracker.CHECK_INTERVAL_MS); } async calculateClientProperties(client) { const baseProperties = {}; this.baseProperties = baseProperties; this.userDomain = client.getDomain() ?? undefined; if (this.userDomain === "matrix.org") { baseProperties.isMatrixDotOrg = true; } else if (this.userDomain !== undefined) { baseProperties.isMatrixDotOrg = false; } const crypto = client.getCrypto(); if (crypto) { const version = crypto.getVersion(); if (version.startsWith("Rust SDK")) { baseProperties.cryptoSDK = "Rust"; } else { baseProperties.cryptoSDK = "Legacy"; } this.userTrustsOwnIdentity = (await crypto.getUserVerificationStatus(client.getUserId())).isCrossSigningVerified(); } } registerHandlers(client) { // After the client attempts to decrypt an event, we examine it to see // if it needs to be reported. const decryptedHandler = e => this.eventDecrypted(e, Date.now()); // When our keys change, we check if the cross-signing keys are now trusted. const keysChangedHandler = () => { this.handleKeysChanged(client).catch(e => { console.log("Error handling KeysChanged event", e); }); }; // When logging out, remove our handlers and destroy state const loggedOutHandler = () => { client.removeListener(_matrix.MatrixEventEvent.Decrypted, decryptedHandler); client.removeListener(_matrix.CryptoEvent.KeysChanged, keysChangedHandler); client.removeListener(_matrix.HttpApiEvent.SessionLoggedOut, loggedOutHandler); this.stop(); }; client.on(_matrix.MatrixEventEvent.Decrypted, decryptedHandler); client.on(_matrix.CryptoEvent.KeysChanged, keysChangedHandler); client.on(_matrix.HttpApiEvent.SessionLoggedOut, loggedOutHandler); } /** * Clear state and stop checking for and tracking failures. */ stop() { if (this.checkInterval) clearInterval(this.checkInterval); if (this.trackInterval) clearInterval(this.trackInterval); this.userTrustsOwnIdentity = undefined; this.failures = new Map(); this.visibleEvents = new Set(); } /** * Mark failures as undecryptable or late. Only mark one failure per event ID. * * @param {number} nowTs the timestamp that represents the time now. */ checkFailures(nowTs) { const failuresNotReady = new Map(); for (const [eventId, failure] of this.failures) { if (failure.timeToDecryptMillis !== undefined || nowTs > failure.ts + DecryptionFailureTracker.MAXIMUM_LATE_DECRYPTION_PERIOD) { // we report failures under two conditions: // - if `timeToDecryptMillis` is set, we successfully decrypted // the event, but we got the key late. We report it so that we // have the late decrytion stats. // - we haven't decrypted yet and it's past the time for it to be // considered a "late" decryption, so we count it as // undecryptable. this.reportFailure(failure); } else { // the event isn't old enough, so we still need to keep track of it failuresNotReady.set(eventId, failure); } } this.failures = failuresNotReady; this.saveReportedEvents(); } /** * If there are failures that should be tracked, call the given trackDecryptionFailure * function with the failures that should be tracked. */ reportFailure(failure) { const errorCode = failure.errorCode; const trackedErrorCode = this.errorCodeMapFn(errorCode); const properties = { timeToDecryptMillis: failure.timeToDecryptMillis ?? -1, wasVisibleToUser: failure.wasVisibleToUser }; if (failure.isFederated !== undefined) { properties.isFederated = failure.isFederated; } if (failure.userTrustsOwnIdentity !== undefined) { properties.userTrustsOwnIdentity = failure.userTrustsOwnIdentity; } if (this.baseProperties) { Object.assign(properties, this.baseProperties); } this.fn(trackedErrorCode, errorCode, properties); this.reportedEvents.add(failure.failedEventId); // once we've added it to reportedEvents, we won't check // visibleEvents for it any more this.visibleEvents.delete(failure.failedEventId); } } exports.DecryptionFailureTracker = DecryptionFailureTracker; _DecryptionFailureTracker = DecryptionFailureTracker; (0, _defineProperty2.default)(DecryptionFailureTracker, "internalInstance", new _DecryptionFailureTracker((errorCode, rawError, properties) => { const event = _objectSpread({ eventName: "Error", domain: "E2EE", name: errorCode, context: `mxc_crypto_error_type_${rawError}` }, properties); _PosthogAnalytics.PosthogAnalytics.instance.trackEvent(event); }, errorCode => { // Map JS-SDK error codes to tracker codes for aggregation switch (errorCode) { case _cryptoApi.DecryptionFailureCode.MEGOLM_UNKNOWN_INBOUND_SESSION_ID: case _cryptoApi.DecryptionFailureCode.MEGOLM_KEY_WITHHELD: return "OlmKeysNotSentError"; case _cryptoApi.DecryptionFailureCode.MEGOLM_KEY_WITHHELD_FOR_UNVERIFIED_DEVICE: return "RoomKeysWithheldForUnverifiedDevice"; case _cryptoApi.DecryptionFailureCode.OLM_UNKNOWN_MESSAGE_INDEX: return "OlmIndexError"; case _cryptoApi.DecryptionFailureCode.HISTORICAL_MESSAGE_NO_KEY_BACKUP: case _cryptoApi.DecryptionFailureCode.HISTORICAL_MESSAGE_BACKUP_UNCONFIGURED: case _cryptoApi.DecryptionFailureCode.HISTORICAL_MESSAGE_WORKING_BACKUP: return "HistoricalMessage"; case _cryptoApi.DecryptionFailureCode.HISTORICAL_MESSAGE_USER_NOT_JOINED: return "ExpectedDueToMembership"; default: return "UnknownError"; } })); /** Call `checkFailures` every `CHECK_INTERVAL_MS`. */ (0, _defineProperty2.default)(DecryptionFailureTracker, "CHECK_INTERVAL_MS", 40000); /** If the event is successfully decrypted in less than 4s, we don't report. */ (0, _defineProperty2.default)(DecryptionFailureTracker, "GRACE_PERIOD_MS", 4000); /** Maximum time for an event to be decrypted to be considered a late * decryption. If it takes longer, we consider it undecryptable. */ (0, _defineProperty2.default)(DecryptionFailureTracker, "MAXIMUM_LATE_DECRYPTION_PERIOD", 60000); //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfYmxvb21GaWx0ZXJzIiwicmVxdWlyZSIsIl9tYXRyaXgiLCJfY3J5cHRvQXBpIiwiX1Bvc3Rob2dBbmFseXRpY3MiLCJfY3J5cHRvIiwiX0RlY3J5cHRpb25GYWlsdXJlVHJhY2tlciIsIm93bktleXMiLCJlIiwiciIsInQiLCJPYmplY3QiLCJrZXlzIiwiZ2V0T3duUHJvcGVydHlTeW1ib2xzIiwibyIsImZpbHRlciIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsImVudW1lcmFibGUiLCJwdXNoIiwiYXBwbHkiLCJfb2JqZWN0U3ByZWFkIiwiYXJndW1lbnRzIiwibGVuZ3RoIiwiZm9yRWFjaCIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJkZWZhdWx0IiwiZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9ycyIsImRlZmluZVByb3BlcnRpZXMiLCJkZWZpbmVQcm9wZXJ0eSIsIkRFQ1JZUFRJT05fRkFJTFVSRV9TVE9SQUdFX0tFWSIsIkRlY3J5cHRpb25GYWlsdXJlIiwiY29uc3RydWN0b3IiLCJmYWlsZWRFdmVudElkIiwiZXJyb3JDb2RlIiwidHMiLCJpc0ZlZGVyYXRlZCIsIndhc1Zpc2libGVUb1VzZXIiLCJ1c2VyVHJ1c3RzT3duSWRlbnRpdHkiLCJleHBvcnRzIiwiRGVjcnlwdGlvbkZhaWx1cmVUcmFja2VyIiwiZm4iLCJlcnJvckNvZGVNYXBGbiIsImNoZWNrUmVwb3J0ZWRFdmVudHMiLCJNYXAiLCJTZXQiLCJTY2FsYWJsZUJsb29tRmlsdGVyIiwidW5kZWZpbmVkIiwiRXJyb3IiLCJpbnN0YW5jZSIsImludGVybmFsSW5zdGFuY2UiLCJsb2FkUmVwb3J0ZWRFdmVudHMiLCJzdG9yZWRGYWlsdXJlcyIsImxvY2FsU3RvcmFnZSIsImdldEl0ZW0iLCJyZXBvcnRlZEV2ZW50cyIsImZyb21KU09OIiwiSlNPTiIsInBhcnNlIiwic2F2ZVJlcG9ydGVkRXZlbnRzIiwic2V0SXRlbSIsInN0cmluZ2lmeSIsInNhdmVBc0pTT04iLCJldmVudERlY3J5cHRlZCIsIm5vd1RzIiwiZ2V0V2lyZUNvbnRlbnQiLCJhbGdvcml0aG0iLCJNRUdPTE1fRU5DUllQVElPTl9BTEdPUklUSE0iLCJlcnJDb2RlIiwiZGVjcnlwdGlvbkZhaWx1cmVSZWFzb24iLCJyZW1vdmVEZWNyeXB0aW9uRmFpbHVyZXNGb3JFdmVudCIsImV2ZW50SWQiLCJnZXRJZCIsImhhcyIsImZhaWx1cmUiLCJmYWlsdXJlcyIsImdldCIsInNlbmRlciIsImdldFNlbmRlciIsInNlbmRlckRvbWFpbiIsInJlcGxhY2UiLCJ1c2VyRG9tYWluIiwidmlzaWJsZUV2ZW50cyIsInNldCIsImFkZFZpc2libGVFdmVudCIsImFkZCIsImRlbGV0ZSIsInRpbWVUb0RlY3J5cHRNaWxsaXMiLCJHUkFDRV9QRVJJT0RfTVMiLCJNQVhJTVVNX0xBVEVfREVDUllQVElPTl9QRVJJT0QiLCJyZXBvcnRGYWlsdXJlIiwiaGFuZGxlS2V5c0NoYW5nZWQiLCJjbGllbnQiLCJjaGVja2luZ1ZlcmlmaWNhdGlvblN0YXR1cyIsInJldHJ5VmVyaWZpY2F0aW9uU3RhdHVzIiwiZ2V0Q3J5cHRvIiwiZ2V0VXNlclZlcmlmaWNhdGlvblN0YXR1cyIsImdldFVzZXJJZCIsImlzQ3Jvc3NTaWduaW5nVmVyaWZpZWQiLCJzdGFydCIsImNhbGN1bGF0ZUNsaWVudFByb3BlcnRpZXMiLCJyZWdpc3RlckhhbmRsZXJzIiwiY2hlY2tJbnRlcnZhbCIsIndpbmRvdyIsInNldEludGVydmFsIiwiY2hlY2tGYWlsdXJlcyIsIkRhdGUiLCJub3ciLCJDSEVDS19JTlRFUlZBTF9NUyIsImJhc2VQcm9wZXJ0aWVzIiwiZ2V0RG9tYWluIiwiaXNNYXRyaXhEb3RPcmciLCJjcnlwdG8iLCJ2ZXJzaW9uIiwiZ2V0VmVyc2lvbiIsInN0YXJ0c1dpdGgiLCJjcnlwdG9TREsiLCJkZWNyeXB0ZWRIYW5kbGVyIiwia2V5c0NoYW5nZWRIYW5kbGVyIiwiY2F0Y2giLCJjb25zb2xlIiwibG9nIiwibG9nZ2VkT3V0SGFuZGxlciIsInJlbW92ZUxpc3RlbmVyIiwiTWF0cml4RXZlbnRFdmVudCIsIkRlY3J5cHRlZCIsIkNyeXB0b0V2ZW50IiwiS2V5c0NoYW5nZWQiLCJIdHRwQXBpRXZlbnQiLCJTZXNzaW9uTG9nZ2VkT3V0Iiwic3RvcCIsIm9uIiwiY2xlYXJJbnRlcnZhbCIsInRyYWNrSW50ZXJ2YWwiLCJmYWlsdXJlc05vdFJlYWR5IiwidHJhY2tlZEVycm9yQ29kZSIsInByb3BlcnRpZXMiLCJhc3NpZ24iLCJyYXdFcnJvciIsImV2ZW50IiwiZXZlbnROYW1lIiwiZG9tYWluIiwibmFtZSIsImNvbnRleHQiLCJQb3N0aG9nQW5hbHl0aWNzIiwidHJhY2tFdmVudCIsIkRlY3J5cHRpb25GYWlsdXJlQ29kZSIsIk1FR09MTV9VTktOT1dOX0lOQk9VTkRfU0VTU0lPTl9JRCIsIk1FR09MTV9LRVlfV0lUSEhFTEQiLCJNRUdPTE1fS0VZX1dJVEhIRUxEX0ZPUl9VTlZFUklGSUVEX0RFVklDRSIsIk9MTV9VTktOT1dOX01FU1NBR0VfSU5ERVgiLCJISVNUT1JJQ0FMX01FU1NBR0VfTk9fS0VZX0JBQ0tVUCIsIkhJU1RPUklDQUxfTUVTU0FHRV9CQUNLVVBfVU5DT05GSUdVUkVEIiwiSElTVE9SSUNBTF9NRVNTQUdFX1dPUktJTkdfQkFDS1VQIiwiSElTVE9SSUNBTF9NRVNTQUdFX1VTRVJfTk9UX0pPSU5FRCJdLCJzb3VyY2VzIjpbIi4uL3NyYy9EZWNyeXB0aW9uRmFpbHVyZVRyYWNrZXIudHMiXSwic291cmNlc0NvbnRlbnQiOlsiLypcbkNvcHlyaWdodCAyMDI0IE5ldyBWZWN0b3IgTHRkLlxuQ29weXJpZ2h0IDIwMTgtMjAyMSBUaGUgTWF0cml4Lm9yZyBGb3VuZGF0aW9uIEMuSS5DLlxuXG5TUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQUdQTC0zLjAtb25seSBPUiBHUEwtMy4wLW9ubHlcblBsZWFzZSBzZWUgTElDRU5TRSBmaWxlcyBpbiB0aGUgcmVwb3NpdG9yeSByb290IGZvciBmdWxsIGRldGFpbHMuXG4qL1xuXG5pbXBvcnQgeyBTY2FsYWJsZUJsb29tRmlsdGVyIH0gZnJvbSBcImJsb29tLWZpbHRlcnNcIjtcbmltcG9ydCB7IENyeXB0b0V2ZW50LCBIdHRwQXBpRXZlbnQsIE1hdHJpeENsaWVudCwgTWF0cml4RXZlbnRFdmVudCwgTWF0cml4RXZlbnQgfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvbWF0cml4XCI7XG5pbXBvcnQgeyBFcnJvciBhcyBFcnJvckV2ZW50IH0gZnJvbSBcIkBtYXRyaXgtb3JnL2FuYWx5dGljcy1ldmVudHMvdHlwZXMvdHlwZXNjcmlwdC9FcnJvclwiO1xuaW1wb3J0IHsgRGVjcnlwdGlvbkZhaWx1cmVDb2RlIH0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL2NyeXB0by1hcGlcIjtcblxuaW1wb3J0IHsgUG9zdGhvZ0FuYWx5dGljcyB9IGZyb20gXCIuL1Bvc3Rob2dBbmFseXRpY3NcIjtcbmltcG9ydCB7IE1FR09MTV9FTkNSWVBUSU9OX0FMR09SSVRITSB9IGZyb20gXCIuL3V0aWxzL2NyeXB0b1wiO1xuXG4vKiogVGhlIGtleSB0aGF0IHdlIHVzZSB0byBzdG9yZSB0aGUgYHJlcG9ydGVkRXZlbnRzYCBibG9vbSBmaWx0ZXIgaW4gbG9jYWxzdG9yYWdlICovXG5jb25zdCBERUNSWVBUSU9OX0ZBSUxVUkVfU1RPUkFHRV9LRVkgPSBcIm14X2RlY3J5cHRpb25fZmFpbHVyZV9ldmVudF9pZHNcIjtcblxuZXhwb3J0IGNsYXNzIERlY3J5cHRpb25GYWlsdXJlIHtcbiAgICAvKipcbiAgICAgKiBUaGUgdGltZSBiZXR3ZWVuIG91ciBpbml0aWFsIGZhaWx1cmUgdG8gZGVjcnlwdCBhbmQgb3VyIHN1Y2Nlc3NmdWxcbiAgICAgKiBkZWNyeXB0aW9uIChpZiB3ZSBtYW5hZ2VkIHRvIGRlY3J5cHQpLlxuICAgICAqL1xuICAgIHB1YmxpYyB0aW1lVG9EZWNyeXB0TWlsbGlzPzogbnVtYmVyO1xuXG4gICAgcHVibGljIGNvbnN0cnVjdG9yKFxuICAgICAgICBwdWJsaWMgcmVhZG9ubHkgZmFpbGVkRXZlbnRJZDogc3RyaW5nLFxuICAgICAgICBwdWJsaWMgcmVhZG9ubHkgZXJyb3JDb2RlOiBEZWNyeXB0aW9uRmFpbHVyZUNvZGUsXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBUaGUgdGltZSB0aGF0IHdlIGZhaWxlZCB0byBkZWNyeXB0IHRoZSBldmVudC4gIElmIHdlIGZhaWxlZCB0byBkZWNyeXB0XG4gICAgICAgICAqIG11bHRpcGxlIHRpbWVzLCB0aGlzIHdpbGwgYmUgdGhlIHRpbWUgb2YgdGhlIGZpcnN0IGZhaWx1cmUuXG4gICAgICAgICAqL1xuICAgICAgICBwdWJsaWMgcmVhZG9ubHkgdHM6IG51bWJlcixcbiAgICAgICAgLyoqXG4gICAgICAgICAqIElzIHRoZSBzZW5kZXIgb24gYSBkaWZmZXJlbnQgc2VydmVyIGZyb20gdXM/XG4gICAgICAgICAqL1xuICAgICAgICBwdWJsaWMgcmVhZG9ubHkgaXNGZWRlcmF0ZWQ6IGJvb2xlYW4gfCB1bmRlZmluZWQsXG4gICAgICAgIC8qKlxuICAgICAgICAgKiBXYXMgdGhlIGZhaWxlZCBldmVudCBldmVyIHZpc2libGUgdG8gdGhlIHVzZXI/XG4gICAgICAgICAqL1xuICAgICAgICBwdWJsaWMgd2FzVmlzaWJsZVRvVXNlcjogYm9vbGVhbixcbiAgICAgICAgLyoqXG4gICAgICAgICAqIEhhcyB0aGUgdXNlciB2ZXJpZmllZCB0aGVpciBvd24gY3Jvc3Mtc2lnbmluZyBpZGVudGl0eSwgYXMgb2YgdGhlIG1vc3RcbiAgICAgICAgICogcmVjZW50IGRlY3J5cHRpb24gYXR0ZW1wdCBmb3IgdGhpcyBldmVudD9cbiAgICAgICAgICovXG4gICAgICAgIHB1YmxpYyB1c2VyVHJ1c3RzT3duSWRlbnRpdHk6IGJvb2xlYW4gfCB1bmRlZmluZWQsXG4gICAgKSB7fVxufVxuXG50eXBlIEVycm9yQ29kZSA9IEVycm9yRXZlbnRbXCJuYW1lXCJdO1xuLyoqIFByb3BlcnRpZXMgYXNzb2NpYXRlZCB3aXRoIGRlY3J5cHRpb24gZXJyb3JzLCBmb3IgY2xhc3NpZnlpbmcgdGhlIGVycm9yLiAqL1xuZXhwb3J0IHR5cGUgRXJyb3JQcm9wZXJ0aWVzID0gT21pdDxFcnJvckV2ZW50LCBcImV2ZW50TmFtZVwiIHwgXCJkb21haW5cIiB8IFwibmFtZVwiIHwgXCJjb250ZXh0XCI+O1xudHlwZSBUcmFja2luZ0ZuID0gKHRyYWNrZWRFcnJDb2RlOiBFcnJvckNvZGUsIHJhd0Vycm9yOiBzdHJpbmcsIHByb3BlcnRpZXM6IEVycm9yUHJvcGVydGllcykgPT4gdm9pZDtcbmV4cG9ydCB0eXBlIEVyckNvZGVNYXBGbiA9IChlcnJjb2RlOiBEZWNyeXB0aW9uRmFpbHVyZUNvZGUpID0+IEVycm9yQ29kZTtcblxuZXhwb3J0IGNsYXNzIERlY3J5cHRpb25GYWlsdXJlVHJhY2tlciB7XG4gICAgcHJpdmF0ZSBzdGF0aWMgaW50ZXJuYWxJbnN0YW5jZSA9IG5ldyBEZWNyeXB0aW9uRmFpbHVyZVRyYWNrZXIoXG4gICAgICAgIChlcnJvckNvZGUsIHJhd0Vycm9yLCBwcm9wZXJ0aWVzKSA9PiB7XG4gICAgICAgICAgICBjb25zdCBldmVudDogRXJyb3JFdmVudCA9IHtcbiAgICAgICAgICAgICAgICBldmVudE5hbWU6IFwiRXJyb3JcIixcbiAgICAgICAgICAgICAgICBkb21haW46IFwiRTJFRVwiLFxuICAgICAgICAgICAgICAgIG5hbWU6IGVycm9yQ29kZSxcbiAgICAgICAgICAgICAgICBjb250ZXh0OiBgbXhjX2NyeXB0b19lcnJvcl90eXBlXyR7cmF3RXJyb3J9YCxcbiAgICAgICAgICAgICAgICAuLi5wcm9wZXJ0aWVzLFxuICAgICAgICAgICAgfTtcbiAgICAgICAgICAgIFBvc3Rob2dBbmFseXRpY3MuaW5zdGFuY2UudHJhY2tFdmVudDxFcnJvckV2ZW50PihldmVudCk7XG4gICAgICAgIH0sXG4gICAgICAgIChlcnJvckNvZGUpID0+IHtcbiAgICAgICAgICAgIC8vIE1hcCBKUy1TREsgZXJyb3IgY29kZXMgdG8gdHJhY2tlciBjb2RlcyBmb3IgYWdncmVnYXRpb25cbiAgICAgICAgICAgIHN3aXRjaCAoZXJyb3JDb2RlKSB7XG4gICAgICAgICAgICAgICAgY2FzZSBEZWNyeXB0aW9uRmFpbHVyZUNvZGUuTUVHT0xNX1VOS05PV05fSU5CT1VORF9TRVNTSU9OX0lEOlxuICAgICAgICAgICAgICAgIGNhc2UgRGVjcnlwdGlvbkZhaWx1cmVDb2RlLk1FR09MTV9LRVlfV0lUSEhFTEQ6XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBcIk9sbUtleXNOb3RTZW50RXJyb3JcIjtcbiAgICAgICAgICAgICAgICBjYXNlIERlY3J5cHRpb25GYWlsdXJlQ29kZS5NRUdPTE1fS0VZX1dJVEhIRUxEX0ZPUl9VTlZFUklGSUVEX0RFVklDRTpcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFwiUm9vbUtleXNXaXRoaGVsZEZvclVudmVyaWZpZWREZXZpY2VcIjtcbiAgICAgICAgICAgICAgICBjYXNlIERlY3J5cHRpb25GYWlsdXJlQ29kZS5PTE1fVU5LTk9XTl9NRVNTQUdFX0lOREVYOlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gXCJPbG1JbmRleEVycm9yXCI7XG4gICAgICAgICAgICAgICAgY2FzZSBEZWNyeXB0aW9uRmFpbHVyZUNvZGUuSElTVE9SSUNBTF9NRVNTQUdFX05PX0tFWV9CQUNLVVA6XG4gICAgICAgICAgICAgICAgY2FzZSBEZWNyeXB0aW9uRmFpbHVyZUNvZGUuSElTVE9SSUNBTF9NRVNTQUdFX0JBQ0tVUF9VTkNPTkZJR1VSRUQ6XG4gICAgICAgICAgICAgICAgY2FzZSBEZWNyeXB0aW9uRmFpbHVyZUNvZGUuSElTVE9SSUNBTF9NRVNTQUdFX1dPUktJTkdfQkFDS1VQOlxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gXCJIaXN0b3JpY2FsTWVzc2FnZVwiO1xuICAgICAgICAgICAgICAgIGNhc2UgRGVjcnlwdGlvbkZhaWx1cmVDb2RlLkhJU1RPUklDQUxfTUVTU0FHRV9VU0VSX05PVF9KT0lORUQ6XG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBcIkV4cGVjdGVkRHVlVG9NZW1iZXJzaGlwXCI7XG4gICAgICAgICAgICAgICAgZGVmYXVsdDpcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFwiVW5rbm93bkVycm9yXCI7XG4gICAgICAgICAgICB9XG4gICAgICAgIH0sXG4gICAgKTtcblxuICAgIC8qKiBNYXAgb2YgZXZlbnQgSURzIHRvIGBEZWNyeXB0aW9uRmFpbHVyZWAgaXRlbXMuXG4gICAgICpcbiAgICAgKiBFdmVyeSBgQ0hFQ0tfSU5URVJWQUxfTVNgLCB0aGlzIG1hcCBpcyBjaGVja2VkIGZvciBmYWlsdXJlcyB0aGF0IGhhcHBlbmVkID5cbiAgICAgKiBgTUFYSU1VTV9MQVRFX0RFQ1JZUFRJT05fUEVSSU9EYCBhZ28gKGNvbnNpZGVyZWQgdW5kZWNyeXB0YWJsZSksIG9yXG4gICAgICogZGVjcnlwdGlvbnMgdGhhdCB0b29rID4gYEdSQUNFX1BFUklPRF9NU2AgKGNvbnNpZGVyZWQgbGF0ZSBkZWNyeXB0aW9ucykuXG4gICAgICpcbiAgICAgKiBBbnkgc3VjaCBldmVudHMgYXJlIHRoZW4gcmVwb3J0ZWQgdmlhIHRoZSBgVHJhY2tpbmdGbmAuXG4gICAgICovXG4gICAgcHVibGljIGZhaWx1cmVzOiBNYXA8c3RyaW5nLCBEZWNyeXB0aW9uRmFpbHVyZT4gPSBuZXcgTWFwKCk7XG5cbiAgICAvKiogU2V0IG9mIGV2ZW50IElEcyB0aGF0IGhhdmUgYmVlbiB2aXNpYmxlIHRvIHRoZSB1c2VyLlxuICAgICAqXG4gICAgICogVGhpcyB3aWxsIG9ubHkgY29udGFpbiBldmVudHMgdGhhdCBhcmUgbm90IGFscmVhZHkgaW4gYHJlcG9ydGVkRXZlbnRzYC5cbiAgICAgKi9cbiAgICBwdWJsaWMgdmlzaWJsZUV2ZW50czogU2V0PHN0cmluZz4gPSBuZXcgU2V0KCk7XG5cbiAgICAvKiogQmxvb20gZmlsdGVyIHRyYWNraW5nIGV2ZW50IElEcyBvZiBmYWlsdXJlcyB0aGF0IHdlcmUgcmVwb3J0ZWQgcHJldmlvdXNseSAqL1xuICAgIHByaXZhdGUgcmVwb3J0ZWRFdmVudHM6IFNjYWxhYmxlQmxvb21GaWx0ZXIgPSBuZXcgU2NhbGFibGVCbG9vbUZpbHRlcigpO1xuXG4gICAgLyoqIFNldCB0byBhbiBpbnRlcnZhbCBJRCB3aGVuIGBzdGFydGAgaXMgY2FsbGVkICovXG4gICAgcHVibGljIGNoZWNrSW50ZXJ2YWw6IG51bWJlciB8IG51bGwgPSBudWxsO1xuICAgIHB1YmxpYyB0cmFja0ludGVydmFsOiBudW1iZXIgfCBudWxsID0gbnVsbDtcblxuICAgIC8qKiBDYWxsIGBjaGVja0ZhaWx1cmVzYCBldmVyeSBgQ0hFQ0tfSU5URVJWQUxfTVNgLiAqL1xuICAgIHB1YmxpYyBzdGF0aWMgQ0hFQ0tfSU5URVJWQUxfTVMgPSA0MDAwMDtcblxuICAgIC8qKiBJZiB0aGUgZXZlbnQgaXMgc3VjY2Vzc2Z1bGx5IGRlY3J5cHRlZCBpbiBsZXNzIHRoYW4gNHMsIHdlIGRvbid0IHJlcG9ydC4gKi9cbiAgICBwdWJsaWMgc3RhdGljIEdSQUNFX1BFUklPRF9NUyA9IDQwMDA7XG5cbiAgICAvKiogTWF4aW11bSB0aW1lIGZvciBhbiBldmVudCB0byBiZSBkZWNyeXB0ZWQgdG8gYmUgY29uc2lkZXJlZCBhIGxhdGVcbiAgICAgKiBkZWNyeXB0aW9uLiAgSWYgaXQgdGFrZXMgbG9uZ2VyLCB3ZSBjb25zaWRlciBpdCB1bmRlY3J5cHRhYmxlLiAqL1xuICAgIHB1YmxpYyBzdGF0aWMgTUFYSU1VTV9MQVRFX0RFQ1JZUFRJT05fUEVSSU9EID0gNjAwMDA7XG5cbiAgICAvKiogUHJvcGVydGllcyB0aGF0IHdpbGwgYmUgYWRkZWQgdG8gYWxsIHJlcG9ydGVkIGV2ZW50cyAobWFpbmx5IHJlcG9ydGluZ1xuICAgICAqIGluZm9ybWF0aW9uIGFib3V0IHRoZSBNYXRyaXggY2xpZW50KS4gKi9cbiAgICBwcml2YXRlIGJhc2VQcm9wZXJ0aWVzPzogRXJyb3JQcm9wZXJ0aWVzID0ge307XG5cbiAgICAvKiogVGhlIHVzZXIncyBkb21haW4gKGhvbWVzZXJ2ZXIgbmFtZSkuICovXG4gICAgcHJpdmF0ZSB1c2VyRG9tYWluPzogc3RyaW5nO1xuXG4gICAgLyoqIFdoZXRoZXIgdGhlIHVzZXIgaGFzIHZlcmlmaWVkIHRoZWlyIG93biBjcm9zcy1zaWduaW5nIGtleXMuICovXG4gICAgcHJpdmF0ZSB1c2VyVHJ1c3RzT3duSWRlbnRpdHk6IGJvb2xlYW4gfCB1bmRlZmluZWQgPSB1bmRlZmluZWQ7XG5cbiAgICAvKiogV2hldGhlciB3ZSBhcmUgY3VycmVudGx5IGNoZWNraW5nIG91ciBvd24gdmVyaWZpY2F0aW9uIHN0YXR1cy4gKi9cbiAgICBwcml2YXRlIGNoZWNraW5nVmVyaWZpY2F0aW9uU3RhdHVzOiBib29sZWFuID0gZmFsc2U7XG5cbiAgICAvKiogV2hldGhlciB3ZSBzaG91bGQgcmV0cnkgY2hlY2tpbmcgb3VyIG93biB2ZXJpZmljYXRpb24gc3RhdHVzIGFmdGVyIHdlJ3JlXG4gICAgICogZG9uZSBvdXIgY3VycmVudCBjaGVjay4gaS5lLiB3ZSBnb3Qgbm90aWZpZWQgdGhhdCBvdXIga2V5cyBjaGFuZ2VkIHdoaWxlXG4gICAgICogd2Ugd2VyZSBhbHJlYWR5IGNoZWNraW5nLCBzbyB0aGUgcmVzdWx0IGNvdWxkIGJlIG91dCBvZiBkYXRlLiAqL1xuICAgIHByaXZhdGUgcmV0cnlWZXJpZmljYXRpb25TdGF0dXM6IGJvb2xlYW4gPSBmYWxzZTtcblxuICAgIC8qKlxuICAgICAqIENyZWF0ZSBhIG5ldyBEZWNyeXB0aW9uRmFpbHVyZVRyYWNrZXIuXG4gICAgICpcbiAgICAgKiBDYWxsIGBzdGFydChjbGllbnQpYCB0byBzdGFydCB0aGUgdHJhY2tlci4gIFRoZSB0cmFja2VyIHdpbGwgbGlzdGVuIGZvclxuICAgICAqIGRlY3J5cHRpb24gZXZlbnRzIG9uIHRoZSBjbGllbnQgYW5kIHRyYWNrIGRlY3J5cHRpb24gZmFpbHVyZXMsIGFuZCB3aWxsXG4gICAgICogYXV0b21hdGljYWxseSBzdG9wIHRyYWNraW5nIHdoZW4gdGhlIGNsaWVudCBsb2dzIG91dC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7ZnVuY3Rpb259IGZuIFRoZSB0cmFja2luZyBmdW5jdGlvbiwgd2hpY2ggd2lsbCBiZSBjYWxsZWQgd2hlbiBmYWlsdXJlc1xuICAgICAqIGFyZSB0cmFja2VkLiBUaGUgZnVuY3Rpb24gc2hvdWxkIGhhdmUgYSBzaWduYXR1cmUgYCh0cmFja2VkRXJyb3JDb2RlLCByYXdFcnJvciwgcHJvcGVydGllcykgPT4gey4uLn1gLFxuICAgICAqIHdoZXJlIGBlcnJvckNvZGVgIG1hdGNoZXMgdGhlIG91dHB1dCBvZiBgZXJyb3JDb2RlTWFwRm5gLCBgcmF3RXJyb3JgIGlzIHRoZSBvcmlnaW5hbFxuICAgICAqIGVycm9yICh0aGF0IGlzLCB0aGUgaW5wdXQgdG8gYGVycm9yQ29kZU1hcEZuYCksIGFuZCBgcHJvcGVydGllc2AgaXMgYSBtYXAgb2YgdGhlXG4gICAgICogZXJyb3IgcHJvcGVydGllcyBmb3IgY2xhc3NpZnlpbmcgdGhlIGVycm9yLlxuICAgICAqXG4gICAgICogQHBhcmFtIHtmdW5jdGlvbn0gZXJyb3JDb2RlTWFwRm4gVGhlIGZ1bmN0aW9uIHVzZWQgdG8gbWFwIGRlY3J5cHRpb24gZmFpbHVyZSByZWFzb24gIGNvZGVzIHRvIHRoZVxuICAgICAqIGB0cmFja2VkRXJyb3JDb2RlYC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7Ym9vbGVhbn0gY2hlY2tSZXBvcnRlZEV2ZW50cyBDaGVjayBpZiB3ZSBoYXZlIGFscmVhZHkgcmVwb3J0ZWQgYW4gZXZlbnQuXG4gICAgICogRGVmYXVsdHMgdG8gYHRydWVgLiBUaGlzIGlzIG9ubHkgdXNlZCBmb3IgdGVzdHMsIHRvIGF2b2lkIHBvc3NpYmxlIGZhbHNlIHBvc2l0aXZlcyBmcm9tXG4gICAgICogdGhlIEJsb29tIGZpbHRlci4gVGhpcyBzaG91bGQgYmUgc2V0IHRvIGBmYWxzZWAgZm9yIGFsbCB0ZXN0cyBleGNlcHQgZm9yIHRob3NlXG4gICAgICogdGhhdCBzcGVjaWZpY2FsbHkgdGVzdCB0aGUgYHJlcG9ydGVkRXZlbnRzYCBmdW5jdGlvbmFsaXR5LlxuICAgICAqL1xuICAgIHByaXZhdGUgY29uc3RydWN0b3IoXG4gICAgICAgIHByaXZhdGUgcmVhZG9ubHkgZm46IFRyYWNraW5nRm4sXG4gICAgICAgIHByaXZhdGUgcmVhZG9ubHkgZXJyb3JDb2RlTWFwRm46IEVyckNvZGVNYXBGbixcbiAgICAgICAgcHJpdmF0ZSByZWFkb25seSBjaGVja1JlcG9ydGVkRXZlbnRzOiBib29sZWFuID0gdHJ1ZSxcbiAgICApIHtcbiAgICAgICAgaWYgKCFmbiB8fCB0eXBlb2YgZm4gIT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiRGVjcnlwdGlvbkZhaWx1cmVUcmFja2VyIHJlcXVpcmVzIHRyYWNraW5nIGZ1bmN0aW9uXCIpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKHR5cGVvZiBlcnJvckNvZGVNYXBGbiAhPT0gXCJmdW5jdGlvblwiKSB7XG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJEZWNyeXB0aW9uRmFpbHVyZVRyYWNrZXIgc2Vjb25kIGNvbnN0cnVjdG9yIGFyZ3VtZW50IHNob3VsZCBiZSBhIGZ1bmN0aW9uXCIpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIHN0YXRpYyBnZXQgaW5zdGFuY2UoKTogRGVjcnlwdGlvbkZhaWx1cmVUcmFja2VyIHtcbiAgICAgICAgcmV0dXJuIERlY3J5cHRpb25GYWlsdXJlVHJhY2tlci5pbnRlcm5hbEluc3RhbmNlO1xuICAgIH1cblxuICAgIHByaXZhdGUgbG9hZFJlcG9ydGVkRXZlbnRzKCk6IHZvaWQge1xuICAgICAgICBjb25zdCBzdG9yZWRGYWlsdXJlcyA9IGxvY2FsU3RvcmFnZS5nZXRJdGVtKERFQ1JZUFRJT05fRkFJTFVSRV9TVE9SQUdFX0tFWSk7XG4gICAgICAgIGlmIChzdG9yZWRGYWlsdXJlcykge1xuICAgICAgICAgICAgdGhpcy5yZXBvcnRlZEV2ZW50cyA9IFNjYWxhYmxlQmxvb21GaWx0ZXIuZnJvbUpTT04oSlNPTi5wYXJzZShzdG9yZWRGYWlsdXJlcykpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhpcy5yZXBvcnRlZEV2ZW50cyA9IG5ldyBTY2FsYWJsZUJsb29tRmlsdGVyKCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIHNhdmVSZXBvcnRlZEV2ZW50cygpOiB2b2lkIHtcbiAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oREVDUllQVElPTl9GQUlMVVJFX1NUT1JBR0VfS0VZLCBKU09OLnN0cmluZ2lmeSh0aGlzLnJlcG9ydGVkRXZlbnRzLnNhdmVBc0pTT04oKSkpO1xuICAgIH1cblxuICAgIC8qKiBDYWxsYmFjayBmb3Igd2hlbiBhbiBldmVudCBpcyBkZWNyeXB0ZWQuXG4gICAgICpcbiAgICAgKiBUaGlzIGZ1bmN0aW9uIGlzIGNhbGxlZCBieSBvdXIgYE1hdHJpeEV2ZW50RXZlbnQuRGVjcnlwdGVkYCBldmVudFxuICAgICAqIGhhbmRsZXIgYWZ0ZXIgYSBkZWNyeXB0aW9uIGF0dGVtcHQgb24gYW4gZXZlbnQsIHdoZXRoZXIgdGhlIGRlY3J5cHRpb25cbiAgICAgKiBpcyBzdWNjZXNzZnVsIG9yIG5vdC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSBlIHRoZSBldmVudCB0aGF0IHdhcyBkZWNyeXB0ZWRcbiAgICAgKlxuICAgICAqIEBwYXJhbSBub3dUcyB0aGUgY3VycmVudCB0aW1lc3RhbXBcbiAgICAgKi9cbiAgICBwcml2YXRlIGV2ZW50RGVjcnlwdGVkKGU6IE1hdHJpeEV2ZW50LCBub3dUczogbnVtYmVyKTogdm9pZCB7XG4gICAgICAgIC8vIGZvciBub3cgd2Ugb25seSB0cmFjayBtZWdvbG0gZGVjcnlwdGlvbiBmYWlsdXJlc1xuICAgICAgICBpZiAoZS5nZXRXaXJlQ29udGVudCgpLmFsZ29yaXRobSAhPSBNRUdPTE1fRU5DUllQVElPTl9BTEdPUklUSE0pIHtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBlcnJDb2RlID0gZS5kZWNyeXB0aW9uRmFpbHVyZVJlYXNvbjtcbiAgICAgICAgaWYgKGVyckNvZGUgPT09IG51bGwpIHtcbiAgICAgICAgICAgIC8vIENvdWxkIGJlIGFuIGV2ZW50IGluIHRoZSBmYWlsdXJlcywgcmVtb3ZlIGl0XG4gICAgICAgICAgICB0aGlzLnJlbW92ZURlY3J5cHRpb25GYWlsdXJlc0ZvckV2ZW50KGUsIG5vd1RzKTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IGV2ZW50SWQgPSBlLmdldElkKCkhO1xuXG4gICAgICAgIC8vIGlmIGl0J3MgYWxyZWFkeSByZXBvcnRlZCwgd2UgZG9uJ3QgbmVlZCB0byBkbyBhbnl0aGluZ1xuICAgICAgICBpZiAodGhpcy5yZXBvcnRlZEV2ZW50cy5oYXMoZXZlbnRJZCkgJiYgdGhpcy5jaGVja1JlcG9ydGVkRXZlbnRzKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBpZiB3ZSBhbHJlYWR5IGhhdmUgYSByZWNvcmQgb2YgdGhpcyBldmVudCwgdXNlIHRoZSBwcmV2aW91c2x5LXJlY29yZGVkIHRpbWVzdGFtcFxuICAgICAgICBjb25zdCBmYWlsdXJlID0gdGhpcy5mYWlsdXJlcy5nZXQoZXZlbnRJZCk7XG4gICAgICAgIGNvbnN0IHRzID0gZmFpbHVyZSA/IGZhaWx1cmUudHMgOiBub3dUcztcblxuICAgICAgICBjb25zdCBzZW5kZXIgPSBlLmdldFNlbmRlcigpO1xuICAgICAgICBjb25zdCBzZW5kZXJEb21haW4gPSBzZW5kZXI/LnJlcGxhY2UoL14uKj86LywgXCJcIik7XG4gICAgICAgIGxldCBpc0ZlZGVyYXRlZDogYm9vbGVhbiB8IHVuZGVmaW5lZDtcbiAgICAgICAgaWYgKHRoaXMudXNlckRvbWFpbiAhPT0gdW5kZWZpbmVkICYmIHNlbmRlckRvbWFpbiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBpc0ZlZGVyYXRlZCA9IHRoaXMudXNlckRvbWFpbiAhPT0gc2VuZGVyRG9tYWluO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3Qgd2FzVmlzaWJsZVRvVXNlciA9IHRoaXMudmlzaWJsZUV2ZW50cy5oYXMoZXZlbnRJZCk7XG4gICAgICAgIHRoaXMuZmFpbHVyZXMuc2V0KFxuICAgICAgICAgICAgZXZlbnRJZCxcbiAgICAgICAgICAgIG5ldyBEZWNyeXB0aW9uRmFpbHVyZShldmVudElkLCBlcnJDb2RlLCB0cywgaXNGZWRlcmF0ZWQsIHdhc1Zpc2libGVUb1VzZXIsIHRoaXMudXNlclRydXN0c093bklkZW50aXR5KSxcbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwdWJsaWMgYWRkVmlzaWJsZUV2ZW50KGU6IE1hdHJpeEV2ZW50KTogdm9pZCB7XG4gICAgICAgIGNvbnN0IGV2ZW50SWQgPSBlLmdldElkKCkhO1xuXG4gICAgICAgIC8vIGlmIGl0J3MgYWxyZWFkeSByZXBvcnRlZCwgd2UgZG9uJ3QgbmVlZCB0byBkbyBhbnl0aGluZ1xuICAgICAgICBpZiAodGhpcy5yZXBvcnRlZEV2ZW50cy5oYXMoZXZlbnRJZCkgJiYgdGhpcy5jaGVja1JlcG9ydGVkRXZlbnRzKSB7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgIH1cblxuICAgICAgICAvLyBpZiB3ZSd2ZSBhbHJlYWR5IG1hcmtlZCB0aGUgZXZlbnQgYXMgYSBmYWlsdXJlLCBtYXJrIGl0IGFzIHZpc2libGVcbiAgICAgICAgLy8gaW4gdGhlIGZhaWx1cmUgb2JqZWN0XG4gICAgICAgIGNvbnN0IGZhaWx1cmUgPSB0aGlzLmZhaWx1cmVzLmdldChldmVudElkKTtcbiAgICAgICAgaWYgKGZhaWx1cmUpIHtcbiAgICAgICAgICAgIGZhaWx1cmUud2FzVmlzaWJsZVRvVXNlciA9IHRydWU7XG4gICAgICAgIH1cblxuICAgICAgICB0aGlzLnZpc2libGVFdmVudHMuYWRkKGV2ZW50SWQpO1xuICAgIH1cblxuICAgIHB1YmxpYyByZW1vdmVEZWNyeXB0aW9uRmFpbHVyZXNGb3JFdmVudChlOiBNYXRyaXhFdmVudCwgbm93VHM6IG51bWJlcik6IHZvaWQge1xuICAgICAgICBjb25zdCBldmVudElkID0gZS5nZXRJZCgpITtcbiAgICAgICAgY29uc3QgZmFpbHVyZSA9IHRoaXMuZmFpbHVyZXMuZ2V0KGV2ZW50SWQpO1xuICAgICAgICBpZiAoZmFpbHVyZSkge1xuICAgICAgICAgICAgdGhpcy5mYWlsdXJlcy5kZWxldGUoZXZlbnRJZCk7XG5cbiAgICAgICAgICAgIGNvbnN0IHRpbWVUb0RlY3J5cHRNaWxsaXMgPSBub3dUcyAtIGZhaWx1cmUudHM7XG4gICAgICAgICAgICBpZiAodGltZVRvRGVjcnlwdE1pbGxpcyA8IERlY3J5cHRpb25GYWlsdXJlVHJhY2tlci5HUkFDRV9QRVJJT0RfTVMpIHtcbiAgICAgICAgICAgICAgICAvLyB0aGUgZXZlbnQgZGVjcnlwdGVkIG9uIHRpbWUsIHNvIHdlIGRvbid0IG5lZWQgdG8gcmVwb3J0IGl0XG4gICAgICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICAgICAgfSBlbHNlIGlmICh0aW1lVG9EZWNyeXB0TWlsbGlzIDw9IERlY3J5cHRpb25GYWlsdXJlVHJhY2tlci5NQVhJTVVNX0xBVEVfREVDUllQVElPTl9QRVJJT0QpIHtcbiAgICAgICAgICAgICAgICAvLyBUaGUgZXZlbnQgaXMgYSBsYXRlIGRlY3J5cHRpb24sIHNvIHN0b3JlIHRoZSB0aW1lIGl0IHRvb2suXG4gICAgICAgICAgICAgICAgLy8gSWYgdGhlIHRpbWUgdG8gZGVjcnlwdCBpcyBsb25nZXIgdGhhblxuICAgICAgICAgICAgICAgIC8vIE1BWElNVU1fTEFURV9ERUNSWVBUSU9OX1BFUklPRCwgd2UgY29uc2lkZXIgdGhlIGV2ZW50IGFzXG4gICAgICAgICAgICAgICAgLy8gdW5kZWNyeXB0YWJsZSwgYW5kIGxlYXZlIHRpbWVUb0RlY3J5cHRNaWxsaXMgdW5kZWZpbmVkXG4gICAgICAgICAgICAgICAgZmFpbHVyZS50aW1lVG9EZWNyeXB0TWlsbGlzID0gdGltZVRvRGVjcnlwdE1pbGxpcztcbiAgICAgICAgICAgIH1cbiAgICAgICAgICAgIHRoaXMucmVwb3J0RmFpbHVyZShmYWlsdXJlKTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgaGFuZGxlS2V5c0NoYW5nZWQoY2xpZW50OiBNYXRyaXhDbGllbnQpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAgICAgaWYgKHRoaXMuY2hlY2tpbmdWZXJpZmljYXRpb25TdGF0dXMpIHtcbiAgICAgICAgICAgIC8vIEZsYWcgdGhhdCB3ZSdsbCBuZWVkIHRvIGRvIGFub3RoZXIgY2hlY2sgb25jZSB0aGUgY3VycmVudCBjaGVjayBjb21wbGV0ZXMuXG4gICAgICAgICAgICB0aGlzLnJldHJ5VmVyaWZpY2F0aW9uU3RhdHVzID0gdHJ1ZTtcbiAgICAgICAgICAgIHJldHVybjtcbiAgICAgICAgfVxuXG4gICAgICAgIHRoaXMuY2hlY2tpbmdWZXJpZmljYXRpb25TdGF0dXMgPSB0cnVlO1xuICAgICAgICB0cnkge1xuICAgICAgICAgICAgZG8ge1xuICAgICAgICAgICAgICAgIHRoaXMucmV0cnlWZXJpZmljYXRpb25TdGF0dXMgPSBmYWxzZTtcbiAgICAgICAgICAgICAgICB0aGlzLnVzZXJUcnVzdHNPd25JZGVudGl0eSA9IChcbiAgICAgICAgICAgICAgICAgICAgYXdhaXQgY2xpZW50LmdldENyeXB0bygpIS5nZXRVc2VyVmVyaWZpY2F0aW9uU3RhdHVzKGNsaWVudC5nZXRVc2VySWQoKSEpXG4gICAgICAgICAgICAgICAgKS5pc0Nyb3NzU2lnbmluZ1ZlcmlmaWVkKCk7XG4gICAgICAgICAgICB9IHdoaWxlICh0aGlzLnJldHJ5VmVyaWZpY2F0aW9uU3RhdHVzKTtcbiAgICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgICAgIHRoaXMuY2hlY2tpbmdWZXJpZmljYXRpb25TdGF0dXMgPSBmYWxzZTtcbiAgICAgICAgfVxuICAgIH1cblxuICAgIC8qKlxuICAgICAqIFN0YXJ0IGNoZWNraW5nIGZvciBhbmQgdHJhY2tpbmcgZmFpbHVyZXMuXG4gICAgICovXG4gICAgcHVibGljIGFzeW5jIHN0YXJ0KGNsaWVudDogTWF0cml4Q2xpZW50KTogUHJvbWlzZTx2b2lkPiB7XG4gICAgICAgIHRoaXMubG9hZFJlcG9ydGVkRXZlbnRzKCk7XG4gICAgICAgIGF3YWl0IHRoaXMuY2FsY3VsYXRlQ2xpZW50UHJvcGVydGllcyhjbGllbnQpO1xuICAgICAgICB0aGlzLnJlZ2lzdGVySGFuZGxlcnMoY2xpZW50KTtcbiAgICAgICAgdGhpcy5jaGVja0ludGVydmFsID0gd2luZG93LnNldEludGVydmFsKFxuICAgICAgICAgICAgKCkgPT4gdGhpcy5jaGVja0ZhaWx1cmVzKERhdGUubm93KCkpLFxuICAgICAgICAgICAgRGVjcnlwdGlvbkZhaWx1cmVUcmFja2VyLkNIRUNLX0lOVEVSVkFMX01TLFxuICAgICAgICApO1xuICAgIH1cblxuICAgIHByaXZhdGUgYXN5bmMgY2FsY3VsYXRlQ2xpZW50UHJvcGVydGllcyhjbGllbnQ6IE1hdHJpeENsaWVudCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBjb25zdCBiYXNlUHJvcGVydGllczogRXJyb3JQcm9wZXJ0aWVzID0ge307XG4gICAgICAgIHRoaXMuYmFzZVByb3BlcnRpZXMgPSBiYXNlUHJvcGVydGllcztcblxuICAgICAgICB0aGlzLnVzZXJEb21haW4gPSBjbGllbnQuZ2V0RG9tYWluKCkgPz8gdW5kZWZpbmVkO1xuICAgICAgICBpZiAodGhpcy51c2VyRG9tYWluID09PSBcIm1hdHJpeC5vcmdcIikge1xuICAgICAgICAgICAgYmFzZVByb3BlcnRpZXMuaXNNYXRyaXhEb3RPcmcgPSB0cnVlO1xuICAgICAgICB9IGVsc2UgaWYgKHRoaXMudXNlckRvbWFpbiAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBiYXNlUHJvcGVydGllcy5pc01hdHJpeERvdE9yZyA9IGZhbHNlO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgY3J5cHRvID0gY2xpZW50LmdldENyeXB0bygpO1xuICAgICAgICBpZiAoY3J5cHRvKSB7XG4gICAgICAgICAgICBjb25zdCB2ZXJzaW9uID0gY3J5cHRvLmdldFZlcnNpb24oKTtcbiAgICAgICAgICAgIGlmICh2ZXJzaW9uLnN0YXJ0c1dpdGgoXCJSdXN0IFNES1wiKSkge1xuICAgICAgICAgICAgICAgIGJhc2VQcm9wZXJ0aWVzLmNyeXB0b1NESyA9IFwiUnVzdFwiO1xuICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICBiYXNlUHJvcGVydGllcy5jcnlwdG9TREsgPSBcIkxlZ2FjeVwiO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgdGhpcy51c2VyVHJ1c3RzT3duSWRlbnRpdHkgPSAoXG4gICAgICAgICAgICAgICAgYXdhaXQgY3J5cHRvLmdldFVzZXJWZXJpZmljYXRpb25TdGF0dXMoY2xpZW50LmdldFVzZXJJZCgpISlcbiAgICAgICAgICAgICkuaXNDcm9zc1NpZ25pbmdWZXJpZmllZCgpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHJpdmF0ZSByZWdpc3RlckhhbmRsZXJzKGNsaWVudDogTWF0cml4Q2xpZW50KTogdm9pZCB7XG4gICAgICAgIC8vIEFmdGVyIHRoZSBjbGllbnQgYXR0ZW1wdHMgdG8gZGVjcnlwdCBhbiBldmVudCwgd2UgZXhhbWluZSBpdCB0byBzZWVcbiAgICAgICAgLy8gaWYgaXQgbmVlZHMgdG8gYmUgcmVwb3J0ZWQuXG4gICAgICAgIGNvbnN0IGRlY3J5cHRlZEhhbmRsZXIgPSAoZTogTWF0cml4RXZlbnQpOiB2b2lkID0+IHRoaXMuZXZlbnREZWNyeXB0ZWQoZSwgRGF0ZS5ub3coKSk7XG4gICAgICAgIC8vIFdoZW4gb3VyIGtleXMgY2hhbmdlLCB3ZSBjaGVjayBpZiB0aGUgY3Jvc3Mtc2lnbmluZyBrZXlzIGFyZSBub3cgdHJ1c3RlZC5cbiAgICAgICAgY29uc3Qga2V5c0NoYW5nZWRIYW5kbGVyID0gKCk6IHZvaWQgPT4ge1xuICAgICAgICAgICAgdGhpcy5oYW5kbGVLZXlzQ2hhbmdlZChjbGllbnQpLmNhdGNoKChlKSA9PiB7XG4gICAgICAgICAgICAgICAgY29uc29sZS5sb2coXCJFcnJvciBoYW5kbGluZyBLZXlzQ2hhbmdlZCBldmVudFwiLCBlKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9O1xuICAgICAgICAvLyBXaGVuIGxvZ2dpbmcgb3V0LCByZW1vdmUgb3VyIGhhbmRsZXJzIGFuZCBkZXN0cm95IHN0YXRlXG4gICAgICAgIGNvbnN0IGxvZ2dlZE91dEhhbmRsZXIgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgICAgICBjbGllbnQucmVtb3ZlTGlzdGVuZXIoTWF0cml4RXZlbnRFdmVudC5EZWNyeXB0ZWQsIGRlY3J5cHRlZEhhbmRsZXIpO1xuICAgICAgICAgICAgY2xpZW50LnJlbW92ZUxpc3RlbmVyKENyeXB0b0V2ZW50LktleXNDaGFuZ2VkLCBrZXlzQ2hhbmdlZEhhbmRsZXIpO1xuICAgICAgICAgICAgY2xpZW50LnJlbW92ZUxpc3RlbmVyKEh0dHBBcGlFdmVudC5TZXNzaW9uTG9nZ2VkT3V0LCBsb2dnZWRPdXRIYW5kbGVyKTtcbiAgICAgICAgICAgIHRoaXMuc3RvcCgpO1xuICAgICAgICB9O1xuXG4gICAgICAgIGNsaWVudC5vbihNYXRyaXhFdmVudEV2ZW50LkRlY3J5cHRlZCwgZGVjcnlwdGVkSGFuZGxlcik7XG4gICAgICAgIGNsaWVudC5vbihDcnlwdG9FdmVudC5LZXlzQ2hhbmdlZCwga2V5c0NoYW5nZWRIYW5kbGVyKTtcbiAgICAgICAgY2xpZW50Lm9uKEh0dHBBcGlFdmVudC5TZXNzaW9uTG9nZ2VkT3V0LCBsb2dnZWRPdXRIYW5kbGVyKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBDbGVhciBzdGF0ZSBhbmQgc3RvcCBjaGVja2luZyBmb3IgYW5kIHRyYWNraW5nIGZhaWx1cmVzLlxuICAgICAqL1xuICAgIHByaXZhdGUgc3RvcCgpOiB2b2lkIHtcbiAgICAgICAgaWYgKHRoaXMuY2hlY2tJbnRlcnZhbCkgY2xlYXJJbnRlcnZhbCh0aGlzLmNoZWNrSW50ZXJ2YWwpO1xuICAgICAgICBpZiAodGhpcy50cmFja0ludGVydmFsKSBjbGVhckludGVydmFsKHRoaXMudHJhY2tJbnRlcnZhbCk7XG5cbiAgICAgICAgdGhpcy51c2VyVHJ1c3RzT3duSWRlbnRpdHkgPSB1bmRlZmluZWQ7XG4gICAgICAgIHRoaXMuZmFpbHVyZXMgPSBuZXcgTWFwKCk7XG4gICAgICAgIHRoaXMudmlzaWJsZUV2ZW50cyA9IG5ldyBTZXQoKTtcbiAgICB9XG5cbiAgICAvKipcbiAgICAgKiBNYXJrIGZhaWx1cmVzIGFzIHVuZGVjcnlwdGFibGUgb3IgbGF0ZS4gT25seSBtYXJrIG9uZSBmYWlsdXJlIHBlciBldmVudCBJRC5cbiAgICAgKlxuICAgICAqIEBwYXJhbSB7bnVtYmVyfSBub3dUcyB0aGUgdGltZXN0YW1wIHRoYXQgcmVwcmVzZW50cyB0aGUgdGltZSBub3cuXG4gICAgICovXG4gICAgcHVibGljIGNoZWNrRmFpbHVyZXMobm93VHM6IG51bWJlcik6IHZvaWQge1xuICAgICAgICBjb25zdCBmYWlsdXJlc05vdFJlYWR5OiBNYXA8c3RyaW5nLCBEZWNyeXB0aW9uRmFpbHVyZT4gPSBuZXcgTWFwKCk7XG4gICAgICAgIGZvciAoY29uc3QgW2V2ZW50SWQsIGZhaWx1cmVdIG9mIHRoaXMuZmFpbHVyZXMpIHtcbiAgICAgICAgICAgIGlmIChcbiAgICAgICAgICAgICAgICBmYWlsdXJlLnRpbWVUb0RlY3J5cHRNaWxsaXMgIT09IHVuZGVmaW5lZCB8fFxuICAgICAgICAgICAgICAgIG5vd1RzID4gZmFpbHVyZS50cyArIERlY3J5cHRpb25GYWlsdXJlVHJhY2tlci5NQVhJTVVNX0xBVEVfREVDUllQVElPTl9QRVJJT0RcbiAgICAgICAgICAgICkge1xuICAgICAgICAgICAgICAgIC8vIHdlIHJlcG9ydCBmYWlsdXJlcyB1bmRlciB0d28gY29uZGl0aW9uczpcbiAgICAgICAgICAgICAgICAvLyAtIGlmIGB0aW1lVG9EZWNyeXB0TWlsbGlzYCBpcyBzZXQsIHdlIHN1Y2Nlc3NmdWxseSBkZWNyeXB0ZWRcbiAgICAgICAgICAgICAgICAvLyAgIHRoZSBldmVudCwgYnV0IHdlIGdvdCB0aGUga2V5IGxhdGUuICBXZSByZXBvcnQgaXQgc28gdGhhdCB3ZVxuICAgICAgICAgICAgICAgIC8vICAgaGF2ZSB0aGUgbGF0ZSBkZWNyeXRpb24gc3RhdHMuXG4gICAgICAgICAgICAgICAgLy8gLSB3ZSBoYXZlbid0IGRlY3J5cHRlZCB5ZXQgYW5kIGl0J3MgcGFzdCB0aGUgdGltZSBmb3IgaXQgdG8gYmVcbiAgICAgICAgICAgICAgICAvLyAgIGNvbnNpZGVyZWQgYSBcImxhdGVcIiBkZWNyeXB0aW9uLCBzbyB3ZSBjb3VudCBpdCBhc1xuICAgICAgICAgICAgICAgIC8vICAgdW5kZWNyeXB0YWJsZS5cbiAgICAgICAgICAgICAgICB0aGlzLnJlcG9ydEZhaWx1cmUoZmFpbHVyZSk7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIC8vIHRoZSBldmVudCBpc24ndCBvbGQgZW5vdWdoLCBzbyB3ZSBzdGlsbCBuZWVkIHRvIGtlZXAgdHJhY2sgb2YgaXRcbiAgICAgICAgICAgICAgICBmYWlsdXJlc05vdFJlYWR5LnNldChldmVudElkLCBmYWlsdXJlKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgICB0aGlzLmZhaWx1cmVzID0gZmFpbHVyZXNOb3RSZWFkeTtcblxuICAgICAgICB0aGlzLnNhdmVSZXBvcnRlZEV2ZW50cygpO1xuICAgIH1cblxuICAgIC8qKlxuICAgICAqIElmIHRoZXJlIGFyZSBmYWlsdXJlcyB0aGF0IHNob3VsZCBiZSB0cmFja2VkLCBjYWxsIHRoZSBnaXZlbiB0cmFja0RlY3J5cHRpb25GYWlsdXJlXG4gICAgICogZnVuY3Rpb24gd2l0aCB0aGUgZmFpbHVyZXMgdGhhdCBzaG91bGQgYmUgdHJhY2tlZC5cbiAgICAgKi9cbiAgICBwcml2YXRlIHJlcG9ydEZhaWx1cmUoZmFpbHVyZTogRGVjcnlwdGlvbkZhaWx1cmUpOiB2b2lkIHtcbiAgICAgICAgY29uc3QgZXJyb3JDb2RlID0gZmFpbHVyZS5lcnJvckNvZGU7XG4gICAgICAgIGNvbnN0IHRyYWNrZWRFcnJvckNvZGUgPSB0aGlzLmVycm9yQ29kZU1hcEZuKGVycm9yQ29kZSk7XG4gICAgICAgIGNvbnN0IHByb3BlcnRpZXM6IEVycm9yUHJvcGVydGllcyA9IHtcbiAgICAgICAgICAgIHRpbWVUb0RlY3J5cHRNaWxsaXM6IGZhaWx1cmUudGltZVRvRGVjcnlwdE1pbGxpcyA/PyAtMSxcbiAgICAgICAgICAgIHdhc1Zpc2libGVUb1VzZXI6IGZhaWx1cmUud2FzVmlzaWJsZVRvVXNlcixcbiAgICAgICAgfTtcbiAgICAgICAgaWYgKGZhaWx1cmUuaXNGZWRlcmF0ZWQgIT09IHVuZGVmaW5lZCkge1xuICAgICAgICAgICAgcHJvcGVydGllcy5pc0ZlZGVyYXRlZCA9IGZhaWx1cmUuaXNGZWRlcmF0ZWQ7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKGZhaWx1cmUudXNlclRydXN0c093bklkZW50aXR5ICE9PSB1bmRlZmluZWQpIHtcbiAgICAgICAgICAgIHByb3BlcnRpZXMudXNlclRydXN0c093bklkZW50aXR5ID0gZmFpbHVyZS51c2VyVHJ1c3RzT3duSWRlbnRpdHk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHRoaXMuYmFzZVByb3BlcnRpZXMpIHtcbiAgICAgICAgICAgIE9iamVjdC5hc3NpZ24ocHJvcGVydGllcywgdGhpcy5iYXNlUHJvcGVydGllcyk7XG4gICAgICAgIH1cbiAgICAgICAgdGhpcy5mbih0cmFja2VkRXJyb3JDb2RlLCBlcnJvckNvZGUsIHByb3BlcnRpZXMpO1xuXG4gICAgICAgIHRoaXMucmVwb3J0ZWRFdmVudHMuYWRkKGZhaWx1cmUuZmFpbGVkRXZlbnRJZCk7XG4gICAgICAgIC8vIG9uY2Ugd2UndmUgYWRkZWQgaXQgdG8gcmVwb3J0ZWRFdmVudHMsIHdlIHdvbid0IGNoZWNrXG4gICAgICAgIC8vIHZpc2libGVFdmVudHMgZm9yIGl0IGFueSBtb3JlXG4gICAgICAgIHRoaXMudmlzaWJsZUV2ZW50cy5kZWxldGUoZmFpbHVyZS5mYWlsZWRFdmVudElkKTtcbiAgICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBUUEsSUFBQUEsYUFBQSxHQUFBQyxPQUFBO0FBQ0EsSUFBQUMsT0FBQSxHQUFBRCxPQUFBO0FBRUEsSUFBQUUsVUFBQSxHQUFBRixPQUFBO0FBRUEsSUFBQUcsaUJBQUEsR0FBQUgsT0FBQTtBQUNBLElBQUFJLE9BQUEsR0FBQUosT0FBQTtBQUE2RCxJQUFBSyx5QkFBQTtBQWQ3RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQU5BLFNBQUFDLFFBQUFDLENBQUEsRUFBQUMsQ0FBQSxRQUFBQyxDQUFBLEdBQUFDLE1BQUEsQ0FBQUMsSUFBQSxDQUFBSixDQUFBLE9BQUFHLE1BQUEsQ0FBQUUscUJBQUEsUUFBQUMsQ0FBQSxHQUFBSCxNQUFBLENBQUFFLHFCQUFBLENBQUFMLENBQUEsR0FBQUMsQ0FBQSxLQUFBSyxDQUFBLEdBQUFBLENBQUEsQ0FBQUMsTUFBQSxXQUFBTixDQUFBLFdBQUFFLE1BQUEsQ0FBQUssd0JBQUEsQ0FBQVIsQ0FBQSxFQUFBQyxDQUFBLEVBQUFRLFVBQUEsT0FBQVAsQ0FBQSxDQUFBUSxJQUFBLENBQUFDLEtBQUEsQ0FBQVQsQ0FBQSxFQUFBSSxDQUFBLFlBQUFKLENBQUE7QUFBQSxTQUFBVSxjQUFBWixDQUFBLGFBQUFDLENBQUEsTUFBQUEsQ0FBQSxHQUFBWSxTQUFBLENBQUFDLE1BQUEsRUFBQWIsQ0FBQSxVQUFBQyxDQUFBLFdBQUFXLFNBQUEsQ0FBQVosQ0FBQSxJQUFBWSxTQUFBLENBQUFaLENBQUEsUUFBQUEsQ0FBQSxPQUFBRixPQUFBLENBQUFJLE1BQUEsQ0FBQUQsQ0FBQSxPQUFBYSxPQUFBLFdBQUFkLENBQUEsUUFBQWUsZ0JBQUEsQ0FBQUMsT0FBQSxFQUFBakIsQ0FBQSxFQUFBQyxDQUFBLEVBQUFD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