matrix-react-sdk
Version:
SDK for matrix.org using React
392 lines (385 loc) • 66 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireDefault(require("react"));
var _cryptoApi = require("matrix-js-sdk/src/crypto-api");
var _logger = require("matrix-js-sdk/src/logger");
var _types = require("matrix-js-sdk/src/types");
var _MatrixClientPeg = require("../../../MatrixClientPeg");
var _VerificationQRCode = _interopRequireDefault(require("../elements/crypto/VerificationQRCode"));
var _languageHandler = require("../../../languageHandler");
var _SdkConfig = _interopRequireDefault(require("../../../SdkConfig"));
var _E2EIcon = _interopRequireWildcard(require("../rooms/E2EIcon"));
var _Spinner = _interopRequireDefault(require("../elements/Spinner"));
var _AccessibleButton = _interopRequireDefault(require("../elements/AccessibleButton"));
var _VerificationShowSas = _interopRequireDefault(require("../verification/VerificationShowSas"));
var _deviceInfo = require("../../../utils/crypto/deviceInfo");
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, 2020 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.
*/
class VerificationPanel extends _react.default.PureComponent {
constructor(props) {
super(props);
(0, _defineProperty2.default)(this, "hasVerifier", void 0);
/** have we yet tried to check the other device's info */
(0, _defineProperty2.default)(this, "haveCheckedDevice", false);
/** have we yet tried to get the QR code */
(0, _defineProperty2.default)(this, "haveFetchedQRCode", false);
(0, _defineProperty2.default)(this, "onReciprocateYesClick", () => {
if (!this.state.reciprocateQREvent) return;
this.setState({
reciprocateButtonClicked: true
});
this.state.reciprocateQREvent?.confirm();
});
(0, _defineProperty2.default)(this, "onReciprocateNoClick", () => {
if (!this.state.reciprocateQREvent) return;
this.setState({
reciprocateButtonClicked: true
});
this.state.reciprocateQREvent?.cancel();
});
(0, _defineProperty2.default)(this, "startSAS", async () => {
this.setState({
emojiButtonClicked: true
});
await this.props.request.startVerification(_types.VerificationMethod.Sas);
});
(0, _defineProperty2.default)(this, "onSasMatchesClick", () => {
this.state.sasEvent?.confirm();
});
(0, _defineProperty2.default)(this, "onSasMismatchesClick", () => {
this.state.sasEvent?.mismatch();
});
(0, _defineProperty2.default)(this, "updateVerifierState", () => {
// this method is only called once we know there is a verifier.
const verifier = this.props.request.verifier;
const sasEvent = verifier.getShowSasCallbacks();
const reciprocateQREvent = verifier.getReciprocateQrCodeCallbacks();
verifier.off(_cryptoApi.VerifierEvent.ShowSas, this.updateVerifierState);
verifier.off(_cryptoApi.VerifierEvent.ShowReciprocateQr, this.updateVerifierState);
this.setState({
sasEvent,
reciprocateQREvent
});
});
(0, _defineProperty2.default)(this, "onRequestChange", async () => {
const {
request
} = this.props;
// if we have a device ID and did not have one before, fetch the device's details
this.maybeGetOtherDevice();
// if we have had a reply from the other side (ie, the phase is "ready") and we have not
// yet done so, fetch the QR code
if (request.phase === _cryptoApi.VerificationPhase.Ready && !this.haveFetchedQRCode) {
this.haveFetchedQRCode = true;
request.generateQRCode().then(buf => {
this.setState({
qrCodeBytes: buf
});
}, error => {
console.error("Error generating QR code:", error);
});
}
const hadVerifier = this.hasVerifier;
this.hasVerifier = !!request.verifier;
if (!hadVerifier && this.hasVerifier) {
request.verifier?.on(_cryptoApi.VerifierEvent.ShowSas, this.updateVerifierState);
request.verifier?.on(_cryptoApi.VerifierEvent.ShowReciprocateQr, this.updateVerifierState);
try {
// on the requester side, this is also awaited in startSAS,
// but that's ok as verify should return the same promise.
await request.verifier?.verify();
} catch (err) {
_logger.logger.error("error verify", err);
}
}
});
this.state = {
qrCodeBytes: undefined,
sasEvent: null,
reciprocateQREvent: null
};
this.hasVerifier = false;
}
renderQRPhase() {
const {
member,
request
} = this.props;
const showSAS = request.otherPartySupportsMethod(_types.VerificationMethod.Sas);
const showQR = request.otherPartySupportsMethod(_types.VerificationMethod.ScanQrCode);
const brand = _SdkConfig.default.get().brand;
const noCommonMethodError = !showSAS && !showQR ? /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("encryption|verification|no_support_qr_emoji", {
brand
})) : null;
if (this.props.layout === "dialog") {
// HACK: This is a terrible idea.
let qrBlockDialog;
let sasBlockDialog;
if (showQR) {
qrBlockDialog = /*#__PURE__*/_react.default.createElement("div", {
className: "mx_VerificationPanel_QRPhase_startOption"
}, /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("encryption|verification|qr_prompt")), /*#__PURE__*/_react.default.createElement(_VerificationQRCode.default, {
qrCodeBytes: this.state.qrCodeBytes
}));
}
if (showSAS) {
sasBlockDialog = /*#__PURE__*/_react.default.createElement("div", {
className: "mx_VerificationPanel_QRPhase_startOption"
}, /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("encryption|verification|sas_prompt")), /*#__PURE__*/_react.default.createElement("span", {
className: "mx_VerificationPanel_QRPhase_helpText"
}, (0, _languageHandler._t)("encryption|verification|sas_description")), /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
disabled: this.state.emojiButtonClicked,
onClick: this.startSAS,
kind: "primary"
}, (0, _languageHandler._t)("action|start")));
}
const or = qrBlockDialog && sasBlockDialog ? /*#__PURE__*/_react.default.createElement("div", {
className: "mx_VerificationPanel_QRPhase_betweenText"
}, (0, _languageHandler._t)("encryption|verification|qr_or_sas", {
emojiCompare: "",
qrCode: ""
})) : null;
return /*#__PURE__*/_react.default.createElement("div", null, (0, _languageHandler._t)("encryption|verification|qr_or_sas_header"), /*#__PURE__*/_react.default.createElement("div", {
className: "mx_VerificationPanel_QRPhase_startOptions"
}, qrBlockDialog, or, sasBlockDialog, noCommonMethodError));
}
let qrBlock;
if (showQR) {
qrBlock = /*#__PURE__*/_react.default.createElement("div", {
className: "mx_UserInfo_container"
}, /*#__PURE__*/_react.default.createElement("h3", null, (0, _languageHandler._t)("encryption|verification|scan_qr")), /*#__PURE__*/_react.default.createElement("p", null, (0, _languageHandler._t)("encryption|verification|scan_qr_explainer", {
displayName: member.displayName || member.name || member.userId
})), /*#__PURE__*/_react.default.createElement("div", {
className: "mx_VerificationPanel_qrCode"
}, /*#__PURE__*/_react.default.createElement(_VerificationQRCode.default, {
qrCodeBytes: this.state.qrCodeBytes
})));
}
let sasBlock;
if (showSAS) {
const disabled = this.state.emojiButtonClicked;
const sasLabel = showQR ? (0, _languageHandler._t)("encryption|verification|verify_emoji_prompt_qr") : (0, _languageHandler._t)("encryption|verification|verify_emoji_prompt");
// Note: mx_VerificationPanel_verifyByEmojiButton is for the end-to-end tests
sasBlock = /*#__PURE__*/_react.default.createElement("div", {
className: "mx_UserInfo_container"
}, /*#__PURE__*/_react.default.createElement("h3", null, (0, _languageHandler._t)("encryption|verification|verify_emoji")), /*#__PURE__*/_react.default.createElement("p", null, sasLabel), /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
disabled: disabled,
kind: "primary",
className: "mx_UserInfo_wideButton mx_VerificationPanel_verifyByEmojiButton",
onClick: this.startSAS
}, (0, _languageHandler._t)("encryption|verification|verify_emoji")));
}
const noCommonMethodBlock = noCommonMethodError ? /*#__PURE__*/_react.default.createElement("div", {
className: "mx_UserInfo_container"
}, noCommonMethodError) : null;
// TODO: add way to open camera to scan a QR code
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, qrBlock, sasBlock, noCommonMethodBlock);
}
/**
* Get details of the other device involved in the verification, if we haven't before, and store in the state.
*/
async maybeGetOtherDevice() {
if (this.haveCheckedDevice) return;
const client = _MatrixClientPeg.MatrixClientPeg.safeGet();
const deviceId = this.props.request?.otherDeviceId;
const userId = client.getUserId();
if (!deviceId || !userId) {
return;
}
this.haveCheckedDevice = true;
this.setState({
otherDeviceDetails: await (0, _deviceInfo.getDeviceCryptoInfo)(client, userId, deviceId)
});
}
renderQRReciprocatePhase() {
const {
member,
request
} = this.props;
const description = request.isSelfVerification ? (0, _languageHandler._t)("encryption|verification|qr_reciprocate_same_shield_device") : (0, _languageHandler._t)("encryption|verification|qr_reciprocate_same_shield_user", {
displayName: member.displayName || member.name || member.userId
});
let body;
if (this.state.reciprocateQREvent) {
// Element Web doesn't support scanning yet, so assume here we're the client being scanned.
body = /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("p", null, description), /*#__PURE__*/_react.default.createElement(_E2EIcon.default, {
isUser: true,
status: _E2EIcon.E2EState.Verified,
size: 128,
hideTooltip: true
}), /*#__PURE__*/_react.default.createElement("div", {
className: "mx_VerificationPanel_reciprocateButtons"
}, /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
kind: "danger",
disabled: this.state.reciprocateButtonClicked,
onClick: this.onReciprocateNoClick
}, (0, _languageHandler._t)("action|no")), /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
kind: "primary",
disabled: this.state.reciprocateButtonClicked,
onClick: this.onReciprocateYesClick
}, (0, _languageHandler._t)("action|yes"))));
} else {
body = /*#__PURE__*/_react.default.createElement("p", null, /*#__PURE__*/_react.default.createElement(_Spinner.default, null));
}
return /*#__PURE__*/_react.default.createElement("div", {
className: "mx_UserInfo_container mx_VerificationPanel_reciprocate_section"
}, /*#__PURE__*/_react.default.createElement("h3", null, (0, _languageHandler._t)("encryption|verification|scan_qr")), body);
}
renderVerifiedPhase() {
const {
member,
request
} = this.props;
let text;
if (!request.isSelfVerification) {
if (this.props.isRoomEncrypted) {
text = (0, _languageHandler._t)("encryption|verification|prompt_encrypted");
} else {
text = (0, _languageHandler._t)("encryption|verification|prompt_unencrypted");
}
}
let description;
if (request.isSelfVerification) {
const device = this.state.otherDeviceDetails;
if (!device) {
// This can happen if the device is logged out while we're still showing verification
// UI for it.
_logger.logger.warn("Verified device we don't know about: " + this.props.request.otherDeviceId);
description = (0, _languageHandler._t)("encryption|verification|successful_own_device");
} else {
description = (0, _languageHandler._t)("encryption|verification|successful_device", {
deviceName: device.displayName,
deviceId: device.deviceId
});
}
} else {
description = (0, _languageHandler._t)("encryption|verification|successful_user", {
displayName: member.displayName || member.name || member.userId
});
}
return /*#__PURE__*/_react.default.createElement("div", {
className: "mx_UserInfo_container mx_VerificationPanel_verified_section"
}, /*#__PURE__*/_react.default.createElement("p", null, description), /*#__PURE__*/_react.default.createElement(_E2EIcon.default, {
isUser: true,
status: _E2EIcon.E2EState.Verified,
size: 128,
hideTooltip: true
}), text ? /*#__PURE__*/_react.default.createElement("p", null, text) : null, /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
kind: "primary",
className: "mx_UserInfo_wideButton",
onClick: this.props.onClose
}, (0, _languageHandler._t)("action|got_it")));
}
renderCancelledPhase() {
const {
member,
request
} = this.props;
let startAgainInstruction;
if (request.isSelfVerification) {
startAgainInstruction = (0, _languageHandler._t)("encryption|verification|prompt_self");
} else {
startAgainInstruction = (0, _languageHandler._t)("encryption|verification|prompt_user");
}
let text;
if (request.cancellationCode === "m.timeout") {
text = (0, _languageHandler._t)("encryption|verification|timed_out") + ` ${startAgainInstruction}`;
} else if (request.cancellingUserId === request.otherUserId) {
if (request.isSelfVerification) {
text = (0, _languageHandler._t)("encryption|verification|cancelled_self");
} else {
text = (0, _languageHandler._t)("encryption|verification|cancelled_user", {
displayName: member.displayName || member.name || member.userId
});
}
text = `${text} ${startAgainInstruction}`;
} else {
text = (0, _languageHandler._t)("encryption|verification|cancelled") + ` ${startAgainInstruction}`;
}
return /*#__PURE__*/_react.default.createElement("div", {
className: "mx_UserInfo_container"
}, /*#__PURE__*/_react.default.createElement("h3", null, (0, _languageHandler._t)("common|verification_cancelled")), /*#__PURE__*/_react.default.createElement("p", null, text), /*#__PURE__*/_react.default.createElement(_AccessibleButton.default, {
kind: "primary",
className: "mx_UserInfo_wideButton",
onClick: this.props.onClose
}, (0, _languageHandler._t)("action|got_it")));
}
render() {
const {
member,
phase,
request
} = this.props;
const displayName = member.displayName || member.name || member.userId;
switch (phase) {
case _cryptoApi.VerificationPhase.Ready:
return this.renderQRPhase();
case _cryptoApi.VerificationPhase.Started:
switch (request.chosenMethod) {
case _types.VerificationMethod.Reciprocate:
return this.renderQRReciprocatePhase();
case _types.VerificationMethod.Sas:
{
const emojis = this.state.sasEvent ? /*#__PURE__*/_react.default.createElement(_VerificationShowSas.default, {
displayName: displayName,
otherDeviceDetails: this.state.otherDeviceDetails,
sas: this.state.sasEvent.sas,
onCancel: this.onSasMismatchesClick,
onDone: this.onSasMatchesClick,
inDialog: this.props.inDialog,
isSelf: request.isSelfVerification
}) : /*#__PURE__*/_react.default.createElement(_Spinner.default, null);
return /*#__PURE__*/_react.default.createElement("div", {
className: "mx_UserInfo_container"
}, emojis);
}
default:
return null;
}
case _cryptoApi.VerificationPhase.Done:
return this.renderVerifiedPhase();
case _cryptoApi.VerificationPhase.Cancelled:
return this.renderCancelledPhase();
}
_logger.logger.error("VerificationPanel unhandled phase:", phase);
return null;
}
componentDidMount() {
const {
request
} = this.props;
request.on(_cryptoApi.VerificationRequestEvent.Change, this.onRequestChange);
if (request.verifier) {
const sasEvent = request.verifier.getShowSasCallbacks();
const reciprocateQREvent = request.verifier.getReciprocateQrCodeCallbacks();
this.setState({
sasEvent,
reciprocateQREvent
});
}
this.onRequestChange();
}
componentWillUnmount() {
const {
request
} = this.props;
if (request.verifier) {
request.verifier.off(_cryptoApi.VerifierEvent.ShowSas, this.updateVerifierState);
request.verifier.off(_cryptoApi.VerifierEvent.ShowReciprocateQr, this.updateVerifierState);
}
request.off(_cryptoApi.VerificationRequestEvent.Change, this.onRequestChange);
}
}
exports.default = VerificationPanel;
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfcmVhY3QiLCJfaW50ZXJvcFJlcXVpcmVEZWZhdWx0IiwicmVxdWlyZSIsIl9jcnlwdG9BcGkiLCJfbG9nZ2VyIiwiX3R5cGVzIiwiX01hdHJpeENsaWVudFBlZyIsIl9WZXJpZmljYXRpb25RUkNvZGUiLCJfbGFuZ3VhZ2VIYW5kbGVyIiwiX1Nka0NvbmZpZyIsIl9FMkVJY29uIiwiX2ludGVyb3BSZXF1aXJlV2lsZGNhcmQiLCJfU3Bpbm5lciIsIl9BY2Nlc3NpYmxlQnV0dG9uIiwiX1ZlcmlmaWNhdGlvblNob3dTYXMiLCJfZGV2aWNlSW5mbyIsIl9nZXRSZXF1aXJlV2lsZGNhcmRDYWNoZSIsImUiLCJXZWFrTWFwIiwiciIsInQiLCJfX2VzTW9kdWxlIiwiZGVmYXVsdCIsImhhcyIsImdldCIsIm4iLCJfX3Byb3RvX18iLCJhIiwiT2JqZWN0IiwiZGVmaW5lUHJvcGVydHkiLCJnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IiLCJ1IiwiaGFzT3duUHJvcGVydHkiLCJjYWxsIiwiaSIsInNldCIsIlZlcmlmaWNhdGlvblBhbmVsIiwiUmVhY3QiLCJQdXJlQ29tcG9uZW50IiwiY29uc3RydWN0b3IiLCJwcm9wcyIsIl9kZWZpbmVQcm9wZXJ0eTIiLCJzdGF0ZSIsInJlY2lwcm9jYXRlUVJFdmVudCIsInNldFN0YXRlIiwicmVjaXByb2NhdGVCdXR0b25DbGlja2VkIiwiY29uZmlybSIsImNhbmNlbCIsImVtb2ppQnV0dG9uQ2xpY2tlZCIsInJlcXVlc3QiLCJzdGFydFZlcmlmaWNhdGlvbiIsIlZlcmlmaWNhdGlvbk1ldGhvZCIsIlNhcyIsInNhc0V2ZW50IiwibWlzbWF0Y2giLCJ2ZXJpZmllciIsImdldFNob3dTYXNDYWxsYmFja3MiLCJnZXRSZWNpcHJvY2F0ZVFyQ29kZUNhbGxiYWNrcyIsIm9mZiIsIlZlcmlmaWVyRXZlbnQiLCJTaG93U2FzIiwidXBkYXRlVmVyaWZpZXJTdGF0ZSIsIlNob3dSZWNpcHJvY2F0ZVFyIiwibWF5YmVHZXRPdGhlckRldmljZSIsInBoYXNlIiwiUGhhc2UiLCJSZWFkeSIsImhhdmVGZXRjaGVkUVJDb2RlIiwiZ2VuZXJhdGVRUkNvZGUiLCJ0aGVuIiwiYnVmIiwicXJDb2RlQnl0ZXMiLCJlcnJvciIsImNvbnNvbGUiLCJoYWRWZXJpZmllciIsImhhc1ZlcmlmaWVyIiwib24iLCJ2ZXJpZnkiLCJlcnIiLCJsb2dnZXIiLCJ1bmRlZmluZWQiLCJyZW5kZXJRUlBoYXNlIiwibWVtYmVyIiwic2hvd1NBUyIsIm90aGVyUGFydHlTdXBwb3J0c01ldGhvZCIsInNob3dRUiIsIlNjYW5RckNvZGUiLCJicmFuZCIsIlNka0NvbmZpZyIsIm5vQ29tbW9uTWV0aG9kRXJyb3IiLCJjcmVhdGVFbGVtZW50IiwiX3QiLCJsYXlvdXQiLCJxckJsb2NrRGlhbG9nIiwic2FzQmxvY2tEaWFsb2ciLCJjbGFzc05hbWUiLCJkaXNhYmxlZCIsIm9uQ2xpY2siLCJzdGFydFNBUyIsImtpbmQiLCJvciIsImVtb2ppQ29tcGFyZSIsInFyQ29kZSIsInFyQmxvY2siLCJkaXNwbGF5TmFtZSIsIm5hbWUiLCJ1c2VySWQiLCJzYXNCbG9jayIsInNhc0xhYmVsIiwibm9Db21tb25NZXRob2RCbG9jayIsIkZyYWdtZW50IiwiaGF2ZUNoZWNrZWREZXZpY2UiLCJjbGllbnQiLCJNYXRyaXhDbGllbnRQZWciLCJzYWZlR2V0IiwiZGV2aWNlSWQiLCJvdGhlckRldmljZUlkIiwiZ2V0VXNlcklkIiwib3RoZXJEZXZpY2VEZXRhaWxzIiwiZ2V0RGV2aWNlQ3J5cHRvSW5mbyIsInJlbmRlclFSUmVjaXByb2NhdGVQaGFzZSIsImRlc2NyaXB0aW9uIiwiaXNTZWxmVmVyaWZpY2F0aW9uIiwiYm9keSIsImlzVXNlciIsInN0YXR1cyIsIkUyRVN0YXRlIiwiVmVyaWZpZWQiLCJzaXplIiwiaGlkZVRvb2x0aXAiLCJvblJlY2lwcm9jYXRlTm9DbGljayIsIm9uUmVjaXByb2NhdGVZZXNDbGljayIsInJlbmRlclZlcmlmaWVkUGhhc2UiLCJ0ZXh0IiwiaXNSb29tRW5jcnlwdGVkIiwiZGV2aWNlIiwid2FybiIsImRldmljZU5hbWUiLCJvbkNsb3NlIiwicmVuZGVyQ2FuY2VsbGVkUGhhc2UiLCJzdGFydEFnYWluSW5zdHJ1Y3Rpb24iLCJjYW5jZWxsYXRpb25Db2RlIiwiY2FuY2VsbGluZ1VzZXJJZCIsIm90aGVyVXNlcklkIiwicmVuZGVyIiwiU3RhcnRlZCIsImNob3Nlbk1ldGhvZCIsIlJlY2lwcm9jYXRlIiwiZW1vamlzIiwic2FzIiwib25DYW5jZWwiLCJvblNhc01pc21hdGNoZXNDbGljayIsIm9uRG9uZSIsIm9uU2FzTWF0Y2hlc0NsaWNrIiwiaW5EaWFsb2ciLCJpc1NlbGYiLCJEb25lIiwiQ2FuY2VsbGVkIiwiY29tcG9uZW50RGlkTW91bnQiLCJWZXJpZmljYXRpb25SZXF1ZXN0RXZlbnQiLCJDaGFuZ2UiLCJvblJlcXVlc3RDaGFuZ2UiLCJjb21wb25lbnRXaWxsVW5tb3VudCIsImV4cG9ydHMiXSwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvY29tcG9uZW50cy92aWV3cy9yaWdodF9wYW5lbC9WZXJpZmljYXRpb25QYW5lbC50c3giXSwic291cmNlc0NvbnRlbnQiOlsiLypcbkNvcHlyaWdodCAyMDI0IE5ldyBWZWN0b3IgTHRkLlxuQ29weXJpZ2h0IDIwMTksIDIwMjAgVGhlIE1hdHJpeC5vcmcgRm91bmRhdGlvbiBDLkkuQy5cblxuU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFHUEwtMy4wLW9ubHkgT1IgR1BMLTMuMC1vbmx5XG5QbGVhc2Ugc2VlIExJQ0VOU0UgZmlsZXMgaW4gdGhlIHJlcG9zaXRvcnkgcm9vdCBmb3IgZnVsbCBkZXRhaWxzLlxuKi9cblxuaW1wb3J0IFJlYWN0IGZyb20gXCJyZWFjdFwiO1xuaW1wb3J0IHtcbiAgICBTaG93UXJDb2RlQ2FsbGJhY2tzLFxuICAgIFNob3dTYXNDYWxsYmFja3MsXG4gICAgVmVyaWZpY2F0aW9uUGhhc2UgYXMgUGhhc2UsXG4gICAgVmVyaWZpY2F0aW9uUmVxdWVzdCxcbiAgICBWZXJpZmljYXRpb25SZXF1ZXN0RXZlbnQsXG4gICAgVmVyaWZpZXJFdmVudCxcbn0gZnJvbSBcIm1hdHJpeC1qcy1zZGsvc3JjL2NyeXB0by1hcGlcIjtcbmltcG9ydCB7IERldmljZSwgUm9vbU1lbWJlciwgVXNlciB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy9tYXRyaXhcIjtcbmltcG9ydCB7IGxvZ2dlciB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy9sb2dnZXJcIjtcbmltcG9ydCB7IFZlcmlmaWNhdGlvbk1ldGhvZCB9IGZyb20gXCJtYXRyaXgtanMtc2RrL3NyYy90eXBlc1wiO1xuXG5pbXBvcnQgeyBNYXRyaXhDbGllbnRQZWcgfSBmcm9tIFwiLi4vLi4vLi4vTWF0cml4Q2xpZW50UGVnXCI7XG5pbXBvcnQgVmVyaWZpY2F0aW9uUVJDb2RlIGZyb20gXCIuLi9lbGVtZW50cy9jcnlwdG8vVmVyaWZpY2F0aW9uUVJDb2RlXCI7XG5pbXBvcnQgeyBfdCB9IGZyb20gXCIuLi8uLi8uLi9sYW5ndWFnZUhhbmRsZXJcIjtcbmltcG9ydCBTZGtDb25maWcgZnJvbSBcIi4uLy4uLy4uL1Nka0NvbmZpZ1wiO1xuaW1wb3J0IEUyRUljb24sIHsgRTJFU3RhdGUgfSBmcm9tIFwiLi4vcm9vbXMvRTJFSWNvblwiO1xuaW1wb3J0IFNwaW5uZXIgZnJvbSBcIi4uL2VsZW1lbnRzL1NwaW5uZXJcIjtcbmltcG9ydCBBY2Nlc3NpYmxlQnV0dG9uIGZyb20gXCIuLi9lbGVtZW50cy9BY2Nlc3NpYmxlQnV0dG9uXCI7XG5pbXBvcnQgVmVyaWZpY2F0aW9uU2hvd1NhcyBmcm9tIFwiLi4vdmVyaWZpY2F0aW9uL1ZlcmlmaWNhdGlvblNob3dTYXNcIjtcbmltcG9ydCB7IGdldERldmljZUNyeXB0b0luZm8gfSBmcm9tIFwiLi4vLi4vLi4vdXRpbHMvY3J5cHRvL2RldmljZUluZm9cIjtcblxuaW50ZXJmYWNlIElQcm9wcyB7XG4gICAgbGF5b3V0OiBzdHJpbmc7XG4gICAgcmVxdWVzdDogVmVyaWZpY2F0aW9uUmVxdWVzdDtcbiAgICBtZW1iZXI6IFJvb21NZW1iZXIgfCBVc2VyO1xuICAgIHBoYXNlPzogUGhhc2U7XG4gICAgb25DbG9zZTogKCkgPT4gdm9pZDtcbiAgICBpc1Jvb21FbmNyeXB0ZWQ6IGJvb2xlYW47XG4gICAgaW5EaWFsb2c6IGJvb2xlYW47XG59XG5cbmludGVyZmFjZSBJU3RhdGUge1xuICAgIC8qKlxuICAgICAqIFRoZSBkYXRhIGZvciB0aGUgUVIgY29kZSB0byBkaXNwbGF5LlxuICAgICAqXG4gICAgICogV2UgYXR0ZW1wdCB0byBjYWxjdWxhdGUgdGhpcyBvbmNlIHRoZSB2ZXJpZmljYXRpb24gcmVxdWVzdCB0cmFuc2l0aW9ucyBpbnRvIHRoZSBcIlJlYWR5XCIgcGhhc2UuIElmIHRoZSBvdGhlclxuICAgICAqIHNpZGUgY2Fubm90IHNjYW4gUVIgY29kZXMsIGl0IHdpbGwgcmVtYWluIGB1bmRlZmluZWRgLlxuICAgICAqL1xuICAgIHFyQ29kZUJ5dGVzOiBCdWZmZXIgfCB1bmRlZmluZWQ7XG5cbiAgICBzYXNFdmVudDogU2hvd1Nhc0NhbGxiYWNrcyB8IG51bGw7XG4gICAgZW1vamlCdXR0b25DbGlja2VkPzogYm9vbGVhbjtcbiAgICByZWNpcHJvY2F0ZUJ1dHRvbkNsaWNrZWQ/OiBib29sZWFuO1xuICAgIHJlY2lwcm9jYXRlUVJFdmVudDogU2hvd1FyQ29kZUNhbGxiYWNrcyB8IG51bGw7XG5cbiAgICAvKipcbiAgICAgKiBEZXRhaWxzIG9mIHRoZSBvdGhlciBkZXZpY2UgaW52b2x2ZWQgaW4gdGhlIHRyYW5zYWN0aW9uLlxuICAgICAqXG4gICAgICogYHVuZGVmaW5lZGAgaWYgdGhlcmUgaXMgbm90ICh5ZXQpIGFub3RoZXIgZGV2aWNlIGluIHRoZSB0cmFuc2FjdGlvbiwgb3IgaWYgd2UgZG8gbm90IGtub3cgYWJvdXQgaXQuXG4gICAgICovXG4gICAgb3RoZXJEZXZpY2VEZXRhaWxzPzogRGV2aWNlO1xufVxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBWZXJpZmljYXRpb25QYW5lbCBleHRlbmRzIFJlYWN0LlB1cmVDb21wb25lbnQ8SVByb3BzLCBJU3RhdGU+IHtcbiAgICBwcml2YXRlIGhhc1ZlcmlmaWVyOiBib29sZWFuO1xuXG4gICAgLyoqIGhhdmUgd2UgeWV0IHRyaWVkIHRvIGNoZWNrIHRoZSBvdGhlciBkZXZpY2UncyBpbmZvICovXG4gICAgcHJpdmF0ZSBoYXZlQ2hlY2tlZERldmljZSA9IGZhbHNlO1xuXG4gICAgLyoqIGhhdmUgd2UgeWV0IHRyaWVkIHRvIGdldCB0aGUgUVIgY29kZSAqL1xuICAgIHByaXZhdGUgaGF2ZUZldGNoZWRRUkNvZGUgPSBmYWxzZTtcblxuICAgIHB1YmxpYyBjb25zdHJ1Y3Rvcihwcm9wczogSVByb3BzKSB7XG4gICAgICAgIHN1cGVyKHByb3BzKTtcbiAgICAgICAgdGhpcy5zdGF0ZSA9IHsgcXJDb2RlQnl0ZXM6IHVuZGVmaW5lZCwgc2FzRXZlbnQ6IG51bGwsIHJlY2lwcm9jYXRlUVJFdmVudDogbnVsbCB9O1xuICAgICAgICB0aGlzLmhhc1ZlcmlmaWVyID0gZmFsc2U7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSByZW5kZXJRUlBoYXNlKCk6IEpTWC5FbGVtZW50IHtcbiAgICAgICAgY29uc3QgeyBtZW1iZXIsIHJlcXVlc3QgfSA9IHRoaXMucHJvcHM7XG4gICAgICAgIGNvbnN0IHNob3dTQVM6IGJvb2xlYW4gPSByZXF1ZXN0Lm90aGVyUGFydHlTdXBwb3J0c01ldGhvZChWZXJpZmljYXRpb25NZXRob2QuU2FzKTtcbiAgICAgICAgY29uc3Qgc2hvd1FSOiBib29sZWFuID0gcmVxdWVzdC5vdGhlclBhcnR5U3VwcG9ydHNNZXRob2QoVmVyaWZpY2F0aW9uTWV0aG9kLlNjYW5RckNvZGUpO1xuICAgICAgICBjb25zdCBicmFuZCA9IFNka0NvbmZpZy5nZXQoKS5icmFuZDtcblxuICAgICAgICBjb25zdCBub0NvbW1vbk1ldGhvZEVycm9yOiBKU1guRWxlbWVudCB8IG51bGwgPVxuICAgICAgICAgICAgIXNob3dTQVMgJiYgIXNob3dRUiA/IDxwPntfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufG5vX3N1cHBvcnRfcXJfZW1vamlcIiwgeyBicmFuZCB9KX08L3A+IDogbnVsbDtcblxuICAgICAgICBpZiAodGhpcy5wcm9wcy5sYXlvdXQgPT09IFwiZGlhbG9nXCIpIHtcbiAgICAgICAgICAgIC8vIEhBQ0s6IFRoaXMgaXMgYSB0ZXJyaWJsZSBpZGVhLlxuICAgICAgICAgICAgbGV0IHFyQmxvY2tEaWFsb2c6IEpTWC5FbGVtZW50IHwgdW5kZWZpbmVkO1xuICAgICAgICAgICAgbGV0IHNhc0Jsb2NrRGlhbG9nOiBKU1guRWxlbWVudCB8IHVuZGVmaW5lZDtcbiAgICAgICAgICAgIGlmIChzaG93UVIpIHtcbiAgICAgICAgICAgICAgICBxckJsb2NrRGlhbG9nID0gKFxuICAgICAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzTmFtZT1cIm14X1ZlcmlmaWNhdGlvblBhbmVsX1FSUGhhc2Vfc3RhcnRPcHRpb25cIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxwPntfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufHFyX3Byb21wdFwiKX08L3A+XG4gICAgICAgICAgICAgICAgICAgICAgICA8VmVyaWZpY2F0aW9uUVJDb2RlIHFyQ29kZUJ5dGVzPXt0aGlzLnN0YXRlLnFyQ29kZUJ5dGVzfSAvPlxuICAgICAgICAgICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgfVxuICAgICAgICAgICAgaWYgKHNob3dTQVMpIHtcbiAgICAgICAgICAgICAgICBzYXNCbG9ja0RpYWxvZyA9IChcbiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJteF9WZXJpZmljYXRpb25QYW5lbF9RUlBoYXNlX3N0YXJ0T3B0aW9uXCI+XG4gICAgICAgICAgICAgICAgICAgICAgICA8cD57X3QoXCJlbmNyeXB0aW9ufHZlcmlmaWNhdGlvbnxzYXNfcHJvbXB0XCIpfTwvcD5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxzcGFuIGNsYXNzTmFtZT1cIm14X1ZlcmlmaWNhdGlvblBhbmVsX1FSUGhhc2VfaGVscFRleHRcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7X3QoXCJlbmNyeXB0aW9ufHZlcmlmaWNhdGlvbnxzYXNfZGVzY3JpcHRpb25cIil9XG4gICAgICAgICAgICAgICAgICAgICAgICA8L3NwYW4+XG4gICAgICAgICAgICAgICAgICAgICAgICA8QWNjZXNzaWJsZUJ1dHRvblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc2FibGVkPXt0aGlzLnN0YXRlLmVtb2ppQnV0dG9uQ2xpY2tlZH1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBvbkNsaWNrPXt0aGlzLnN0YXJ0U0FTfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtpbmQ9XCJwcmltYXJ5XCJcbiAgICAgICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB7X3QoXCJhY3Rpb258c3RhcnRcIil9XG4gICAgICAgICAgICAgICAgICAgICAgICA8L0FjY2Vzc2libGVCdXR0b24+XG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICBjb25zdCBvciA9XG4gICAgICAgICAgICAgICAgcXJCbG9ja0RpYWxvZyAmJiBzYXNCbG9ja0RpYWxvZyA/IChcbiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJteF9WZXJpZmljYXRpb25QYW5lbF9RUlBoYXNlX2JldHdlZW5UZXh0XCI+XG4gICAgICAgICAgICAgICAgICAgICAgICB7X3QoXCJlbmNyeXB0aW9ufHZlcmlmaWNhdGlvbnxxcl9vcl9zYXNcIiwge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVtb2ppQ29tcGFyZTogXCJcIixcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBxckNvZGU6IFwiXCIsXG4gICAgICAgICAgICAgICAgICAgICAgICB9KX1cbiAgICAgICAgICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICAgICAgICAgKSA6IG51bGw7XG4gICAgICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgICAgIDxkaXY+XG4gICAgICAgICAgICAgICAgICAgIHtfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufHFyX29yX3Nhc19oZWFkZXJcIil9XG4gICAgICAgICAgICAgICAgICAgIDxkaXYgY2xhc3NOYW1lPVwibXhfVmVyaWZpY2F0aW9uUGFuZWxfUVJQaGFzZV9zdGFydE9wdGlvbnNcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIHtxckJsb2NrRGlhbG9nfVxuICAgICAgICAgICAgICAgICAgICAgICAge29yfVxuICAgICAgICAgICAgICAgICAgICAgICAge3Nhc0Jsb2NrRGlhbG9nfVxuICAgICAgICAgICAgICAgICAgICAgICAge25vQ29tbW9uTWV0aG9kRXJyb3J9XG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBxckJsb2NrOiBKU1guRWxlbWVudCB8IHVuZGVmaW5lZDtcbiAgICAgICAgaWYgKHNob3dRUikge1xuICAgICAgICAgICAgcXJCbG9jayA9IChcbiAgICAgICAgICAgICAgICA8ZGl2IGNsYXNzTmFtZT1cIm14X1VzZXJJbmZvX2NvbnRhaW5lclwiPlxuICAgICAgICAgICAgICAgICAgICA8aDM+e190KFwiZW5jcnlwdGlvbnx2ZXJpZmljYXRpb258c2Nhbl9xclwiKX08L2gzPlxuICAgICAgICAgICAgICAgICAgICA8cD5cbiAgICAgICAgICAgICAgICAgICAgICAgIHtfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufHNjYW5fcXJfZXhwbGFpbmVyXCIsIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXNwbGF5TmFtZTogKG1lbWJlciBhcyBVc2VyKS5kaXNwbGF5TmFtZSB8fCAobWVtYmVyIGFzIFJvb21NZW1iZXIpLm5hbWUgfHwgbWVtYmVyLnVzZXJJZCxcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pfVxuICAgICAgICAgICAgICAgICAgICA8L3A+XG5cbiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJteF9WZXJpZmljYXRpb25QYW5lbF9xckNvZGVcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxWZXJpZmljYXRpb25RUkNvZGUgcXJDb2RlQnl0ZXM9e3RoaXMuc3RhdGUucXJDb2RlQnl0ZXN9IC8+XG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBzYXNCbG9jazogSlNYLkVsZW1lbnQgfCB1bmRlZmluZWQ7XG4gICAgICAgIGlmIChzaG93U0FTKSB7XG4gICAgICAgICAgICBjb25zdCBkaXNhYmxlZCA9IHRoaXMuc3RhdGUuZW1vamlCdXR0b25DbGlja2VkO1xuICAgICAgICAgICAgY29uc3Qgc2FzTGFiZWwgPSBzaG93UVJcbiAgICAgICAgICAgICAgICA/IF90KFwiZW5jcnlwdGlvbnx2ZXJpZmljYXRpb258dmVyaWZ5X2Vtb2ppX3Byb21wdF9xclwiKVxuICAgICAgICAgICAgICAgIDogX3QoXCJlbmNyeXB0aW9ufHZlcmlmaWNhdGlvbnx2ZXJpZnlfZW1vamlfcHJvbXB0XCIpO1xuXG4gICAgICAgICAgICAvLyBOb3RlOiBteF9WZXJpZmljYXRpb25QYW5lbF92ZXJpZnlCeUVtb2ppQnV0dG9uIGlzIGZvciB0aGUgZW5kLXRvLWVuZCB0ZXN0c1xuICAgICAgICAgICAgc2FzQmxvY2sgPSAoXG4gICAgICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJteF9Vc2VySW5mb19jb250YWluZXJcIj5cbiAgICAgICAgICAgICAgICAgICAgPGgzPntfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufHZlcmlmeV9lbW9qaVwiKX08L2gzPlxuICAgICAgICAgICAgICAgICAgICA8cD57c2FzTGFiZWx9PC9wPlxuICAgICAgICAgICAgICAgICAgICA8QWNjZXNzaWJsZUJ1dHRvblxuICAgICAgICAgICAgICAgICAgICAgICAgZGlzYWJsZWQ9e2Rpc2FibGVkfVxuICAgICAgICAgICAgICAgICAgICAgICAga2luZD1cInByaW1hcnlcIlxuICAgICAgICAgICAgICAgICAgICAgICAgY2xhc3NOYW1lPVwibXhfVXNlckluZm9fd2lkZUJ1dHRvbiBteF9WZXJpZmljYXRpb25QYW5lbF92ZXJpZnlCeUVtb2ppQnV0dG9uXCJcbiAgICAgICAgICAgICAgICAgICAgICAgIG9uQ2xpY2s9e3RoaXMuc3RhcnRTQVN9XG4gICAgICAgICAgICAgICAgICAgID5cbiAgICAgICAgICAgICAgICAgICAgICAgIHtfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufHZlcmlmeV9lbW9qaVwiKX1cbiAgICAgICAgICAgICAgICAgICAgPC9BY2Nlc3NpYmxlQnV0dG9uPlxuICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgKTtcbiAgICAgICAgfVxuXG4gICAgICAgIGNvbnN0IG5vQ29tbW9uTWV0aG9kQmxvY2sgPSBub0NvbW1vbk1ldGhvZEVycm9yID8gKFxuICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJteF9Vc2VySW5mb19jb250YWluZXJcIj57bm9Db21tb25NZXRob2RFcnJvcn08L2Rpdj5cbiAgICAgICAgKSA6IG51bGw7XG5cbiAgICAgICAgLy8gVE9ETzogYWRkIHdheSB0byBvcGVuIGNhbWVyYSB0byBzY2FuIGEgUVIgY29kZVxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgPFJlYWN0LkZyYWdtZW50PlxuICAgICAgICAgICAgICAgIHtxckJsb2NrfVxuICAgICAgICAgICAgICAgIHtzYXNCbG9ja31cbiAgICAgICAgICAgICAgICB7bm9Db21tb25NZXRob2RCbG9ja31cbiAgICAgICAgICAgIDwvUmVhY3QuRnJhZ21lbnQ+XG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBvblJlY2lwcm9jYXRlWWVzQ2xpY2sgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIGlmICghdGhpcy5zdGF0ZS5yZWNpcHJvY2F0ZVFSRXZlbnQpIHJldHVybjtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IHJlY2lwcm9jYXRlQnV0dG9uQ2xpY2tlZDogdHJ1ZSB9KTtcbiAgICAgICAgdGhpcy5zdGF0ZS5yZWNpcHJvY2F0ZVFSRXZlbnQ/LmNvbmZpcm0oKTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvblJlY2lwcm9jYXRlTm9DbGljayA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgaWYgKCF0aGlzLnN0YXRlLnJlY2lwcm9jYXRlUVJFdmVudCkgcmV0dXJuO1xuICAgICAgICB0aGlzLnNldFN0YXRlKHsgcmVjaXByb2NhdGVCdXR0b25DbGlja2VkOiB0cnVlIH0pO1xuICAgICAgICB0aGlzLnN0YXRlLnJlY2lwcm9jYXRlUVJFdmVudD8uY2FuY2VsKCk7XG4gICAgfTtcblxuICAgIC8qKlxuICAgICAqIEdldCBkZXRhaWxzIG9mIHRoZSBvdGhlciBkZXZpY2UgaW52b2x2ZWQgaW4gdGhlIHZlcmlmaWNhdGlvbiwgaWYgd2UgaGF2ZW4ndCBiZWZvcmUsIGFuZCBzdG9yZSBpbiB0aGUgc3RhdGUuXG4gICAgICovXG4gICAgcHJpdmF0ZSBhc3luYyBtYXliZUdldE90aGVyRGV2aWNlKCk6IFByb21pc2U8dm9pZD4ge1xuICAgICAgICBpZiAodGhpcy5oYXZlQ2hlY2tlZERldmljZSkgcmV0dXJuO1xuXG4gICAgICAgIGNvbnN0IGNsaWVudCA9IE1hdHJpeENsaWVudFBlZy5zYWZlR2V0KCk7XG4gICAgICAgIGNvbnN0IGRldmljZUlkID0gdGhpcy5wcm9wcy5yZXF1ZXN0Py5vdGhlckRldmljZUlkO1xuICAgICAgICBjb25zdCB1c2VySWQgPSBjbGllbnQuZ2V0VXNlcklkKCk7XG4gICAgICAgIGlmICghZGV2aWNlSWQgfHwgIXVzZXJJZCkge1xuICAgICAgICAgICAgcmV0dXJuO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMuaGF2ZUNoZWNrZWREZXZpY2UgPSB0cnVlO1xuICAgICAgICB0aGlzLnNldFN0YXRlKHsgb3RoZXJEZXZpY2VEZXRhaWxzOiBhd2FpdCBnZXREZXZpY2VDcnlwdG9JbmZvKGNsaWVudCwgdXNlcklkLCBkZXZpY2VJZCkgfSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSByZW5kZXJRUlJlY2lwcm9jYXRlUGhhc2UoKTogSlNYLkVsZW1lbnQge1xuICAgICAgICBjb25zdCB7IG1lbWJlciwgcmVxdWVzdCB9ID0gdGhpcy5wcm9wcztcbiAgICAgICAgY29uc3QgZGVzY3JpcHRpb24gPSByZXF1ZXN0LmlzU2VsZlZlcmlmaWNhdGlvblxuICAgICAgICAgICAgPyBfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufHFyX3JlY2lwcm9jYXRlX3NhbWVfc2hpZWxkX2RldmljZVwiKVxuICAgICAgICAgICAgOiBfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufHFyX3JlY2lwcm9jYXRlX3NhbWVfc2hpZWxkX3VzZXJcIiwge1xuICAgICAgICAgICAgICAgICAgZGlzcGxheU5hbWU6IChtZW1iZXIgYXMgVXNlcikuZGlzcGxheU5hbWUgfHwgKG1lbWJlciBhcyBSb29tTWVtYmVyKS5uYW1lIHx8IG1lbWJlci51c2VySWQsXG4gICAgICAgICAgICAgIH0pO1xuICAgICAgICBsZXQgYm9keTogSlNYLkVsZW1lbnQ7XG4gICAgICAgIGlmICh0aGlzLnN0YXRlLnJlY2lwcm9jYXRlUVJFdmVudCkge1xuICAgICAgICAgICAgLy8gRWxlbWVudCBXZWIgZG9lc24ndCBzdXBwb3J0IHNjYW5uaW5nIHlldCwgc28gYXNzdW1lIGhlcmUgd2UncmUgdGhlIGNsaWVudCBiZWluZyBzY2FubmVkLlxuICAgICAgICAgICAgYm9keSA9IChcbiAgICAgICAgICAgICAgICA8UmVhY3QuRnJhZ21lbnQ+XG4gICAgICAgICAgICAgICAgICAgIDxwPntkZXNjcmlwdGlvbn08L3A+XG4gICAgICAgICAgICAgICAgICAgIDxFMkVJY29uIGlzVXNlcj17dHJ1ZX0gc3RhdHVzPXtFMkVTdGF0ZS5WZXJpZmllZH0gc2l6ZT17MTI4fSBoaWRlVG9vbHRpcD17dHJ1ZX0gLz5cbiAgICAgICAgICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJteF9WZXJpZmljYXRpb25QYW5lbF9yZWNpcHJvY2F0ZUJ1dHRvbnNcIj5cbiAgICAgICAgICAgICAgICAgICAgICAgIDxBY2Nlc3NpYmxlQnV0dG9uXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAga2luZD1cImRhbmdlclwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzYWJsZWQ9e3RoaXMuc3RhdGUucmVjaXByb2NhdGVCdXR0b25DbGlja2VkfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uQ2xpY2s9e3RoaXMub25SZWNpcHJvY2F0ZU5vQ2xpY2t9XG4gICAgICAgICAgICAgICAgICAgICAgICA+XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAge190KFwiYWN0aW9ufG5vXCIpfVxuICAgICAgICAgICAgICAgICAgICAgICAgPC9BY2Nlc3NpYmxlQnV0dG9uPlxuICAgICAgICAgICAgICAgICAgICAgICAgPEFjY2Vzc2libGVCdXR0b25cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBraW5kPVwicHJpbWFyeVwiXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlzYWJsZWQ9e3RoaXMuc3RhdGUucmVjaXByb2NhdGVCdXR0b25DbGlja2VkfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uQ2xpY2s9e3RoaXMub25SZWNpcHJvY2F0ZVllc0NsaWNrfVxuICAgICAgICAgICAgICAgICAgICAgICAgPlxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHtfdChcImFjdGlvbnx5ZXNcIil9XG4gICAgICAgICAgICAgICAgICAgICAgICA8L0FjY2Vzc2libGVCdXR0b24+XG4gICAgICAgICAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICAgICAgICAgIDwvUmVhY3QuRnJhZ21lbnQ+XG4gICAgICAgICAgICApO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgYm9keSA9IChcbiAgICAgICAgICAgICAgICA8cD5cbiAgICAgICAgICAgICAgICAgICAgPFNwaW5uZXIgLz5cbiAgICAgICAgICAgICAgICA8L3A+XG4gICAgICAgICAgICApO1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICA8ZGl2IGNsYXNzTmFtZT1cIm14X1VzZXJJbmZvX2NvbnRhaW5lciBteF9WZXJpZmljYXRpb25QYW5lbF9yZWNpcHJvY2F0ZV9zZWN0aW9uXCI+XG4gICAgICAgICAgICAgICAgPGgzPntfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufHNjYW5fcXJcIil9PC9oMz5cbiAgICAgICAgICAgICAgICB7Ym9keX1cbiAgICAgICAgICAgIDwvZGl2PlxuICAgICAgICApO1xuICAgIH1cblxuICAgIHByaXZhdGUgcmVuZGVyVmVyaWZpZWRQaGFzZSgpOiBKU1guRWxlbWVudCB7XG4gICAgICAgIGNvbnN0IHsgbWVtYmVyLCByZXF1ZXN0IH0gPSB0aGlzLnByb3BzO1xuXG4gICAgICAgIGxldCB0ZXh0OiBzdHJpbmcgfCB1bmRlZmluZWQ7XG4gICAgICAgIGlmICghcmVxdWVzdC5pc1NlbGZWZXJpZmljYXRpb24pIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnByb3BzLmlzUm9vbUVuY3J5cHRlZCkge1xuICAgICAgICAgICAgICAgIHRleHQgPSBfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufHByb21wdF9lbmNyeXB0ZWRcIik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRleHQgPSBfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufHByb21wdF91bmVuY3J5cHRlZFwiKTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGxldCBkZXNjcmlwdGlvbjogc3RyaW5nO1xuICAgICAgICBpZiAocmVxdWVzdC5pc1NlbGZWZXJpZmljYXRpb24pIHtcbiAgICAgICAgICAgIGNvbnN0IGRldmljZSA9IHRoaXMuc3RhdGUub3RoZXJEZXZpY2VEZXRhaWxzO1xuICAgICAgICAgICAgaWYgKCFkZXZpY2UpIHtcbiAgICAgICAgICAgICAgICAvLyBUaGlzIGNhbiBoYXBwZW4gaWYgdGhlIGRldmljZSBpcyBsb2dnZWQgb3V0IHdoaWxlIHdlJ3JlIHN0aWxsIHNob3dpbmcgdmVyaWZpY2F0aW9uXG4gICAgICAgICAgICAgICAgLy8gVUkgZm9yIGl0LlxuICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuKFwiVmVyaWZpZWQgZGV2aWNlIHdlIGRvbid0IGtub3cgYWJvdXQ6IFwiICsgdGhpcy5wcm9wcy5yZXF1ZXN0Lm90aGVyRGV2aWNlSWQpO1xuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uID0gX3QoXCJlbmNyeXB0aW9ufHZlcmlmaWNhdGlvbnxzdWNjZXNzZnVsX293bl9kZXZpY2VcIik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIGRlc2NyaXB0aW9uID0gX3QoXCJlbmNyeXB0aW9ufHZlcmlmaWNhdGlvbnxzdWNjZXNzZnVsX2RldmljZVwiLCB7XG4gICAgICAgICAgICAgICAgICAgIGRldmljZU5hbWU6IGRldmljZS5kaXNwbGF5TmFtZSxcbiAgICAgICAgICAgICAgICAgICAgZGV2aWNlSWQ6IGRldmljZS5kZXZpY2VJZCxcbiAgICAgICAgICAgICAgICB9KTtcbiAgICAgICAgICAgIH1cbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIGRlc2NyaXB0aW9uID0gX3QoXCJlbmNyeXB0aW9ufHZlcmlmaWNhdGlvbnxzdWNjZXNzZnVsX3VzZXJcIiwge1xuICAgICAgICAgICAgICAgIGRpc3BsYXlOYW1lOiAobWVtYmVyIGFzIFVzZXIpLmRpc3BsYXlOYW1lIHx8IChtZW1iZXIgYXMgUm9vbU1lbWJlcikubmFtZSB8fCBtZW1iZXIudXNlcklkLFxuICAgICAgICAgICAgfSk7XG4gICAgICAgIH1cblxuICAgICAgICByZXR1cm4gKFxuICAgICAgICAgICAgPGRpdiBjbGFzc05hbWU9XCJteF9Vc2VySW5mb19jb250YWluZXIgbXhfVmVyaWZpY2F0aW9uUGFuZWxfdmVyaWZpZWRfc2VjdGlvblwiPlxuICAgICAgICAgICAgICAgIDxwPntkZXNjcmlwdGlvbn08L3A+XG4gICAgICAgICAgICAgICAgPEUyRUljb24gaXNVc2VyPXt0cnVlfSBzdGF0dXM9e0UyRVN0YXRlLlZlcmlmaWVkfSBzaXplPXsxMjh9IGhpZGVUb29sdGlwPXt0cnVlfSAvPlxuICAgICAgICAgICAgICAgIHt0ZXh0ID8gPHA+e3RleHR9PC9wPiA6IG51bGx9XG4gICAgICAgICAgICAgICAgPEFjY2Vzc2libGVCdXR0b24ga2luZD1cInByaW1hcnlcIiBjbGFzc05hbWU9XCJteF9Vc2VySW5mb193aWRlQnV0dG9uXCIgb25DbGljaz17dGhpcy5wcm9wcy5vbkNsb3NlfT5cbiAgICAgICAgICAgICAgICAgICAge190KFwiYWN0aW9ufGdvdF9pdFwiKX1cbiAgICAgICAgICAgICAgICA8L0FjY2Vzc2libGVCdXR0b24+XG4gICAgICAgICAgICA8L2Rpdj5cbiAgICAgICAgKTtcbiAgICB9XG5cbiAgICBwcml2YXRlIHJlbmRlckNhbmNlbGxlZFBoYXNlKCk6IEpTWC5FbGVtZW50IHtcbiAgICAgICAgY29uc3QgeyBtZW1iZXIsIHJlcXVlc3QgfSA9IHRoaXMucHJvcHM7XG5cbiAgICAgICAgbGV0IHN0YXJ0QWdhaW5JbnN0cnVjdGlvbjogc3RyaW5nO1xuICAgICAgICBpZiAocmVxdWVzdC5pc1NlbGZWZXJpZmljYXRpb24pIHtcbiAgICAgICAgICAgIHN0YXJ0QWdhaW5JbnN0cnVjdGlvbiA9IF90KFwiZW5jcnlwdGlvbnx2ZXJpZmljYXRpb258cHJvbXB0X3NlbGZcIik7XG4gICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBzdGFydEFnYWluSW5zdHJ1Y3Rpb24gPSBfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufHByb21wdF91c2VyXCIpO1xuICAgICAgICB9XG5cbiAgICAgICAgbGV0IHRleHQ6IHN0cmluZztcbiAgICAgICAgaWYgKHJlcXVlc3QuY2FuY2VsbGF0aW9uQ29kZSA9PT0gXCJtLnRpbWVvdXRcIikge1xuICAgICAgICAgICAgdGV4dCA9IF90KFwiZW5jcnlwdGlvbnx2ZXJpZmljYXRpb258dGltZWRfb3V0XCIpICsgYCAke3N0YXJ0QWdhaW5JbnN0cnVjdGlvbn1gO1xuICAgICAgICB9IGVsc2UgaWYgKHJlcXVlc3QuY2FuY2VsbGluZ1VzZXJJZCA9PT0gcmVxdWVzdC5vdGhlclVzZXJJZCkge1xuICAgICAgICAgICAgaWYgKHJlcXVlc3QuaXNTZWxmVmVyaWZpY2F0aW9uKSB7XG4gICAgICAgICAgICAgICAgdGV4dCA9IF90KFwiZW5jcnlwdGlvbnx2ZXJpZmljYXRpb258Y2FuY2VsbGVkX3NlbGZcIik7XG4gICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgIHRleHQgPSBfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufGNhbmNlbGxlZF91c2VyXCIsIHtcbiAgICAgICAgICAgICAgICAgICAgZGlzcGxheU5hbWU6IChtZW1iZXIgYXMgVXNlcikuZGlzcGxheU5hbWUgfHwgKG1lbWJlciBhcyBSb29tTWVtYmVyKS5uYW1lIHx8IG1lbWJlci51c2VySWQsXG4gICAgICAgICAgICAgICAgfSk7XG4gICAgICAgICAgICB9XG4gICAgICAgICAgICB0ZXh0ID0gYCR7dGV4dH0gJHtzdGFydEFnYWluSW5zdHJ1Y3Rpb259YDtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgIHRleHQgPSBfdChcImVuY3J5cHRpb258dmVyaWZpY2F0aW9ufGNhbmNlbGxlZFwiKSArIGAgJHtzdGFydEFnYWluSW5zdHJ1Y3Rpb259YDtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiAoXG4gICAgICAgICAgICA8ZGl2IGNsYXNzTmFtZT1cIm14X1VzZXJJbmZvX2NvbnRhaW5lclwiPlxuICAgICAgICAgICAgICAgIDxoMz57X3QoXCJjb21tb258dmVyaWZpY2F0aW9uX2NhbmNlbGxlZFwiKX08L2gzPlxuICAgICAgICAgICAgICAgIDxwPnt0ZXh0fTwvcD5cblxuICAgICAgICAgICAgICAgIDxBY2Nlc3NpYmxlQnV0dG9uIGtpbmQ9XCJwcmltYXJ5XCIgY2xhc3NOYW1lPVwibXhfVXNlckluZm9fd2lkZUJ1dHRvblwiIG9uQ2xpY2s9e3RoaXMucHJvcHMub25DbG9zZX0+XG4gICAgICAgICAgICAgICAgICAgIHtfdChcImFjdGlvbnxnb3RfaXRcIil9XG4gICAgICAgICAgICAgICAgPC9BY2Nlc3NpYmxlQnV0dG9uPlxuICAgICAgICAgICAgPC9kaXY+XG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHVibGljIHJlbmRlcigpOiBSZWFjdC5SZWFjdE5vZGUge1xuICAgICAgICBjb25zdCB7IG1lbWJlciwgcGhhc2UsIHJlcXVlc3QgfSA9IHRoaXMucHJvcHM7XG5cbiAgICAgICAgY29uc3QgZGlzcGxheU5hbWUgPSAobWVtYmVyIGFzIFVzZXIpLmRpc3BsYXlOYW1lIHx8IChtZW1iZXIgYXMgUm9vbU1lbWJlcikubmFtZSB8fCBtZW1iZXIudXNlcklkO1xuXG4gICAgICAgIHN3aXRjaCAocGhhc2UpIHtcbiAgICAgICAgICAgIGNhc2UgUGhhc2UuUmVhZHk6XG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMucmVuZGVyUVJQaGFzZSgpO1xuICAgICAgICAgICAgY2FzZSBQaGFzZS5TdGFydGVkOlxuICAgICAgICAgICAgICAgIHN3aXRjaCAocmVxdWVzdC5jaG9zZW5NZXRob2QpIHtcbiAgICAgICAgICAgICAgICAgICAgY2FzZSBWZXJpZmljYXRpb25NZXRob2QuUmVjaXByb2NhdGU6XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5yZW5kZXJRUlJlY2lwcm9jYXRlUGhhc2UoKTtcbiAgICAgICAgICAgICAgICAgICAgY2FzZSBWZXJpZmljYXRpb25NZXRob2QuU2FzOiB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBlbW9qaXMgPSB0aGlzLnN0YXRlLnNhc0V2ZW50ID8gKFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIDxWZXJpZmljYXRpb25TaG93U2FzXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3BsYXlOYW1lPXtkaXNwbGF5TmFtZX1cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3RoZXJEZXZpY2VEZXRhaWxzPXt0aGlzLnN0YXRlLm90aGVyRGV2aWNlRGV0YWlsc31cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2FzPXt0aGlzLnN0YXRlLnNhc0V2ZW50LnNhc31cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25DYW5jZWw9e3RoaXMub25TYXNNaXNtYXRjaGVzQ2xpY2t9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9uRG9uZT17dGhpcy5vblNhc01hdGNoZXNDbGlja31cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5EaWFsb2c9e3RoaXMucHJvcHMuaW5EaWFsb2d9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlzU2VsZj17cmVxdWVzdC5pc1NlbGZWZXJpZmljYXRpb259XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLz5cbiAgICAgICAgICAgICAgICAgICAgICAgICkgOiAoXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgPFNwaW5uZXIgLz5cbiAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gPGRpdiBjbGFzc05hbWU9XCJteF9Vc2VySW5mb19jb250YWluZXJcIj57ZW1vamlzfTwvZGl2PjtcbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG51bGw7XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgY2FzZSBQaGFzZS5Eb25lOlxuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnJlbmRlclZlcmlmaWVkUGhhc2UoKTtcbiAgICAgICAgICAgIGNhc2UgUGhhc2UuQ2FuY2VsbGVkOlxuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnJlbmRlckNhbmNlbGxlZFBoYXNlKCk7XG4gICAgICAgIH1cbiAgICAgICAgbG9nZ2VyLmVycm9yKFwiVmVyaWZpY2F0aW9uUGFuZWwgdW5oYW5kbGVkIHBoYXNlOlwiLCBwaGFzZSk7XG4gICAgICAgIHJldHVybiBudWxsO1xuICAgIH1cblxuICAgIHByaXZhdGUgc3RhcnRTQVMgPSBhc3luYyAoKTogUHJvbWlzZTx2b2lkPiA9PiB7XG4gICAgICAgIHRoaXMuc2V0U3RhdGUoeyBlbW9qaUJ1dHRvbkNsaWNrZWQ6IHRydWUgfSk7XG4gICAgICAgIGF3YWl0IHRoaXMucHJvcHMucmVxdWVzdC5zdGFydFZlcmlmaWNhdGlvbihWZXJpZmljYXRpb25NZXRob2QuU2FzKTtcbiAgICB9O1xuXG4gICAgcHJpdmF0ZSBvblNhc01hdGNoZXNDbGljayA9ICgpOiB2b2lkID0+IHtcbiAgICAgICAgdGhpcy5zdGF0ZS5zYXNFdmVudD8uY29uZmlybSgpO1xuICAgIH07XG5cbiAgICBwcml2YXRlIG9uU2FzTWlzbWF0Y2hlc0NsaWNrID0gKCk6IHZvaWQgPT4ge1xuICAgICAgICB0aGlzLnN0YXRlLnNhc0V2ZW50Py5taXNtYXRjaCgpO1xuICAgIH07XG5cbiAgICBwcml2YXRlIHVwZGF0ZVZlcmlmaWVyU3RhdGUgPSAoKTogdm9pZCA9PiB7XG4gICAgICAgIC8vIHRoaXMgbWV0aG9kIGlzIG9ubHkgY2FsbGVkIG9uY2Ugd2Uga25vdyB0aGVyZSBpcyBhIHZlcmlmaWVyLlxuICAgICAgICBjb25zdCB2ZXJpZmllciA9IHRoaXMucHJvcHMucmVxdWVzdC52ZXJpZmllciE7XG4gICAgICAgIGNvbnN0IHNhc0V2ZW50ID0gdmVyaWZpZXIuZ2V0U2hvd1Nhc0NhbGxiYWNrcygpO1xuICAgICAgICBjb25zdCByZWNpcHJvY2F0ZVFSRXZlbnQgPSB2ZXJpZmllci5nZXRSZWNpcHJvY2F0ZVFyQ29kZUNhbGxiYWNrcygpO1xuICAgICAgICB2ZXJpZmllci5vZmYoVmVyaWZpZXJFdmVudC5TaG93U2FzLCB0aGlzLnVwZGF0ZVZlcmlmaWVyU3RhdGUpO1xuICAgICAgICB2ZXJpZmllci5vZmYoVmVyaWZpZXJFdmVudC5TaG93UmVjaXByb2NhdGVRciwgdGhpcy51cGRhdGVWZXJpZmllclN0YXRlKTtcbiAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IHNhc0V2ZW50LCByZWNpcHJvY2F0ZVFSRXZlbnQgfSk7XG4gICAgfTtcblxuICAgIHByaXZhdGUgb25SZXF1ZXN0Q2hhbmdlID0gYXN5bmMgKCk6IFByb21pc2U8dm9pZD4gPT4ge1xuICAgICAgICBjb25zdCB7IHJlcXVlc3QgfSA9IHRoaXMucHJvcHM7XG5cbiAgICAgICAgLy8gaWYgd2UgaGF2ZSBhIGRldmljZSBJRCBhbmQgZGlkIG5vdCBoYXZlIG9uZSBiZWZvcmUsIGZldGNoIHRoZSBkZXZpY2UncyBkZXRhaWxzXG4gICAgICAgIHRoaXMubWF5YmVHZXRPdGhlckRldmljZSgpO1xuXG4gICAgICAgIC8vIGlmIHdlIGhhdmUgaGFkIGEgcmVwbHkgZnJvbSB0aGUgb3RoZXIgc2lkZSAoaWUsIHRoZSBwaGFzZSBpcyBcInJlYWR5XCIpIGFuZCB3ZSBoYXZlIG5vdFxuICAgICAgICAvLyB5ZXQgZG9uZSBzbywgZmV0Y2ggdGhlIFFSIGNvZGVcbiAgICAgICAgaWYgKHJlcXVlc3QucGhhc2UgPT09IFBoYXNlLlJlYWR5ICYmICF0aGlzLmhhdmVGZXRjaGVkUVJDb2RlKSB7XG4gICAgICAgICAgICB0aGlzLmhhdmVGZXRjaGVkUVJDb2RlID0gdHJ1ZTtcbiAgICAgICAgICAgIHJlcXVlc3QuZ2VuZXJhdGVRUkNvZGUoKS50aGVuKFxuICAgICAgICAgICAgICAgIChidWYpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5zZXRTdGF0ZSh7IHFyQ29kZUJ5dGVzOiBidWYgfSk7XG4gICAgICAgICAgICAgICAgfSxcbiAgICAgICAgICAgICAgICAoZXJyb3IpID0+IHtcbiAgICAgICAgICAgICAgICAgICAgY29uc29sZS5lcnJvcihcIkVycm9yIGdlbmVyYXRpbmcgUVIgY29kZTpcIiwgZXJyb3IpO1xuICAgICAgICAgICAgICAgIH0sXG4gICAgICAgICAgICApO1xuICAgICAgICB9XG5cbiAgICAgICAgY29uc3QgaGFkVmVyaWZpZXIgPSB0aGlzLmhhc1ZlcmlmaWVyO1xuICAgICAgICB0aGlzLmhhc1ZlcmlmaWVyID0gISFyZXF1ZXN0LnZlcmlmaWVyO1xuICAgICAgICBpZiAoIWhhZFZlcmlmaWVyICYmIHRoaXMuaGFzVmVyaWZpZXIpIHtcbiAgICAgICAgICAgIHJlcXVlc3QudmVyaWZpZXI/Lm9uKFZlcmlmaWVyRXZlbnQuU2hvd1NhcywgdGhpcy51cGRhdGVWZXJpZmllclN0YXRlKTtcbiAgICAgICAgICAgIHJlcXVlc3QudmVyaWZpZXI/Lm9uKFZlcmlmaWVyRXZlbnQuU2hvd1JlY2lwcm9jYXRlUXIsIHRoaXMudXBkYXRlVmVyaWZpZXJTdGF0ZSk7XG4gICAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgICAgIC8vIG9uIHRoZSByZXF1ZXN0ZXIgc2lkZSwgdGhpcyBpcyBhbHNvIGF3YWl0ZWQgaW4gc3RhcnRTQVMsXG4gICAgICAgICAgICAgICAgLy8gYnV0IHRoYXQncyBvayBhcyB2ZXJpZnkgc2hvdWxkIHJldHVybiB0aGUgc2FtZSBwcm9taXNlLlxuICAgICAgICAgICAgICAgIGF3YWl0IHJlcXVlc3QudmVyaWZpZXI/LnZlcmlmeSgpO1xuICAgICAgICAgICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAgICAgICAgICAgbG9nZ2VyLmVycm9yKFwiZXJyb3IgdmVyaWZ5XCIsIGVycik7XG4gICAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICB9O1xuXG4gICAgcHVibGljIGNvbXBvbmVudERpZE1vdW50KCk6IHZvaWQge1xuICAgICAgICBjb25zdCB7IHJlcXVlc3QgfSA9IHRoaXMucHJvcHM7XG4gICAgICAgIHJlcXVlc3Qub24oVmVyaWZpY2F0aW9uUmVxdWVzdEV2ZW50LkNoYW5nZSwgdGhpcy5vblJlcXVlc3RDaGFuZ2UpO1xuICAgICAgICBpZiAocmVxdWVzdC52ZXJpZmllcikge1xuICAgICAgICAgICAgY29uc3Qgc2FzRXZlbnQgPSByZXF1ZXN0LnZlcmlmaWVyLmdldFNob3dTYXNDYWxsYmFja3MoKTtcbiAgICAgICAgICAgIGNvbnN0IHJlY2lwcm9jYXRlUVJFdmVudCA9IHJlcXVlc3QudmVyaWZpZXIuZ2V0UmVjaXByb2NhdGVRckNvZGVDYWxsYmFja3MoKTtcbiAgICAgICAgICAgIHRoaXMuc2V0U3RhdGUoeyBzYXNFdmVudCwgcmVjaXByb2NhdGVRUkV2ZW50IH0pO1xuICAgICAgICB9XG4gICAgICAgIHRoaXMub25SZXF1ZXN0Q2hhbmdlKCk7XG4gICAgfVxuXG4gICAgcHVibGljIGNvbXBvbmVudFdpbGxVbm1vdW50KCk6IHZvaWQge1xuICAgICAgICBjb25zdCB7IHJlcXVlc3QgfSA9IHRoaXMucHJvcHM7XG4gICAgICAgIGlmIChyZXF1ZXN0LnZlcmlmaWVyKSB7XG4gICAgICAgICAgICByZXF1ZXN0LnZlcmlmaWVyLm9mZihWZXJpZmllckV2ZW50LlNob3dTYXMsIHRoaXMudXBkYXRlVmVyaWZpZXJTdGF0ZSk7XG4gICAgICAgICAgICByZXF1ZXN0LnZlcmlmaWVyLm9mZihWZXJpZmllckV2ZW50LlNob3dSZWNpcHJvY2F0ZVFyLCB0aGlzLnVwZGF0ZVZlcmlmaWVyU3RhdGUpO1xuICAgICAgICB9XG4gICAgICAgIHJlcXVlc3Qub2ZmKFZlcmlmaWNhdGlv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