appium-adb-test
Version:
Android Debug Bridge interface
466 lines (359 loc) • 29.8 kB
JavaScript
;
var _regeneratorRuntime = require('babel-runtime/regenerator')['default'];
var _getIterator = require('babel-runtime/core-js/get-iterator')['default'];
var _interopRequireDefault = require('babel-runtime/helpers/interop-require-default')['default'];
Object.defineProperty(exports, '__esModule', {
value: true
});
var _teen_process = require('teen_process');
var _path = require('path');
var _path2 = _interopRequireDefault(_path);
var _loggerJs = require('../logger.js');
var _loggerJs2 = _interopRequireDefault(_loggerJs);
var _appiumSupport = require('appium-support');
var _admZip = require('adm-zip');
var _admZip2 = _interopRequireDefault(_admZip);
var _helpersJs = require('../helpers.js');
var apkSigningMethods = {};
apkSigningMethods.signWithDefaultCert = function callee$0$0(apk) {
var java, signPath;
return _regeneratorRuntime.async(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
java = (0, _helpersJs.getJavaForOs)();
signPath = _path2['default'].resolve(this.helperJarPath, 'sign.jar');
_loggerJs2['default'].debug("Resigning apk.");
context$1$0.prev = 3;
context$1$0.next = 6;
return _regeneratorRuntime.awrap(_appiumSupport.fs.exists(apk));
case 6:
if (context$1$0.sent) {
context$1$0.next = 8;
break;
}
throw new Error(apk + ' file doesn\'t exist.');
case 8:
context$1$0.next = 10;
return _regeneratorRuntime.awrap((0, _teen_process.exec)(java, ['-jar', signPath, apk, '--override']));
case 10:
context$1$0.next = 15;
break;
case 12:
context$1$0.prev = 12;
context$1$0.t0 = context$1$0['catch'](3);
_loggerJs2['default'].errorAndThrow('Could not sign with default ceritficate. Original error ' + context$1$0.t0.message);
case 15:
case 'end':
return context$1$0.stop();
}
}, null, this, [[3, 12]]);
};
apkSigningMethods.signWithCustomCert = function callee$0$0(apk) {
var java, javaHome, jarsigner;
return _regeneratorRuntime.async(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
_loggerJs2['default'].debug('Signing \'' + apk + '\' with custom cert');
java = (0, _helpersJs.getJavaForOs)();
javaHome = (0, _helpersJs.getJavaHome)();
jarsigner = _path2['default'].resolve(javaHome, 'bin', 'jarsigner');
if (_appiumSupport.system.isWindows()) {
jarsigner = jarsigner + '.exe';
}
context$1$0.next = 7;
return _regeneratorRuntime.awrap(_appiumSupport.fs.exists(this.keystorePath));
case 7:
if (context$1$0.sent) {
context$1$0.next = 9;
break;
}
throw new Error('Keystore: ' + this.keystorePath + ' doesn\'t exist.');
case 9:
context$1$0.next = 11;
return _regeneratorRuntime.awrap(_appiumSupport.fs.exists(apk));
case 11:
if (context$1$0.sent) {
context$1$0.next = 13;
break;
}
throw new Error(apk + ' file doesn\'t exist.');
case 13:
context$1$0.prev = 13;
_loggerJs2['default'].debug("Unsigning apk.");
context$1$0.next = 17;
return _regeneratorRuntime.awrap((0, _teen_process.exec)(java, ['-jar', _path2['default'].resolve(this.helperJarPath, 'unsign.jar'), apk]));
case 17:
_loggerJs2['default'].debug("Signing apk.");
context$1$0.next = 20;
return _regeneratorRuntime.awrap((0, _teen_process.exec)(jarsigner, ['-sigalg', 'MD5withRSA', '-digestalg', 'SHA1', '-keystore', this.keystorePath, '-storepass', this.keystorePassword, '-keypass', this.keyPassword, apk, this.keyAlias]));
case 20:
context$1$0.next = 25;
break;
case 22:
context$1$0.prev = 22;
context$1$0.t0 = context$1$0['catch'](13);
_loggerJs2['default'].errorAndThrow('Could not sign with custom ceritficate. Original error ' + context$1$0.t0.message);
case 25:
case 'end':
return context$1$0.stop();
}
}, null, this, [[13, 22]]);
};
apkSigningMethods.sign = function callee$0$0(apk) {
return _regeneratorRuntime.async(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
if (!this.useKeystore) {
context$1$0.next = 5;
break;
}
context$1$0.next = 3;
return _regeneratorRuntime.awrap(this.signWithCustomCert(apk));
case 3:
context$1$0.next = 7;
break;
case 5:
context$1$0.next = 7;
return _regeneratorRuntime.awrap(this.signWithDefaultCert(apk));
case 7:
context$1$0.next = 9;
return _regeneratorRuntime.awrap(this.zipAlignApk(apk));
case 9:
case 'end':
return context$1$0.stop();
}
}, null, this);
};
apkSigningMethods.zipAlignApk = function callee$0$0(apk) {
var alignedApk;
return _regeneratorRuntime.async(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
_loggerJs2['default'].debug('Zip-aligning \'' + apk + '\'');
context$1$0.next = 3;
return _regeneratorRuntime.awrap(this.initZipAlign());
case 3:
context$1$0.next = 5;
return _regeneratorRuntime.awrap(_appiumSupport.tempDir.path({ prefix: 'appium', suffix: '.tmp' }));
case 5:
alignedApk = context$1$0.sent;
context$1$0.next = 8;
return _regeneratorRuntime.awrap((0, _appiumSupport.mkdirp)(_path2['default'].dirname(alignedApk)));
case 8:
_loggerJs2['default'].debug("Zip-aligning apk.");
context$1$0.prev = 9;
context$1$0.next = 12;
return _regeneratorRuntime.awrap((0, _teen_process.exec)(this.binaries.zipalign, ['-f', '4', apk, alignedApk]));
case 12:
context$1$0.next = 14;
return _regeneratorRuntime.awrap(_appiumSupport.fs.mv(alignedApk, apk, { mkdirp: true }));
case 14:
context$1$0.next = 19;
break;
case 16:
context$1$0.prev = 16;
context$1$0.t0 = context$1$0['catch'](9);
_loggerJs2['default'].errorAndThrow('zipAlignApk failed. Original error: ' + context$1$0.t0.message);
case 19:
case 'end':
return context$1$0.stop();
}
}, null, this, [[9, 16]]);
};
// returns true when already signed, false otherwise.
apkSigningMethods.checkApkCert = function callee$0$0(apk, pkg) {
var java;
return _regeneratorRuntime.async(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
java = (0, _helpersJs.getJavaForOs)();
context$1$0.next = 3;
return _regeneratorRuntime.awrap(_appiumSupport.fs.exists(apk));
case 3:
if (context$1$0.sent) {
context$1$0.next = 6;
break;
}
_loggerJs2['default'].debug('APK doesn\'t exist. ' + apk);
return context$1$0.abrupt('return', false);
case 6:
if (!this.useKeystore) {
context$1$0.next = 10;
break;
}
context$1$0.next = 9;
return _regeneratorRuntime.awrap(this.checkCustomApkCert(apk, pkg));
case 9:
return context$1$0.abrupt('return', context$1$0.sent);
case 10:
_loggerJs2['default'].debug('Checking app cert for ' + apk + '.');
context$1$0.prev = 11;
context$1$0.next = 14;
return _regeneratorRuntime.awrap((0, _teen_process.exec)(java, ['-jar', _path2['default'].resolve(this.helperJarPath, 'verify.jar'), apk]));
case 14:
_loggerJs2['default'].debug("App already signed.");
context$1$0.next = 17;
return _regeneratorRuntime.awrap(this.zipAlignApk(apk));
case 17:
return context$1$0.abrupt('return', true);
case 20:
context$1$0.prev = 20;
context$1$0.t0 = context$1$0['catch'](11);
_loggerJs2['default'].debug("App not signed with debug cert.");
return context$1$0.abrupt('return', false);
case 24:
case 'end':
return context$1$0.stop();
}
}, null, this, [[11, 20]]);
};
apkSigningMethods.checkCustomApkCert = function callee$0$0(apk, pkg) {
var h, md5Str, md5, javaHome, keytool, keystoreHash;
return _regeneratorRuntime.async(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
_loggerJs2['default'].debug('Checking custom app cert for ' + apk);
h = "a-fA-F0-9";
md5Str = ['.*MD5.*((?:[' + h + ']{2}:){15}[' + h + ']{2})'];
md5 = new RegExp(md5Str, 'mi');
javaHome = (0, _helpersJs.getJavaHome)();
keytool = _path2['default'].resolve(javaHome, 'bin', 'keytool');
keytool = _appiumSupport.system.isWindows() ? keytool + '.exe' : keytool;
context$1$0.next = 9;
return _regeneratorRuntime.awrap(this.getKeystoreMd5(keytool, md5));
case 9:
keystoreHash = context$1$0.sent;
context$1$0.next = 12;
return _regeneratorRuntime.awrap(this.checkApkKeystoreMatch(keytool, md5, keystoreHash, pkg, apk));
case 12:
return context$1$0.abrupt('return', context$1$0.sent);
case 13:
case 'end':
return context$1$0.stop();
}
}, null, this);
};
apkSigningMethods.getKeystoreMd5 = function callee$0$0(keytool, md5re) {
var keystoreHash, _ref, stdout;
return _regeneratorRuntime.async(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
keystoreHash = undefined;
_loggerJs2['default'].debug("Printing keystore md5.");
context$1$0.prev = 2;
context$1$0.next = 5;
return _regeneratorRuntime.awrap((0, _teen_process.exec)(keytool, ['-v', '-list', '-alias', this.keyAlias, '-keystore', this.keystorePath, '-storepass', this.keystorePassword]));
case 5:
_ref = context$1$0.sent;
stdout = _ref.stdout;
keystoreHash = md5re.exec(stdout);
keystoreHash = keystoreHash ? keystoreHash[1] : null;
_loggerJs2['default'].debug('Keystore MD5: ' + keystoreHash);
return context$1$0.abrupt('return', keystoreHash);
case 13:
context$1$0.prev = 13;
context$1$0.t0 = context$1$0['catch'](2);
_loggerJs2['default'].errorAndThrow('getKeystoreMd5 failed. Original error: ' + context$1$0.t0.message);
case 16:
case 'end':
return context$1$0.stop();
}
}, null, this, [[2, 13]]);
};
apkSigningMethods.checkApkKeystoreMatch = function callee$0$0(keytool, md5re, keystoreHash, pkg, apk) {
var entryHash, zip, rsa, entries, _iteratorNormalCompletion, _didIteratorError, _iteratorError, _iterator, _step, entry, entryPath, entryFile, _ref2, stdout, matchesKeystore;
return _regeneratorRuntime.async(function callee$0$0$(context$1$0) {
while (1) switch (context$1$0.prev = context$1$0.next) {
case 0:
entryHash = null;
zip = new _admZip2['default'](apk);
rsa = /^META-INF\/.*\.[rR][sS][aA]$/;
entries = zip.getEntries();
_iteratorNormalCompletion = true;
_didIteratorError = false;
_iteratorError = undefined;
context$1$0.prev = 7;
_iterator = _getIterator(entries);
case 9:
if (_iteratorNormalCompletion = (_step = _iterator.next()).done) {
context$1$0.next = 39;
break;
}
entry = _step.value;
entry = entry.entryName;
if (rsa.test(entry)) {
context$1$0.next = 14;
break;
}
return context$1$0.abrupt('continue', 36);
case 14:
_loggerJs2['default'].debug('Entry: ' + entry);
entryPath = _path2['default'].join(this.tmpDir, pkg, 'cert');
_loggerJs2['default'].debug('entryPath: ' + entryPath);
entryFile = _path2['default'].join(entryPath, entry);
_loggerJs2['default'].debug('entryFile: ' + entryFile);
// ensure /tmp/pkg/cert/ doesn't exist or extract will fail.
context$1$0.next = 21;
return _regeneratorRuntime.awrap(_appiumSupport.fs.rimraf(entryPath));
case 21:
// META-INF/CERT.RSA
zip.extractEntryTo(entry, entryPath, true); // overwrite = true
_loggerJs2['default'].debug("extracted!");
// check for match
_loggerJs2['default'].debug("Printing apk md5.");
context$1$0.next = 26;
return _regeneratorRuntime.awrap((0, _teen_process.exec)(keytool, ['-v', '-printcert', '-file', entryFile]));
case 26:
_ref2 = context$1$0.sent;
stdout = _ref2.stdout;
entryHash = md5re.exec(stdout);
entryHash = entryHash ? entryHash[1] : null;
_loggerJs2['default'].debug('entryHash MD5: ' + entryHash);
_loggerJs2['default'].debug('keystore MD5: ' + keystoreHash);
matchesKeystore = entryHash && entryHash === keystoreHash;
_loggerJs2['default'].debug('Matches keystore? ' + matchesKeystore);
if (!matchesKeystore) {
context$1$0.next = 36;
break;
}
return context$1$0.abrupt('return', true);
case 36:
_iteratorNormalCompletion = true;
context$1$0.next = 9;
break;
case 39:
context$1$0.next = 45;
break;
case 41:
context$1$0.prev = 41;
context$1$0.t0 = context$1$0['catch'](7);
_didIteratorError = true;
_iteratorError = context$1$0.t0;
case 45:
context$1$0.prev = 45;
context$1$0.prev = 46;
if (!_iteratorNormalCompletion && _iterator['return']) {
_iterator['return']();
}
case 48:
context$1$0.prev = 48;
if (!_didIteratorError) {
context$1$0.next = 51;
break;
}
throw _iteratorError;
case 51:
return context$1$0.finish(48);
case 52:
return context$1$0.finish(45);
case 53:
return context$1$0.abrupt('return', false);
case 54:
case 'end':
return context$1$0.stop();
}
}, null, this, [[7, 41, 45, 53], [46,, 48, 52]]);
};
exports['default'] = apkSigningMethods;
module.exports = exports['default'];
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbImxpYi90b29scy9hcGstc2lnbmluZy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7Ozs7Ozs7NEJBQXFCLGNBQWM7O29CQUNsQixNQUFNOzs7O3dCQUNQLGNBQWM7Ozs7NkJBQ2MsZ0JBQWdCOztzQkFDekMsU0FBUzs7Ozt5QkFDYyxlQUFlOztBQUV6RCxJQUFJLGlCQUFpQixHQUFHLEVBQUUsQ0FBQzs7QUFHM0IsaUJBQWlCLENBQUMsbUJBQW1CLEdBQUcsb0JBQWdCLEdBQUc7TUFDbkQsSUFBSSxFQUNOLFFBQVE7Ozs7QUFETixZQUFJLEdBQUcsOEJBQWM7QUFDdkIsZ0JBQVEsR0FBRyxrQkFBSyxPQUFPLENBQUMsSUFBSSxDQUFDLGFBQWEsRUFBRSxVQUFVLENBQUM7O0FBQzNELDhCQUFJLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDOzs7eUNBRWQsa0JBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQzs7Ozs7Ozs7Y0FDbEIsSUFBSSxLQUFLLENBQUksR0FBRywyQkFBdUI7Ozs7eUNBRXpDLHdCQUFLLElBQUksRUFBRSxDQUFDLE1BQU0sRUFBRSxRQUFRLEVBQUUsR0FBRyxFQUFFLFlBQVksQ0FBQyxDQUFDOzs7Ozs7Ozs7O0FBRXZELDhCQUFJLGFBQWEsOERBQTRELGVBQUUsT0FBTyxDQUFHLENBQUM7Ozs7Ozs7Q0FFN0YsQ0FBQzs7QUFFRixpQkFBaUIsQ0FBQyxrQkFBa0IsR0FBRyxvQkFBZ0IsR0FBRztNQUVsRCxJQUFJLEVBQ04sUUFBUSxFQUNSLFNBQVM7Ozs7QUFIYiw4QkFBSSxLQUFLLGdCQUFhLEdBQUcseUJBQXFCLENBQUM7QUFDekMsWUFBSSxHQUFHLDhCQUFjO0FBQ3ZCLGdCQUFRLEdBQUcsNkJBQWE7QUFDeEIsaUJBQVMsR0FBRyxrQkFBSyxPQUFPLENBQUMsUUFBUSxFQUFFLEtBQUssRUFBRSxXQUFXLENBQUM7O0FBQzFELFlBQUksc0JBQU8sU0FBUyxFQUFFLEVBQUU7QUFDdEIsbUJBQVMsR0FBRyxTQUFTLEdBQUcsTUFBTSxDQUFDO1NBQ2hDOzt5Q0FDVyxrQkFBRyxNQUFNLENBQUMsSUFBSSxDQUFDLFlBQVksQ0FBQzs7Ozs7Ozs7Y0FDaEMsSUFBSSxLQUFLLGdCQUFjLElBQUksQ0FBQyxZQUFZLHNCQUFrQjs7Ozt5Q0FFdEQsa0JBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQzs7Ozs7Ozs7Y0FDbEIsSUFBSSxLQUFLLENBQUksR0FBRywyQkFBdUI7Ozs7O0FBRzdDLDhCQUFJLEtBQUssQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDOzt5Q0FDdEIsd0JBQUssSUFBSSxFQUFFLENBQUMsTUFBTSxFQUFFLGtCQUFLLE9BQU8sQ0FBQyxJQUFJLENBQUMsYUFBYSxFQUFFLFlBQVksQ0FBQyxFQUFFLEdBQUcsQ0FBQyxDQUFDOzs7QUFDL0UsOEJBQUksS0FBSyxDQUFDLGNBQWMsQ0FBQyxDQUFDOzt5Q0FDcEIsd0JBQUssU0FBUyxFQUFFLENBQUMsU0FBUyxFQUFFLFlBQVksRUFBRSxZQUFZLEVBQUUsTUFBTSxFQUM3QyxXQUFXLEVBQUUsSUFBSSxDQUFDLFlBQVksRUFBRSxZQUFZLEVBQUUsSUFBSSxDQUFDLGdCQUFnQixFQUNuRSxVQUFVLEVBQUUsSUFBSSxDQUFDLFdBQVcsRUFBRSxHQUFHLEVBQUUsSUFBSSxDQUFDLFFBQVEsQ0FBQyxDQUFDOzs7Ozs7Ozs7O0FBRXpFLDhCQUFJLGFBQWEsNkRBQTJELGVBQUUsT0FBTyxDQUFHLENBQUM7Ozs7Ozs7Q0FFNUYsQ0FBQzs7QUFFRixpQkFBaUIsQ0FBQyxJQUFJLEdBQUcsb0JBQWdCLEdBQUc7Ozs7YUFDdEMsSUFBSSxDQUFDLFdBQVc7Ozs7Ozt5Q0FDWixJQUFJLENBQUMsa0JBQWtCLENBQUMsR0FBRyxDQUFDOzs7Ozs7Ozt5Q0FFNUIsSUFBSSxDQUFDLG1CQUFtQixDQUFDLEdBQUcsQ0FBQzs7Ozt5Q0FFL0IsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLENBQUM7Ozs7Ozs7Q0FDNUIsQ0FBQzs7QUFFRixpQkFBaUIsQ0FBQyxXQUFXLEdBQUcsb0JBQWdCLEdBQUc7TUFHN0MsVUFBVTs7OztBQUZkLDhCQUFJLEtBQUsscUJBQWtCLEdBQUcsUUFBSSxDQUFDOzt5Q0FDN0IsSUFBSSxDQUFDLFlBQVksRUFBRTs7Ozt5Q0FDRix1QkFBUSxJQUFJLENBQUMsRUFBQyxNQUFNLEVBQUUsUUFBUSxFQUFFLE1BQU0sRUFBRSxNQUFNLEVBQUMsQ0FBQzs7O0FBQW5FLGtCQUFVOzt5Q0FDUiwyQkFBTyxrQkFBSyxPQUFPLENBQUMsVUFBVSxDQUFDLENBQUM7OztBQUN0Qyw4QkFBSSxLQUFLLENBQUMsbUJBQW1CLENBQUMsQ0FBQzs7O3lDQUV2Qix3QkFBSyxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsRUFBRSxDQUFDLElBQUksRUFBRSxHQUFHLEVBQUUsR0FBRyxFQUFFLFVBQVUsQ0FBQyxDQUFDOzs7O3lDQUMxRCxrQkFBRyxFQUFFLENBQUMsVUFBVSxFQUFFLEdBQUcsRUFBRSxFQUFFLE1BQU0sRUFBRSxJQUFJLEVBQUUsQ0FBQzs7Ozs7Ozs7OztBQUU5Qyw4QkFBSSxhQUFhLDBDQUF3QyxlQUFFLE9BQU8sQ0FBRyxDQUFDOzs7Ozs7O0NBRXpFLENBQUM7OztBQUdGLGlCQUFpQixDQUFDLFlBQVksR0FBRyxvQkFBZ0IsR0FBRyxFQUFFLEdBQUc7TUFDakQsSUFBSTs7OztBQUFKLFlBQUksR0FBRyw4QkFBYzs7eUNBQ2Ysa0JBQUcsTUFBTSxDQUFDLEdBQUcsQ0FBQzs7Ozs7Ozs7QUFDeEIsOEJBQUksS0FBSywwQkFBdUIsR0FBRyxDQUFHLENBQUM7NENBQ2hDLEtBQUs7OzthQUVWLElBQUksQ0FBQyxXQUFXOzs7Ozs7eUNBQ0wsSUFBSSxDQUFDLGtCQUFrQixDQUFDLEdBQUcsRUFBRSxHQUFHLENBQUM7Ozs7OztBQUVoRCw4QkFBSSxLQUFLLDRCQUEwQixHQUFHLE9BQUksQ0FBQzs7O3lDQUVuQyx3QkFBSyxJQUFJLEVBQUUsQ0FBQyxNQUFNLEVBQUUsa0JBQUssT0FBTyxDQUFDLElBQUksQ0FBQyxhQUFhLEVBQUUsWUFBWSxDQUFDLEVBQUUsR0FBRyxDQUFDLENBQUM7OztBQUMvRSw4QkFBSSxLQUFLLENBQUMscUJBQXFCLENBQUMsQ0FBQzs7eUNBQzNCLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxDQUFDOzs7NENBQ3BCLElBQUk7Ozs7OztBQUVYLDhCQUFJLEtBQUssQ0FBQyxpQ0FBaUMsQ0FBQyxDQUFDOzRDQUN0QyxLQUFLOzs7Ozs7O0NBRWYsQ0FBQzs7QUFFRixpQkFBaUIsQ0FBQyxrQkFBa0IsR0FBRyxvQkFBZ0IsR0FBRyxFQUFFLEdBQUc7TUFFekQsQ0FBQyxFQUNELE1BQU0sRUFDTixHQUFHLEVBQ0gsUUFBUSxFQUNSLE9BQU8sRUFFUCxZQUFZOzs7O0FBUGhCLDhCQUFJLEtBQUssbUNBQWlDLEdBQUcsQ0FBRyxDQUFDO0FBQzdDLFNBQUMsR0FBRyxXQUFXO0FBQ2YsY0FBTSxHQUFHLGtCQUFnQixDQUFDLG1CQUFjLENBQUMsV0FBUTtBQUNqRCxXQUFHLEdBQUcsSUFBSSxNQUFNLENBQUMsTUFBTSxFQUFFLElBQUksQ0FBQztBQUM5QixnQkFBUSxHQUFHLDZCQUFhO0FBQ3hCLGVBQU8sR0FBRyxrQkFBSyxPQUFPLENBQUMsUUFBUSxFQUFFLEtBQUssRUFBRSxTQUFTLENBQUM7O0FBQ3RELGVBQU8sR0FBRyxzQkFBTyxTQUFTLEVBQUUsR0FBTSxPQUFPLFlBQVMsT0FBTyxDQUFDOzt5Q0FDakMsSUFBSSxDQUFDLGNBQWMsQ0FBQyxPQUFPLEVBQUUsR0FBRyxDQUFDOzs7QUFBdEQsb0JBQVk7O3lDQUNILElBQUksQ0FBQyxxQkFBcUIsQ0FBQyxPQUFPLEVBQUUsR0FBRyxFQUFFLFlBQVksRUFBRSxHQUFHLEVBQUUsR0FBRyxDQUFDOzs7Ozs7Ozs7O0NBQzlFLENBQUM7O0FBRUYsaUJBQWlCLENBQUMsY0FBYyxHQUFHLG9CQUFnQixPQUFPLEVBQUUsS0FBSztNQUMzRCxZQUFZLFFBR1QsTUFBTTs7Ozs7QUFIVCxvQkFBWTs7QUFDaEIsOEJBQUksS0FBSyxDQUFDLHdCQUF3QixDQUFDLENBQUM7Ozt5Q0FFYix3QkFBSyxPQUFPLEVBQUUsQ0FBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLFFBQVEsRUFBRSxJQUFJLENBQUMsUUFBUSxFQUN0RCxXQUFXLEVBQUUsSUFBSSxDQUFDLFlBQVksRUFBRSxZQUFZLEVBQzNDLElBQUksQ0FBQyxnQkFBZ0IsQ0FBQyxDQUFDOzs7O0FBRnZDLGNBQU0sUUFBTixNQUFNOztBQUdYLG9CQUFZLEdBQUcsS0FBSyxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsQ0FBQztBQUNsQyxvQkFBWSxHQUFHLFlBQVksR0FBRyxZQUFZLENBQUMsQ0FBQyxDQUFDLEdBQUcsSUFBSSxDQUFDO0FBQ3JELDhCQUFJLEtBQUssb0JBQWtCLFlBQVksQ0FBRyxDQUFDOzRDQUNwQyxZQUFZOzs7Ozs7QUFFbkIsOEJBQUksYUFBYSw2Q0FBMkMsZUFBRSxPQUFPLENBQUcsQ0FBQzs7Ozs7OztDQUU1RSxDQUFDOztBQUVGLGlCQUFpQixDQUFDLHFCQUFxQixHQUFHLG9CQUFnQixPQUFPLEVBQUUsS0FBSyxFQUFFLFlBQVksRUFDbEYsR0FBRyxFQUFFLEdBQUc7TUFDTixTQUFTLEVBQ1QsR0FBRyxFQUNILEdBQUcsRUFDSCxPQUFPLGtGQUVGLEtBQUssRUFNUixTQUFTLEVBRVQsU0FBUyxTQVNSLE1BQU0sRUFLUCxlQUFlOzs7OztBQTNCakIsaUJBQVMsR0FBRyxJQUFJO0FBQ2hCLFdBQUcsR0FBRyx3QkFBVyxHQUFHLENBQUM7QUFDckIsV0FBRyxHQUFHLDhCQUE4QjtBQUNwQyxlQUFPLEdBQUcsR0FBRyxDQUFDLFVBQVUsRUFBRTs7Ozs7aUNBRVosT0FBTzs7Ozs7Ozs7QUFBaEIsYUFBSzs7QUFDWixhQUFLLEdBQUcsS0FBSyxDQUFDLFNBQVMsQ0FBQzs7WUFDbkIsR0FBRyxDQUFDLElBQUksQ0FBQyxLQUFLLENBQUM7Ozs7Ozs7O0FBR3BCLDhCQUFJLEtBQUssYUFBVyxLQUFLLENBQUcsQ0FBQztBQUN6QixpQkFBUyxHQUFHLGtCQUFLLElBQUksQ0FBQyxJQUFJLENBQUMsTUFBTSxFQUFFLEdBQUcsRUFBRSxNQUFNLENBQUM7O0FBQ25ELDhCQUFJLEtBQUssaUJBQWUsU0FBUyxDQUFHLENBQUM7QUFDakMsaUJBQVMsR0FBRyxrQkFBSyxJQUFJLENBQUMsU0FBUyxFQUFFLEtBQUssQ0FBQzs7QUFDM0MsOEJBQUksS0FBSyxpQkFBZSxTQUFTLENBQUcsQ0FBQzs7O3lDQUUvQixrQkFBRyxNQUFNLENBQUMsU0FBUyxDQUFDOzs7O0FBRTFCLFdBQUcsQ0FBQyxjQUFjLENBQUMsS0FBSyxFQUFFLFNBQVMsRUFBRSxJQUFJLENBQUMsQ0FBQztBQUMzQyw4QkFBSSxLQUFLLENBQUMsWUFBWSxDQUFDLENBQUM7O0FBRXhCLDhCQUFJLEtBQUssQ0FBQyxtQkFBbUIsQ0FBQyxDQUFDOzt5Q0FDVix3QkFBSyxPQUFPLEVBQUUsQ0FBQyxJQUFJLEVBQUUsWUFBWSxFQUFFLE9BQU8sRUFBRSxTQUFTLENBQUMsQ0FBQzs7OztBQUF2RSxjQUFNLFNBQU4sTUFBTTs7QUFDWCxpQkFBUyxHQUFHLEtBQUssQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLENBQUM7QUFDL0IsaUJBQVMsR0FBRyxTQUFTLEdBQUcsU0FBUyxDQUFDLENBQUMsQ0FBQyxHQUFHLElBQUksQ0FBQztBQUM1Qyw4QkFBSSxLQUFLLHFCQUFtQixTQUFTLENBQUcsQ0FBQztBQUN6Qyw4QkFBSSxLQUFLLG9CQUFrQixZQUFZLENBQUcsQ0FBQztBQUN2Qyx1QkFBZSxHQUFHLFNBQVMsSUFBSSxTQUFTLEtBQUssWUFBWTs7QUFDN0QsOEJBQUksS0FBSyx3QkFBc0IsZUFBZSxDQUFHLENBQUM7O2FBQzlDLGVBQWU7Ozs7OzRDQUNWLElBQUk7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs0Q0FHUixLQUFLOzs7Ozs7O0NBQ2IsQ0FBQzs7cUJBRWEsaUJBQWlCIiwiZmlsZSI6ImxpYi90b29scy9hcGstc2lnbmluZy5qcyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGV4ZWMgfSBmcm9tICd0ZWVuX3Byb2Nlc3MnO1xyXG5pbXBvcnQgcGF0aCBmcm9tICdwYXRoJztcclxuaW1wb3J0IGxvZyBmcm9tICcuLi9sb2dnZXIuanMnO1xyXG5pbXBvcnQgeyB0ZW1wRGlyLCBzeXN0ZW0sIG1rZGlycCwgZnMgfSBmcm9tICdhcHBpdW0tc3VwcG9ydCc7XHJcbmltcG9ydCBBZG1aaXAgZnJvbSAnYWRtLXppcCc7XHJcbmltcG9ydCB7IGdldEphdmFGb3JPcywgZ2V0SmF2YUhvbWUgfSBmcm9tICcuLi9oZWxwZXJzLmpzJztcclxuXHJcbmxldCBhcGtTaWduaW5nTWV0aG9kcyA9IHt9O1xyXG5cclxuXHJcbmFwa1NpZ25pbmdNZXRob2RzLnNpZ25XaXRoRGVmYXVsdENlcnQgPSBhc3luYyBmdW5jdGlvbiAoYXBrKSB7XHJcbiAgY29uc3QgamF2YSA9IGdldEphdmFGb3JPcygpO1xyXG4gIGxldCBzaWduUGF0aCA9IHBhdGgucmVzb2x2ZSh0aGlzLmhlbHBlckphclBhdGgsICdzaWduLmphcicpO1xyXG4gIGxvZy5kZWJ1ZyhcIlJlc2lnbmluZyBhcGsuXCIpO1xyXG4gIHRyeSB7XHJcbiAgICBpZiAoIShhd2FpdCBmcy5leGlzdHMoYXBrKSkpIHtcclxuICAgICAgdGhyb3cgbmV3IEVycm9yKGAke2Fwa30gZmlsZSBkb2Vzbid0IGV4aXN0LmApO1xyXG4gICAgfVxyXG4gICAgYXdhaXQgZXhlYyhqYXZhLCBbJy1qYXInLCBzaWduUGF0aCwgYXBrLCAnLS1vdmVycmlkZSddKTtcclxuICB9IGNhdGNoIChlKSB7XHJcbiAgICBsb2cuZXJyb3JBbmRUaHJvdyhgQ291bGQgbm90IHNpZ24gd2l0aCBkZWZhdWx0IGNlcml0ZmljYXRlLiBPcmlnaW5hbCBlcnJvciAke2UubWVzc2FnZX1gKTtcclxuICB9XHJcbn07XHJcblxyXG5hcGtTaWduaW5nTWV0aG9kcy5zaWduV2l0aEN1c3RvbUNlcnQgPSBhc3luYyBmdW5jdGlvbiAoYXBrKSB7XHJcbiAgbG9nLmRlYnVnKGBTaWduaW5nICcke2Fwa30nIHdpdGggY3VzdG9tIGNlcnRgKTtcclxuICBjb25zdCBqYXZhID0gZ2V0SmF2YUZvck9zKCk7XHJcbiAgbGV0IGphdmFIb21lID0gZ2V0SmF2YUhvbWUoKTtcclxuICBsZXQgamFyc2lnbmVyID0gcGF0aC5yZXNvbHZlKGphdmFIb21lLCAnYmluJywgJ2phcnNpZ25lcicpO1xyXG4gIGlmIChzeXN0ZW0uaXNXaW5kb3dzKCkpIHtcclxuICAgIGphcnNpZ25lciA9IGphcnNpZ25lciArICcuZXhlJztcclxuICB9XHJcbiAgaWYgKCEoYXdhaXQgZnMuZXhpc3RzKHRoaXMua2V5c3RvcmVQYXRoKSkpIHtcclxuICAgIHRocm93IG5ldyBFcnJvcihgS2V5c3RvcmU6ICR7dGhpcy5rZXlzdG9yZVBhdGh9IGRvZXNuJ3QgZXhpc3QuYCk7XHJcbiAgfVxyXG4gIGlmICghKGF3YWl0IGZzLmV4aXN0cyhhcGspKSkge1xyXG4gICAgdGhyb3cgbmV3IEVycm9yKGAke2Fwa30gZmlsZSBkb2Vzbid0IGV4aXN0LmApO1xyXG4gIH1cclxuICB0cnkge1xyXG4gICAgbG9nLmRlYnVnKFwiVW5zaWduaW5nIGFway5cIik7XHJcbiAgICBhd2FpdCBleGVjKGphdmEsIFsnLWphcicsIHBhdGgucmVzb2x2ZSh0aGlzLmhlbHBlckphclBhdGgsICd1bnNpZ24uamFyJyksIGFwa10pO1xyXG4gICAgbG9nLmRlYnVnKFwiU2lnbmluZyBhcGsuXCIpO1xyXG4gICAgYXdhaXQgZXhlYyhqYXJzaWduZXIsIFsnLXNpZ2FsZycsICdNRDV3aXRoUlNBJywgJy1kaWdlc3RhbGcnLCAnU0hBMScsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICcta2V5c3RvcmUnLCB0aGlzLmtleXN0b3JlUGF0aCwgJy1zdG9yZXBhc3MnLCB0aGlzLmtleXN0b3JlUGFzc3dvcmQsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICcta2V5cGFzcycsIHRoaXMua2V5UGFzc3dvcmQsIGFwaywgdGhpcy5rZXlBbGlhc10pO1xyXG4gIH0gY2F0Y2ggKGUpIHtcclxuICAgIGxvZy5lcnJvckFuZFRocm93KGBDb3VsZCBub3Qgc2lnbiB3aXRoIGN1c3RvbSBjZXJpdGZpY2F0ZS4gT3JpZ2luYWwgZXJyb3IgJHtlLm1lc3NhZ2V9YCk7XHJcbiAgfVxyXG59O1xyXG5cclxuYXBrU2lnbmluZ01ldGhvZHMuc2lnbiA9IGFzeW5jIGZ1bmN0aW9uIChhcGspIHtcclxuICBpZiAodGhpcy51c2VLZXlzdG9yZSkge1xyXG4gICAgYXdhaXQgdGhpcy5zaWduV2l0aEN1c3RvbUNlcnQoYXBrKTtcclxuICB9IGVsc2Uge1xyXG4gICAgYXdhaXQgdGhpcy5zaWduV2l0aERlZmF1bHRDZXJ0KGFwayk7XHJcbiAgfVxyXG4gIGF3YWl0IHRoaXMuemlwQWxpZ25BcGsoYXBrKTtcclxufTtcclxuXHJcbmFwa1NpZ25pbmdNZXRob2RzLnppcEFsaWduQXBrID0gYXN5bmMgZnVuY3Rpb24gKGFwaykge1xyXG4gIGxvZy5kZWJ1ZyhgWmlwLWFsaWduaW5nICcke2Fwa30nYCk7XHJcbiAgYXdhaXQgdGhpcy5pbml0WmlwQWxpZ24oKTtcclxuICBsZXQgYWxpZ25lZEFwayA9IGF3YWl0IHRlbXBEaXIucGF0aCh7cHJlZml4OiAnYXBwaXVtJywgc3VmZml4OiAnLnRtcCd9KTtcclxuICBhd2FpdCBta2RpcnAocGF0aC5kaXJuYW1lKGFsaWduZWRBcGspKTtcclxuICBsb2cuZGVidWcoXCJaaXAtYWxpZ25pbmcgYXBrLlwiKTtcclxuICB0cnkge1xyXG4gICAgYXdhaXQgZXhlYyh0aGlzLmJpbmFyaWVzLnppcGFsaWduLCBbJy1mJywgJzQnLCBhcGssIGFsaWduZWRBcGtdKTtcclxuICAgIGF3YWl0IGZzLm12KGFsaWduZWRBcGssIGFwaywgeyBta2RpcnA6IHRydWUgfSk7XHJcbiAgfSBjYXRjaCAoZSkge1xyXG4gICAgbG9nLmVycm9yQW5kVGhyb3coYHppcEFsaWduQXBrIGZhaWxlZC4gT3JpZ2luYWwgZXJyb3I6ICR7ZS5tZXNzYWdlfWApO1xyXG4gIH1cclxufTtcclxuXHJcbi8vIHJldHVybnMgdHJ1ZSB3aGVuIGFscmVhZHkgc2lnbmVkLCBmYWxzZSBvdGhlcndpc2UuXHJcbmFwa1NpZ25pbmdNZXRob2RzLmNoZWNrQXBrQ2VydCA9IGFzeW5jIGZ1bmN0aW9uIChhcGssIHBrZykge1xyXG4gIGNvbnN0IGphdmEgPSBnZXRKYXZhRm9yT3MoKTtcclxuICBpZiAoIShhd2FpdCBmcy5leGlzdHMoYXBrKSkpIHtcclxuICAgIGxvZy5kZWJ1ZyhgQVBLIGRvZXNuJ3QgZXhpc3QuICR7YXBrfWApO1xyXG4gICAgcmV0dXJuIGZhbHNlO1xyXG4gIH1cclxuICBpZiAodGhpcy51c2VLZXlzdG9yZSkge1xyXG4gICAgcmV0dXJuIGF3YWl0IHRoaXMuY2hlY2tDdXN0b21BcGtDZXJ0KGFwaywgcGtnKTtcclxuICB9XHJcbiAgbG9nLmRlYnVnKGBDaGVja2luZyBhcHAgY2VydCBmb3IgJHthcGt9LmApO1xyXG4gIHRyeSB7XHJcbiAgICBhd2FpdCBleGVjKGphdmEsIFsnLWphcicsIHBhdGgucmVzb2x2ZSh0aGlzLmhlbHBlckphclBhdGgsICd2ZXJpZnkuamFyJyksIGFwa10pO1xyXG4gICAgbG9nLmRlYnVnKFwiQXBwIGFscmVhZHkgc2lnbmVkLlwiKTtcclxuICAgIGF3YWl0IHRoaXMuemlwQWxpZ25BcGsoYXBrKTtcclxuICAgIHJldHVybiB0cnVlO1xyXG4gIH0gY2F0Y2ggKGUpIHtcclxuICAgIGxvZy5kZWJ1ZyhcIkFwcCBub3Qgc2lnbmVkIHdpdGggZGVidWcgY2VydC5cIik7XHJcbiAgICByZXR1cm4gZmFsc2U7XHJcbiAgfVxyXG59O1xyXG5cclxuYXBrU2lnbmluZ01ldGhvZHMuY2hlY2tDdXN0b21BcGtDZXJ0ID0gYXN5bmMgZnVuY3Rpb24gKGFwaywgcGtnKSB7XHJcbiAgbG9nLmRlYnVnKGBDaGVja2luZyBjdXN0b20gYXBwIGNlcnQgZm9yICR7YXBrfWApO1xyXG4gIGxldCBoID0gXCJhLWZBLUYwLTlcIjtcclxuICBsZXQgbWQ1U3RyID0gW2AuKk1ENS4qKCg/Olske2h9XXsyfTopezE1fVske2h9XXsyfSlgXTtcclxuICBsZXQgbWQ1ID0gbmV3IFJlZ0V4cChtZDVTdHIsICdtaScpO1xyXG4gIGxldCBqYXZhSG9tZSA9IGdldEphdmFIb21lKCk7XHJcbiAgbGV0IGtleXRvb2wgPSBwYXRoLnJlc29sdmUoamF2YUhvbWUsICdiaW4nLCAna2V5dG9vbCcpO1xyXG4gIGtleXRvb2wgPSBzeXN0ZW0uaXNXaW5kb3dzKCkgPyBgJHtrZXl0b29sfS5leGVgIDoga2V5dG9vbDtcclxuICBsZXQga2V5c3RvcmVIYXNoID0gYXdhaXQgdGhpcy5nZXRLZXlzdG9yZU1kNShrZXl0b29sLCBtZDUpO1xyXG4gIHJldHVybiBhd2FpdCB0aGlzLmNoZWNrQXBrS2V5c3RvcmVNYXRjaChrZXl0b29sLCBtZDUsIGtleXN0b3JlSGFzaCwgcGtnLCBhcGspO1xyXG59O1xyXG5cclxuYXBrU2lnbmluZ01ldGhvZHMuZ2V0S2V5c3RvcmVNZDUgPSBhc3luYyBmdW5jdGlvbiAoa2V5dG9vbCwgbWQ1cmUpIHtcclxuICBsZXQga2V5c3RvcmVIYXNoO1xyXG4gIGxvZy5kZWJ1ZyhcIlByaW50aW5nIGtleXN0b3JlIG1kNS5cIik7XHJcbiAgdHJ5IHtcclxuICAgIGxldCB7c3Rkb3V0fSA9IGF3YWl0IGV4ZWMoa2V5dG9vbCwgWyctdicsICctbGlzdCcsICctYWxpYXMnLCB0aGlzLmtleUFsaWFzLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAnLWtleXN0b3JlJywgdGhpcy5rZXlzdG9yZVBhdGgsICctc3RvcmVwYXNzJyxcclxuICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMua2V5c3RvcmVQYXNzd29yZF0pO1xyXG4gICAga2V5c3RvcmVIYXNoID0gbWQ1cmUuZXhlYyhzdGRvdXQpO1xyXG4gICAga2V5c3RvcmVIYXNoID0ga2V5c3RvcmVIYXNoID8ga2V5c3RvcmVIYXNoWzFdIDogbnVsbDtcclxuICAgIGxvZy5kZWJ1ZyhgS2V5c3RvcmUgTUQ1OiAke2tleXN0b3JlSGFzaH1gKTtcclxuICAgIHJldHVybiBrZXlzdG9yZUhhc2g7XHJcbiAgfSBjYXRjaCAoZSkge1xyXG4gICAgbG9nLmVycm9yQW5kVGhyb3coYGdldEtleXN0b3JlTWQ1IGZhaWxlZC4gT3JpZ2luYWwgZXJyb3I6ICR7ZS5tZXNzYWdlfWApO1xyXG4gIH1cclxufTtcclxuXHJcbmFwa1NpZ25pbmdNZXRob2RzLmNoZWNrQXBrS2V5c3RvcmVNYXRjaCA9IGFzeW5jIGZ1bmN0aW9uIChrZXl0b29sLCBtZDVyZSwga2V5c3RvcmVIYXNoLFxyXG4gICAgcGtnLCBhcGspIHtcclxuICBsZXQgZW50cnlIYXNoID0gbnVsbDtcclxuICBsZXQgemlwID0gbmV3IEFkbVppcChhcGspO1xyXG4gIGxldCByc2EgPSAvXk1FVEEtSU5GXFwvLipcXC5bclJdW3NTXVthQV0kLztcclxuICBsZXQgZW50cmllcyA9IHppcC5nZXRFbnRyaWVzKCk7XHJcblxyXG4gIGZvciAobGV0IGVudHJ5IG9mIGVudHJpZXMpIHtcclxuICAgIGVudHJ5ID0gZW50cnkuZW50cnlOYW1lO1xyXG4gICAgaWYgKCFyc2EudGVzdChlbnRyeSkpIHtcclxuICAgICAgY29udGludWU7XHJcbiAgICB9XHJcbiAgICBsb2cuZGVidWcoYEVudHJ5OiAke2VudHJ5fWApO1xyXG4gICAgbGV0IGVudHJ5UGF0aCA9IHBhdGguam9pbih0aGlzLnRtcERpciwgcGtnLCAnY2VydCcpO1xyXG4gICAgbG9nLmRlYnVnKGBlbnRyeVBhdGg6ICR7ZW50cnlQYXRofWApO1xyXG4gICAgbGV0IGVudHJ5RmlsZSA9IHBhdGguam9pbihlbnRyeVBhdGgsIGVudHJ5KTtcclxuICAgIGxvZy5kZWJ1ZyhgZW50cnlGaWxlOiAke2VudHJ5RmlsZX1gKTtcclxuICAgIC8vIGVuc3VyZSAvdG1wL3BrZy9jZXJ0LyBkb2Vzbid0IGV4aXN0IG9yIGV4dHJhY3Qgd2lsbCBmYWlsLlxyXG4gICAgYXdhaXQgZnMucmltcmFmKGVudHJ5UGF0aCk7XHJcbiAgICAvLyBNRVRBLUlORi9DRVJULlJTQVxyXG4gICAgemlwLmV4dHJhY3RFbnRyeVRvKGVudHJ5LCBlbnRyeVBhdGgsIHRydWUpOyAvLyBvdmVyd3JpdGUgPSB0cnVlXHJcbiAgICBsb2cuZGVidWcoXCJleHRyYWN0ZWQhXCIpO1xyXG4gICAgLy8gY2hlY2sgZm9yIG1hdGNoXHJcbiAgICBsb2cuZGVidWcoXCJQcmludGluZyBhcGsgbWQ1LlwiKTtcclxuICAgIGxldCB7c3Rkb3V0fSA9IGF3YWl0IGV4ZWMoa2V5dG9vbCwgWyctdicsICctcHJpbnRjZXJ0JywgJy1maWxlJywgZW50cnlGaWxlXSk7XHJcbiAgICBlbnRyeUhhc2ggPSBtZDVyZS5leGVjKHN0ZG91dCk7XHJcbiAgICBlbnRyeUhhc2ggPSBlbnRyeUhhc2ggPyBlbnRyeUhhc2hbMV0gOiBudWxsO1xyXG4gICAgbG9nLmRlYnVnKGBlbnRyeUhhc2ggTUQ1OiAke2VudHJ5SGFzaH1gKTtcclxuICAgIGxvZy5kZWJ1Zyhga2V5c3RvcmUgTUQ1OiAke2tleXN0b3JlSGFzaH1gKTtcclxuICAgIGxldCBtYXRjaGVzS2V5c3RvcmUgPSBlbnRyeUhhc2ggJiYgZW50cnlIYXNoID09PSBrZXlzdG9yZUhhc2g7XHJcbiAgICBsb2cuZGVidWcoYE1hdGNoZXMga2V5c3RvcmU/ICR7bWF0Y2hlc0tleXN0b3JlfWApO1xyXG4gICAgaWYgKG1hdGNoZXNLZXlzdG9yZSkge1xyXG4gICAgICByZXR1cm4gdHJ1ZTtcclxuICAgIH1cclxuICB9XHJcbiAgcmV0dXJuIGZhbHNlO1xyXG59O1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgYXBrU2lnbmluZ01ldGhvZHM7XHJcbiJdLCJzb3VyY2VSb290IjoiLi5cXC4uXFwuLiJ9