UNPKG

matrix-react-sdk

Version:
386 lines (369 loc) 57.7 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.collectBugReport = collectBugReport; exports.collectLabels = collectLabels; exports.collectSettings = collectSettings; exports.default = sendBugReport; exports.downloadBugReport = downloadBugReport; exports.submitFeedback = submitFeedback; var _logger = require("matrix-js-sdk/src/logger"); var _matrix = require("matrix-js-sdk/src/matrix"); var _MatrixClientPeg = require("../MatrixClientPeg"); var _PlatformPeg = _interopRequireDefault(require("../PlatformPeg")); var _languageHandler = require("../languageHandler"); var rageshake = _interopRequireWildcard(require("./rageshake")); var _SettingsStore = _interopRequireDefault(require("../settings/SettingsStore")); var _SdkConfig = _interopRequireDefault(require("../SdkConfig")); var _ServerInfo = require("../components/views/dialogs/devtools/ServerInfo"); function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); } function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; } /* Copyright 2024 New Vector Ltd. Copyright 2019 The Matrix.org Foundation C.I.C. Copyright 2018 New Vector Ltd Copyright 2017 OpenMarket Ltd SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ /** * Exported only for testing. * @internal public for test */ async function collectBugReport(opts = {}, gzipLogs = true) { const progressCallback = opts.progressCallback; progressCallback?.((0, _languageHandler._t)("bug_reporting|collecting_information")); _logger.logger.log("Sending bug report."); const body = new FormData(); await collectBaseInformation(body, opts); const client = _MatrixClientPeg.MatrixClientPeg.get(); if (client) { await collectClientInfo(client, body); } collectLabels(client, opts, body); collectSettings(body); await collectStorageStatInfo(body); collectMissingFeatures(body); if (opts.sendLogs) { await collectLogs(body, gzipLogs, progressCallback); } return body; } async function getAppVersion() { try { return await _PlatformPeg.default.get()?.getAppVersion(); } catch (err) { // this happens if no version is set i.e. in dev } } function matchesMediaQuery(query) { try { return String(window.matchMedia(query).matches); } catch (err) { // if not supported in browser } return "UNKNOWN"; } /** * Collects base information about the user and the app to add to the report. */ async function collectBaseInformation(body, opts) { const version = await getAppVersion(); const userAgent = window.navigator?.userAgent ?? "UNKNOWN"; const installedPWA = matchesMediaQuery("(display-mode: standalone)"); const touchInput = matchesMediaQuery("(pointer: coarse)"); body.append("text", opts.userText || "User did not supply any additional text."); body.append("app", opts.customApp || "element-web"); body.append("version", version ?? "UNKNOWN"); body.append("user_agent", userAgent); body.append("installed_pwa", installedPWA); body.append("touch_input", touchInput); if (opts.customFields) { for (const key in opts.customFields) { body.append(key, opts.customFields[key]); } } } /** * Collects client and crypto related info. */ async function collectClientInfo(client, body) { body.append("user_id", client.credentials.userId); body.append("device_id", client.deviceId); const cryptoApi = client.getCrypto(); if (cryptoApi) { await collectCryptoInfo(cryptoApi, body); await collectRecoveryInfo(client, cryptoApi, body); } await collectSynapseSpecific(client, body); } /** * Collects information about the home server. */ async function collectSynapseSpecific(client, body) { try { // XXX: This is synapse-specific but better than nothing until MSC support for a server version endpoint const data = await client.http.request(_matrix.Method.Get, "/server_version", undefined, undefined, { prefix: "/_synapse/admin/v1" }); Object.keys(data).forEach(key => { body.append(`matrix_hs_${key}`, data[key]); }); } catch { try { // XXX: This relies on the federation listener being delegated via well-known // or at the same place as the client server endpoint const data = await (0, _ServerInfo.getServerVersionFromFederationApi)(client); body.append("matrix_hs_name", data.server.name); body.append("matrix_hs_version", data.server.version); } catch { try { // If that fails we'll hit any endpoint and look at the server response header const res = await window.fetch(client.http.getUrl("/login"), { method: "GET", mode: "cors" }); if (res.headers.has("server")) { body.append("matrix_hs_server", res.headers.get("server")); } } catch { // Could not determine server version } } } } /** * Collects crypto related information. */ async function collectCryptoInfo(cryptoApi, body) { body.append("crypto_version", cryptoApi.getVersion()); const ownDeviceKeys = await cryptoApi.getOwnDeviceKeys(); const keys = [`curve25519:${ownDeviceKeys.curve25519}`, `ed25519:${ownDeviceKeys.ed25519}`]; body.append("device_keys", keys.join(", ")); // add cross-signing status information const crossSigningStatus = await cryptoApi.getCrossSigningStatus(); body.append("cross_signing_ready", String(await cryptoApi.isCrossSigningReady())); body.append("cross_signing_key", (await cryptoApi.getCrossSigningKeyId()) ?? "n/a"); body.append("cross_signing_privkey_in_secret_storage", String(crossSigningStatus.privateKeysInSecretStorage)); body.append("cross_signing_master_privkey_cached", String(crossSigningStatus.privateKeysCachedLocally.masterKey)); body.append("cross_signing_self_signing_privkey_cached", String(crossSigningStatus.privateKeysCachedLocally.selfSigningKey)); body.append("cross_signing_user_signing_privkey_cached", String(crossSigningStatus.privateKeysCachedLocally.userSigningKey)); } /** * Collects information about secret storage and backup. */ async function collectRecoveryInfo(client, cryptoApi, body) { const secretStorage = client.secretStorage; body.append("secret_storage_ready", String(await cryptoApi.isSecretStorageReady())); body.append("secret_storage_key_in_account", String(await secretStorage.hasKey())); body.append("session_backup_key_in_secret_storage", String(!!(await client.isKeyBackupKeyStored()))); const sessionBackupKeyFromCache = await cryptoApi.getSessionBackupPrivateKey(); body.append("session_backup_key_cached", String(!!sessionBackupKeyFromCache)); body.append("session_backup_key_well_formed", String(sessionBackupKeyFromCache instanceof Uint8Array)); } /** * Collects labels to add to the report. */ function collectLabels(client, opts, body) { if (client?.getCrypto()?.getVersion()?.startsWith(`Rust SDK`)) { body.append("label", "A-Element-R"); } if (opts.labels) { for (const label of opts.labels) { body.append("label", label); } } } /** * Collects some settings (lab flags and more) to add to the report. */ function collectSettings(body) { // add labs options const enabledLabs = _SettingsStore.default.getFeatureSettingNames().filter(f => _SettingsStore.default.getValue(f)); if (enabledLabs.length) { body.append("enabled_labs", enabledLabs.join(", ")); } // if low bandwidth mode is enabled, say so over rageshake, it causes many issues if (_SettingsStore.default.getValue("lowBandwidth")) { body.append("lowBandwidth", "enabled"); } body.append("mx_local_settings", localStorage.getItem("mx_local_settings")); } /** * Collects storage statistics to add to the report. */ async function collectStorageStatInfo(body) { // add storage persistence/quota information if (navigator.storage && navigator.storage.persisted) { try { body.append("storageManager_persisted", String(await navigator.storage.persisted())); } catch (e) {} } else if (document.hasStorageAccess) { // Safari try { body.append("storageManager_persisted", String(await document.hasStorageAccess())); } catch (e) {} } if (navigator.storage && navigator.storage.estimate) { try { const estimate = await navigator.storage.estimate(); body.append("storageManager_quota", String(estimate.quota)); body.append("storageManager_usage", String(estimate.usage)); if (estimate.usageDetails) { Object.keys(estimate.usageDetails).forEach(k => { body.append(`storageManager_usage_${k}`, String(estimate.usageDetails[k])); }); } } catch (e) {} } } function collectMissingFeatures(body) { if (window.Modernizr) { const missingFeatures = Object.keys(window.Modernizr).filter(key => window.Modernizr[key] === false); if (missingFeatures.length > 0) { body.append("modernizr_missing_features", missingFeatures.join(", ")); } } } /** * Collects logs to add to the report if enabled. */ async function collectLogs(body, gzipLogs, progressCallback) { let pako; if (gzipLogs) { pako = await Promise.resolve().then(() => _interopRequireWildcard(require("pako"))); } progressCallback?.((0, _languageHandler._t)("bug_reporting|collecting_logs")); const logs = await rageshake.getLogsForReport(); for (const entry of logs) { // encode as UTF-8 let buf = new TextEncoder().encode(entry.lines); // compress if (gzipLogs) { buf = pako.gzip(buf); } body.append("compressed-log", new Blob([buf]), entry.id); } } /** * Send a bug report. * * @param {string} bugReportEndpoint HTTP url to send the report to * * @param {object} opts optional dictionary of options * * @param {string} opts.userText Any additional user input. * * @param {boolean} opts.sendLogs True to send logs * * @param {function(string)} opts.progressCallback Callback to call with progress updates * * @return {Promise<string>} URL returned by the rageshake server */ async function sendBugReport(bugReportEndpoint, opts = {}) { if (!bugReportEndpoint) { throw new Error("No bug report endpoint has been set."); } const progressCallback = opts.progressCallback || (() => {}); const body = await collectBugReport(opts); progressCallback((0, _languageHandler._t)("bug_reporting|uploading_logs")); return submitReport(bugReportEndpoint, body, progressCallback); } /** * Downloads the files from a bug report. This is the same as sendBugReport, * but instead causes the browser to download the files locally. * * @param {object} opts optional dictionary of options * * @param {string} opts.userText Any additional user input. * * @param {boolean} opts.sendLogs True to send logs * * @param {function(string)} opts.progressCallback Callback to call with progress updates * * @return {Promise} Resolved when the bug report is downloaded (or started). */ async function downloadBugReport(opts = {}) { const Tar = (await Promise.resolve().then(() => _interopRequireWildcard(require("tar-js")))).default; const progressCallback = opts.progressCallback || (() => {}); const body = await collectBugReport(opts, false); progressCallback((0, _languageHandler._t)("bug_reporting|downloading_logs")); let metadata = ""; const tape = new Tar(); let i = 0; for (const [key, value] of body.entries()) { if (key === "compressed-log") { await new Promise(resolve => { const reader = new FileReader(); reader.addEventListener("loadend", ev => { tape.append(`log-${i++}.log`, new TextDecoder().decode(reader.result)); resolve(); }); reader.readAsArrayBuffer(value); }); } else { metadata += `${key} = ${value}\n`; } } tape.append("issue.txt", metadata); // We have to create a new anchor to download if we want a filename. Otherwise we could // just use window.open. const dl = document.createElement("a"); dl.href = `data:application/octet-stream;base64,${btoa(uint8ToString(tape.out))}`; dl.download = "rageshake.tar"; document.body.appendChild(dl); dl.click(); document.body.removeChild(dl); } // Source: https://github.com/beatgammit/tar-js/blob/master/examples/main.js function uint8ToString(buf) { let out = ""; for (let i = 0; i < buf.length; i += 1) { out += String.fromCharCode(buf[i]); } return out; } async function submitFeedback(label, comment, canContact = false, extraData = {}) { let version; try { version = await _PlatformPeg.default.get()?.getAppVersion(); } catch (err) {} // PlatformPeg already logs this. const body = new FormData(); if (label) body.append("label", label); body.append("text", comment); body.append("can_contact", canContact ? "yes" : "no"); body.append("app", "element-web"); body.append("version", version || "UNKNOWN"); body.append("platform", _PlatformPeg.default.get()?.getHumanReadableName() ?? "n/a"); body.append("user_id", _MatrixClientPeg.MatrixClientPeg.get()?.getUserId() ?? "n/a"); for (const k in extraData) { body.append(k, JSON.stringify(extraData[k])); } const bugReportEndpointUrl = _SdkConfig.default.get().bug_report_endpoint_url; if (bugReportEndpointUrl) { await submitReport(bugReportEndpointUrl, body, () => {}); } } function submitReport(endpoint, body, progressCallback) { return new Promise((resolve, reject) => { const req = new XMLHttpRequest(); req.open("POST", endpoint); req.responseType = "json"; req.timeout = 5 * 60 * 1000; req.onreadystatechange = function () { if (req.readyState === XMLHttpRequest.LOADING) { progressCallback((0, _languageHandler._t)("bug_reporting|waiting_for_server")); } else if (req.readyState === XMLHttpRequest.DONE) { // on done if (req.status < 200 || req.status >= 400) { reject(new Error(`HTTP ${req.status}`)); return; } resolve(req.response.report_url || ""); } }; req.send(body); }); } //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbG9nZ2VyIiwicmVxdWlyZSIsIl9tYXRyaXgiLCJfTWF0cml4Q2xpZW50UGVnIiwiX1BsYXRmb3JtUGVnIiwiX2ludGVyb3BSZXF1aXJlRGVmYXVsdCIsIl9sYW5ndWFnZUhhbmRsZXIiLCJyYWdlc2hha2UiLCJfaW50ZXJvcFJlcXVpcmVXaWxkY2FyZCIsIl9TZXR0aW5nc1N0b3JlIiwiX1Nka0NvbmZpZyIsIl9TZXJ2ZXJJbmZvIiwiX2dldFJlcXVpcmVXaWxkY2FyZENhY2hlIiwiZSIsIldlYWtNYXAiLCJyIiwidCIsIl9fZXNNb2R1bGUiLCJkZWZhdWx0IiwiaGFzIiwiZ2V0IiwibiIsIl9fcHJvdG9fXyIsImEiLCJPYmplY3QiLCJkZWZpbmVQcm9wZXJ0eSIsImdldE93blByb3BlcnR5RGVzY3JpcHRvciIsInUiLCJoYXNPd25Qcm9wZXJ0eSIsImNhbGwiLCJpIiwic2V0IiwiY29sbGVjdEJ1Z1JlcG9ydCIsIm9wdHMiLCJnemlwTG9ncyIsInByb2dyZXNzQ2FsbGJhY2siLCJfdCIsImxvZ2dlciIsImxvZyIsImJvZHkiLCJGb3JtRGF0YSIsImNvbGxlY3RCYXNlSW5mb3JtYXRpb24iLCJjbGllbnQiLCJNYXRyaXhDbGllbnRQZWciLCJjb2xsZWN0Q2xpZW50SW5mbyIsImNvbGxlY3RMYWJlbHMiLCJjb2xsZWN0U2V0dGluZ3MiLCJjb2xsZWN0U3RvcmFnZVN0YXRJbmZvIiwiY29sbGVjdE1pc3NpbmdGZWF0dXJlcyIsInNlbmRMb2dzIiwiY29sbGVjdExvZ3MiLCJnZXRBcHBWZXJzaW9uIiwiUGxhdGZvcm1QZWciLCJlcnIiLCJtYXRjaGVzTWVkaWFRdWVyeSIsInF1ZXJ5IiwiU3RyaW5nIiwid2luZG93IiwibWF0Y2hNZWRpYSIsIm1hdGNoZXMiLCJ2ZXJzaW9uIiwidXNlckFnZW50IiwibmF2aWdhdG9yIiwiaW5zdGFsbGVkUFdBIiwidG91Y2hJbnB1dCIsImFwcGVuZCIsInVzZXJUZXh0IiwiY3VzdG9tQXBwIiwiY3VzdG9tRmllbGRzIiwia2V5IiwiY3JlZGVudGlhbHMiLCJ1c2VySWQiLCJkZXZpY2VJZCIsImNyeXB0b0FwaSIsImdldENyeXB0byIsImNvbGxlY3RDcnlwdG9JbmZvIiwiY29sbGVjdFJlY292ZXJ5SW5mbyIsImNvbGxlY3RTeW5hcHNlU3BlY2lmaWMiLCJkYXRhIiwiaHR0cCIsInJlcXVlc3QiLCJNZXRob2QiLCJHZXQiLCJ1bmRlZmluZWQiLCJwcmVmaXgiLCJrZXlzIiwiZm9yRWFjaCIsImdldFNlcnZlclZlcnNpb25Gcm9tRmVkZXJhdGlvbkFwaSIsInNlcnZlciIsIm5hbWUiLCJyZXMiLCJmZXRjaCIsImdldFVybCIsIm1ldGhvZCIsIm1vZGUiLCJoZWFkZXJzIiwiZ2V0VmVyc2lvbiIsIm93bkRldmljZUtleXMiLCJnZXRPd25EZXZpY2VLZXlzIiwiY3VydmUyNTUxOSIsImVkMjU1MTkiLCJqb2luIiwiY3Jvc3NTaWduaW5nU3RhdHVzIiwiZ2V0Q3Jvc3NTaWduaW5nU3RhdHVzIiwiaXNDcm9zc1NpZ25pbmdSZWFkeSIsImdldENyb3NzU2lnbmluZ0tleUlkIiwicHJpdmF0ZUtleXNJblNlY3JldFN0b3JhZ2UiLCJwcml2YXRlS2V5c0NhY2hlZExvY2FsbHkiLCJtYXN0ZXJLZXkiLCJzZWxmU2lnbmluZ0tleSIsInVzZXJTaWduaW5nS2V5Iiwic2VjcmV0U3RvcmFnZSIsImlzU2VjcmV0U3RvcmFnZVJlYWR5IiwiaGFzS2V5IiwiaXNLZXlCYWNrdXBLZXlTdG9yZWQiLCJzZXNzaW9uQmFja3VwS2V5RnJvbUNhY2hlIiwiZ2V0U2Vzc2lvbkJhY2t1cFByaXZhdGVLZXkiLCJVaW50OEFycmF5Iiwic3RhcnRzV2l0aCIsImxhYmVscyIsImxhYmVsIiwiZW5hYmxlZExhYnMiLCJTZXR0aW5nc1N0b3JlIiwiZ2V0RmVhdHVyZVNldHRpbmdOYW1lcyIsImZpbHRlciIsImYiLCJnZXRWYWx1ZSIsImxlbmd0aCIsImxvY2FsU3RvcmFnZSIsImdldEl0ZW0iLCJzdG9yYWdlIiwicGVyc2lzdGVkIiwiZG9jdW1lbnQiLCJoYXNTdG9yYWdlQWNjZXNzIiwiZXN0aW1hdGUiLCJxdW90YSIsInVzYWdlIiwidXNhZ2VEZXRhaWxzIiwiayIsIk1vZGVybml6ciIsIm1pc3NpbmdGZWF0dXJlcyIsInBha28iLCJQcm9taXNlIiwicmVzb2x2ZSIsInRoZW4iLCJsb2dzIiwiZ2V0TG9nc0ZvclJlcG9ydCIsImVudHJ5IiwiYnVmIiwiVGV4dEVuY29kZXIiLCJlbmNvZGUiLCJsaW5lcyIsImd6aXAiLCJCbG9iIiwiaWQiLCJzZW5kQnVnUmVwb3J0IiwiYnVnUmVwb3J0RW5kcG9pbnQiLCJFcnJvciIsInN1Ym1pdFJlcG9ydCIsImRvd25sb2FkQnVnUmVwb3J0IiwiVGFyIiwibWV0YWRhdGEiLCJ0YXBlIiwidmFsdWUiLCJlbnRyaWVzIiwicmVhZGVyIiwiRmlsZVJlYWRlciIsImFkZEV2ZW50TGlzdGVuZXIiLCJldiIsIlRleHREZWNvZGVyIiwiZGVjb2RlIiwicmVzdWx0IiwicmVhZEFzQXJyYXlCdWZmZXIiLCJkbCIsImNyZWF0ZUVsZW1lbnQiLCJocmVmIiwiYnRvYSIsInVpbnQ4VG9TdHJpbmciLCJvdXQiLCJkb3dubG9hZCIsImFwcGVuZENoaWxkIiwiY2xpY2siLCJyZW1vdmVDaGlsZCIsImZyb21DaGFyQ29kZSIsInN1Ym1pdEZlZWRiYWNrIiwiY29tbWVudCIsImNhbkNvbnRhY3QiLCJleHRyYURhdGEiLCJnZXRIdW1hblJlYWRhYmxlTmFtZSIsImdldFVzZXJJZCIsIkpTT04iLCJzdHJpbmdpZnkiLCJidWdSZXBvcnRFbmRwb2ludFVybCIsIlNka0NvbmZpZyIsImJ1Z19yZXBvcnRfZW5kcG9pbnRfdXJsIiwiZW5kcG9pbnQiLCJyZWplY3QiLCJyZXEiLCJYTUxIdHRwUmVxdWVzdCIsIm9wZW4iLCJyZXNwb25zZVR5cGUiLCJ0aW1lb3V0Iiwib25yZWFkeXN0YXRlY2hhbmdlIiwicmVhZHlTdGF0ZSIsIkxPQURJTkciLCJET05FIiwic3RhdHVzIiwicmVzcG9uc2UiLCJyZXBvcnRfdXJsIiwic2VuZCJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9yYWdlc2hha2Uvc3VibWl0LXJhZ2VzaGFrZS50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKlxuQ29weXJpZ2h0IDIwMjQgTmV3IFZlY3RvciBMdGQuXG5Db3B5cmlnaHQgMjAxOSBUaGUgTWF0cml4Lm9yZyBGb3VuZGF0aW9uIEMuSS5DLlxuQ29weXJpZ2h0IDIwMTggTmV3IFZlY3RvciBMdGRcbkNvcHlyaWdodCAyMDE3IE9wZW5NYXJrZXQgTHRkXG5cblNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBR1BMLTMuMC1vbmx5IE9SIEdQTC0zLjAtb25seVxuUGxlYXNlIHNlZSBMSUNFTlNFIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IHJvb3QgZm9yIGZ1bGwgZGV0YWlscy5cbiovXG5cbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy9sb2dnZXJcIjtcbmltcG9ydCB7IE1ldGhvZCwgTWF0cml4Q2xpZW50LCBDcnlwdG8gfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvbWF0cml4XCI7XG5cbmltcG9ydCB0eXBlICogYXMgUGFrbyBmcm9tIFwicGFrb1wiO1xuaW1wb3J0IHsgTWF0cml4Q2xpZW50UGVnIH0gZnJvbSBcIi4uL01hdHJpeENsaWVudFBlZ1wiO1xuaW1wb3J0IFBsYXRmb3JtUGVnIGZyb20gXCIuLi9QbGF0Zm9ybVBlZ1wiO1xuaW1wb3J0IHsgX3QgfSBmcm9tIFwiLi4vbGFuZ3VhZ2VIYW5kbGVyXCI7XG5pbXBvcnQgKiBhcyByYWdlc2hha2UgZnJvbSBcIi4vcmFnZXNoYWtlXCI7XG5pbXBvcnQgU2V0dGluZ3NTdG9yZSBmcm9tIFwiLi4vc2V0dGluZ3MvU2V0dGluZ3NTdG9yZVwiO1xuaW1wb3J0IFNka0NvbmZpZyBmcm9tIFwiLi4vU2RrQ29uZmlnXCI7XG5pbXBvcnQgeyBnZXRTZXJ2ZXJWZXJzaW9uRnJvbUZlZGVyYXRpb25BcGkgfSBmcm9tIFwiLi4vY29tcG9uZW50cy92aWV3cy9kaWFsb2dzL2RldnRvb2xzL1NlcnZlckluZm9cIjtcblxuaW50ZXJmYWNlIElPcHRzIHtcbiAgICBsYWJlbHM/OiBzdHJpbmdbXTtcbiAgICB1c2VyVGV4dD86IHN0cmluZztcbiAgICBzZW5kTG9ncz86IGJvb2xlYW47XG4gICAgcHJvZ3Jlc3NDYWxsYmFjaz86IChzOiBzdHJpbmcpID0+IHZvaWQ7XG4gICAgY3VzdG9tQXBwPzogc3RyaW5nO1xuICAgIGN1c3RvbUZpZWxkcz86IFJlY29yZDxzdHJpbmcsIHN0cmluZz47XG59XG5cbi8qKlxuICogRXhwb3J0ZWQgb25seSBmb3IgdGVzdGluZy5cbiAqIEBpbnRlcm5hbCBwdWJsaWMgZm9yIHRlc3RcbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGNvbGxlY3RCdWdSZXBvcnQob3B0czogSU9wdHMgPSB7fSwgZ3ppcExvZ3MgPSB0cnVlKTogUHJvbWlzZTxGb3JtRGF0YT4ge1xuICAgIGNvbnN0IHByb2dyZXNzQ2FsbGJhY2sgPSBvcHRzLnByb2dyZXNzQ2FsbGJhY2s7XG5cbiAgICBwcm9ncmVzc0NhbGxiYWNrPy4oX3QoXCJidWdfcmVwb3J0aW5nfGNvbGxlY3RpbmdfaW5mb3JtYXRpb25cIikpO1xuXG4gICAgbG9nZ2VyLmxvZyhcIlNlbmRpbmcgYnVnIHJlcG9ydC5cIik7XG5cbiAgICBjb25zdCBib2R5ID0gbmV3IEZvcm1EYXRhKCk7XG5cbiAgICBhd2FpdCBjb2xsZWN0QmFzZUluZm9ybWF0aW9uKGJvZHksIG9wdHMpO1xuXG4gICAgY29uc3QgY2xpZW50ID0gTWF0cml4Q2xpZW50UGVnLmdldCgpO1xuXG4gICAgaWYgKGNsaWVudCkge1xuICAgICAgICBhd2FpdCBjb2xsZWN0Q2xpZW50SW5mbyhjbGllbnQsIGJvZHkpO1xuICAgIH1cblxuICAgIGNvbGxlY3RMYWJlbHMoY2xpZW50LCBvcHRzLCBib2R5KTtcblxuICAgIGNvbGxlY3RTZXR0aW5ncyhib2R5KTtcblxuICAgIGF3YWl0IGNvbGxlY3RTdG9yYWdlU3RhdEluZm8oYm9keSk7XG5cbiAgICBjb2xsZWN0TWlzc2luZ0ZlYXR1cmVzKGJvZHkpO1xuXG4gICAgaWYgKG9wdHMuc2VuZExvZ3MpIHtcbiAgICAgICAgYXdhaXQgY29sbGVjdExvZ3MoYm9keSwgZ3ppcExvZ3MsIHByb2dyZXNzQ2FsbGJhY2spO1xuICAgIH1cblxuICAgIHJldHVybiBib2R5O1xufVxuXG5hc3luYyBmdW5jdGlvbiBnZXRBcHBWZXJzaW9uKCk6IFByb21pc2U8c3RyaW5nIHwgdW5kZWZpbmVkPiB7XG4gICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIGF3YWl0IFBsYXRmb3JtUGVnLmdldCgpPy5nZXRBcHBWZXJzaW9uKCk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIC8vIHRoaXMgaGFwcGVucyBpZiBubyB2ZXJzaW9uIGlzIHNldCBpLmUuIGluIGRldlxuICAgIH1cbn1cblxuZnVuY3Rpb24gbWF0Y2hlc01lZGlhUXVlcnkocXVlcnk6IHN0cmluZyk6IHN0cmluZyB7XG4gICAgdHJ5IHtcbiAgICAgICAgcmV0dXJuIFN0cmluZyh3aW5kb3cubWF0Y2hNZWRpYShxdWVyeSkubWF0Y2hlcyk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgIC8vIGlmIG5vdCBzdXBwb3J0ZWQgaW4gYnJvd3NlclxuICAgIH1cbiAgICByZXR1cm4gXCJVTktOT1dOXCI7XG59XG5cbi8qKlxuICogQ29sbGVjdHMgYmFzZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgdXNlciBhbmQgdGhlIGFwcCB0byBhZGQgdG8gdGhlIHJlcG9ydC5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gY29sbGVjdEJhc2VJbmZvcm1hdGlvbihib2R5OiBGb3JtRGF0YSwgb3B0czogSU9wdHMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCB2ZXJzaW9uID0gYXdhaXQgZ2V0QXBwVmVyc2lvbigpO1xuXG4gICAgY29uc3QgdXNlckFnZW50ID0gd2luZG93Lm5hdmlnYXRvcj8udXNlckFnZW50ID8/IFwiVU5LTk9XTlwiO1xuXG4gICAgY29uc3QgaW5zdGFsbGVkUFdBID0gbWF0Y2hlc01lZGlhUXVlcnkoXCIoZGlzcGxheS1tb2RlOiBzdGFuZGFsb25lKVwiKTtcbiAgICBjb25zdCB0b3VjaElucHV0ID0gbWF0Y2hlc01lZGlhUXVlcnkoXCIocG9pbnRlcjogY29hcnNlKVwiKTtcblxuICAgIGJvZHkuYXBwZW5kKFwidGV4dFwiLCBvcHRzLnVzZXJUZXh0IHx8IFwiVXNlciBkaWQgbm90IHN1cHBseSBhbnkgYWRkaXRpb25hbCB0ZXh0LlwiKTtcbiAgICBib2R5LmFwcGVuZChcImFwcFwiLCBvcHRzLmN1c3RvbUFwcCB8fCBcImVsZW1lbnQtd2ViXCIpO1xuICAgIGJvZHkuYXBwZW5kKFwidmVyc2lvblwiLCB2ZXJzaW9uID8/IFwiVU5LTk9XTlwiKTtcbiAgICBib2R5LmFwcGVuZChcInVzZXJfYWdlbnRcIiwgdXNlckFnZW50KTtcbiAgICBib2R5LmFwcGVuZChcImluc3RhbGxlZF9wd2FcIiwgaW5zdGFsbGVkUFdBKTtcbiAgICBib2R5LmFwcGVuZChcInRvdWNoX2lucHV0XCIsIHRvdWNoSW5wdXQpO1xuXG4gICAgaWYgKG9wdHMuY3VzdG9tRmllbGRzKSB7XG4gICAgICAgIGZvciAoY29uc3Qga2V5IGluIG9wdHMuY3VzdG9tRmllbGRzKSB7XG4gICAgICAgICAgICBib2R5LmFwcGVuZChrZXksIG9wdHMuY3VzdG9tRmllbGRzW2tleV0pO1xuICAgICAgICB9XG4gICAgfVxufVxuXG4vKipcbiAqIENvbGxlY3RzIGNsaWVudCBhbmQgY3J5cHRvIHJlbGF0ZWQgaW5mby5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gY29sbGVjdENsaWVudEluZm8oY2xpZW50OiBNYXRyaXhDbGllbnQsIGJvZHk6IEZvcm1EYXRhKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYm9keS5hcHBlbmQoXCJ1c2VyX2lkXCIsIGNsaWVudC5jcmVkZW50aWFscy51c2VySWQhKTtcbiAgICBib2R5LmFwcGVuZChcImRldmljZV9pZFwiLCBjbGllbnQuZGV2aWNlSWQhKTtcblxuICAgIGNvbnN0IGNyeXB0b0FwaSA9IGNsaWVudC5nZXRDcnlwdG8oKTtcblxuICAgIGlmIChjcnlwdG9BcGkpIHtcbiAgICAgICAgYXdhaXQgY29sbGVjdENyeXB0b0luZm8oY3J5cHRvQXBpLCBib2R5KTtcbiAgICAgICAgYXdhaXQgY29sbGVjdFJlY292ZXJ5SW5mbyhjbGllbnQsIGNyeXB0b0FwaSwgYm9keSk7XG4gICAgfVxuXG4gICAgYXdhaXQgY29sbGVjdFN5bmFwc2VTcGVjaWZpYyhjbGllbnQsIGJvZHkpO1xufVxuXG4vKipcbiAqIENvbGxlY3RzIGluZm9ybWF0aW9uIGFib3V0IHRoZSBob21lIHNlcnZlci5cbiAqL1xuYXN5bmMgZnVuY3Rpb24gY29sbGVjdFN5bmFwc2VTcGVjaWZpYyhjbGllbnQ6IE1hdHJpeENsaWVudCwgYm9keTogRm9ybURhdGEpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICB0cnkge1xuICAgICAgICAvLyBYWFg6IFRoaXMgaXMgc3luYXBzZS1zcGVjaWZpYyBidXQgYmV0dGVyIHRoYW4gbm90aGluZyB1bnRpbCBNU0Mgc3VwcG9ydCBmb3IgYSBzZXJ2ZXIgdmVyc2lvbiBlbmRwb2ludFxuICAgICAgICBjb25zdCBkYXRhID0gYXdhaXQgY2xpZW50Lmh0dHAucmVxdWVzdDxSZWNvcmQ8c3RyaW5nLCBhbnk+PihcbiAgICAgICAgICAgIE1ldGhvZC5HZXQsXG4gICAgICAgICAgICBcIi9zZXJ2ZXJfdmVyc2lvblwiLFxuICAgICAgICAgICAgdW5kZWZpbmVkLFxuICAgICAgICAgICAgdW5kZWZpbmVkLFxuICAgICAgICAgICAge1xuICAgICAgICAgICAgICAgIHByZWZpeDogXCIvX3N5bmFwc2UvYWRtaW4vdjFcIixcbiAgICAgICAgICAgIH0sXG4gICAgICAgICk7XG4gICAgICAgIE9iamVjdC5rZXlzKGRhdGEpLmZvckVhY2goKGtleSkgPT4ge1xuICAgICAgICAgICAgYm9keS5hcHBlbmQoYG1hdHJpeF9oc18ke2tleX1gLCBkYXRhW2tleV0pO1xuICAgICAgICB9KTtcbiAgICB9IGNhdGNoIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIC8vIFhYWDogVGhpcyByZWxpZXMgb24gdGhlIGZlZGVyYXRpb24gbGlzdGVuZXIgYmVpbmcgZGVsZWdhdGVkIHZpYSB3ZWxsLWtub3duXG4gICAgICAgICAgICAvLyBvciBhdCB0aGUgc2FtZSBwbGFjZSBhcyB0aGUgY2xpZW50IHNlcnZlciBlbmRwb2ludFxuICAgICAgICAgICAgY29uc3QgZGF0YSA9IGF3YWl0IGdldFNlcnZlclZlcnNpb25Gcm9tRmVkZXJhdGlvbkFwaShjbGllbnQpO1xuICAgICAgICAgICAgYm9keS5hcHBlbmQoXCJtYXRyaXhfaHNfbmFtZVwiLCBkYXRhLnNlcnZlci5uYW1lKTtcbiAgICAgICAgICAgIGJvZHkuYXBwZW5kKFwibWF0cml4X2hzX3ZlcnNpb25cIiwgZGF0YS5zZXJ2ZXIudmVyc2lvbik7XG4gICAgICAgIH0gY2F0Y2gge1xuICAgICAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgICAgICAvLyBJZiB0aGF0IGZhaWxzIHdlJ2xsIGhpdCBhbnkgZW5kcG9pbnQgYW5kIGxvb2sgYXQgdGhlIHNlcnZlciByZXNwb25zZSBoZWFkZXJcbiAgICAgICAgICAgICAgICBjb25zdCByZXMgPSBhd2FpdCB3aW5kb3cuZmV0Y2goY2xpZW50Lmh0dHAuZ2V0VXJsKFwiL2xvZ2luXCIpLCB7XG4gICAgICAgICAgICAgICAgICAgIG1ldGhvZDogXCJHRVRcIixcbiAgICAgICAgICAgICAgICAgICAgbW9kZTogXCJjb3JzXCIsXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICAgICAgaWYgKHJlcy5oZWFkZXJzLmhhcyhcInNlcnZlclwiKSkge1xuICAgICAgICAgICAgICAgICAgICBib2R5LmFwcGVuZChcIm1hdHJpeF9oc19zZXJ2ZXJcIiwgcmVzLmhlYWRlcnMuZ2V0KFwic2VydmVyXCIpISk7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfSBjYXRjaCB7XG4gICAgICAgICAgICAgICAgLy8gQ291bGQgbm90IGRldGVybWluZSBzZXJ2ZXIgdmVyc2lvblxuICAgICAgICAgICAgfVxuICAgICAgICB9XG4gICAgfVxufVxuXG4vKipcbiAqIENvbGxlY3RzIGNyeXB0byByZWxhdGVkIGluZm9ybWF0aW9uLlxuICovXG5hc3luYyBmdW5jdGlvbiBjb2xsZWN0Q3J5cHRvSW5mbyhjcnlwdG9BcGk6IENyeXB0by5DcnlwdG9BcGksIGJvZHk6IEZvcm1EYXRhKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgYm9keS5hcHBlbmQoXCJjcnlwdG9fdmVyc2lvblwiLCBjcnlwdG9BcGkuZ2V0VmVyc2lvbigpKTtcblxuICAgIGNvbnN0IG93bkRldmljZUtleXMgPSBhd2FpdCBjcnlwdG9BcGkuZ2V0T3duRGV2aWNlS2V5cygpO1xuICAgIGNvbnN0IGtleXMgPSBbYGN1cnZlMjU1MTk6JHtvd25EZXZpY2VLZXlzLmN1cnZlMjU1MTl9YCwgYGVkMjU1MTk6JHtvd25EZXZpY2VLZXlzLmVkMjU1MTl9YF07XG5cbiAgICBib2R5LmFwcGVuZChcImRldmljZV9rZXlzXCIsIGtleXMuam9pbihcIiwgXCIpKTtcblxuICAgIC8vIGFkZCBjcm9zcy1zaWduaW5nIHN0YXR1cyBpbmZvcm1hdGlvblxuICAgIGNvbnN0IGNyb3NzU2lnbmluZ1N0YXR1cyA9IGF3YWl0IGNyeXB0b0FwaS5nZXRDcm9zc1NpZ25pbmdTdGF0dXMoKTtcblxuICAgIGJvZHkuYXBwZW5kKFwiY3Jvc3Nfc2lnbmluZ19yZWFkeVwiLCBTdHJpbmcoYXdhaXQgY3J5cHRvQXBpLmlzQ3Jvc3NTaWduaW5nUmVhZHkoKSkpO1xuICAgIGJvZHkuYXBwZW5kKFwiY3Jvc3Nfc2lnbmluZ19rZXlcIiwgKGF3YWl0IGNyeXB0b0FwaS5nZXRDcm9zc1NpZ25pbmdLZXlJZCgpKSA/PyBcIm4vYVwiKTtcbiAgICBib2R5LmFwcGVuZChcImNyb3NzX3NpZ25pbmdfcHJpdmtleV9pbl9zZWNyZXRfc3RvcmFnZVwiLCBTdHJpbmcoY3Jvc3NTaWduaW5nU3RhdHVzLnByaXZhdGVLZXlzSW5TZWNyZXRTdG9yYWdlKSk7XG5cbiAgICBib2R5LmFwcGVuZChcImNyb3NzX3NpZ25pbmdfbWFzdGVyX3ByaXZrZXlfY2FjaGVkXCIsIFN0cmluZyhjcm9zc1NpZ25pbmdTdGF0dXMucHJpdmF0ZUtleXNDYWNoZWRMb2NhbGx5Lm1hc3RlcktleSkpO1xuICAgIGJvZHkuYXBwZW5kKFxuICAgICAgICBcImNyb3NzX3NpZ25pbmdfc2VsZl9zaWduaW5nX3ByaXZrZXlfY2FjaGVkXCIsXG4gICAgICAgIFN0cmluZyhjcm9zc1NpZ25pbmdTdGF0dXMucHJpdmF0ZUtleXNDYWNoZWRMb2NhbGx5LnNlbGZTaWduaW5nS2V5KSxcbiAgICApO1xuICAgIGJvZHkuYXBwZW5kKFxuICAgICAgICBcImNyb3NzX3NpZ25pbmdfdXNlcl9zaWduaW5nX3ByaXZrZXlfY2FjaGVkXCIsXG4gICAgICAgIFN0cmluZyhjcm9zc1NpZ25pbmdTdGF0dXMucHJpdmF0ZUtleXNDYWNoZWRMb2NhbGx5LnVzZXJTaWduaW5nS2V5KSxcbiAgICApO1xufVxuXG4vKipcbiAqIENvbGxlY3RzIGluZm9ybWF0aW9uIGFib3V0IHNlY3JldCBzdG9yYWdlIGFuZCBiYWNrdXAuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGNvbGxlY3RSZWNvdmVyeUluZm8oY2xpZW50OiBNYXRyaXhDbGllbnQsIGNyeXB0b0FwaTogQ3J5cHRvLkNyeXB0b0FwaSwgYm9keTogRm9ybURhdGEpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBzZWNyZXRTdG9yYWdlID0gY2xpZW50LnNlY3JldFN0b3JhZ2U7XG4gICAgYm9keS5hcHBlbmQoXCJzZWNyZXRfc3RvcmFnZV9yZWFkeVwiLCBTdHJpbmcoYXdhaXQgY3J5cHRvQXBpLmlzU2VjcmV0U3RvcmFnZVJlYWR5KCkpKTtcbiAgICBib2R5LmFwcGVuZChcInNlY3JldF9zdG9yYWdlX2tleV9pbl9hY2NvdW50XCIsIFN0cmluZyhhd2FpdCBzZWNyZXRTdG9yYWdlLmhhc0tleSgpKSk7XG5cbiAgICBib2R5LmFwcGVuZChcInNlc3Npb25fYmFja3VwX2tleV9pbl9zZWNyZXRfc3RvcmFnZVwiLCBTdHJpbmcoISEoYXdhaXQgY2xpZW50LmlzS2V5QmFja3VwS2V5U3RvcmVkKCkpKSk7XG4gICAgY29uc3Qgc2Vzc2lvbkJhY2t1cEtleUZyb21DYWNoZSA9IGF3YWl0IGNyeXB0b0FwaS5nZXRTZXNzaW9uQmFja3VwUHJpdmF0ZUtleSgpO1xuICAgIGJvZHkuYXBwZW5kKFwic2Vzc2lvbl9iYWNrdXBfa2V5X2NhY2hlZFwiLCBTdHJpbmcoISFzZXNzaW9uQmFja3VwS2V5RnJvbUNhY2hlKSk7XG4gICAgYm9keS5hcHBlbmQoXCJzZXNzaW9uX2JhY2t1cF9rZXlfd2VsbF9mb3JtZWRcIiwgU3RyaW5nKHNlc3Npb25CYWNrdXBLZXlGcm9tQ2FjaGUgaW5zdGFuY2VvZiBVaW50OEFycmF5KSk7XG59XG5cbi8qKlxuICogQ29sbGVjdHMgbGFiZWxzIHRvIGFkZCB0byB0aGUgcmVwb3J0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY29sbGVjdExhYmVscyhjbGllbnQ6IE1hdHJpeENsaWVudCB8IG51bGwsIG9wdHM6IElPcHRzLCBib2R5OiBGb3JtRGF0YSk6IHZvaWQge1xuICAgIGlmIChjbGllbnQ/LmdldENyeXB0bygpPy5nZXRWZXJzaW9uKCk/LnN0YXJ0c1dpdGgoYFJ1c3QgU0RLYCkpIHtcbiAgICAgICAgYm9keS5hcHBlbmQoXCJsYWJlbFwiLCBcIkEtRWxlbWVudC1SXCIpO1xuICAgIH1cblxuICAgIGlmIChvcHRzLmxhYmVscykge1xuICAgICAgICBmb3IgKGNvbnN0IGxhYmVsIG9mIG9wdHMubGFiZWxzKSB7XG4gICAgICAgICAgICBib2R5LmFwcGVuZChcImxhYmVsXCIsIGxhYmVsKTtcbiAgICAgICAgfVxuICAgIH1cbn1cblxuLyoqXG4gKiBDb2xsZWN0cyBzb21lIHNldHRpbmdzIChsYWIgZmxhZ3MgYW5kIG1vcmUpIHRvIGFkZCB0byB0aGUgcmVwb3J0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gY29sbGVjdFNldHRpbmdzKGJvZHk6IEZvcm1EYXRhKTogdm9pZCB7XG4gICAgLy8gYWRkIGxhYnMgb3B0aW9uc1xuICAgIGNvbnN0IGVuYWJsZWRMYWJzID0gU2V0dGluZ3NTdG9yZS5nZXRGZWF0dXJlU2V0dGluZ05hbWVzKCkuZmlsdGVyKChmKSA9PiBTZXR0aW5nc1N0b3JlLmdldFZhbHVlKGYpKTtcbiAgICBpZiAoZW5hYmxlZExhYnMubGVuZ3RoKSB7XG4gICAgICAgIGJvZHkuYXBwZW5kKFwiZW5hYmxlZF9sYWJzXCIsIGVuYWJsZWRMYWJzLmpvaW4oXCIsIFwiKSk7XG4gICAgfVxuICAgIC8vIGlmIGxvdyBiYW5kd2lkdGggbW9kZSBpcyBlbmFibGVkLCBzYXkgc28gb3ZlciByYWdlc2hha2UsIGl0IGNhdXNlcyBtYW55IGlzc3Vlc1xuICAgIGlmIChTZXR0aW5nc1N0b3JlLmdldFZhbHVlKFwibG93QmFuZHdpZHRoXCIpKSB7XG4gICAgICAgIGJvZHkuYXBwZW5kKFwibG93QmFuZHdpZHRoXCIsIFwiZW5hYmxlZFwiKTtcbiAgICB9XG5cbiAgICBib2R5LmFwcGVuZChcIm14X2xvY2FsX3NldHRpbmdzXCIsIGxvY2FsU3RvcmFnZS5nZXRJdGVtKFwibXhfbG9jYWxfc2V0dGluZ3NcIikhKTtcbn1cblxuLyoqXG4gKiBDb2xsZWN0cyBzdG9yYWdlIHN0YXRpc3RpY3MgdG8gYWRkIHRvIHRoZSByZXBvcnQuXG4gKi9cbmFzeW5jIGZ1bmN0aW9uIGNvbGxlY3RTdG9yYWdlU3RhdEluZm8oYm9keTogRm9ybURhdGEpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICAvLyBhZGQgc3RvcmFnZSBwZXJzaXN0ZW5jZS9xdW90YSBpbmZvcm1hdGlvblxuICAgIGlmIChuYXZpZ2F0b3Iuc3RvcmFnZSAmJiBuYXZpZ2F0b3Iuc3RvcmFnZS5wZXJzaXN0ZWQpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGJvZHkuYXBwZW5kKFwic3RvcmFnZU1hbmFnZXJfcGVyc2lzdGVkXCIsIFN0cmluZyhhd2FpdCBuYXZpZ2F0b3Iuc3RvcmFnZS5wZXJzaXN0ZWQoKSkpO1xuICAgICAgICB9IGNhdGNoIChlKSB7fVxuICAgIH0gZWxzZSBpZiAoZG9jdW1lbnQuaGFzU3RvcmFnZUFjY2Vzcykge1xuICAgICAgICAvLyBTYWZhcmlcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICAgIGJvZHkuYXBwZW5kKFwic3RvcmFnZU1hbmFnZXJfcGVyc2lzdGVkXCIsIFN0cmluZyhhd2FpdCBkb2N1bWVudC5oYXNTdG9yYWdlQWNjZXNzKCkpKTtcbiAgICAgICAgfSBjYXRjaCAoZSkge31cbiAgICB9XG4gICAgaWYgKG5hdmlnYXRvci5zdG9yYWdlICYmIG5hdmlnYXRvci5zdG9yYWdlLmVzdGltYXRlKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgICBjb25zdCBlc3RpbWF0ZSA9IGF3YWl0IG5hdmlnYXRvci5zdG9yYWdlLmVzdGltYXRlKCk7XG4gICAgICAgICAgICBib2R5LmFwcGVuZChcInN0b3JhZ2VNYW5hZ2VyX3F1b3RhXCIsIFN0cmluZyhlc3RpbWF0ZS5xdW90YSkpO1xuICAgICAgICAgICAgYm9keS5hcHBlbmQoXCJzdG9yYWdlTWFuYWdlcl91c2FnZVwiLCBTdHJpbmcoZXN0aW1hdGUudXNhZ2UpKTtcbiAgICAgICAgICAgIGlmIChlc3RpbWF0ZS51c2FnZURldGFpbHMpIHtcbiAgICAgICAgICAgICAgICBPYmplY3Qua2V5cyhlc3RpbWF0ZS51c2FnZURldGFpbHMpLmZvckVhY2goKGspID0+IHtcbiAgICAgICAgICAgICAgICAgICAgYm9keS5hcHBlbmQoYHN0b3JhZ2VNYW5hZ2VyX3VzYWdlXyR7a31gLCBTdHJpbmcoZXN0aW1hdGUudXNhZ2VEZXRhaWxzIVtrXSkpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgfVxuICAgICAgICB9IGNhdGNoIChlKSB7fVxuICAgIH1cbn1cblxuZnVuY3Rpb24gY29sbGVjdE1pc3NpbmdGZWF0dXJlcyhib2R5OiBGb3JtRGF0YSk6IHZvaWQge1xuICAgIGlmICh3aW5kb3cuTW9kZXJuaXpyKSB7XG4gICAgICAgIGNvbnN0IG1pc3NpbmdGZWF0dXJlcyA9IChPYmplY3Qua2V5cyh3aW5kb3cuTW9kZXJuaXpyKSBhcyBba2V5b2YgTW9kZXJuaXpyU3RhdGljXSkuZmlsdGVyKFxuICAgICAgICAgICAgKGtleToga2V5b2YgTW9kZXJuaXpyU3RhdGljKSA9PiB3aW5kb3cuTW9kZXJuaXpyW2tleV0gPT09IGZhbHNlLFxuICAgICAgICApO1xuICAgICAgICBpZiAobWlzc2luZ0ZlYXR1cmVzLmxlbmd0aCA+IDApIHtcbiAgICAgICAgICAgIGJvZHkuYXBwZW5kKFwibW9kZXJuaXpyX21pc3NpbmdfZmVhdHVyZXNcIiwgbWlzc2luZ0ZlYXR1cmVzLmpvaW4oXCIsIFwiKSk7XG4gICAgICAgIH1cbiAgICB9XG59XG5cbi8qKlxuICogQ29sbGVjdHMgbG9ncyB0byBhZGQgdG8gdGhlIHJlcG9ydCBpZiBlbmFibGVkLlxuICovXG5hc3luYyBmdW5jdGlvbiBjb2xsZWN0TG9ncyhcbiAgICBib2R5OiBGb3JtRGF0YSxcbiAgICBnemlwTG9nczogYm9vbGVhbixcbiAgICBwcm9ncmVzc0NhbGxiYWNrOiAoKHM6IHN0cmluZykgPT4gdm9pZCkgfCB1bmRlZmluZWQsXG4pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBsZXQgcGFrbzogdHlwZW9mIFBha28gfCB1bmRlZmluZWQ7XG4gICAgaWYgKGd6aXBMb2dzKSB7XG4gICAgICAgIHBha28gPSBhd2FpdCBpbXBvcnQoXCJwYWtvXCIpO1xuICAgIH1cblxuICAgIHByb2dyZXNzQ2FsbGJhY2s/LihfdChcImJ1Z19yZXBvcnRpbmd8Y29sbGVjdGluZ19sb2dzXCIpKTtcbiAgICBjb25zdCBsb2dzID0gYXdhaXQgcmFnZXNoYWtlLmdldExvZ3NGb3JSZXBvcnQoKTtcbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGxvZ3MpIHtcbiAgICAgICAgLy8gZW5jb2RlIGFzIFVURi04XG4gICAgICAgIGxldCBidWYgPSBuZXcgVGV4dEVuY29kZXIoKS5lbmNvZGUoZW50cnkubGluZXMpO1xuXG4gICAgICAgIC8vIGNvbXByZXNzXG4gICAgICAgIGlmIChnemlwTG9ncykge1xuICAgICAgICAgICAgYnVmID0gcGFrbyEuZ3ppcChidWYpO1xuICAgICAgICB9XG5cbiAgICAgICAgYm9keS5hcHBlbmQoXCJjb21wcmVzc2VkLWxvZ1wiLCBuZXcgQmxvYihbYnVmXSksIGVudHJ5LmlkKTtcbiAgICB9XG59XG4vKipcbiAqIFNlbmQgYSBidWcgcmVwb3J0LlxuICpcbiAqIEBwYXJhbSB7c3RyaW5nfSBidWdSZXBvcnRFbmRwb2ludCBIVFRQIHVybCB0byBzZW5kIHRoZSByZXBvcnQgdG9cbiAqXG4gKiBAcGFyYW0ge29iamVjdH0gb3B0cyBvcHRpb25hbCBkaWN0aW9uYXJ5IG9mIG9wdGlvbnNcbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gb3B0cy51c2VyVGV4dCBBbnkgYWRkaXRpb25hbCB1c2VyIGlucHV0LlxuICpcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gb3B0cy5zZW5kTG9ncyBUcnVlIHRvIHNlbmQgbG9nc1xuICpcbiAqIEBwYXJhbSB7ZnVuY3Rpb24oc3RyaW5nKX0gb3B0cy5wcm9ncmVzc0NhbGxiYWNrIENhbGxiYWNrIHRvIGNhbGwgd2l0aCBwcm9ncmVzcyB1cGRhdGVzXG4gKlxuICogQHJldHVybiB7UHJvbWlzZTxzdHJpbmc+fSBVUkwgcmV0dXJuZWQgYnkgdGhlIHJhZ2VzaGFrZSBzZXJ2ZXJcbiAqL1xuZXhwb3J0IGRlZmF1bHQgYXN5bmMgZnVuY3Rpb24gc2VuZEJ1Z1JlcG9ydChidWdSZXBvcnRFbmRwb2ludD86IHN0cmluZywgb3B0czogSU9wdHMgPSB7fSk6IFByb21pc2U8c3RyaW5nPiB7XG4gICAgaWYgKCFidWdSZXBvcnRFbmRwb2ludCkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJObyBidWcgcmVwb3J0IGVuZHBvaW50IGhhcyBiZWVuIHNldC5cIik7XG4gICAgfVxuXG4gICAgY29uc3QgcHJvZ3Jlc3NDYWxsYmFjayA9IG9wdHMucHJvZ3Jlc3NDYWxsYmFjayB8fCAoKCk6IHZvaWQgPT4ge30pO1xuICAgIGNvbnN0IGJvZHkgPSBhd2FpdCBjb2xsZWN0QnVnUmVwb3J0KG9wdHMpO1xuXG4gICAgcHJvZ3Jlc3NDYWxsYmFjayhfdChcImJ1Z19yZXBvcnRpbmd8dXBsb2FkaW5nX2xvZ3NcIikpO1xuICAgIHJldHVybiBzdWJtaXRSZXBvcnQoYnVnUmVwb3J0RW5kcG9pbnQsIGJvZHksIHByb2dyZXNzQ2FsbGJhY2spO1xufVxuXG4vKipcbiAqIERvd25sb2FkcyB0aGUgZmlsZXMgZnJvbSBhIGJ1ZyByZXBvcnQuIFRoaXMgaXMgdGhlIHNhbWUgYXMgc2VuZEJ1Z1JlcG9ydCxcbiAqIGJ1dCBpbnN0ZWFkIGNhdXNlcyB0aGUgYnJvd3NlciB0byBkb3dubG9hZCB0aGUgZmlsZXMgbG9jYWxseS5cbiAqXG4gKiBAcGFyYW0ge29iamVjdH0gb3B0cyBvcHRpb25hbCBkaWN0aW9uYXJ5IG9mIG9wdGlvbnNcbiAqXG4gKiBAcGFyYW0ge3N0cmluZ30gb3B0cy51c2VyVGV4dCBBbnkgYWRkaXRpb25hbCB1c2VyIGlucHV0LlxuICpcbiAqIEBwYXJhbSB7Ym9vbGVhbn0gb3B0cy5zZW5kTG9ncyBUcnVlIHRvIHNlbmQgbG9nc1xuICpcbiAqIEBwYXJhbSB7ZnVuY3Rpb24oc3RyaW5nKX0gb3B0cy5wcm9ncmVzc0NhbGxiYWNrIENhbGxiYWNrIHRvIGNhbGwgd2l0aCBwcm9ncmVzcyB1cGRhdGVzXG4gKlxuICogQHJldHVybiB7UHJvbWlzZX0gUmVzb2x2ZWQgd2hlbiB0aGUgYnVnIHJlcG9ydCBpcyBkb3dubG9hZGVkIChvciBzdGFydGVkKS5cbiAqL1xuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGRvd25sb2FkQnVnUmVwb3J0KG9wdHM6IElPcHRzID0ge30pOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBUYXIgPSAoYXdhaXQgaW1wb3J0KFwidGFyLWpzXCIpKS5kZWZhdWx0O1xuICAgIGNvbnN0IHByb2dyZXNzQ2FsbGJhY2sgPSBvcHRzLnByb2dyZXNzQ2FsbGJhY2sgfHwgKCgpOiB2b2lkID0+IHt9KTtcbiAgICBjb25zdCBib2R5ID0gYXdhaXQgY29sbGVjdEJ1Z1JlcG9ydChvcHRzLCBmYWxzZSk7XG5cbiAgICBwcm9ncmVzc0NhbGxiYWNrKF90KFwiYnVnX3JlcG9ydGluZ3xkb3dubG9hZGluZ19sb2dzXCIpKTtcbiAgICBsZXQgbWV0YWRhdGEgPSBcIlwiO1xuICAgIGNvbnN0IHRhcGUgPSBuZXcgVGFyKCk7XG4gICAgbGV0IGkgPSAwO1xuICAgIGZvciAoY29uc3QgW2tleSwgdmFsdWVdIG9mIGJvZHkuZW50cmllcygpKSB7XG4gICAgICAgIGlmIChrZXkgPT09IFwiY29tcHJlc3NlZC1sb2dcIikge1xuICAgICAgICAgICAgYXdhaXQgbmV3IFByb21pc2U8dm9pZD4oKHJlc29sdmUpID0+IHtcbiAgICAgICAgICAgICAgICBjb25zdCByZWFkZXIgPSBuZXcgRmlsZVJlYWRlcigpO1xuICAgICAgICAgICAgICAgIHJlYWRlci5hZGRFdmVudExpc3RlbmVyKFwibG9hZGVuZFwiLCAoZXYpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGFwZS5hcHBlbmQoYGxvZy0ke2krK30ubG9nYCwgbmV3IFRleHREZWNvZGVyKCkuZGVjb2RlKHJlYWRlci5yZXN1bHQgYXMgQXJyYXlCdWZmZXIpKTtcbiAgICAgICAgICAgICAgICAgICAgcmVzb2x2ZSgpO1xuICAgICAgICAgICAgICAgIH0pO1xuICAgICAgICAgICAgICAgIHJlYWRlci5yZWFkQXNBcnJheUJ1ZmZlcih2YWx1ZSBhcyBCbG9iKTtcbiAgICAgICAgICAgIH0pO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgbWV0YWRhdGEgKz0gYCR7a2V5fSA9ICR7dmFsdWUgYXMgc3RyaW5nfVxcbmA7XG4gICAgICAgIH1cbiAgICB9XG4gICAgdGFwZS5hcHBlbmQoXCJpc3N1ZS50eHRcIiwgbWV0YWRhdGEpO1xuXG4gICAgLy8gV2UgaGF2ZSB0byBjcmVhdGUgYSBuZXcgYW5jaG9yIHRvIGRvd25sb2FkIGlmIHdlIHdhbnQgYSBmaWxlbmFtZS4gT3RoZXJ3aXNlIHdlIGNvdWxkXG4gICAgLy8ganVzdCB1c2Ugd2luZG93Lm9wZW4uXG4gICAgY29uc3QgZGwgPSBkb2N1bWVudC5jcmVhdGVFbGVtZW50KFwiYVwiKTtcbiAgICBkbC5ocmVmID0gYGRhdGE6YXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtO2Jhc2U2NCwke2J0b2EodWludDhUb1N0cmluZyh0YXBlLm91dCkpfWA7XG4gICAgZGwuZG93bmxvYWQgPSBcInJhZ2VzaGFrZS50YXJcIjtcbiAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKGRsKTtcbiAgICBkbC5jbGljaygpO1xuICAgIGRvY3VtZW50LmJvZHkucmVtb3ZlQ2hpbGQoZGwpO1xufVxuXG4vLyBTb3VyY2U6IGh0dHBzOi8vZ2l0aHViLmNvbS9iZWF0Z2FtbWl0L3Rhci1qcy9ibG9iL21hc3Rlci9leGFtcGxlcy9tYWluLmpzXG5mdW5jdGlvbiB1aW50OFRvU3RyaW5nKGJ1ZjogVWludDhBcnJheSk6IHN0cmluZyB7XG4gICAgbGV0IG91dCA9IFwiXCI7XG4gICAgZm9yIChsZXQgaSA9IDA7IGkgPCBidWYubGVuZ3RoOyBpICs9IDEpIHtcbiAgICAgICAgb3V0ICs9IFN0cmluZy5mcm9tQ2hhckNvZGUoYnVmW2ldKTtcbiAgICB9XG4gICAgcmV0dXJuIG91dDtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIHN1Ym1pdEZlZWRiYWNrKFxuICAgIGxhYmVsOiBzdHJpbmcgfCB1bmRlZmluZWQsXG4gICAgY29tbWVudDogc3RyaW5nLFxuICAgIGNhbkNvbnRhY3QgPSBmYWxzZSxcbiAgICBleHRyYURhdGE6IFJlY29yZDxzdHJpbmcsIGFueT4gPSB7fSxcbik6IFByb21pc2U8dm9pZD4ge1xuICAgIGxldCB2ZXJzaW9uOiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gICAgdHJ5IHtcbiAgICAgICAgdmVyc2lvbiA9IGF3YWl0IFBsYXRmb3JtUGVnLmdldCgpPy5nZXRBcHBWZXJzaW9uKCk7XG4gICAgfSBjYXRjaCAoZXJyKSB7fSAvLyBQbGF0Zm9ybVBlZyBhbHJlYWR5IGxvZ3MgdGhpcy5cblxuICAgIGNvbnN0IGJvZHkgPSBuZXcgRm9ybURhdGEoKTtcbiAgICBpZiAobGFiZWwpIGJvZHkuYXBwZW5kKFwibGFiZWxcIiwgbGFiZWwpO1xuICAgIGJvZHkuYXBwZW5kKFwidGV4dFwiLCBjb21tZW50KTtcbiAgICBib2R5LmFwcGVuZChcImNhbl9jb250YWN0XCIsIGNhbkNvbnRhY3QgPyBcInllc1wiIDogXCJub1wiKTtcblxuICAgIGJvZHkuYXBwZW5kKFwiYXBwXCIsIFwiZWxlbWVudC13ZWJcIik7XG4gICAgYm9keS5hcHBlbmQoXCJ2ZXJzaW9uXCIsIHZlcnNpb24gfHwgXCJVTktOT1dOXCIpO1xuICAgIGJvZHkuYXBwZW5kKFwicGxhdGZvcm1cIiwgUGxhdGZvcm1QZWcuZ2V0KCk/LmdldEh1bWFuUmVhZGFibGVOYW1lKCkgPz8gXCJuL2FcIik7XG4gICAgYm9keS5hcHBlbmQoXCJ1c2VyX2lkXCIsIE1hdHJpeENsaWVudFBlZy5nZXQoKT8uZ2V0VXNlcklkKCkgPz8gXCJuL2FcIik7XG5cbiAgICBmb3IgKGNvbnN0IGsgaW4gZXh0cmFEYXRhKSB7XG4gICAgICAgIGJvZHkuYXBwZW5kKGssIEpTT04uc3RyaW5naWZ5KGV4dHJhRGF0YVtrXSkpO1xuICAgIH1cblxuICAgIGNvbnN0IGJ1Z1JlcG9ydEVuZHBvaW50VXJsID0gU2RrQ29uZmlnLmdldCgpLmJ1Z19yZXBvcnRfZW5kcG9pbnRfdXJsO1xuXG4gICAgaWYgKGJ1Z1JlcG9ydEVuZHBvaW50VXJsKSB7XG4gICAgICAgIGF3YWl0IHN1Ym1pdFJlcG9ydChidWdSZXBvcnRFbmRwb2ludFVybCwgYm9keSwgKCkgPT4ge30pO1xuICAgIH1cbn1cblxuZnVuY3Rpb24gc3VibWl0UmVwb3J0KGVuZHBvaW50OiBzdHJpbmcsIGJvZHk6IEZvcm1EYXRhLCBwcm9ncmVzc0NhbGxiYWNrOiAoc3RyOiBzdHJpbmcpID0+IHZvaWQpOiBQcm9taXNlPHN0cmluZz4ge1xuICAgIHJldHVybiBuZXcgUHJvbWlzZTxzdHJpbmc+KChyZXNvbHZlLCByZWplY3QpID0+IHtcbiAgICAgICAgY29uc3QgcmVxID0gbmV3IFhNTEh0dHBSZXF1ZXN0KCk7XG4gICAgICAgIHJlcS5vcGVuKFwiUE9TVFwiLCBlbmRwb2ludCk7XG4gICAgICAgIHJlcS5yZXNwb25zZVR5cGUgPSBcImpzb25cIjtcbiAgICAgICAgcmVxLnRpbWVvdXQgPSA1ICogNjAgKiAxMDAwO1xuICAgICAgICByZXEub25yZWFkeXN0YXRlY2hhbmdlID0gZnVuY3Rpb24gKCk6IHZvaWQge1xuICAgICAgICAgICAgaWYgKHJlcS5yZWFkeVN0YXRlID09PSBYTUxIdHRwUmVxdWVzdC5MT0FESU5HKSB7XG4gICAgICAgICAgICAgICAgcHJvZ3Jlc3NDYWxsYmFjayhfdChcImJ1Z19yZXBvcnRpbmd8d2FpdGluZ19mb3Jfc2VydmVyXCIpKTtcbiAgICAgICAgICAgIH0gZWxzZSBpZiAocmVxLnJlYWR5U3RhdGUgPT09IFhNTEh0dHBSZXF1ZXN0LkRPTkUpIHtcbiAgICAgICAgICAgICAgICAvLyBvbiBkb25lXG4gICAgICAgICAgICAgICAgaWYgKHJlcS5zdGF0dXMgPCAyMDAgfHwgcmVxLnN0YXR1cyA+PSA0MDApIHtcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KG5ldyBFcnJvcihgSFRUUCAke3JlcS5zdGF0dXN9YCkpO1xuICAgICAgICAgICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIHJlc29sdmUocmVxLnJlc3BvbnNlLnJlcG9ydF91cmwgfHwgXCJcIik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH07XG4gICAgICAgIHJlcS5zZW5kKGJvZHkpO1xuICAgIH0pO1xufVxuIl0sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7QUFVQSxJQUFBQSxPQUFBLEdBQUFDLE9BQUE7QUFDQSxJQUFBQyxPQUFBLEdBQUFELE9BQUE7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