From e128f820a02984e6e728d35098278e586e4354c2 Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Tue, 12 May 2026 13:46:39 +0300 Subject: [PATCH 1/8] fix: trigger initial --issue from cron on post-migration first run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cronRenewCert.php ran acme.sh --cron and then unconditionally called GetSslMain::run(). After an upgrade from the legacy getssl client, --cron silently no-ops (no renewal config exists yet), and run() picked up the leftover legacy cert from db/getssl// and reinstalled it into PbxSettings, logging "SSL certificate installed" while the cert was actually the same expired file. The bug went undetected until a real expiry hit production. - cronRenewCert.php: detect via hasAcmeDomain() whether acme.sh already has a renewal config; if not, call startGetCertSsl(false) for a full --issue. Drop the trailing run() call — acme.sh triggers reloadCmd.php (--reloadcmd) on success, which already invokes run(). - GetSslMain::run() / getCertPath() / getPrivateKeyPath(): remove the legacy-getssl fallback. After migration the source of truth is acme.sh's config home; falling back to legacy paths masked the bug above. ECDSA (_ecc) is checked first, RSA second, then '' is returned so run() reports "not found" instead of reinstalling stale files. --- Lib/GetSslMain.php | 77 +++++++++++++++++++++++++++++-------------- bin/cronRenewCert.php | 38 +++++++++++---------- 2 files changed, 73 insertions(+), 42 deletions(-) diff --git a/Lib/GetSslMain.php b/Lib/GetSslMain.php index 493135f..32a3b2a 100644 --- a/Lib/GetSslMain.php +++ b/Lib/GetSslMain.php @@ -427,14 +427,15 @@ public function checkResult(): PBXApiResult } /** - * Runs the SSL certificate update process. - * Checks acme.sh paths first, then falls back to legacy getssl paths. + * Installs the acme.sh-issued certificate into PbxSettings. + * Called by reloadCmd.php (--reloadcmd hook) after a successful issue/renew. */ public function run(): void { $certPath = $this->getCertPath(); $privateKeyPath = $this->getPrivateKeyPath(); - if (file_exists($privateKeyPath) && file_exists($certPath)) { + if ($certPath !== '' && $privateKeyPath !== '' + && file_exists($privateKeyPath) && file_exists($certPath)) { $this->updateKey('WEBHTTPSPublicKey', $certPath); $this->updateKey('WEBHTTPSPrivateKey', $privateKeyPath); $this->appendLog('SSL certificate installed into PbxSettings'); @@ -444,39 +445,65 @@ public function run(): void } /** - * Returns the path to the SSL certificate. - * Checks acme.sh location first, then falls back to legacy getssl. - * - * @return string The certificate path. + * Returns true when acme.sh has a renewal config for the configured domain. + * Presence of the .conf file is required — acme.sh creates it only after a + * successful --issue, so it reliably distinguishes a leftover cert (from a + * legacy getssl migration) from a domain that acme.sh actually manages. + */ + public function hasAcmeDomain(): bool + { + $domain = $this->module_settings['domainName'] ?? ''; + if ($domain === '') { + return false; + } + $configHome = $this->dirs['acmeConfigHome']; + return is_file("$configHome/{$domain}_ecc/{$domain}.conf") + || is_file("$configHome/{$domain}/{$domain}.conf"); + } + + /** + * Returns the path to the SSL certificate issued by acme.sh, + * or an empty string if no acme.sh-issued certificate is present. */ private function getCertPath(): string { - $extHostname = $this->module_settings['domainName']; - // acme.sh path - $acmePath = $this->dirs['acmeConfigHome'] . '/' . $extHostname . '/fullchain.cer'; - if (file_exists($acmePath)) { - return $acmePath; + $domain = $this->module_settings['domainName'] ?? ''; + if ($domain === '') { + return ''; } - // Legacy getssl path - return $this->dirs['confDir'] . '/' . $extHostname . '/fullchain.crt'; + $configHome = $this->dirs['acmeConfigHome']; + // ECDSA is the acme.sh default + $eccPath = "$configHome/{$domain}_ecc/fullchain.cer"; + if (file_exists($eccPath)) { + return $eccPath; + } + $rsaPath = "$configHome/{$domain}/fullchain.cer"; + if (file_exists($rsaPath)) { + return $rsaPath; + } + return ''; } /** - * Returns the path to the private SSL key. - * Checks acme.sh location first, then falls back to legacy getssl. - * - * @return string The private key path. + * Returns the path to the acme.sh-issued private key, + * or an empty string if no acme.sh-issued key is present. */ private function getPrivateKeyPath(): string { - $extHostname = $this->module_settings['domainName']; - // acme.sh path - $acmePath = $this->dirs['acmeConfigHome'] . '/' . $extHostname . '/' . $extHostname . '.key'; - if (file_exists($acmePath)) { - return $acmePath; + $domain = $this->module_settings['domainName'] ?? ''; + if ($domain === '') { + return ''; + } + $configHome = $this->dirs['acmeConfigHome']; + $eccPath = "$configHome/{$domain}_ecc/{$domain}.key"; + if (file_exists($eccPath)) { + return $eccPath; } - // Legacy getssl path - return $this->dirs['confDir'] . '/' . $extHostname . '/' . $extHostname . '.key'; + $rsaPath = "$configHome/{$domain}/{$domain}.key"; + if (file_exists($rsaPath)) { + return $rsaPath; + } + return ''; } /** diff --git a/bin/cronRenewCert.php b/bin/cronRenewCert.php index 51748b3..3c8ced9 100644 --- a/bin/cronRenewCert.php +++ b/bin/cronRenewCert.php @@ -37,26 +37,30 @@ try { $moduleMain->prepareAcmeEnvironment(); - $acmeHome = $moduleMain->dirs['acmeHome']; - $acmeConfigHome = $moduleMain->dirs['acmeConfigHome']; - $shPath = Util::which('sh'); - $tsWrapper = $moduleMain->dirs['binDir'] . '/timestampWrapper.sh'; + if ($moduleMain->hasAcmeDomain()) { + // acme.sh already manages this domain — do a normal renewal pass. + // On success acme.sh invokes --reloadcmd (reloadCmd.php), which calls run() + // and installs the new cert into PbxSettings. No extra run() needed here. + $acmeHome = $moduleMain->dirs['acmeHome']; + $acmeConfigHome = $moduleMain->dirs['acmeConfigHome']; + $shPath = Util::which('sh'); + $tsWrapper = $moduleMain->dirs['binDir'] . '/timestampWrapper.sh'; - // Build acme.sh cron renewal command - $cmd = GetSslMain::ACME_SH_BIN - . ' --cron' - . ' --home ' . escapeshellarg($acmeHome) - . ' --config-home ' . escapeshellarg($acmeConfigHome); + $cmd = GetSslMain::ACME_SH_BIN + . ' --cron' + . ' --home ' . escapeshellarg($acmeHome) + . ' --config-home ' . escapeshellarg($acmeConfigHome); - // For DNS-01: prepend env exports - if ($moduleMain->isDns01()) { - $envExports = $moduleMain->buildDnsCredentialEnvString(); - $cmd = $envExports . $cmd; - } - - Processes::mwExec("$shPath $tsWrapper $cmd"); + if ($moduleMain->isDns01()) { + $cmd = $moduleMain->buildDnsCredentialEnvString() . $cmd; + } - $moduleMain->run(); + Processes::mwExec("$shPath $tsWrapper $cmd"); + } else { + // First run after upgrade from legacy getssl: no renewal config exists yet. + // acme.sh --cron would silently no-op, so trigger a full --issue instead. + $moduleMain->startGetCertSsl(false); + } } finally { if ($portManager !== null) { $portManager->closePort(); From 87961ac12dfcb73b85c87965ce2f0631ef57df2d Mon Sep 17 00:00:00 2001 From: Alexey Portnov Date: Fri, 15 May 2026 13:02:09 +0300 Subject: [PATCH 2/8] fix: suppress event-bus exception from PbxSettings save in reloadCmd The afterSave hook in MikoPBX core fires a publish to nchan via HTTP; when triggered from acme.sh --reloadcmd it races with nginx reload and fails with cURL error 56. The DB write itself has already succeeded, so swallow the exception and log a short note instead of polluting last-result.log with a 31-line stack trace per key. --- Lib/GetSslMain.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Lib/GetSslMain.php b/Lib/GetSslMain.php index 32a3b2a..eb12c2f 100644 --- a/Lib/GetSslMain.php +++ b/Lib/GetSslMain.php @@ -522,7 +522,14 @@ private function updateKey(string $name, string $path): void $dbRec = PbxSettings::findFirst($filter) ?? new PbxSettings(); $dbRec->key = $name; $dbRec->value = $key; - $dbRec->save(); + try { + $dbRec->save(); + } catch (\Throwable $e) { + // afterSave triggers an event-bus publish that races nginx reload + // when called from acme.sh --reloadcmd. The DB write itself has + // already succeeded; swallow the noisy nchan failure. + $this->appendLog('updateKey ' . $name . ': ' . $e->getMessage()); + } } } From 2eae9f9147325ac66460723cafe97f7566f9dab4 Mon Sep 17 00:00:00 2001 From: boffart <5922739+boffart@users.noreply.github.com> Date: Fri, 31 Jul 2026 15:55:52 +0300 Subject: [PATCH 3/8] docs: design IP certificate lifetime warning --- ...026-07-31-ip-certificate-warning-design.md | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-31-ip-certificate-warning-design.md diff --git a/docs/superpowers/specs/2026-07-31-ip-certificate-warning-design.md b/docs/superpowers/specs/2026-07-31-ip-certificate-warning-design.md new file mode 100644 index 0000000..f566c86 --- /dev/null +++ b/docs/superpowers/specs/2026-07-31-ip-certificate-warning-design.md @@ -0,0 +1,21 @@ +# IP certificate lifetime warning + +## Goal + +Warn users who enter an IPv4 or IPv6 address that Let's Encrypt IP certificates are short-lived and therefore renewed more frequently. + +## User interface + +Add a hidden native Semantic UI `ui warning message` directly below the existing address field. Its localized message says that Let's Encrypt IP certificates are valid for about six days and that the module will renew the certificate more frequently. + +The warning appears immediately when the field contains a valid IPv4 or IPv6 address, including an address restored when the page loads. It remains hidden for domain names, empty input, and malformed IP-like input. + +## Implementation + +Keep IP recognition in a small JavaScript predicate on the existing `ModuleGetSsl` form controller. Bind warning-state refresh to the address field's `input` event and call it once during initialization. Use strict IPv4 validation and browser URL parsing plus colon checks for IPv6; do not classify hostnames as IP addresses. + +Add English and Russian translation keys. Other locale files will fall back according to the module's existing translation behavior. Update both the ES6 source and its compiled browser asset using the project's Babel workflow. + +## Verification + +An automated Node test extracts and exercises the real predicate with valid and invalid IPv4/IPv6/domain inputs and verifies that the view uses the Semantic UI warning markup. Syntax checks and the available project analysis complete verification. From 6cdb497fc3f12d86073ceb8e0b74b3b0d94f54b7 Mon Sep 17 00:00:00 2001 From: boffart <5922739+boffart@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:01:30 +0300 Subject: [PATCH 4/8] feat: warn about short-lived IP certificates --- App/Views/ModuleGetSsl/index.volt | 3 + Messages/en.php | 1 + Messages/ru.php | 1 + .../2026-07-31-ip-certificate-warning.md | 73 ++++++ public/assets/js/module-get-ssl-index.js | 236 ++++++++++-------- public/assets/js/src/module-get-ssl-index.js | 45 ++++ tests/ip-address-warning.test.mjs | 84 +++++++ 7 files changed, 335 insertions(+), 108 deletions(-) create mode 100644 docs/superpowers/plans/2026-07-31-ip-certificate-warning.md create mode 100644 tests/ip-address-warning.test.mjs diff --git a/App/Views/ModuleGetSsl/index.volt b/App/Views/ModuleGetSsl/index.volt index 92b8b6a..40cf8f5 100644 --- a/App/Views/ModuleGetSsl/index.volt +++ b/App/Views/ModuleGetSsl/index.volt @@ -7,6 +7,9 @@
{{ form.render('domainName') }} +
diff --git a/Messages/en.php b/Messages/en.php index bae0434..f14d9e9 100644 --- a/Messages/en.php +++ b/Messages/en.php @@ -15,6 +15,7 @@ 'module_getssl_autoUpdateLabel' => 'Renew certificate automatically', 'module_getssl_getUpdateSSLButton' => 'Get/Renew SSL Certificate', 'module_getssl_DomainNameEmpty' => 'Enter the domain value to generate the certificate', + 'module_getssl_IpAddressCertificateWarning' => 'Let’s Encrypt certificates for IP addresses are valid for about 6 days. The module will renew this certificate more frequently.', 'module_getssl_getUpdateLogHeader' => 'Lets Encrypt Certificate Request Result', 'module_getssl_ConfigStartsGenerating' => 'Generating configuration files...', 'module_getssl_ConfigGenerated' => 'Configuration files created...', diff --git a/Messages/ru.php b/Messages/ru.php index e196efe..38d9789 100644 --- a/Messages/ru.php +++ b/Messages/ru.php @@ -27,6 +27,7 @@ 'module_getssl_getUpdateSSLButton' => 'Получить/обновить SSL сертификат', 'module_getssl_getUpdateLogHeader' => 'Результат запроса сертификата в Lets Encrypt', 'module_getssl_DomainNameEmpty' => 'Введите значение домена для генерации сертификата', + 'module_getssl_IpAddressCertificateWarning' => 'Сертификаты Let’s Encrypt для IP-адресов действуют около 6 дней. Модуль будет обновлять такой сертификат чаще.', 'module_getssl_ConfigStartsGenerating' => 'Генерируем конфигурационные файлы...', 'module_getssl_ConfigGenerated' => 'Конфигурационные файлы созданы...', 'module_getssl_GetSSLProcessing' => 'Выполняется запрос данных в Lets Encrypt...', diff --git a/docs/superpowers/plans/2026-07-31-ip-certificate-warning.md b/docs/superpowers/plans/2026-07-31-ip-certificate-warning.md new file mode 100644 index 0000000..6582196 --- /dev/null +++ b/docs/superpowers/plans/2026-07-31-ip-certificate-warning.md @@ -0,0 +1,73 @@ +# IP Certificate Lifetime Warning Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Show a localized native Semantic UI warning whenever the configured certificate address is a valid IPv4 or IPv6 address. + +**Architecture:** The existing form controller owns a pure `isIpAddress(value)` predicate and toggles a warning element during initialization and on every address-field input event. The Volt view owns the native Semantic UI markup, while English and Russian message catalogs own the copy. + +**Tech Stack:** JavaScript ES6, jQuery, Semantic UI, Volt, PHP translation arrays, Node test runner, Babel-generated browser asset. + +## Global Constraints + +- The warning text states that Let's Encrypt IP certificates are valid for about six days and that the module renews them more frequently. +- Valid IPv4 and IPv6 values show the warning; domains, empty input, and malformed values hide it. +- The warning reacts immediately and is correct for a value restored on page load. +- Do not add dependencies or change certificate issuance behavior. + +--- + +### Task 1: IP lifetime warning behavior + +**Files:** +- Create: `tests/ip-address-warning.test.mjs` +- Modify: `public/assets/js/src/module-get-ssl-index.js` +- Modify: `public/assets/js/module-get-ssl-index.js` +- Modify: `App/Views/ModuleGetSsl/index.volt` +- Modify: `Messages/en.php` +- Modify: `Messages/ru.php` + +**Interfaces:** +- Consumes: existing `#domainName` input, jQuery visibility methods, and translation function `t._()`. +- Produces: `ModuleGetSsl.isIpAddress(value): boolean`, `ModuleGetSsl.updateIpAddressWarning(): void`, and `#ip-address-certificate-warning` UI state. + +- [x] **Step 1: Write the failing behavior test** + +Create a Node test that loads the real form controller in a minimal VM-backed jQuery/DOM harness. Assert that initialization and `input` events show `#ip-address-certificate-warning` for `203.0.113.10`, `2001:db8::1`, and `[2001:db8::1]`, and hide it for `pbx.example.com`, empty text, `999.1.1.1`, and `2001:::1`. This catches removal or weakening of either recognition branch and missing reactive updates. + +- [x] **Step 2: Run the test and verify RED** + +Run: `node --test tests/ip-address-warning.test.mjs` + +Expected: FAIL because the warning selector and update behavior do not exist. + +- [x] **Step 3: Add minimal UI behavior and localized markup** + +Add cached `$domainName` and `$ipAddressWarning` elements. Implement strict dotted-decimal IPv4 validation and IPv6 recognition through bracket normalization plus `new URL('http://[' + value + ']')`, requiring a colon and an exact normalized hostname. Bind `input` to `updateIpAddressWarning` and call it from `initialize()`. + +Insert below the address field: + +```html + +``` + +Add the approved English and Russian translations. + +- [x] **Step 4: Run the focused test and verify GREEN** + +Run: `node --test tests/ip-address-warning.test.mjs` + +Expected: PASS for initialization, reactive toggling, IPv4, IPv6, bracketed IPv6, domain, empty, and malformed inputs. + +- [x] **Step 5: Rebuild and verify browser assets** + +Compile `public/assets/js/src/module-get-ssl-index.js` to `public/assets/js/module-get-ssl-index.js` using the available project Babel binary. Run the Node test again, `node --check` on both JavaScript files, `php -l` on both message files, and `git diff --check`. + +- [x] **Step 6: Commit the implementation** + +```bash +git add tests/ip-address-warning.test.mjs public/assets/js/src/module-get-ssl-index.js public/assets/js/module-get-ssl-index.js App/Views/ModuleGetSsl/index.volt Messages/en.php Messages/ru.php docs/superpowers/plans/2026-07-31-ip-certificate-warning.md +git commit -m "feat: warn about short-lived IP certificates" +``` diff --git a/public/assets/js/module-get-ssl-index.js b/public/assets/js/module-get-ssl-index.js index 4bdfa1a..1909a7a 100644 --- a/public/assets/js/module-get-ssl-index.js +++ b/public/assets/js/module-get-ssl-index.js @@ -1,15 +1,12 @@ -"use strict"; - /* global globalRootUrl, globalTranslate, Form, Config, PbxApi, dnsProvidersMeta */ -// Constants related to the form and module -var idUrl = 'module-get-ssl'; // API endpoint for SSL module -var idForm = 'module-get-ssl-form'; // Form element ID for SSL module +// Constants related to the form and module +const idUrl = 'module-get-ssl'; // API endpoint for SSL module +const idForm = 'module-get-ssl-form'; // Form element ID for SSL module +const className = 'ModuleGetSsl'; // Class name for this module -var className = 'ModuleGetSsl'; // Class name for this module // Main ModuleGetSsl class definition - -var ModuleGetSsl = { +const ModuleGetSsl = { // Cache commonly used jQuery objects $formObj: $('#' + idForm), $checkBoxes: $('#' + idForm + ' .ui.checkbox'), @@ -17,12 +14,14 @@ var ModuleGetSsl = { $statusToggle: $('#module-status-toggle'), $submitButton: $('#submitbutton'), $moduleStatus: $('#status'), + $domainName: $('#domainName'), $challengeType: $('#challengeType'), $dnsProvider: $('#dnsProvider'), $httpChallengeInfo: $('#http-challenge-info'), $dnsSettingsBlock: $('#dns-settings-block'), $dnsCredentialsFields: $('#dns-credentials-fields'), $dnsCredentialsInput: $('input[name="dnsCredentials"]'), + $ipAddressWarning: $('#ip-address-certificate-warning'), // Validation rules for the form validateRules: { domainName: { @@ -33,44 +32,82 @@ var ModuleGetSsl = { }] } }, - /** * Initialize the module, bind event listeners, and setup the form. */ - initialize: function initialize() { + initialize() { // Initialize Semantic UI checkboxes - this.$checkBoxes.checkbox(); // Initialize dropdowns + this.$checkBoxes.checkbox(); + // Initialize dropdowns this.$challengeType.dropdown({ onChange: ModuleGetSsl.onChangeChallengeType }); this.$dnsProvider.dropdown({ fullTextSearch: true, onChange: ModuleGetSsl.onChangeDnsProvider - }); // Check and set module status on load and when the status changes + }); + // Check and set module status on load and when the status changes this.checkStatusToggle(); - window.addEventListener('ModuleStatusChanged', this.checkStatusToggle); // Initialize form with validation and submit handlers + window.addEventListener('ModuleStatusChanged', this.checkStatusToggle); + // Initialize form with validation and submit handlers this.initializeForm(); - moduleGetSSLStatusLoopWorker.$resultBlock.hide(); // Restore saved state + this.bindIpAddressWarning(); + moduleGetSSLStatusLoopWorker.$resultBlock.hide(); - var currentChallenge = this.$challengeType.dropdown('get value') || 'http'; + // Restore saved state + const currentChallenge = this.$challengeType.dropdown('get value') || 'http'; this.onChangeChallengeType(currentChallenge); this.restoreSavedCredentials(); }, - + /** + * Check whether a value is a valid IPv4 or IPv6 address. + * @param {string} value + * @returns {boolean} + */ + isIpAddress(value) { + const address = String(value || '').trim(); + const ipv4Parts = address.split('.'); + if (ipv4Parts.length === 4) { + return ipv4Parts.every(part => /^\d{1,3}$/.test(part) && Number(part) <= 255 && (part === '0' || part[0] !== '0')); + } + const hasOpeningBracket = address.startsWith('['); + const hasClosingBracket = address.endsWith(']'); + if (hasOpeningBracket !== hasClosingBracket) return false; + const ipv6 = hasOpeningBracket ? address.slice(1, -1) : address; + if (!ipv6.includes(':') || /\s/.test(ipv6)) return false; + try { + new URL(`http://[${ipv6}]/`); + return true; + } catch (e) { + return false; + } + }, + /** Update warning visibility when the configured address changes. */ + updateIpAddressWarning() { + if (this.isIpAddress(this.$domainName.val())) { + this.$ipAddressWarning.show(); + } else { + this.$ipAddressWarning.hide(); + } + }, + /** Bind reactive IP warning behavior and initialize its state. */ + bindIpAddressWarning() { + this.$domainName.on('input', () => this.updateIpAddressWarning()); + this.updateIpAddressWarning(); + }, /** * Handle challenge type change: show/hide relevant sections. * @param {string} value - 'http' or 'dns' */ - onChangeChallengeType: function onChangeChallengeType(value) { + onChangeChallengeType(value) { if (value === 'dns') { ModuleGetSsl.$httpChallengeInfo.hide(); - ModuleGetSsl.$dnsSettingsBlock.show(); // Trigger provider change to render credential fields - - var currentProvider = ModuleGetSsl.$dnsProvider.dropdown('get value'); - + ModuleGetSsl.$dnsSettingsBlock.show(); + // Trigger provider change to render credential fields + const currentProvider = ModuleGetSsl.$dnsProvider.dropdown('get value'); if (currentProvider) { ModuleGetSsl.onChangeDnsProvider(currentProvider); } @@ -79,93 +116,91 @@ var ModuleGetSsl = { ModuleGetSsl.$dnsSettingsBlock.hide(); } }, - /** * Handle DNS provider change: dynamically render credential fields. * @param {string} value - provider ID (e.g. 'dns_cf') */ - onChangeDnsProvider: function onChangeDnsProvider(value) { - var $container = ModuleGetSsl.$dnsCredentialsFields; + onChangeDnsProvider(value) { + const $container = ModuleGetSsl.$dnsCredentialsFields; $container.empty(); - if (!value || typeof dnsProvidersMeta === 'undefined') { return; - } // Find provider metadata - - - var provider = dnsProvidersMeta.find(function (p) { - return p.id === value; - }); + } + // Find provider metadata + const provider = dnsProvidersMeta.find(p => p.id === value); if (!provider || !provider.fields) { return; - } // Decode existing saved credentials for pre-filling - - - var savedCreds = {}; - var encodedVal = ModuleGetSsl.$dnsCredentialsInput.val(); + } + // Decode existing saved credentials for pre-filling + let savedCreds = {}; + const encodedVal = ModuleGetSsl.$dnsCredentialsInput.val(); if (encodedVal) { try { - var decoded = atob(encodedVal); + const decoded = atob(encodedVal); savedCreds = JSON.parse(decoded); - } catch (e) {// ignore decode errors + } catch (e) { + // ignore decode errors } - } // Render fields - + } - provider.fields.forEach(function (field) { - var hasSaved = Boolean(savedCreds[field["var"]]); - var displayValue = hasSaved ? '••••••••' : ''; - var maskedAttr = hasSaved ? 'data-masked="true"' : ''; - var html = "\n\t\t\t\t
\n\t\t\t\t\t\n\t\t\t\t\t\n\t\t\t\t
"); + // Render fields + provider.fields.forEach(field => { + const hasSaved = Boolean(savedCreds[field.var]); + const displayValue = hasSaved ? '••••••••' : ''; + const maskedAttr = hasSaved ? 'data-masked="true"' : ''; + const html = ` +
+ + +
`; $container.append(html); - }); // On focus: clear mask so user can enter new value - + }); + // On focus: clear mask so user can enter new value $container.on('focus', '.dns-cred-input[data-masked]', function () { $(this).val('').removeAttr('data-masked'); - }); // Clear error highlight when user starts typing - + }); + // Clear error highlight when user starts typing $container.on('input', '.dns-cred-input', function () { $(this).closest('.field').removeClass('error'); }); }, - /** * Collect DNS credential field values into base64 JSON and write to hidden input. * Masked fields (unchanged) are restored from the previously saved credentials. */ - collectDnsCredentials: function collectDnsCredentials() { - var challengeType = ModuleGetSsl.$challengeType.dropdown('get value'); - + collectDnsCredentials() { + const challengeType = ModuleGetSsl.$challengeType.dropdown('get value'); if (challengeType !== 'dns') { return; - } // Decode currently stored credentials (source of truth for masked fields) - - - var savedCreds = {}; - var encodedVal = ModuleGetSsl.$dnsCredentialsInput.val(); - + } + // Decode currently stored credentials (source of truth for masked fields) + let savedCreds = {}; + const encodedVal = ModuleGetSsl.$dnsCredentialsInput.val(); if (encodedVal) { try { savedCreds = JSON.parse(atob(encodedVal)); - } catch (e) {// ignore + } catch (e) { + // ignore } } - - var creds = {}; + const creds = {}; $('.dns-cred-input').each(function () { - var varName = $(this).data('var'); + const varName = $(this).data('var'); if (!varName) return; - if ($(this).is('[data-masked]')) { // User didn't change this field — keep stored value if (savedCreds[varName]) { creds[varName] = savedCreds[varName]; } } else { - var val = $(this).val(); - + const val = $(this).val(); if (val) { creds[varName] = val; } @@ -173,70 +208,63 @@ var ModuleGetSsl = { }); ModuleGetSsl.$dnsCredentialsInput.val(btoa(JSON.stringify(creds))); }, - /** * Restore saved credentials into the DNS provider fields on page load. */ - restoreSavedCredentials: function restoreSavedCredentials() { - var currentProvider = ModuleGetSsl.$dnsProvider.dropdown('get value'); - + restoreSavedCredentials() { + const currentProvider = ModuleGetSsl.$dnsProvider.dropdown('get value'); if (currentProvider) { ModuleGetSsl.onChangeDnsProvider(currentProvider); } }, - /** * Escape HTML special characters for safe insertion into attributes. * @param {string} text * @returns {string} */ - escapeHtml: function escapeHtml(text) { - var map = { + escapeHtml(text) { + const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }; - return String(text).replace(/[&<>"']/g, function (m) { - return map[m]; - }); + return String(text).replace(/[&<>"']/g, m => map[m]); }, - /** * Request an SSL certificate by calling the server-side API. */ - getSsl: function getSsl() { + getSsl() { $.api({ - url: "".concat(Config.pbxUrl, "/pbxcore/api/modules/").concat(className, "/get-cert"), + url: `${Config.pbxUrl}/pbxcore/api/modules/${className}/get-cert`, on: 'now', method: 'POST', - beforeXHR: function beforeXHR(xhr) { + beforeXHR(xhr) { xhr.setRequestHeader('X-Async-Response-Channel-Id', moduleGetSSLStatusLoopWorker.channelId); xhr.setRequestHeader('X-Processor-Timeout', '120'); return xhr; }, - beforeSend: function beforeSend(settings) { + beforeSend(settings) { ModuleGetSsl.$submitButton.addClass('loading disabled'); moduleGetSSLStatusLoopWorker.$resultBlock.show(); moduleGetSSLStatusLoopWorker.editor.getSession().setValue(globalTranslate.module_getssl_GetSSLProcessing + '\n'); return settings; }, successTest: PbxApi.successTest, - onSuccess: function onSuccess(response) { + onSuccess: function (response) { ModuleGetSsl.$submitButton.removeClass('loading disabled'); }, - onFailure: function onFailure(response) { + onFailure: function (response) { ModuleGetSsl.$submitButton.removeClass('loading disabled'); UserMessage.showMultiString(response.message); } }); }, - /** * Toggles the form fields and status visibility based on the module's status. */ - checkStatusToggle: function checkStatusToggle() { + checkStatusToggle() { if (ModuleGetSsl.$statusToggle.checkbox('is checked')) { ModuleGetSsl.$disabilityFields.removeClass('disabled'); ModuleGetSsl.$moduleStatus.show(); @@ -245,26 +273,22 @@ var ModuleGetSsl = { ModuleGetSsl.$moduleStatus.hide(); } }, - /** * Validate DNS provider and credentials when DNS-01 is selected. * Returns true if valid, false otherwise. */ - validateDnsFields: function validateDnsFields() { + validateDnsFields() { if (ModuleGetSsl.$challengeType.dropdown('get value') !== 'dns') { return true; } - if (!ModuleGetSsl.$dnsProvider.dropdown('get value')) { UserMessage.showError(globalTranslate.module_getssl_DnsProviderEmpty); return false; } - - var hasEmpty = false; + let hasEmpty = false; $('.dns-cred-input').each(function () { - var $field = $(this).closest('.field'); - var isMasked = $(this).is('[data-masked]'); - + const $field = $(this).closest('.field'); + const isMasked = $(this).is('[data-masked]'); if (!isMasked && !$(this).val().trim()) { $field.addClass('error'); hasEmpty = true; @@ -272,56 +296,52 @@ var ModuleGetSsl = { $field.removeClass('error'); } }); - if (hasEmpty) { UserMessage.showError(globalTranslate.module_getssl_DnsCredentialsEmpty); return false; } - return true; }, - /** * Callback before sending the form. * @param {Object} settings - Ajax request settings. * @returns {Object} The modified Ajax request settings. */ - cbBeforeSendForm: function cbBeforeSendForm(settings) { + cbBeforeSendForm(settings) { if (!ModuleGetSsl.validateDnsFields()) { return false; } - - var result = settings; // Collect DNS credentials into hidden field before form submission - + const result = settings; + // Collect DNS credentials into hidden field before form submission ModuleGetSsl.collectDnsCredentials(); result.data = ModuleGetSsl.$formObj.form('get values'); return result; }, - /** * Callback function after sending the form. */ - cbAfterSendForm: function cbAfterSendForm(response) { + cbAfterSendForm(response) { if (Form.checkSuccess(response)) { ModuleGetSsl.getSsl(); } }, - /** * Initializes the form validation and submission logic. */ - initializeForm: function initializeForm() { + initializeForm() { Form.$formObj = ModuleGetSsl.$formObj; - Form.url = "".concat(globalRootUrl).concat(idUrl, "/").concat(idUrl, "/save"); + Form.url = `${globalRootUrl}${idUrl}/${idUrl}/save`; Form.validateRules = ModuleGetSsl.validateRules; Form.enableDirrity = false; Form.cbAfterSendForm = ModuleGetSsl.cbAfterSendForm; Form.cbBeforeSendForm = ModuleGetSsl.cbBeforeSendForm; Form.initialize(); } -}; // Initialize the ModuleGetSsl class when the document is ready +}; -$(document).ready(function () { +// Initialize the ModuleGetSsl class when the document is ready +$(document).ready(() => { ModuleGetSsl.initialize(); }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInNyYy9tb2R1bGUtZ2V0LXNzbC1pbmRleC5qcyJdLCJuYW1lcyI6WyJpZFVybCIsImlkRm9ybSIsImNsYXNzTmFtZSIsIk1vZHVsZUdldFNzbCIsIiRmb3JtT2JqIiwiJCIsIiRjaGVja0JveGVzIiwiJGRpc2FiaWxpdHlGaWVsZHMiLCIkc3RhdHVzVG9nZ2xlIiwiJHN1Ym1pdEJ1dHRvbiIsIiRtb2R1bGVTdGF0dXMiLCIkY2hhbGxlbmdlVHlwZSIsIiRkbnNQcm92aWRlciIsIiRodHRwQ2hhbGxlbmdlSW5mbyIsIiRkbnNTZXR0aW5nc0Jsb2NrIiwiJGRuc0NyZWRlbnRpYWxzRmllbGRzIiwiJGRuc0NyZWRlbnRpYWxzSW5wdXQiLCJ2YWxpZGF0ZVJ1bGVzIiwiZG9tYWluTmFtZSIsImlkZW50aWZpZXIiLCJydWxlcyIsInR5cGUiLCJwcm9tcHQiLCJnbG9iYWxUcmFuc2xhdGUiLCJtb2R1bGVfZ2V0c3NsX0RvbWFpbk5hbWVFbXB0eSIsImluaXRpYWxpemUiLCJjaGVja2JveCIsImRyb3Bkb3duIiwib25DaGFuZ2UiLCJvbkNoYW5nZUNoYWxsZW5nZVR5cGUiLCJmdWxsVGV4dFNlYXJjaCIsIm9uQ2hhbmdlRG5zUHJvdmlkZXIiLCJjaGVja1N0YXR1c1RvZ2dsZSIsIndpbmRvdyIsImFkZEV2ZW50TGlzdGVuZXIiLCJpbml0aWFsaXplRm9ybSIsIm1vZHVsZUdldFNTTFN0YXR1c0xvb3BXb3JrZXIiLCIkcmVzdWx0QmxvY2siLCJoaWRlIiwiY3VycmVudENoYWxsZW5nZSIsInJlc3RvcmVTYXZlZENyZWRlbnRpYWxzIiwidmFsdWUiLCJzaG93IiwiY3VycmVudFByb3ZpZGVyIiwiJGNvbnRhaW5lciIsImVtcHR5IiwiZG5zUHJvdmlkZXJzTWV0YSIsInByb3ZpZGVyIiwiZmluZCIsInAiLCJpZCIsImZpZWxkcyIsInNhdmVkQ3JlZHMiLCJlbmNvZGVkVmFsIiwidmFsIiwiZGVjb2RlZCIsImF0b2IiLCJKU09OIiwicGFyc2UiLCJlIiwiZm9yRWFjaCIsImZpZWxkIiwiaGFzU2F2ZWQiLCJCb29sZWFuIiwiZGlzcGxheVZhbHVlIiwibWFza2VkQXR0ciIsImh0bWwiLCJsYWJlbCIsImVzY2FwZUh0bWwiLCJhcHBlbmQiLCJvbiIsInJlbW92ZUF0dHIiLCJjbG9zZXN0IiwicmVtb3ZlQ2xhc3MiLCJjb2xsZWN0RG5zQ3JlZGVudGlhbHMiLCJjaGFsbGVuZ2VUeXBlIiwiY3JlZHMiLCJlYWNoIiwidmFyTmFtZSIsImRhdGEiLCJpcyIsImJ0b2EiLCJzdHJpbmdpZnkiLCJ0ZXh0IiwibWFwIiwiU3RyaW5nIiwicmVwbGFjZSIsIm0iLCJnZXRTc2wiLCJhcGkiLCJ1cmwiLCJDb25maWciLCJwYnhVcmwiLCJtZXRob2QiLCJiZWZvcmVYSFIiLCJ4aHIiLCJzZXRSZXF1ZXN0SGVhZGVyIiwiY2hhbm5lbElkIiwiYmVmb3JlU2VuZCIsInNldHRpbmdzIiwiYWRkQ2xhc3MiLCJlZGl0b3IiLCJnZXRTZXNzaW9uIiwic2V0VmFsdWUiLCJtb2R1bGVfZ2V0c3NsX0dldFNTTFByb2Nlc3NpbmciLCJzdWNjZXNzVGVzdCIsIlBieEFwaSIsIm9uU3VjY2VzcyIsInJlc3BvbnNlIiwib25GYWlsdXJlIiwiVXNlck1lc3NhZ2UiLCJzaG93TXVsdGlTdHJpbmciLCJtZXNzYWdlIiwidmFsaWRhdGVEbnNGaWVsZHMiLCJzaG93RXJyb3IiLCJtb2R1bGVfZ2V0c3NsX0Ruc1Byb3ZpZGVyRW1wdHkiLCJoYXNFbXB0eSIsIiRmaWVsZCIsImlzTWFza2VkIiwidHJpbSIsIm1vZHVsZV9nZXRzc2xfRG5zQ3JlZGVudGlhbHNFbXB0eSIsImNiQmVmb3JlU2VuZEZvcm0iLCJyZXN1bHQiLCJmb3JtIiwiY2JBZnRlclNlbmRGb3JtIiwiRm9ybSIsImNoZWNrU3VjY2VzcyIsImdsb2JhbFJvb3RVcmwiLCJlbmFibGVEaXJyaXR5IiwiZG9jdW1lbnQiLCJyZWFkeSJdLCJtYXBwaW5ncyI6Ijs7QUFBQTtBQUVBO0FBQ0EsSUFBTUEsS0FBSyxHQUFPLGdCQUFsQixDLENBQWlEOztBQUNqRCxJ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QUFDNEgsUUFBRCxDQUFELENBQVlDLEtBQVosQ0FBa0IsWUFBTTtBQUN2Qi9ILEVBQUFBLFlBQVksQ0FBQ3NCLFVBQWI7QUFDQSxDQUZEIiwic291cmNlc0NvbnRlbnQiOlsiLyogZ2xvYmFsIGdsb2JhbFJvb3RVcmwsIGdsb2JhbFRyYW5zbGF0ZSwgRm9ybSwgQ29uZmlnLCBQYnhBcGksIGRuc1Byb3ZpZGVyc01ldGEgKi9cblxuLy8gQ29uc3RhbnRzIHJlbGF0ZWQgdG8gdGhlIGZvcm0gYW5kIG1vZHVsZVxuY29uc3QgaWRVcmwgICAgID0gJ21vZHVsZS1nZXQtc3NsJzsgICAgICAgICAgICAgIC8vIEFQSSBlbmRwb2ludCBmb3IgU1NMIG1vZHVsZVxuY29uc3QgaWRGb3JtICAgID0gJ21vZHVsZS1nZXQtc3NsLWZvcm0nOyAgICAgICAgIC8vIEZvcm0gZWxlbWVudCBJRCBmb3IgU1NMIG1vZHVsZVxuY29uc3QgY2xhc3NOYW1lID0gJ01vZHVsZUdldFNzbCc7ICAgICAgICAgICAgICAgIC8vIENsYXNzIG5hbWUgZm9yIHRoaXMgbW9kdWxlXG5cbi8vIE1haW4gTW9kdWxlR2V0U3NsIGNsYXNzIGRlZmluaXRpb25cbmNvbnN0IE1vZHVsZUdldFNzbCA9IHtcblx0Ly8gQ2FjaGUgY29tbW9ubHkgdXNlZCBqUXVlcnkgb2JqZWN0c1xuXHQkZm9ybU9iajogJCgnIycgKyBpZEZvcm0pLFxuXHQkY2hlY2tCb3hlczogJCgnIycgKyBpZEZvcm0gKyAnIC51aS5jaGVja2JveCcpLFxuXHQkZGlzYWJpbGl0eUZpZWxkczogJCgnIycgKyBpZEZvcm0gKyAnIC5kaXNhYmlsaXR5JyksXG5cdCRzdGF0dXNUb2dnbGU6ICQoJyNtb2R1bGUtc3RhdHVzLXRvZ2dsZScpLFxuXHQkc3VibWl0QnV0dG9uOiAkKCcjc3VibWl0YnV0dG9uJyksXG5cdCRtb2R1bGVTdGF0dXM6ICQoJyNzdGF0dXMnKSxcblx0JGNoYWxsZW5nZVR5cGU6ICQoJyNjaGFsbGVuZ2VUeXBlJyksXG5cdCRkbnNQcm92aWRlcjogJCgnI2Ruc1Byb3ZpZGVyJyksXG5cdCRodHRwQ2hhbGxlbmdlSW5mbzogJCgnI2h0dHAtY2hhbGxlbmdlLWluZm8nKSxcblx0JGRuc1NldHRpbmdzQmxvY2s6ICQoJyNkbnMtc2V0dGluZ3MtYmxvY2snKSxcblx0JGRuc0NyZWRlbnRpYWxzRmllbGRzOiAkKCcjZG5zLWNyZWRlbnRpYWxzLWZpZWxkcycpLFxuXHQkZG5zQ3JlZGVudGlhbHNJbnB1dDogJCgnaW5wdXRbbmFtZT1cImRuc0NyZWRlbnRpYWxzXCJdJyksXG5cblx0Ly8gVmFsaWRhdGlvbiBydWxlcyBmb3IgdGhlIGZvcm1cblx0dmFsaWRhdGVSdWxlczoge1xuXHRcdGRvbWFpbk5hbWU6IHtcblx0XHRcdGlkZW50aWZpZXI6ICdkb21haW5OYW1lJyxcblx0XHRcdHJ1bGVzOiBbXG5cdFx0XHRcdHtcblx0XHRcdFx0XHR0eXBlOiAnZW1wdHknLFxuXHRcdFx0XHRcdHByb21wdDogZ2xvYmFsVHJhbnNsYXRlLm1vZHVsZV9nZXRzc2xfRG9tYWluTmFtZUVtcHR5LFxuXHRcdFx0XHR9LFxuXHRcdFx0XSxcblx0XHR9LFxuXHR9LFxuXG5cdC8qKlxuXHQgKiBJbml0aWFsaXplIHRoZSBtb2R1bGUsIGJpbmQgZXZlbnQgbGlzdGVuZXJzLCBhbmQgc2V0dXAgdGhlIGZvcm0uXG5cdCAqL1xuXHRpbml0aWFsaXplKCkge1xuXHRcdC8vIEluaXRpYWxpemUgU2VtYW50aWMgVUkgY2hlY2tib3hlc1xuXHRcdHRoaXMuJGNoZWNrQm94ZXMuY2hlY2tib3goKTtcblxuXHRcdC8vIEluaXRpYWxpemUgZHJvcGRvd25zXG5cdFx0dGhpcy4kY2hhbGxlbmdlVHlwZS5kcm9wZG93bih7XG5cdFx0XHRvbkNoYW5nZTogTW9kdWxlR2V0U3NsLm9uQ2hhbmdlQ2hhbGxlbmdlVHlwZSxcblx0XHR9KTtcblx0XHR0aGlzLiRkbnNQcm92aWRlci5kcm9wZG93bih7XG5cdFx0XHRmdWxsVGV4dFNlYXJjaDogdHJ1ZSxcblx0XHRcdG9uQ2hhbmdlOiBNb2R1bGVHZXRTc2wub25DaGFuZ2VEbnNQcm92aWRlcixcblx0XHR9KTtcblxuXHRcdC8vIENoZWNrIGFuZCBzZXQgbW9kdWxlIHN0YXR1cyBvbiBsb2FkIGFuZCB3aGVuIHRoZSBzdGF0dXMgY2hhbmdlc1xuXHRcdHRoaXMuY2hlY2tTdGF0dXNUb2dnbGUoKTtcblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignTW9kdWxlU3RhdHVzQ2hhbmdlZCcsIHRoaXMuY2hlY2tTdGF0dXNUb2dnbGUpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBmb3JtIHdpdGggdmFsaWRhdGlvbiBhbmQgc3VibWl0IGhhbmRsZXJzXG5cdFx0dGhpcy5pbml0aWFsaXplRm9ybSgpO1xuXG5cdFx0bW9kdWxlR2V0U1NMU3RhdHVzTG9vcFdvcmtlci4kcmVzdWx0QmxvY2suaGlkZSgpO1xuXG5cdFx0Ly8gUmVzdG9yZSBzYXZlZCBzdGF0ZVxuXHRcdGNvbnN0IGN1cnJlbnRDaGFsbGVuZ2UgPSB0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdnZXQgdmFsdWUnKSB8fCAnaHR0cCc7XG5cdFx0dGhpcy5vbkNoYW5nZUNoYWxsZW5nZVR5cGUoY3VycmVudENoYWxsZW5nZSk7XG5cdFx0dGhpcy5yZXN0b3JlU2F2ZWRDcmVkZW50aWFscygpO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBIYW5kbGUgY2hhbGxlbmdlIHR5cGUgY2hhbmdlOiBzaG93L2hpZGUgcmVsZXZhbnQgc2VjdGlvbnMuXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZSAtICdodHRwJyBvciAnZG5zJ1xuXHQgKi9cblx0b25DaGFuZ2VDaGFsbGVuZ2VUeXBlKHZhbHVlKSB7XG5cdFx0aWYgKHZhbHVlID09PSAnZG5zJykge1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRodHRwQ2hhbGxlbmdlSW5mby5oaWRlKCk7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGRuc1NldHRpbmdzQmxvY2suc2hvdygpO1xuXHRcdFx0Ly8gVHJpZ2dlciBwcm92aWRlciBjaGFuZ2UgdG8gcmVuZGVyIGNyZWRlbnRpYWwgZmllbGRzXG5cdFx0XHRjb25zdCBjdXJyZW50UHJvdmlkZXIgPSBNb2R1bGVHZXRTc2wuJGRuc1Byb3ZpZGVyLmRyb3Bkb3duKCdnZXQgdmFsdWUnKTtcblx0XHRcdGlmIChjdXJyZW50UHJvdmlkZXIpIHtcblx0XHRcdFx0TW9kdWxlR2V0U3NsLm9uQ2hhbmdlRG5zUHJvdmlkZXIoY3VycmVudFByb3ZpZGVyKTtcblx0XHRcdH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRodHRwQ2hhbGxlbmdlSW5mby5zaG93KCk7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGRuc1NldHRpbmdzQmxvY2suaGlkZSgpO1xuXHRcdH1cblx0fSxcblxuXHQvKipcblx0ICogSGFuZGxlIEROUyBwcm92aWRlciBjaGFuZ2U6IGR5bmFtaWNhbGx5IHJlbmRlciBjcmVkZW50aWFsIGZpZWxkcy5cblx0ICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gcHJvdmlkZXIgSUQgKGUuZy4gJ2Ruc19jZicpXG5cdCAqL1xuXHRvbkNoYW5nZURuc1Byb3ZpZGVyKHZhbHVlKSB7XG5cdFx0Y29uc3QgJGNvbnRhaW5lciA9IE1vZHVsZUdldFNzbC4kZG5zQ3JlZGVudGlhbHNGaWVsZHM7XG5cdFx0JGNvbnRhaW5lci5lbXB0eSgpO1xuXG5cdFx0aWYgKCF2YWx1ZSB8fCB0eXBlb2YgZG5zUHJvdmlkZXJzTWV0YSA9PT0gJ3VuZGVmaW5lZCcpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvLyBGaW5kIHByb3ZpZGVyIG1ldGFkYXRhXG5cdFx0Y29uc3QgcHJvdmlkZXIgPSBkbnNQcm92aWRlcnNNZXRhLmZpbmQocCA9PiBwLmlkID09PSB2YWx1ZSk7XG5cdFx0aWYgKCFwcm92aWRlciB8fCAhcHJvdmlkZXIuZmllbGRzKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gRGVjb2RlIGV4aXN0aW5nIHNhdmVkIGNyZWRlbnRpYWxzIGZvciBwcmUtZmlsbGluZ1xuXHRcdGxldCBzYXZlZENyZWRzID0ge307XG5cdFx0Y29uc3QgZW5jb2RlZFZhbCA9IE1vZHVsZUdldFNzbC4kZG5zQ3JlZGVudGlhbHNJbnB1dC52YWwoKTtcblx0XHRpZiAoZW5jb2RlZFZhbCkge1xuXHRcdFx0dHJ5IHtcblx0XHRcdFx0Y29uc3QgZGVjb2RlZCA9IGF0b2IoZW5jb2RlZFZhbCk7XG5cdFx0XHRcdHNhdmVkQ3JlZHMgPSBKU09OLnBhcnNlKGRlY29kZWQpO1xuXHRcdFx0fSBjYXRjaCAoZSkge1xuXHRcdFx0XHQvLyBpZ25vcmUgZGVjb2RlIGVycm9yc1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIFJlbmRlciBmaWVsZHNcblx0XHRwcm92aWRlci5maWVsZHMuZm9yRWFjaChmaWVsZCA9PiB7XG5cdFx0XHRjb25zdCBoYXNTYXZlZCA9IEJvb2xlYW4oc2F2ZWRDcmVkc1tmaWVsZC52YXJdKTtcblx0XHRcdGNvbnN0IGRpc3BsYXlWYWx1ZSA9IGhhc1NhdmVkID8gJ+KAouKAouKAouKAouKAouKAouKAouKAoicgOiAnJztcblx0XHRcdGNvbnN0IG1hc2tlZEF0dHIgPSBoYXNTYXZlZCA/ICdkYXRhLW1hc2tlZD1cInRydWVcIicgOiAnJztcblx0XHRcdGNvbnN0IGh0bWwgPSBgXG5cdFx0XHRcdDxkaXYgY2xhc3M9XCJmaWVsZFwiPlxuXHRcdFx0XHRcdDxsYWJlbD4ke2ZpZWxkLmxhYmVsfTwvbGFiZWw+XG5cdFx0XHRcdFx0PGlucHV0IHR5cGU9XCJwYXNzd29yZFwiXG5cdFx0XHRcdFx0XHQgICBjbGFzcz1cImRucy1jcmVkLWlucHV0XCJcblx0XHRcdFx0XHRcdCAgIGRhdGEtdmFyPVwiJHtmaWVsZC52YXJ9XCJcblx0XHRcdFx0XHRcdCAgICR7bWFza2VkQXR0cn1cblx0XHRcdFx0XHRcdCAgIHZhbHVlPVwiJHtNb2R1bGVHZXRTc2wuZXNjYXBlSHRtbChkaXNwbGF5VmFsdWUpfVwiXG5cdFx0XHRcdFx0XHQgICBwbGFjZWhvbGRlcj1cIiR7ZmllbGQubGFiZWx9XCI+XG5cdFx0XHRcdDwvZGl2PmA7XG5cdFx0XHQkY29udGFpbmVyLmFwcGVuZChodG1sKTtcblx0XHR9KTtcblx0XHQvLyBPbiBmb2N1czogY2xlYXIgbWFzayBzbyB1c2VyIGNhbiBlbnRlciBuZXcgdmFsdWVcblx0XHQkY29udGFpbmVyLm9uKCdmb2N1cycsICcuZG5zLWNyZWQtaW5wdXRbZGF0YS1tYXNrZWRdJywgZnVuY3Rpb24gKCkge1xuXHRcdFx0JCh0aGlzKS52YWwoJycpLnJlbW92ZUF0dHIoJ2RhdGEtbWFza2VkJyk7XG5cdFx0fSk7XG5cdFx0Ly8gQ2xlYXIgZXJyb3IgaGlnaGxpZ2h0IHdoZW4gdXNlciBzdGFydHMgdHlwaW5nXG5cdFx0JGNvbnRhaW5lci5vbignaW5wdXQnLCAnLmRucy1jcmVkLWlucHV0JywgZnVuY3Rpb24gKCkge1xuXHRcdFx0JCh0aGlzKS5jbG9zZXN0KCcuZmllbGQnKS5yZW1vdmVDbGFzcygnZXJyb3InKTtcblx0XHR9KTtcblx0fSxcblxuXHQvKipcblx0ICogQ29sbGVjdCBETlMgY3JlZGVudGlhbCBmaWVsZCB2YWx1ZXMgaW50byBiYXNlNjQgSlNPTiBhbmQgd3JpdGUgdG8gaGlkZGVuIGlucHV0LlxuXHQgKiBNYXNrZWQgZmllbGRzICh1bmNoYW5nZWQpIGFyZSByZXN0b3JlZCBmcm9tIHRoZSBwcmV2aW91c2x5IHNhdmVkIGNyZWRlbnRpYWxzLlxuXHQgKi9cblx0Y29sbGVjdERuc0NyZWRlbnRpYWxzKCkge1xuXHRcdGNvbnN0IGNoYWxsZW5nZVR5cGUgPSBNb2R1bGVHZXRTc2wuJGNoYWxsZW5nZVR5cGUuZHJvcGRvd24oJ2dldCB2YWx1ZScpO1xuXHRcdGlmIChjaGFsbGVuZ2VUeXBlICE9PSAnZG5zJykge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHQvLyBEZWNvZGUgY3VycmVudGx5IHN0b3JlZCBjcmVkZW50aWFscyAoc291cmNlIG9mIHRydXRoIGZvciBtYXNrZWQgZmllbGRzKVxuXHRcdGxldCBzYXZlZENyZWRzID0ge307XG5cdFx0Y29uc3QgZW5jb2RlZFZhbCA9IE1vZHVsZUdldFNzbC4kZG5zQ3JlZGVudGlhbHNJbnB1dC52YWwoKTtcblx0XHRpZiAoZW5jb2RlZFZhbCkge1xuXHRcdFx0dHJ5IHtcblx0XHRcdFx0c2F2ZWRDcmVkcyA9IEpTT04ucGFyc2UoYXRvYihlbmNvZGVkVmFsKSk7XG5cdFx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRcdC8vIGlnbm9yZVxuXHRcdFx0fVxuXHRcdH1cblx0XHRjb25zdCBjcmVkcyA9IHt9O1xuXHRcdCQoJy5kbnMtY3JlZC1pbnB1dCcpLmVhY2goZnVuY3Rpb24gKCkge1xuXHRcdFx0Y29uc3QgdmFyTmFtZSA9ICQodGhpcykuZGF0YSgndmFyJyk7XG5cdFx0XHRpZiAoIXZhck5hbWUpIHJldHVybjtcblx0XHRcdGlmICgkKHRoaXMpLmlzKCdbZGF0YS1tYXNrZWRdJykpIHtcblx0XHRcdFx0Ly8gVXNlciBkaWRuJ3QgY2hhbmdlIHRoaXMgZmllbGQg4oCUIGtlZXAgc3RvcmVkIHZhbHVlXG5cdFx0XHRcdGlmIChzYXZlZENyZWRzW3Zhck5hbWVdKSB7XG5cdFx0XHRcdFx0Y3JlZHNbdmFyTmFtZV0gPSBzYXZlZENyZWRzW3Zhck5hbWVdO1xuXHRcdFx0XHR9XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRjb25zdCB2YWwgPSAkKHRoaXMpLnZhbCgpO1xuXHRcdFx0XHRpZiAodmFsKSB7XG5cdFx0XHRcdFx0Y3JlZHNbdmFyTmFtZV0gPSB2YWw7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9KTtcblx0XHRNb2R1bGVHZXRTc2wuJGRuc0NyZWRlbnRpYWxzSW5wdXQudmFsKGJ0b2EoSlNPTi5zdHJpbmdpZnkoY3JlZHMpKSk7XG5cdH0sXG5cblx0LyoqXG5cdCAqIFJlc3RvcmUgc2F2ZWQgY3JlZGVudGlhbHMgaW50byB0aGUgRE5TIHByb3ZpZGVyIGZpZWxkcyBvbiBwYWdlIGxvYWQuXG5cdCAqL1xuXHRyZXN0b3JlU2F2ZWRDcmVkZW50aWFscygpIHtcblx0XHRjb25zdCBjdXJyZW50UHJvdmlkZXIgPSBNb2R1bGVHZXRTc2wuJGRuc1Byb3ZpZGVyLmRyb3Bkb3duKCdnZXQgdmFsdWUnKTtcblx0XHRpZiAoY3VycmVudFByb3ZpZGVyKSB7XG5cdFx0XHRNb2R1bGVHZXRTc2wub25DaGFuZ2VEbnNQcm92aWRlcihjdXJyZW50UHJvdmlkZXIpO1xuXHRcdH1cblx0fSxcblxuXHQvKipcblx0ICogRXNjYXBlIEhUTUwgc3BlY2lhbCBjaGFyYWN0ZXJzIGZvciBzYWZlIGluc2VydGlvbiBpbnRvIGF0dHJpYnV0ZXMuXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0XG5cdCAqIEByZXR1cm5zIHtzdHJpbmd9XG5cdCAqL1xuXHRlc2NhcGVIdG1sKHRleHQpIHtcblx0XHRjb25zdCBtYXAgPSB7ICcmJzogJyZhbXA7JywgJzwnOiAnJmx0OycsICc+JzogJyZndDsnLCAnXCInOiAnJnF1b3Q7JywgXCInXCI6ICcmIzAzOTsnIH07XG5cdFx0cmV0dXJuIFN0cmluZyh0ZXh0KS5yZXBsYWNlKC9bJjw+XCInXS9nLCBtID0+IG1hcFttXSk7XG5cdH0sXG5cblx0LyoqXG5cdCAqIFJlcXVlc3QgYW4gU1NMIGNlcnRpZmljYXRlIGJ5IGNhbGxpbmcgdGhlIHNlcnZlci1zaWRlIEFQSS5cblx0ICovXG5cdGdldFNzbCgpIHtcblx0XHQkLmFwaSh7XG5cdFx0XHR1cmw6IGAke0NvbmZpZy5wYnhVcmx9L3BieGNvcmUvYXBpL21vZHVsZXMvJHtjbGFzc05hbWV9L2dldC1jZXJ0YCxcblx0XHRcdG9uOiAnbm93Jyxcblx0XHRcdG1ldGhvZDogJ1BPU1QnLFxuXHRcdFx0YmVmb3JlWEhSKHhocikge1xuXHRcdFx0XHR4aHIuc2V0UmVxdWVzdEhlYWRlciAoJ1gtQXN5bmMtUmVzcG9uc2UtQ2hhbm5lbC1JZCcsIG1vZHVsZUdldFNTTFN0YXR1c0xvb3BXb3JrZXIuY2hhbm5lbElkKTtcblx0XHRcdFx0eGhyLnNldFJlcXVlc3RIZWFkZXIgKCdYLVByb2Nlc3Nvci1UaW1lb3V0JywgJzEyMCcpO1xuXHRcdFx0XHRyZXR1cm4geGhyO1xuXHRcdFx0fSxcblx0XHRcdGJlZm9yZVNlbmQoc2V0dGluZ3MpIHtcblx0XHRcdFx0TW9kdWxlR2V0U3NsLiRzdWJtaXRCdXR0b24uYWRkQ2xhc3MoJ2xvYWRpbmcgZGlzYWJsZWQnKTtcblx0XHRcdFx0bW9kdWxlR2V0U1NMU3RhdHVzTG9vcFdvcmtlci4kcmVzdWx0QmxvY2suc2hvdygpO1xuXHRcdFx0XHRtb2R1bGVHZXRTU0xTdGF0dXNMb29wV29ya2VyLmVkaXRvci5nZXRTZXNzaW9uKCkuc2V0VmFsdWUoXG5cdFx0XHRcdGdsb2JhbFRyYW5zbGF0ZS5tb2R1bGVfZ2V0c3NsX0dldFNTTFByb2Nlc3NpbmcgKyAnXFxuJ1xuXHRcdFx0KTtcblx0XHRcdFx0cmV0dXJuIHNldHRpbmdzO1xuXHRcdFx0fSxcblx0XHRcdHN1Y2Nlc3NUZXN0OiBQYnhBcGkuc3VjY2Vzc1Rlc3QsXG5cdFx0XHRvblN1Y2Nlc3M6IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuXHRcdFx0XHRNb2R1bGVHZXRTc2wuJHN1Ym1pdEJ1dHRvbi5yZW1vdmVDbGFzcygnbG9hZGluZyBkaXNhYmxlZCcpO1xuXHRcdFx0fSxcblx0XHRcdG9uRmFpbHVyZTogZnVuY3Rpb24ocmVzcG9uc2UpIHtcblx0XHRcdFx0TW9kdWxlR2V0U3NsLiRzdWJtaXRCdXR0b24ucmVtb3ZlQ2xhc3MoJ2xvYWRpbmcgZGlzYWJsZWQnKTtcblx0XHRcdFx0VXNlck1lc3NhZ2Uuc2hvd011bHRpU3RyaW5nKHJlc3BvbnNlLm1lc3NhZ2UpO1xuXHRcdFx0fSxcblx0XHR9KVxuXHR9LFxuXG5cdC8qKlxuXHQgKiBUb2dnbGVzIHRoZSBmb3JtIGZpZWxkcyBhbmQgc3RhdHVzIHZpc2liaWxpdHkgYmFzZWQgb24gdGhlIG1vZHVsZSdzIHN0YXR1cy5cblx0ICovXG5cdGNoZWNrU3RhdHVzVG9nZ2xlKCkge1xuXHRcdGlmIChNb2R1bGVHZXRTc2wuJHN0YXR1c1RvZ2dsZS5jaGVja2JveCgnaXMgY2hlY2tlZCcpKSB7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGRpc2FiaWxpdHlGaWVsZHMucmVtb3ZlQ2xhc3MoJ2Rpc2FibGVkJyk7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJG1vZHVsZVN0YXR1cy5zaG93KCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdE1vZHVsZUdldFNzbC4kZGlzYWJpbGl0eUZpZWxkcy5hZGRDbGFzcygnZGlzYWJsZWQnKTtcblx0XHRcdE1vZHVsZUdldFNzbC4kbW9kdWxlU3RhdHVzLmhpZGUoKTtcblx0XHR9XG5cdH0sXG5cblx0LyoqXG5cdCAqIFZhbGlkYXRlIEROUyBwcm92aWRlciBhbmQgY3JlZGVudGlhbHMgd2hlbiBETlMtMDEgaXMgc2VsZWN0ZWQuXG5cdCAqIFJldHVybnMgdHJ1ZSBpZiB2YWxpZCwgZmFsc2Ugb3RoZXJ3aXNlLlxuXHQgKi9cblx0dmFsaWRhdGVEbnNGaWVsZHMoKSB7XG5cdFx0aWYgKE1vZHVsZUdldFNzbC4kY2hhbGxlbmdlVHlwZS5kcm9wZG93bignZ2V0IHZhbHVlJykgIT09ICdkbnMnKSB7XG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHR9XG5cdFx0aWYgKCFNb2R1bGVHZXRTc2wuJGRuc1Byb3ZpZGVyLmRyb3Bkb3duKCdnZXQgdmFsdWUnKSkge1xuXHRcdFx0VXNlck1lc3NhZ2Uuc2hvd0Vycm9yKGdsb2JhbFRyYW5zbGF0ZS5tb2R1bGVfZ2V0c3NsX0Ruc1Byb3ZpZGVyRW1wdHkpO1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0XHRsZXQgaGFzRW1wdHkgPSBmYWxzZTtcblx0XHQkKCcuZG5zLWNyZWQtaW5wdXQnKS5lYWNoKGZ1bmN0aW9uICgpIHtcblx0XHRcdGNvbnN0ICRmaWVsZCA9ICQodGhpcykuY2xvc2VzdCgnLmZpZWxkJyk7XG5cdFx0XHRjb25zdCBpc01hc2tlZCA9ICQodGhpcykuaXMoJ1tkYXRhLW1hc2tlZF0nKTtcblx0XHRcdGlmICghaXNNYXNrZWQgJiYgISQodGhpcykudmFsKCkudHJpbSgpKSB7XG5cdFx0XHRcdCRmaWVsZC5hZGRDbGFzcygnZXJyb3InKTtcblx0XHRcdFx0aGFzRW1wdHkgPSB0cnVlO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0JGZpZWxkLnJlbW92ZUNsYXNzKCdlcnJvcicpO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHRcdGlmIChoYXNFbXB0eSkge1xuXHRcdFx0VXNlck1lc3NhZ2Uuc2hvd0Vycm9yKGdsb2JhbFRyYW5zbGF0ZS5tb2R1bGVfZ2V0c3NsX0Ruc0NyZWRlbnRpYWxzRW1wdHkpO1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0XHRyZXR1cm4gdHJ1ZTtcblx0fSxcblxuXHQvKipcblx0ICogQ2FsbGJhY2sgYmVmb3JlIHNlbmRpbmcgdGhlIGZvcm0uXG5cdCAqIEBwYXJhbSB7T2JqZWN0fSBzZXR0aW5ncyAtIEFqYXggcmVxdWVzdCBzZXR0aW5ncy5cblx0ICogQHJldHVybnMge09iamVjdH0gVGhlIG1vZGlmaWVkIEFqYXggcmVxdWVzdCBzZXR0aW5ncy5cblx0ICovXG5cdGNiQmVmb3JlU2VuZEZvcm0oc2V0dGluZ3MpIHtcblx0XHRpZiAoIU1vZHVsZUdldFNzbC52YWxpZGF0ZURuc0ZpZWxkcygpKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHRcdGNvbnN0IHJlc3VsdCA9IHNldHRpbmdzO1xuXHRcdC8vIENvbGxlY3QgRE5TIGNyZWRlbnRpYWxzIGludG8gaGlkZGVuIGZpZWxkIGJlZm9yZSBmb3JtIHN1Ym1pc3Npb25cblx0XHRNb2R1bGVHZXRTc2wuY29sbGVjdERuc0NyZWRlbnRpYWxzKCk7XG5cdFx0cmVzdWx0LmRhdGEgPSBNb2R1bGVHZXRTc2wuJGZvcm1PYmouZm9ybSgnZ2V0IHZhbHVlcycpO1xuXHRcdHJldHVybiByZXN1bHQ7XG5cdH0sXG5cblx0LyoqXG5cdCAqIENhbGxiYWNrIGZ1bmN0aW9uIGFmdGVyIHNlbmRpbmcgdGhlIGZvcm0uXG5cdCAqL1xuXHRjYkFmdGVyU2VuZEZvcm0ocmVzcG9uc2UpIHtcblx0XHRpZiAoRm9ybS5jaGVja1N1Y2Nlc3MocmVzcG9uc2UpKXtcblx0XHRcdE1vZHVsZUdldFNzbC5nZXRTc2woKTtcblx0XHR9XG5cdH0sXG5cblxuXHQvKipcblx0ICogSW5pdGlhbGl6ZXMgdGhlIGZvcm0gdmFsaWRhdGlvbiBhbmQgc3VibWlzc2lvbiBsb2dpYy5cblx0ICovXG5cdGluaXRpYWxpemVGb3JtKCkge1xuXHRcdEZvcm0uJGZvcm1PYmogPSBNb2R1bGVHZXRTc2wuJGZvcm1PYmo7XG5cdFx0Rm9ybS51cmwgPSBgJHtnbG9iYWxSb290VXJsfSR7aWRVcmx9LyR7aWRVcmx9L3NhdmVgO1xuXHRcdEZvcm0udmFsaWRhdGVSdWxlcyA9IE1vZHVsZUdldFNzbC52YWxpZGF0ZVJ1bGVzO1xuXHRcdEZvcm0uZW5hYmxlRGlycml0eSA9IGZhbHNlO1xuXHRcdEZvcm0uY2JBZnRlclNlbmRGb3JtID0gTW9kdWxlR2V0U3NsLmNiQWZ0ZXJTZW5kRm9ybTtcblx0XHRGb3JtLmNiQmVmb3JlU2VuZEZvcm0gPSBNb2R1bGVHZXRTc2wuY2JCZWZvcmVTZW5kRm9ybTtcblx0XHRGb3JtLmluaXRpYWxpemUoKTtcblx0fSxcbn07XG5cbi8vIEluaXRpYWxpemUgdGhlIE1vZHVsZUdldFNzbCBjbGFzcyB3aGVuIHRoZSBkb2N1bWVudCBpcyByZWFkeVxuJChkb2N1bWVudCkucmVhZHkoKCkgPT4ge1xuXHRNb2R1bGVHZXRTc2wuaW5pdGlhbGl6ZSgpO1xufSk7XG4iXX0= \ No newline at end of file + +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kdWxlLWdldC1zc2wtaW5kZXguanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsic3JjL21vZHVsZS1nZXQtc3NsLWluZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGdsb2JhbCBnbG9iYWxSb290VXJsLCBnbG9iYWxUcmFuc2xhdGUsIEZvcm0sIENvbmZpZywgUGJ4QXBpLCBkbnNQcm92aWRlcnNNZXRhICovXG5cbi8vIENvbnN0YW50cyByZWxhdGVkIHRvIHRoZSBmb3JtIGFuZCBtb2R1bGVcbmNvbnN0IGlkVXJsICAgICA9ICdtb2R1bGUtZ2V0LXNzbCc7ICAgICAgICAgICAgICAvLyBBUEkgZW5kcG9pbnQgZm9yIFNTTCBtb2R1bGVcbmNvbnN0IGlkRm9ybSAgICA9ICdtb2R1bGUtZ2V0LXNzbC1mb3JtJzsgICAgICAgICAvLyBGb3JtIGVsZW1lbnQgSUQgZm9yIFNTTCBtb2R1bGVcbmNvbnN0IGNsYXNzTmFtZSA9ICdNb2R1bGVHZXRTc2wnOyAgICAgICAgICAgICAgICAvLyBDbGFzcyBuYW1lIGZvciB0aGlzIG1vZHVsZVxuXG4vLyBNYWluIE1vZHVsZUdldFNzbCBjbGFzcyBkZWZpbml0aW9uXG5jb25zdCBNb2R1bGVHZXRTc2wgPSB7XG5cdC8vIENhY2hlIGNvbW1vbmx5IHVzZWQgalF1ZXJ5IG9iamVjdHNcblx0JGZvcm1PYmo6ICQoJyMnICsgaWRGb3JtKSxcblx0JGNoZWNrQm94ZXM6ICQoJyMnICsgaWRGb3JtICsgJyAudWkuY2hlY2tib3gnKSxcblx0JGRpc2FiaWxpdHlGaWVsZHM6ICQoJyMnICsgaWRGb3JtICsgJyAuZGlzYWJpbGl0eScpLFxuXHQkc3RhdHVzVG9nZ2xlOiAkKCcjbW9kdWxlLXN0YXR1cy10b2dnbGUnKSxcblx0JHN1Ym1pdEJ1dHRvbjogJCgnI3N1Ym1pdGJ1dHRvbicpLFxuXHQkbW9kdWxlU3RhdHVzOiAkKCcjc3RhdHVzJyksXG5cdCRkb21haW5OYW1lOiAkKCcjZG9tYWluTmFtZScpLFxuXHQkY2hhbGxlbmdlVHlwZTogJCgnI2NoYWxsZW5nZVR5cGUnKSxcblx0JGRuc1Byb3ZpZGVyOiAkKCcjZG5zUHJvdmlkZXInKSxcblx0JGh0dHBDaGFsbGVuZ2VJbmZvOiAkKCcjaHR0cC1jaGFsbGVuZ2UtaW5mbycpLFxuXHQkZG5zU2V0dGluZ3NCbG9jazogJCgnI2Rucy1zZXR0aW5ncy1ibG9jaycpLFxuXHQkZG5zQ3JlZGVudGlhbHNGaWVsZHM6ICQoJyNkbnMtY3JlZGVudGlhbHMtZmllbGRzJyksXG5cdCRkbnNDcmVkZW50aWFsc0lucHV0OiAkKCdpbnB1dFtuYW1lPVwiZG5zQ3JlZGVudGlhbHNcIl0nKSxcblx0JGlwQWRkcmVzc1dhcm5pbmc6ICQoJyNpcC1hZGRyZXNzLWNlcnRpZmljYXRlLXdhcm5pbmcnKSxcblxuXHQvLyBWYWxpZGF0aW9uIHJ1bGVzIGZvciB0aGUgZm9ybVxuXHR2YWxpZGF0ZVJ1bGVzOiB7XG5cdFx0ZG9tYWluTmFtZToge1xuXHRcdFx0aWRlbnRpZmllcjogJ2RvbWFpbk5hbWUnLFxuXHRcdFx0cnVsZXM6IFtcblx0XHRcdFx0e1xuXHRcdFx0XHRcdHR5cGU6ICdlbXB0eScsXG5cdFx0XHRcdFx0cHJvbXB0OiBnbG9iYWxUcmFuc2xhdGUubW9kdWxlX2dldHNzbF9Eb21haW5OYW1lRW1wdHksXG5cdFx0XHRcdH0sXG5cdFx0XHRdLFxuXHRcdH0sXG5cdH0sXG5cblx0LyoqXG5cdCAqIEluaXRpYWxpemUgdGhlIG1vZHVsZSwgYmluZCBldmVudCBsaXN0ZW5lcnMsIGFuZCBzZXR1cCB0aGUgZm9ybS5cblx0ICovXG5cdGluaXRpYWxpemUoKSB7XG5cdFx0Ly8gSW5pdGlhbGl6ZSBTZW1hbnRpYyBVSSBjaGVja2JveGVzXG5cdFx0dGhpcy4kY2hlY2tCb3hlcy5jaGVja2JveCgpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBkcm9wZG93bnNcblx0XHR0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKHtcblx0XHRcdG9uQ2hhbmdlOiBNb2R1bGVHZXRTc2wub25DaGFuZ2VDaGFsbGVuZ2VUeXBlLFxuXHRcdH0pO1xuXHRcdHRoaXMuJGRuc1Byb3ZpZGVyLmRyb3Bkb3duKHtcblx0XHRcdGZ1bGxUZXh0U2VhcmNoOiB0cnVlLFxuXHRcdFx0b25DaGFuZ2U6IE1vZHVsZUdldFNzbC5vbkNoYW5nZURuc1Byb3ZpZGVyLFxuXHRcdH0pO1xuXG5cdFx0Ly8gQ2hlY2sgYW5kIHNldCBtb2R1bGUgc3RhdHVzIG9uIGxvYWQgYW5kIHdoZW4gdGhlIHN0YXR1cyBjaGFuZ2VzXG5cdFx0dGhpcy5jaGVja1N0YXR1c1RvZ2dsZSgpO1xuXHRcdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdNb2R1bGVTdGF0dXNDaGFuZ2VkJywgdGhpcy5jaGVja1N0YXR1c1RvZ2dsZSk7XG5cblx0XHQvLyBJbml0aWFsaXplIGZvcm0gd2l0aCB2YWxpZGF0aW9uIGFuZCBzdWJtaXQgaGFuZGxlcnNcblx0XHR0aGlzLmluaXRpYWxpemVGb3JtKCk7XG5cdFx0dGhpcy5iaW5kSXBBZGRyZXNzV2FybmluZygpO1xuXG5cdFx0bW9kdWxlR2V0U1NMU3RhdHVzTG9vcFdvcmtlci4kcmVzdWx0QmxvY2suaGlkZSgpO1xuXG5cdFx0Ly8gUmVzdG9yZSBzYXZlZCBzdGF0ZVxuXHRcdGNvbnN0IGN1cnJlbnRDaGFsbGVuZ2UgPSB0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdnZXQgdmFsdWUnKSB8fCAnaHR0cCc7XG5cdFx0dGhpcy5vbkNoYW5nZUNoYWxsZW5nZVR5cGUoY3VycmVudENoYWxsZW5nZSk7XG5cdFx0dGhpcy5yZXN0b3JlU2F2ZWRDcmVkZW50aWFscygpO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDaGVjayB3aGV0aGVyIGEgdmFsdWUgaXMgYSB2YWxpZCBJUHY0IG9yIElQdjYgYWRkcmVzcy5cblx0ICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlXG5cdCAqIEByZXR1cm5zIHtib29sZWFufVxuXHQgKi9cblx0aXNJcEFkZHJlc3ModmFsdWUpIHtcblx0XHRjb25zdCBhZGRyZXNzID0gU3RyaW5nKHZhbHVlIHx8ICcnKS50cmltKCk7XG5cdFx0Y29uc3QgaXB2NFBhcnRzID0gYWRkcmVzcy5zcGxpdCgnLicpO1xuXHRcdGlmIChpcHY0UGFydHMubGVuZ3RoID09PSA0KSB7XG5cdFx0XHRyZXR1cm4gaXB2NFBhcnRzLmV2ZXJ5KHBhcnQgPT4gL15cXGR7MSwzfSQvLnRlc3QocGFydClcblx0XHRcdFx0JiYgTnVtYmVyKHBhcnQpIDw9IDI1NVxuXHRcdFx0XHQmJiAocGFydCA9PT0gJzAnIHx8IHBhcnRbMF0gIT09ICcwJykpO1xuXHRcdH1cblxuXHRcdGNvbnN0IGhhc09wZW5pbmdCcmFja2V0ID0gYWRkcmVzcy5zdGFydHNXaXRoKCdbJyk7XG5cdFx0Y29uc3QgaGFzQ2xvc2luZ0JyYWNrZXQgPSBhZGRyZXNzLmVuZHNXaXRoKCddJyk7XG5cdFx0aWYgKGhhc09wZW5pbmdCcmFja2V0ICE9PSBoYXNDbG9zaW5nQnJhY2tldCkgcmV0dXJuIGZhbHNlO1xuXHRcdGNvbnN0IGlwdjYgPSBoYXNPcGVuaW5nQnJhY2tldCA/IGFkZHJlc3Muc2xpY2UoMSwgLTEpIDogYWRkcmVzcztcblx0XHRpZiAoIWlwdjYuaW5jbHVkZXMoJzonKSB8fCAvXFxzLy50ZXN0KGlwdjYpKSByZXR1cm4gZmFsc2U7XG5cdFx0dHJ5IHtcblx0XHRcdG5ldyBVUkwoYGh0dHA6Ly9bJHtpcHY2fV0vYCk7XG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKiBVcGRhdGUgd2FybmluZyB2aXNpYmlsaXR5IHdoZW4gdGhlIGNvbmZpZ3VyZWQgYWRkcmVzcyBjaGFuZ2VzLiAqL1xuXHR1cGRhdGVJcEFkZHJlc3NXYXJuaW5nKCkge1xuXHRcdGlmICh0aGlzLmlzSXBBZGRyZXNzKHRoaXMuJGRvbWFpbk5hbWUudmFsKCkpKSB7XG5cdFx0XHR0aGlzLiRpcEFkZHJlc3NXYXJuaW5nLnNob3coKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy4kaXBBZGRyZXNzV2FybmluZy5oaWRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKiBCaW5kIHJlYWN0aXZlIElQIHdhcm5pbmcgYmVoYXZpb3IgYW5kIGluaXRpYWxpemUgaXRzIHN0YXRlLiAqL1xuXHRiaW5kSXBBZGRyZXNzV2FybmluZygpIHtcblx0XHR0aGlzLiRkb21haW5OYW1lLm9uKCdpbnB1dCcsICgpID0+IHRoaXMudXBkYXRlSXBBZGRyZXNzV2FybmluZygpKTtcblx0XHR0aGlzLnVwZGF0ZUlwQWRkcmVzc1dhcm5pbmcoKTtcblx0fSxcblxuXHQvKipcblx0ICogSGFuZGxlIGNoYWxsZW5nZSB0eXBlIGNoYW5nZTogc2hvdy9oaWRlIHJlbGV2YW50IHNlY3Rpb25zLlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWUgLSAnaHR0cCcgb3IgJ2Rucydcblx0ICovXG5cdG9uQ2hhbmdlQ2hhbGxlbmdlVHlwZSh2YWx1ZSkge1xuXHRcdGlmICh2YWx1ZSA9PT0gJ2RucycpIHtcblx0XHRcdE1vZHVsZUdldFNzbC4kaHR0cENoYWxsZW5nZUluZm8uaGlkZSgpO1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRkbnNTZXR0aW5nc0Jsb2NrLnNob3coKTtcblx0XHRcdC8vIFRyaWdnZXIgcHJvdmlkZXIgY2hhbmdlIHRvIHJlbmRlciBjcmVkZW50aWFsIGZpZWxkc1xuXHRcdFx0Y29uc3QgY3VycmVudFByb3ZpZGVyID0gTW9kdWxlR2V0U3NsLiRkbnNQcm92aWRlci5kcm9wZG93bignZ2V0IHZhbHVlJyk7XG5cdFx0XHRpZiAoY3VycmVudFByb3ZpZGVyKSB7XG5cdFx0XHRcdE1vZHVsZUdldFNzbC5vbkNoYW5nZURuc1Byb3ZpZGVyKGN1cnJlbnRQcm92aWRlcik7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdE1vZHVsZUdldFNzbC4kaHR0cENoYWxsZW5nZUluZm8uc2hvdygpO1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRkbnNTZXR0aW5nc0Jsb2NrLmhpZGUoKTtcblx0XHR9XG5cdH0sXG5cblx0LyoqXG5cdCAqIEhhbmRsZSBETlMgcHJvdmlkZXIgY2hhbmdlOiBkeW5hbWljYWxseSByZW5kZXIgY3JlZGVudGlhbCBmaWVsZHMuXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZSAtIHByb3ZpZGVyIElEIChlLmcuICdkbnNfY2YnKVxuXHQgKi9cblx0b25DaGFuZ2VEbnNQcm92aWRlcih2YWx1ZSkge1xuXHRcdGNvbnN0ICRjb250YWluZXIgPSBNb2R1bGVHZXRTc2wuJGRuc0NyZWRlbnRpYWxzRmllbGRzO1xuXHRcdCRjb250YWluZXIuZW1wdHkoKTtcblxuXHRcdGlmICghdmFsdWUgfHwgdHlwZW9mIGRuc1Byb3ZpZGVyc01ldGEgPT09ICd1bmRlZmluZWQnKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gRmluZCBwcm92aWRlciBtZXRhZGF0YVxuXHRcdGNvbnN0IHByb3ZpZGVyID0gZG5zUHJvdmlkZXJzTWV0YS5maW5kKHAgPT4gcC5pZCA9PT0gdmFsdWUpO1xuXHRcdGlmICghcHJvdmlkZXIgfHwgIXByb3ZpZGVyLmZpZWxkcykge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIERlY29kZSBleGlzdGluZyBzYXZlZCBjcmVkZW50aWFscyBmb3IgcHJlLWZpbGxpbmdcblx0XHRsZXQgc2F2ZWRDcmVkcyA9IHt9O1xuXHRcdGNvbnN0IGVuY29kZWRWYWwgPSBNb2R1bGVHZXRTc2wuJGRuc0NyZWRlbnRpYWxzSW5wdXQudmFsKCk7XG5cdFx0aWYgKGVuY29kZWRWYWwpIHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdGNvbnN0IGRlY29kZWQgPSBhdG9iKGVuY29kZWRWYWwpO1xuXHRcdFx0XHRzYXZlZENyZWRzID0gSlNPTi5wYXJzZShkZWNvZGVkKTtcblx0XHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdFx0Ly8gaWdub3JlIGRlY29kZSBlcnJvcnNcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBSZW5kZXIgZmllbGRzXG5cdFx0cHJvdmlkZXIuZmllbGRzLmZvckVhY2goZmllbGQgPT4ge1xuXHRcdFx0Y29uc3QgaGFzU2F2ZWQgPSBCb29sZWFuKHNhdmVkQ3JlZHNbZmllbGQudmFyXSk7XG5cdFx0XHRjb25zdCBkaXNwbGF5VmFsdWUgPSBoYXNTYXZlZCA/ICfigKLigKLigKLigKLigKLigKLigKLigKInIDogJyc7XG5cdFx0XHRjb25zdCBtYXNrZWRBdHRyID0gaGFzU2F2ZWQgPyAnZGF0YS1tYXNrZWQ9XCJ0cnVlXCInIDogJyc7XG5cdFx0XHRjb25zdCBodG1sID0gYFxuXHRcdFx0XHQ8ZGl2IGNsYXNzPVwiZmllbGRcIj5cblx0XHRcdFx0XHQ8bGFiZWw+JHtmaWVsZC5sYWJlbH08L2xhYmVsPlxuXHRcdFx0XHRcdDxpbnB1dCB0eXBlPVwicGFzc3dvcmRcIlxuXHRcdFx0XHRcdFx0ICAgY2xhc3M9XCJkbnMtY3JlZC1pbnB1dFwiXG5cdFx0XHRcdFx0XHQgICBkYXRhLXZhcj1cIiR7ZmllbGQudmFyfVwiXG5cdFx0XHRcdFx0XHQgICAke21hc2tlZEF0dHJ9XG5cdFx0XHRcdFx0XHQgICB2YWx1ZT1cIiR7TW9kdWxlR2V0U3NsLmVzY2FwZUh0bWwoZGlzcGxheVZhbHVlKX1cIlxuXHRcdFx0XHRcdFx0ICAgcGxhY2Vob2xkZXI9XCIke2ZpZWxkLmxhYmVsfVwiPlxuXHRcdFx0XHQ8L2Rpdj5gO1xuXHRcdFx0JGNvbnRhaW5lci5hcHBlbmQoaHRtbCk7XG5cdFx0fSk7XG5cdFx0Ly8gT24gZm9jdXM6IGNsZWFyIG1hc2sgc28gdXNlciBjYW4gZW50ZXIgbmV3IHZhbHVlXG5cdFx0JGNvbnRhaW5lci5vbignZm9jdXMnLCAnLmRucy1jcmVkLWlucHV0W2RhdGEtbWFza2VkXScsIGZ1bmN0aW9uICgpIHtcblx0XHRcdCQodGhpcykudmFsKCcnKS5yZW1vdmVBdHRyKCdkYXRhLW1hc2tlZCcpO1xuXHRcdH0pO1xuXHRcdC8vIENsZWFyIGVycm9yIGhpZ2hsaWdodCB3aGVuIHVzZXIgc3RhcnRzIHR5cGluZ1xuXHRcdCRjb250YWluZXIub24oJ2lucHV0JywgJy5kbnMtY3JlZC1pbnB1dCcsIGZ1bmN0aW9uICgpIHtcblx0XHRcdCQodGhpcykuY2xvc2VzdCgnLmZpZWxkJykucmVtb3ZlQ2xhc3MoJ2Vycm9yJyk7XG5cdFx0fSk7XG5cdH0sXG5cblx0LyoqXG5cdCAqIENvbGxlY3QgRE5TIGNyZWRlbnRpYWwgZmllbGQgdmFsdWVzIGludG8gYmFzZTY0IEpTT04gYW5kIHdyaXRlIHRvIGhpZGRlbiBpbnB1dC5cblx0ICogTWFza2VkIGZpZWxkcyAodW5jaGFuZ2VkKSBhcmUgcmVzdG9yZWQgZnJvbSB0aGUgcHJldmlvdXNseSBzYXZlZCBjcmVkZW50aWFscy5cblx0ICovXG5cdGNvbGxlY3REbnNDcmVkZW50aWFscygpIHtcblx0XHRjb25zdCBjaGFsbGVuZ2VUeXBlID0gTW9kdWxlR2V0U3NsLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdnZXQgdmFsdWUnKTtcblx0XHRpZiAoY2hhbGxlbmdlVHlwZSAhPT0gJ2RucycpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0Ly8gRGVjb2RlIGN1cnJlbnRseSBzdG9yZWQgY3JlZGVudGlhbHMgKHNvdXJjZSBvZiB0cnV0aCBmb3IgbWFza2VkIGZpZWxkcylcblx0XHRsZXQgc2F2ZWRDcmVkcyA9IHt9O1xuXHRcdGNvbnN0IGVuY29kZWRWYWwgPSBNb2R1bGVHZXRTc2wuJGRuc0NyZWRlbnRpYWxzSW5wdXQudmFsKCk7XG5cdFx0aWYgKGVuY29kZWRWYWwpIHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdHNhdmVkQ3JlZHMgPSBKU09OLnBhcnNlKGF0b2IoZW5jb2RlZFZhbCkpO1xuXHRcdFx0fSBjYXRjaCAoZSkge1xuXHRcdFx0XHQvLyBpZ25vcmVcblx0XHRcdH1cblx0XHR9XG5cdFx0Y29uc3QgY3JlZHMgPSB7fTtcblx0XHQkKCcuZG5zLWNyZWQtaW5wdXQnKS5lYWNoKGZ1bmN0aW9uICgpIHtcblx0XHRcdGNvbnN0IHZhck5hbWUgPSAkKHRoaXMpLmRhdGEoJ3ZhcicpO1xuXHRcdFx0aWYgKCF2YXJOYW1lKSByZXR1cm47XG5cdFx0XHRpZiAoJCh0aGlzKS5pcygnW2RhdGEtbWFza2VkXScpKSB7XG5cdFx0XHRcdC8vIFVzZXIgZGlkbid0IGNoYW5nZSB0aGlzIGZpZWxkIOKAlCBrZWVwIHN0b3JlZCB2YWx1ZVxuXHRcdFx0XHRpZiAoc2F2ZWRDcmVkc1t2YXJOYW1lXSkge1xuXHRcdFx0XHRcdGNyZWRzW3Zhck5hbWVdID0gc2F2ZWRDcmVkc1t2YXJOYW1lXTtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Y29uc3QgdmFsID0gJCh0aGlzKS52YWwoKTtcblx0XHRcdFx0aWYgKHZhbCkge1xuXHRcdFx0XHRcdGNyZWRzW3Zhck5hbWVdID0gdmFsO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSk7XG5cdFx0TW9kdWxlR2V0U3NsLiRkbnNDcmVkZW50aWFsc0lucHV0LnZhbChidG9hKEpTT04uc3RyaW5naWZ5KGNyZWRzKSkpO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBSZXN0b3JlIHNhdmVkIGNyZWRlbnRpYWxzIGludG8gdGhlIEROUyBwcm92aWRlciBmaWVsZHMgb24gcGFnZSBsb2FkLlxuXHQgKi9cblx0cmVzdG9yZVNhdmVkQ3JlZGVudGlhbHMoKSB7XG5cdFx0Y29uc3QgY3VycmVudFByb3ZpZGVyID0gTW9kdWxlR2V0U3NsLiRkbnNQcm92aWRlci5kcm9wZG93bignZ2V0IHZhbHVlJyk7XG5cdFx0aWYgKGN1cnJlbnRQcm92aWRlcikge1xuXHRcdFx0TW9kdWxlR2V0U3NsLm9uQ2hhbmdlRG5zUHJvdmlkZXIoY3VycmVudFByb3ZpZGVyKTtcblx0XHR9XG5cdH0sXG5cblx0LyoqXG5cdCAqIEVzY2FwZSBIVE1MIHNwZWNpYWwgY2hhcmFjdGVycyBmb3Igc2FmZSBpbnNlcnRpb24gaW50byBhdHRyaWJ1dGVzLlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdGV4dFxuXHQgKiBAcmV0dXJucyB7c3RyaW5nfVxuXHQgKi9cblx0ZXNjYXBlSHRtbCh0ZXh0KSB7XG5cdFx0Y29uc3QgbWFwID0geyAnJic6ICcmYW1wOycsICc8JzogJyZsdDsnLCAnPic6ICcmZ3Q7JywgJ1wiJzogJyZxdW90OycsIFwiJ1wiOiAnJiMwMzk7JyB9O1xuXHRcdHJldHVybiBTdHJpbmcodGV4dCkucmVwbGFjZSgvWyY8PlwiJ10vZywgbSA9PiBtYXBbbV0pO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBSZXF1ZXN0IGFuIFNTTCBjZXJ0aWZpY2F0ZSBieSBjYWxsaW5nIHRoZSBzZXJ2ZXItc2lkZSBBUEkuXG5cdCAqL1xuXHRnZXRTc2woKSB7XG5cdFx0JC5hcGkoe1xuXHRcdFx0dXJsOiBgJHtDb25maWcucGJ4VXJsfS9wYnhjb3JlL2FwaS9tb2R1bGVzLyR7Y2xhc3NOYW1lfS9nZXQtY2VydGAsXG5cdFx0XHRvbjogJ25vdycsXG5cdFx0XHRtZXRob2Q6ICdQT1NUJyxcblx0XHRcdGJlZm9yZVhIUih4aHIpIHtcblx0XHRcdFx0eGhyLnNldFJlcXVlc3RIZWFkZXIgKCdYLUFzeW5jLVJlc3BvbnNlLUNoYW5uZWwtSWQnLCBtb2R1bGVHZXRTU0xTdGF0dXNMb29wV29ya2VyLmNoYW5uZWxJZCk7XG5cdFx0XHRcdHhoci5zZXRSZXF1ZXN0SGVhZGVyICgnWC1Qcm9jZXNzb3ItVGltZW91dCcsICcxMjAnKTtcblx0XHRcdFx0cmV0dXJuIHhocjtcblx0XHRcdH0sXG5cdFx0XHRiZWZvcmVTZW5kKHNldHRpbmdzKSB7XG5cdFx0XHRcdE1vZHVsZUdldFNzbC4kc3VibWl0QnV0dG9uLmFkZENsYXNzKCdsb2FkaW5nIGRpc2FibGVkJyk7XG5cdFx0XHRcdG1vZHVsZUdldFNTTFN0YXR1c0xvb3BXb3JrZXIuJHJlc3VsdEJsb2NrLnNob3coKTtcblx0XHRcdFx0bW9kdWxlR2V0U1NMU3RhdHVzTG9vcFdvcmtlci5lZGl0b3IuZ2V0U2Vzc2lvbigpLnNldFZhbHVlKFxuXHRcdFx0XHRnbG9iYWxUcmFuc2xhdGUubW9kdWxlX2dldHNzbF9HZXRTU0xQcm9jZXNzaW5nICsgJ1xcbidcblx0XHRcdCk7XG5cdFx0XHRcdHJldHVybiBzZXR0aW5ncztcblx0XHRcdH0sXG5cdFx0XHRzdWNjZXNzVGVzdDogUGJ4QXBpLnN1Y2Nlc3NUZXN0LFxuXHRcdFx0b25TdWNjZXNzOiBmdW5jdGlvbiAocmVzcG9uc2UpIHtcblx0XHRcdFx0TW9kdWxlR2V0U3NsLiRzdWJtaXRCdXR0b24ucmVtb3ZlQ2xhc3MoJ2xvYWRpbmcgZGlzYWJsZWQnKTtcblx0XHRcdH0sXG5cdFx0XHRvbkZhaWx1cmU6IGZ1bmN0aW9uKHJlc3BvbnNlKSB7XG5cdFx0XHRcdE1vZHVsZUdldFNzbC4kc3VibWl0QnV0dG9uLnJlbW92ZUNsYXNzKCdsb2FkaW5nIGRpc2FibGVkJyk7XG5cdFx0XHRcdFVzZXJNZXNzYWdlLnNob3dNdWx0aVN0cmluZyhyZXNwb25zZS5tZXNzYWdlKTtcblx0XHRcdH0sXG5cdFx0fSlcblx0fSxcblxuXHQvKipcblx0ICogVG9nZ2xlcyB0aGUgZm9ybSBmaWVsZHMgYW5kIHN0YXR1cyB2aXNpYmlsaXR5IGJhc2VkIG9uIHRoZSBtb2R1bGUncyBzdGF0dXMuXG5cdCAqL1xuXHRjaGVja1N0YXR1c1RvZ2dsZSgpIHtcblx0XHRpZiAoTW9kdWxlR2V0U3NsLiRzdGF0dXNUb2dnbGUuY2hlY2tib3goJ2lzIGNoZWNrZWQnKSkge1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRkaXNhYmlsaXR5RmllbGRzLnJlbW92ZUNsYXNzKCdkaXNhYmxlZCcpO1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRtb2R1bGVTdGF0dXMuc2hvdygpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGRpc2FiaWxpdHlGaWVsZHMuYWRkQ2xhc3MoJ2Rpc2FibGVkJyk7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJG1vZHVsZVN0YXR1cy5oaWRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKlxuXHQgKiBWYWxpZGF0ZSBETlMgcHJvdmlkZXIgYW5kIGNyZWRlbnRpYWxzIHdoZW4gRE5TLTAxIGlzIHNlbGVjdGVkLlxuXHQgKiBSZXR1cm5zIHRydWUgaWYgdmFsaWQsIGZhbHNlIG90aGVyd2lzZS5cblx0ICovXG5cdHZhbGlkYXRlRG5zRmllbGRzKCkge1xuXHRcdGlmIChNb2R1bGVHZXRTc2wuJGNoYWxsZW5nZVR5cGUuZHJvcGRvd24oJ2dldCB2YWx1ZScpICE9PSAnZG5zJykge1xuXHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0fVxuXHRcdGlmICghTW9kdWxlR2V0U3NsLiRkbnNQcm92aWRlci5kcm9wZG93bignZ2V0IHZhbHVlJykpIHtcblx0XHRcdFVzZXJNZXNzYWdlLnNob3dFcnJvcihnbG9iYWxUcmFuc2xhdGUubW9kdWxlX2dldHNzbF9EbnNQcm92aWRlckVtcHR5KTtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdFx0bGV0IGhhc0VtcHR5ID0gZmFsc2U7XG5cdFx0JCgnLmRucy1jcmVkLWlucHV0JykuZWFjaChmdW5jdGlvbiAoKSB7XG5cdFx0XHRjb25zdCAkZmllbGQgPSAkKHRoaXMpLmNsb3Nlc3QoJy5maWVsZCcpO1xuXHRcdFx0Y29uc3QgaXNNYXNrZWQgPSAkKHRoaXMpLmlzKCdbZGF0YS1tYXNrZWRdJyk7XG5cdFx0XHRpZiAoIWlzTWFza2VkICYmICEkKHRoaXMpLnZhbCgpLnRyaW0oKSkge1xuXHRcdFx0XHQkZmllbGQuYWRkQ2xhc3MoJ2Vycm9yJyk7XG5cdFx0XHRcdGhhc0VtcHR5ID0gdHJ1ZTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdCRmaWVsZC5yZW1vdmVDbGFzcygnZXJyb3InKTtcblx0XHRcdH1cblx0XHR9KTtcblx0XHRpZiAoaGFzRW1wdHkpIHtcblx0XHRcdFVzZXJNZXNzYWdlLnNob3dFcnJvcihnbG9iYWxUcmFuc2xhdGUubW9kdWxlX2dldHNzbF9EbnNDcmVkZW50aWFsc0VtcHR5KTtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdFx0cmV0dXJuIHRydWU7XG5cdH0sXG5cblx0LyoqXG5cdCAqIENhbGxiYWNrIGJlZm9yZSBzZW5kaW5nIHRoZSBmb3JtLlxuXHQgKiBAcGFyYW0ge09iamVjdH0gc2V0dGluZ3MgLSBBamF4IHJlcXVlc3Qgc2V0dGluZ3MuXG5cdCAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBtb2RpZmllZCBBamF4IHJlcXVlc3Qgc2V0dGluZ3MuXG5cdCAqL1xuXHRjYkJlZm9yZVNlbmRGb3JtKHNldHRpbmdzKSB7XG5cdFx0aWYgKCFNb2R1bGVHZXRTc2wudmFsaWRhdGVEbnNGaWVsZHMoKSkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0XHRjb25zdCByZXN1bHQgPSBzZXR0aW5ncztcblx0XHQvLyBDb2xsZWN0IEROUyBjcmVkZW50aWFscyBpbnRvIGhpZGRlbiBmaWVsZCBiZWZvcmUgZm9ybSBzdWJtaXNzaW9uXG5cdFx0TW9kdWxlR2V0U3NsLmNvbGxlY3REbnNDcmVkZW50aWFscygpO1xuXHRcdHJlc3VsdC5kYXRhID0gTW9kdWxlR2V0U3NsLiRmb3JtT2JqLmZvcm0oJ2dldCB2YWx1ZXMnKTtcblx0XHRyZXR1cm4gcmVzdWx0O1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDYWxsYmFjayBmdW5jdGlvbiBhZnRlciBzZW5kaW5nIHRoZSBmb3JtLlxuXHQgKi9cblx0Y2JBZnRlclNlbmRGb3JtKHJlc3BvbnNlKSB7XG5cdFx0aWYgKEZvcm0uY2hlY2tTdWNjZXNzKHJlc3BvbnNlKSl7XG5cdFx0XHRNb2R1bGVHZXRTc2wuZ2V0U3NsKCk7XG5cdFx0fVxuXHR9LFxuXG5cblx0LyoqXG5cdCAqIEluaXRpYWxpemVzIHRoZSBmb3JtIHZhbGlkYXRpb24gYW5kIHN1Ym1pc3Npb24gbG9naWMuXG5cdCAqL1xuXHRpbml0aWFsaXplRm9ybSgpIHtcblx0XHRGb3JtLiRmb3JtT2JqID0gTW9kdWxlR2V0U3NsLiRmb3JtT2JqO1xuXHRcdEZvcm0udXJsID0gYCR7Z2xvYmFsUm9vdFVybH0ke2lkVXJsfS8ke2lkVXJsfS9zYXZlYDtcblx0XHRGb3JtLnZhbGlkYXRlUnVsZXMgPSBNb2R1bGVHZXRTc2wudmFsaWRhdGVSdWxlcztcblx0XHRGb3JtLmVuYWJsZURpcnJpdHkgPSBmYWxzZTtcblx0XHRGb3JtLmNiQWZ0ZXJTZW5kRm9ybSA9IE1vZHVsZUdldFNzbC5jYkFmdGVyU2VuZEZvcm07XG5cdFx0Rm9ybS5jYkJlZm9yZVNlbmRGb3JtID0gTW9kdWxlR2V0U3NsLmNiQmVmb3JlU2VuZEZvcm07XG5cdFx0Rm9ybS5pbml0aWFsaXplKCk7XG5cdH0sXG59O1xuXG4vLyBJbml0aWFsaXplIHRoZSBNb2R1bGVHZXRTc2wgY2xhc3Mgd2hlbiB0aGUgZG9jdW1lbnQgaXMgcmVhZHlcbiQoZG9jdW1lbnQpLnJlYWR5KCgpID0+IHtcblx0TW9kdWxlR2V0U3NsLmluaXRpYWxpemUoKTtcbn0pO1xuIl0sIm1hcHBpbmdzIjoiQUFBQTs7QUFFQTtBQUNBLE1BQU0sS0FBSyxHQUFPLGdCQUFnQixDQUFDLENBQWM7QUFDakQsTUFBTSxNQUFNLEdBQU0scUJBQXFCLENBQUMsQ0FBUztBQUNqRCxNQUFNLFNBQVMsR0FBRyxjQUFjLENBQUMsQ0FBZ0I7O0FBRWpEO0FBQ0EsTUFBTSxZQUFZLEdBQUc7RUFDcEI7RUFDQSxRQUFRLEVBQUUsQ0FBQyxDQUFDLEdBQUcsR0FBRyxNQUFNLENBQUM7RUFDekIsV0FBVyxFQUFFLENBQUMsQ0FBQyxHQUFHLEdBQUcsTUFBTSxHQUFHLGVBQWUsQ0FBQztFQUM5QyxpQkFBaUIsRUFBRSxDQUFDLENBQUMsR0FBRyxHQUFHLE1BQU0sR0FBRyxjQUFjLENBQUM7RUFDbkQsYUFBYSxFQUFFLENBQUMsQ0FBQyx1QkFBdUIsQ0FBQztFQUN6QyxhQUFhLEVBQUUsQ0FBQyxDQUFDLGVBQWUsQ0FBQztFQUNqQyxhQUFhLEVBQUUsQ0FBQyxDQUFDLFNBQVMsQ0FBQztFQUMzQixXQUFXLEVBQUUsQ0FBQyxDQUFDLGFBQWEsQ0FBQztFQUM3QixjQUFjLEVBQUUsQ0FBQyxDQUFDLGdCQUFnQixDQUFDO0VBQ25DLFlBQVksRUFBRSxDQUFDLENBQUMsY0FBYyxDQUFDO0VBQy9CLGtCQUFrQixFQUFFLENBQUMsQ0FBQyxzQkFBc0IsQ0FBQztFQUM3QyxpQkFBaUIsRUFBRSxDQUFDLENBQUMscUJBQXFCLENBQUM7RUFD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 \ No newline at end of file diff --git a/public/assets/js/src/module-get-ssl-index.js b/public/assets/js/src/module-get-ssl-index.js index e465bb2..0bf5077 100644 --- a/public/assets/js/src/module-get-ssl-index.js +++ b/public/assets/js/src/module-get-ssl-index.js @@ -14,12 +14,14 @@ const ModuleGetSsl = { $statusToggle: $('#module-status-toggle'), $submitButton: $('#submitbutton'), $moduleStatus: $('#status'), + $domainName: $('#domainName'), $challengeType: $('#challengeType'), $dnsProvider: $('#dnsProvider'), $httpChallengeInfo: $('#http-challenge-info'), $dnsSettingsBlock: $('#dns-settings-block'), $dnsCredentialsFields: $('#dns-credentials-fields'), $dnsCredentialsInput: $('input[name="dnsCredentials"]'), + $ipAddressWarning: $('#ip-address-certificate-warning'), // Validation rules for the form validateRules: { @@ -56,6 +58,7 @@ const ModuleGetSsl = { // Initialize form with validation and submit handlers this.initializeForm(); + this.bindIpAddressWarning(); moduleGetSSLStatusLoopWorker.$resultBlock.hide(); @@ -65,6 +68,48 @@ const ModuleGetSsl = { this.restoreSavedCredentials(); }, + /** + * Check whether a value is a valid IPv4 or IPv6 address. + * @param {string} value + * @returns {boolean} + */ + isIpAddress(value) { + const address = String(value || '').trim(); + const ipv4Parts = address.split('.'); + if (ipv4Parts.length === 4) { + return ipv4Parts.every(part => /^\d{1,3}$/.test(part) + && Number(part) <= 255 + && (part === '0' || part[0] !== '0')); + } + + const hasOpeningBracket = address.startsWith('['); + const hasClosingBracket = address.endsWith(']'); + if (hasOpeningBracket !== hasClosingBracket) return false; + const ipv6 = hasOpeningBracket ? address.slice(1, -1) : address; + if (!ipv6.includes(':') || /\s/.test(ipv6)) return false; + try { + new URL(`http://[${ipv6}]/`); + return true; + } catch (e) { + return false; + } + }, + + /** Update warning visibility when the configured address changes. */ + updateIpAddressWarning() { + if (this.isIpAddress(this.$domainName.val())) { + this.$ipAddressWarning.show(); + } else { + this.$ipAddressWarning.hide(); + } + }, + + /** Bind reactive IP warning behavior and initialize its state. */ + bindIpAddressWarning() { + this.$domainName.on('input', () => this.updateIpAddressWarning()); + this.updateIpAddressWarning(); + }, + /** * Handle challenge type change: show/hide relevant sections. * @param {string} value - 'http' or 'dns' diff --git a/tests/ip-address-warning.test.mjs b/tests/ip-address-warning.test.mjs new file mode 100644 index 0000000..2576e4c --- /dev/null +++ b/tests/ip-address-warning.test.mjs @@ -0,0 +1,84 @@ +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import test from 'node:test'; +import vm from 'node:vm'; + +const sourcePath = new URL('../public/assets/js/src/module-get-ssl-index.js', import.meta.url); + +function loadController() { + const elements = new Map(); + const makeElement = selector => ({ + selector, + value: '', + visible: false, + handlers: {}, + val(value) { + if (arguments.length === 0) return this.value; + this.value = value; + return this; + }, + show() { this.visible = true; return this; }, + hide() { this.visible = false; return this; }, + on(event, handler) { this.handlers[event] = handler; return this; }, + trigger(event) { this.handlers[event]?.call(this); return this; }, + }); + const $ = selector => { + if (!elements.has(selector)) elements.set(selector, makeElement(selector)); + return elements.get(selector); + }; + + const context = { + $, + document: {}, + URL, + console, + globalRootUrl: '', + globalTranslate: {}, + Form: {}, + Config: {}, + PbxApi: {}, + dnsProvidersMeta: [], + moduleGetSSLStatusLoopWorker: {}, + UserMessage: {}, + }; + vm.createContext(context); + const source = fs.readFileSync(sourcePath, 'utf8') + .replace(/\/\/ Initialize the ModuleGetSsl class[\s\S]*$/, '') + .concat('\n;globalThis.__ModuleGetSsl = ModuleGetSsl;'); + vm.runInContext(source, context); + return { controller: context.__ModuleGetSsl, elements }; +} + +test('recognizes valid IP addresses without treating domains or malformed values as IPs', () => { + const { controller } = loadController(); + const cases = [ + ['203.0.113.10', true], + ['2001:db8::1', true], + ['[2001:db8::1]', true], + ['pbx.example.com', false], + ['', false], + ['999.1.1.1', false], + ['2001:::1', false], + ['[2001:db8::1', false], + ['2001:db8::1]', false], + ]; + + for (const [value, expected] of cases) { + assert.equal(controller.isIpAddress(value), expected, value); + } +}); + +test('updates the warning immediately when the address input changes', () => { + const { controller, elements } = loadController(); + const address = elements.get('#domainName'); + const warning = elements.get('#ip-address-certificate-warning'); + + controller.bindIpAddressWarning(); + assert.equal(warning.visible, false); + + address.val('203.0.113.10').trigger('input'); + assert.equal(warning.visible, true); + + address.val('pbx.example.com').trigger('input'); + assert.equal(warning.visible, false); +}); From 4ebf1306f265e22031d4918d24f56dbee151a34a Mon Sep 17 00:00:00 2001 From: boffart <5922739+boffart@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:13:18 +0300 Subject: [PATCH 5/8] docs: design combined domain and IP certificates --- ...-07-31-domain-and-ip-certificate-design.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/superpowers/specs/2026-07-31-domain-and-ip-certificate-design.md diff --git a/docs/superpowers/specs/2026-07-31-domain-and-ip-certificate-design.md b/docs/superpowers/specs/2026-07-31-domain-and-ip-certificate-design.md new file mode 100644 index 0000000..c63fa9c --- /dev/null +++ b/docs/superpowers/specs/2026-07-31-domain-and-ip-certificate-design.md @@ -0,0 +1,33 @@ +# Domain and public IP certificate + +## Goal + +Allow a user who configured a domain name to include one explicit public IPv4 or IPv6 address in the same Let's Encrypt certificate. + +## User interface + +When the primary address is a domain, show a checkbox named “Add public IP address to the certificate” (“Добавить публичный IP-адрес в сертификат”). Hide the checkbox when the primary address is itself an IP address. + +Enabling the checkbox reveals a required “Public IP address” field. Show a native Semantic UI warning explaining that the certificate covers both identifiers, is valid for about six days, and is renewed more frequently. Reject domains, private/malformed values, and unmatched IPv6 brackets in the additional IP field. Preserve the entered IP while the checkbox is temporarily hidden. + +When either the primary identifier is an IP or the additional-IP checkbox is active, select HTTP-01 in the UI, disable DNS-01, enable automatic renewal, and prevent the user from disabling it. Restore normal controls when no IP identifier is requested. + +## Persistence and migration + +Add nullable model columns `includeIpAddress` (integer, default 0) and `publicIpAddress` (string). Existing installations migrate with the checkbox disabled and no additional address. The controller normalizes unchecked state to `0` and validates the additional address server-side before saving. + +## Certificate issuance + +Build the identifier list from the primary `domainName` plus `publicIpAddress` only when the checkbox is active and the primary identifier is not an IP. Any order containing an IP uses HTTP-01 and adds `--cert-profile shortlived`. A mixed order passes both identifiers to `acme.sh` as separate `-d` arguments. The primary identifier remains first, preserving certificate storage paths and renewal configuration lookup. + +Reject inconsistent persisted or submitted settings rather than silently issuing a different certificate. Shell arguments remain escaped independently. + +## Renewal + +Certificates containing an IP require automatic renewal. Their renewal check runs hourly at a stable non-zero minute; ordinary domain-only certificates retain the existing twice-monthly schedule. `acme.sh` decides whether renewal is due, so hourly invocations normally exit without issuing. + +The saved acme.sh renewal configuration retains the identifiers and `shortlived` profile. The existing reload hook continues installing renewed material into MikoPBX settings. + +## Testing + +Add focused tests for UI visibility/control state and IPv4/IPv6 validation, backend identifier/command policy, persistence normalization, and cron selection. Verify the test first fails for each new behavior, then passes. Rebuild the tracked browser asset and run JavaScript/PHP syntax checks plus `git diff --check`. From 76e1c110f12a941f5928a885760a722533ea41ca Mon Sep 17 00:00:00 2001 From: boffart <5922739+boffart@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:22:19 +0300 Subject: [PATCH 6/8] feat: include public IP in domain certificates --- App/Controllers/ModuleGetSslController.php | 19 +++ App/Forms/ModuleGetSslForm.php | 6 + App/Views/ModuleGetSsl/index.volt | 17 ++- Lib/AcmeHttpPort.php | 21 +-- Lib/CertificateIdentifierPolicy.php | 125 ++++++++++++++++++ Lib/GetSslMain.php | 22 ++- Messages/en.php | 4 + Messages/ru.php | 4 + Models/ModuleGetSsl.php | 14 ++ Setup/PbxExtensionSetup.php | 4 + .../2026-07-31-domain-and-ip-certificate.md | 81 ++++++++++++ public/assets/js/module-get-ssl-index.js | 63 ++++++++- public/assets/js/src/module-get-ssl-index.js | 77 +++++++++++ tests/CertificateIdentifierPolicyTest.php | 70 ++++++++++ tests/GetSslPolicyIntegrationTest.php | 47 +++++++ tests/ip-address-warning.test.mjs | 93 +++++++++++++ 16 files changed, 651 insertions(+), 16 deletions(-) create mode 100644 Lib/CertificateIdentifierPolicy.php create mode 100644 docs/superpowers/plans/2026-07-31-domain-and-ip-certificate.md create mode 100644 tests/CertificateIdentifierPolicyTest.php create mode 100644 tests/GetSslPolicyIntegrationTest.php diff --git a/App/Controllers/ModuleGetSslController.php b/App/Controllers/ModuleGetSslController.php index e9707ab..58cc21b 100644 --- a/App/Controllers/ModuleGetSslController.php +++ b/App/Controllers/ModuleGetSslController.php @@ -25,6 +25,7 @@ use MikoPBX\Common\Models\LanInterfaces; use Modules\ModuleGetSsl\App\Forms\ModuleGetSslForm; use Modules\ModuleGetSsl\Lib\DnsProviderRegistry; +use Modules\ModuleGetSsl\Lib\CertificateIdentifierPolicy; use Modules\ModuleGetSsl\Models\ModuleGetSsl; class ModuleGetSslController extends BaseController @@ -92,8 +93,12 @@ public function saveAction(): void case 'id': break; case 'autoUpdate': + case 'includeIpAddress': $record->$key = ($newVal === 'on') ? '1' : '0'; break; + case 'publicIpAddress': + $record->$key = CertificateIdentifierPolicy::normalizeIpAddress($newVal); + break; case 'dnsCredentials': // Store raw base64-encoded JSON as-is from the frontend $record->$key = $newVal; @@ -103,6 +108,20 @@ public function saveAction(): void } } + try { + $settings = $record->toArray(); + CertificateIdentifierPolicy::getIdentifiers($settings); + if (CertificateIdentifierPolicy::containsIpAddress($settings)) { + $record->challengeType = 'http'; + $record->autoUpdate = '1'; + } + } catch (\InvalidArgumentException $e) { + $this->flash->error($this->translation->_('module_getssl_PublicIpAddressInvalid')); + $this->view->success = false; + $this->db->rollback(); + return; + } + if ($record->save() === false) { $errors = $record->getMessages(); $this->flash->error(implode('
', $errors)); diff --git a/App/Forms/ModuleGetSslForm.php b/App/Forms/ModuleGetSslForm.php index 6cdea56..298ed62 100644 --- a/App/Forms/ModuleGetSslForm.php +++ b/App/Forms/ModuleGetSslForm.php @@ -37,6 +37,12 @@ public function initialize($entity = null, $options = null): void // DomainName $this->add(new Text('domainName')); + // Optional additional public IP SAN + $this->addCheckBox('includeIpAddress', intval($entity->includeIpAddress ?? 0) === 1); + $this->add(new Text('publicIpAddress', [ + 'value' => $entity->publicIpAddress ?? '', + ])); + // Challenge type: http or dns $challengeTypeOptions = [ 'http' => 'HTTP-01 (port 80)', diff --git a/App/Views/ModuleGetSsl/index.volt b/App/Views/ModuleGetSsl/index.volt index 40cf8f5..76b157d 100644 --- a/App/Views/ModuleGetSsl/index.volt +++ b/App/Views/ModuleGetSsl/index.volt @@ -12,6 +12,21 @@
+ + + +
{{ form.render('challengeType') }} @@ -40,7 +55,7 @@
-
+
{{ form.render('autoUpdate') }}
diff --git a/Lib/AcmeHttpPort.php b/Lib/AcmeHttpPort.php index 76e6474..5923f68 100644 --- a/Lib/AcmeHttpPort.php +++ b/Lib/AcmeHttpPort.php @@ -66,22 +66,22 @@ public function openPort(): bool $lockData = json_encode(['pid' => getmypid(), 'time' => time()]); file_put_contents(self::LOCK_FILE, $lockData); - $domainName = $this->getDomainName(); - if (empty($domainName)) { + $serverNames = $this->getServerNames(); + if (empty($serverNames)) { unlink(self::LOCK_FILE); $this->log('Port 80 open skipped: domain name is empty'); return false; } - $this->createNginxConf($domainName); + $this->createNginxConf($serverNames); $this->reloadNginx(); $firewallManaged = $this->isFirewallManaged(); if ($firewallManaged) { $this->addFirewallRules(); - $this->log("Port 80 opened for $domainName (nginx + iptables)"); + $this->log("Port 80 opened for $serverNames (nginx + iptables)"); } else { - $this->log("Port 80 opened for $domainName (nginx only, firewall not managed)"); + $this->log("Port 80 opened for $serverNames (nginx only, firewall not managed)"); } return true; @@ -164,16 +164,17 @@ private function isAlreadyOpen(): bool return false; } - /** - * Gets domain name from module settings. - */ - private function getDomainName(): string + private function getServerNames(): string { $settings = ModuleGetSsl::findFirst(); if ($settings === null) { return ''; } - return $settings->domainName ?? ''; + try { + return implode(' ', CertificateIdentifierPolicy::getIdentifiers($settings->toArray())); + } catch (\InvalidArgumentException $e) { + return $settings->domainName ?? ''; + } } /** diff --git a/Lib/CertificateIdentifierPolicy.php b/Lib/CertificateIdentifierPolicy.php new file mode 100644 index 0000000..c8f92cd --- /dev/null +++ b/Lib/CertificateIdentifierPolicy.php @@ -0,0 +1,125 @@ += 2 && $value[0] === '[' && substr($value, -1) === ']') { + return substr($value, 1, -1); + } + return $value; + } + + public static function isIpAddress(string $value): bool + { + return filter_var(self::normalizeIpAddress($value), FILTER_VALIDATE_IP) !== false; + } + + public static function isPublicIpAddress(string $value): bool + { + $value = self::normalizeIpAddress($value); + $flags = FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE; + if (filter_var($value, FILTER_VALIDATE_IP, $flags) === false) { + return false; + } + + foreach (['100.64.0.0/10', '192.0.2.0/24', '198.51.100.0/24', '203.0.113.0/24', + '2001:db8::/32'] as $reservedRange) { + if (self::isInCidr($value, $reservedRange)) { + return false; + } + } + return true; + } + + public static function getIdentifiers(array $settings): array + { + $primary = trim((string)($settings['domainName'] ?? '')); + if ($primary === '') { + throw new InvalidArgumentException('Primary certificate identifier is empty'); + } + if (self::isIpAddress($primary)) { + $primary = self::normalizeIpAddress($primary); + if (!self::isPublicIpAddress($primary)) { + throw new InvalidArgumentException('The primary IP address must be public'); + } + } + + $includeIp = (int)($settings['includeIpAddress'] ?? 0) === 1; + if (!$includeIp) { + return [$primary]; + } + if (self::isIpAddress($primary)) { + throw new InvalidArgumentException('An IP primary identifier cannot include an additional IP'); + } + + $publicIp = self::normalizeIpAddress((string)($settings['publicIpAddress'] ?? '')); + if (!self::isPublicIpAddress($publicIp)) { + throw new InvalidArgumentException('The additional IP address must be public'); + } + return [$primary, $publicIp]; + } + + public static function containsIpAddress(array $settings): bool + { + foreach (self::getIdentifiers($settings) as $identifier) { + if (self::isIpAddress($identifier)) { + return true; + } + } + return false; + } + + public static function requiresHttp01(array $settings): bool + { + return self::containsIpAddress($settings); + } + + public static function requiresAutoUpdate(array $settings): bool + { + return self::containsIpAddress($settings); + } + + public static function getCronSchedule(array $settings): string + { + return self::containsIpAddress($settings) ? '17 * * * *' : '0 1 1,15 * *'; + } + + public static function buildAcmeIdentifierArguments(array $settings): string + { + $arguments = ''; + foreach (self::getIdentifiers($settings) as $identifier) { + $arguments .= ' -d ' . escapeshellarg($identifier); + } + if (self::containsIpAddress($settings)) { + $arguments .= ' --cert-profile shortlived'; + } + return $arguments; + } + + private static function isInCidr(string $ipAddress, string $cidr): bool + { + [$network, $prefixLength] = explode('/', $cidr, 2); + $ipBytes = inet_pton($ipAddress); + $networkBytes = inet_pton($network); + if ($ipBytes === false || $networkBytes === false || strlen($ipBytes) !== strlen($networkBytes)) { + return false; + } + + $remainingBits = (int)$prefixLength; + for ($index = 0, $length = strlen($ipBytes); $index < $length && $remainingBits > 0; $index++) { + $bits = min(8, $remainingBits); + $mask = (0xff << (8 - $bits)) & 0xff; + if ((ord($ipBytes[$index]) & $mask) !== (ord($networkBytes[$index]) & $mask)) { + return false; + } + $remainingBits -= $bits; + } + return true; + } +} diff --git a/Lib/GetSslMain.php b/Lib/GetSslMain.php index eb12c2f..25e2cb2 100644 --- a/Lib/GetSslMain.php +++ b/Lib/GetSslMain.php @@ -159,7 +159,18 @@ private function getModuleDirs(): array */ public function isDns01(): bool { - return ($this->module_settings['challengeType'] ?? 'http') === 'dns'; + return ($this->module_settings['challengeType'] ?? 'http') === 'dns' + && !CertificateIdentifierPolicy::requiresHttp01($this->module_settings); + } + + public function getCertificateIdentifiers(): array + { + return CertificateIdentifierPolicy::getIdentifiers($this->module_settings); + } + + public function hasIpIdentifier(): bool + { + return CertificateIdentifierPolicy::containsIpAddress($this->module_settings); } /** @@ -359,7 +370,8 @@ public function startGetCertSsl(bool $asynchronously = true): PBXApiResult // Build acme.sh command $cmd = self::ACME_SH_BIN - . " --issue -d " . escapeshellarg($extHostname) + . " --issue" + . CertificateIdentifierPolicy::buildAcmeIdentifierArguments($this->module_settings) . " --home " . escapeshellarg($acmeHome) . " --config-home " . escapeshellarg($acmeConfigHome) . " --server letsencrypt" @@ -551,12 +563,14 @@ public function getCronTask(): string { if ( PbxExtensionUtils::isEnabled($this->moduleUniqueID) - && intval($this->module_settings['autoUpdate']) === 1 && !empty($this->module_settings['domainName']) + && (intval($this->module_settings['autoUpdate'] ?? 0) === 1 + || CertificateIdentifierPolicy::requiresAutoUpdate($this->module_settings)) ) { $phpPath = Util::which('php'); $cronScript = $this->dirs['moduleDir'] . '/bin/cronRenewCert.php'; - return "0 1 1,15 * * $phpPath -f $cronScript > /dev/null 2>&1" . PHP_EOL; + $schedule = CertificateIdentifierPolicy::getCronSchedule($this->module_settings); + return "$schedule $phpPath -f $cronScript > /dev/null 2>&1" . PHP_EOL; } return ''; } diff --git a/Messages/en.php b/Messages/en.php index f14d9e9..b8a271b 100644 --- a/Messages/en.php +++ b/Messages/en.php @@ -16,6 +16,10 @@ 'module_getssl_getUpdateSSLButton' => 'Get/Renew SSL Certificate', 'module_getssl_DomainNameEmpty' => 'Enter the domain value to generate the certificate', 'module_getssl_IpAddressCertificateWarning' => 'Let’s Encrypt certificates for IP addresses are valid for about 6 days. The module will renew this certificate more frequently.', + 'module_getssl_IncludeIpAddressLabel' => 'Add public IP address to the certificate', + 'module_getssl_PublicIpAddressLabel' => 'Public IP address', + 'module_getssl_PublicIpAddressInvalid' => 'Enter a valid public IPv4 or IPv6 address', + 'module_getssl_DomainAndIpCertificateWarning' => 'The certificate will cover the domain and the specified IP address. It will be valid for about 6 days, so automatic renewal will run more frequently.', 'module_getssl_getUpdateLogHeader' => 'Lets Encrypt Certificate Request Result', 'module_getssl_ConfigStartsGenerating' => 'Generating configuration files...', 'module_getssl_ConfigGenerated' => 'Configuration files created...', diff --git a/Messages/ru.php b/Messages/ru.php index 38d9789..75e38fc 100644 --- a/Messages/ru.php +++ b/Messages/ru.php @@ -28,6 +28,10 @@ 'module_getssl_getUpdateLogHeader' => 'Результат запроса сертификата в Lets Encrypt', 'module_getssl_DomainNameEmpty' => 'Введите значение домена для генерации сертификата', 'module_getssl_IpAddressCertificateWarning' => 'Сертификаты Let’s Encrypt для IP-адресов действуют около 6 дней. Модуль будет обновлять такой сертификат чаще.', + 'module_getssl_IncludeIpAddressLabel' => 'Добавить публичный IP-адрес в сертификат', + 'module_getssl_PublicIpAddressLabel' => 'Публичный IP-адрес', + 'module_getssl_PublicIpAddressInvalid' => 'Введите корректный публичный IPv4- или IPv6-адрес', + 'module_getssl_DomainAndIpCertificateWarning' => 'Сертификат будет действовать для домена и указанного IP-адреса. Срок действия составит около 6 дней, поэтому автоматическое обновление будет выполняться чаще.', 'module_getssl_ConfigStartsGenerating' => 'Генерируем конфигурационные файлы...', 'module_getssl_ConfigGenerated' => 'Конфигурационные файлы созданы...', 'module_getssl_GetSSLProcessing' => 'Выполняется запрос данных в Lets Encrypt...', diff --git a/Models/ModuleGetSsl.php b/Models/ModuleGetSsl.php index c6c6582..c7df33b 100644 --- a/Models/ModuleGetSsl.php +++ b/Models/ModuleGetSsl.php @@ -38,6 +38,20 @@ class ModuleGetSsl extends ModulesModelsBase */ public $domainName; + /** + * Include an explicit public IP address in the certificate SAN list. + * + * @Column(type="integer", default="0", nullable=true) + */ + public $includeIpAddress; + + /** + * Public IPv4 or IPv6 address included in the certificate. + * + * @Column(type="string", nullable=true) + */ + public $publicIpAddress; + /** * Auto update SSL certificate * diff --git a/Setup/PbxExtensionSetup.php b/Setup/PbxExtensionSetup.php index 2847987..9084c15 100644 --- a/Setup/PbxExtensionSetup.php +++ b/Setup/PbxExtensionSetup.php @@ -70,6 +70,7 @@ public function installDB(): bool if ($settings === null) { $settings = new ModuleGetSsl(); $settings->autoUpdate = '1'; + $settings->includeIpAddress = '0'; $res = LanInterfaces::findFirst("internet = '1'")->toArray(); $settings->domainName = $res['exthostname'] ?? ''; } @@ -77,6 +78,9 @@ public function installDB(): bool if (empty($settings->challengeType)) { $settings->challengeType = 'http'; } + if ($settings->includeIpAddress === null || $settings->includeIpAddress === '') { + $settings->includeIpAddress = '0'; + } $result = $settings->save(); } diff --git a/docs/superpowers/plans/2026-07-31-domain-and-ip-certificate.md b/docs/superpowers/plans/2026-07-31-domain-and-ip-certificate.md new file mode 100644 index 0000000..9ed146b --- /dev/null +++ b/docs/superpowers/plans/2026-07-31-domain-and-ip-certificate.md @@ -0,0 +1,81 @@ +# Domain and Public IP Certificate Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Issue and automatically renew one short-lived certificate containing a configured domain and an explicit public IP address. + +**Architecture:** A dependency-free `CertificateIdentifierPolicy` centralizes IP recognition, public-address validation, SAN construction, challenge constraints, and renewal cadence. The model/controller persist two new settings, `GetSslMain` consumes the policy for issuance and cron behavior, and the existing form controller mirrors those rules for immediate feedback. + +**Tech Stack:** PHP 7.4+, Phalcon model/forms, acme.sh 3.1.3, ES6/jQuery/Semantic UI, Node test runner, Babel. + +## Global Constraints + +- Keep the primary domain first in the ACME identifier list so existing certificate paths remain stable. +- Any IP-containing order uses HTTP-01, `--cert-profile shortlived`, mandatory auto-renewal, and hourly renewal checks. +- The additional address accepts only public IPv4/IPv6 and is ignored unless explicitly enabled for a domain primary identifier. +- Existing domain-only installations retain their current behavior. +- Every shell identifier is escaped independently. + +--- + +### Task 1: Certificate identifier policy + +**Files:** +- Create: `Lib/CertificateIdentifierPolicy.php` +- Create: `tests/CertificateIdentifierPolicyTest.php` + +**Interfaces:** +- Produces: `isIpAddress(string): bool`, `isPublicIpAddress(string): bool`, `getIdentifiers(array): array`, `containsIpAddress(array): bool`, `requiresHttp01(array): bool`, `getCronSchedule(array): string`. + +- [x] **Step 1: Write failing policy tests** covering domain-only, IP-only, enabled domain+public IPv4/IPv6, disabled additional IP, private/malformed IP rejection, forced HTTP-01, and cron strings `17 * * * *` versus `0 1 1,15 * *`. +- [x] **Step 2: Run `php tests/CertificateIdentifierPolicyTest.php`** and confirm failure because the policy class is absent. +- [x] **Step 3: Implement the static dependency-free policy** using `filter_var`, `FILTER_FLAG_NO_PRIV_RANGE`, and `FILTER_FLAG_NO_RES_RANGE`; throw `InvalidArgumentException` for inconsistent enabled settings. +- [x] **Step 4: Re-run the policy test** and require all assertions to pass. + +### Task 2: Persistence and issuance + +**Files:** +- Modify: `Models/ModuleGetSsl.php` +- Modify: `Setup/PbxExtensionSetup.php` +- Modify: `App/Forms/ModuleGetSslForm.php` +- Modify: `App/Controllers/ModuleGetSslController.php` +- Modify: `Lib/GetSslMain.php` +- Create: `tests/GetSslPolicyIntegrationTest.php` + +**Interfaces:** +- Consumes: `CertificateIdentifierPolicy` methods from Task 1. +- Produces: persisted `includeIpAddress` and `publicIpAddress`; `GetSslMain::getCertificateIdentifiers()`, `hasIpIdentifier()`, and policy-driven cron/issue behavior. + +- [x] **Step 1: Write a failing source-level integration test** that loads the real policy and verifies a command-argument builder returns two independently escaped `-d` arguments plus `--cert-profile shortlived`, while domain-only output omits the profile. +- [x] **Step 2: Run the test and verify RED** for the missing integration method. +- [x] **Step 3: Add model annotations/default migration and form elements.** Normalize checkbox state in the controller; force `challengeType=http` and `autoUpdate=1` when the policy contains IP; reject invalid additional IP with `module_getssl_PublicIpAddressInvalid`. +- [x] **Step 4: Refactor `GetSslMain` to build identifiers through the policy.** Append one escaped `-d` per identifier, append the short-lived profile for IP orders, treat IP orders as HTTP-01 even if stale settings say DNS, and select the policy cron schedule. +- [x] **Step 5: Run both PHP tests** and require PASS. + +### Task 3: Reactive Semantic UI form + +**Files:** +- Modify: `App/Views/ModuleGetSsl/index.volt` +- Modify: `Messages/en.php` +- Modify: `Messages/ru.php` +- Modify: `public/assets/js/src/module-get-ssl-index.js` +- Modify: `public/assets/js/module-get-ssl-index.js` +- Modify: `tests/ip-address-warning.test.mjs` + +**Interfaces:** +- Consumes: `#domainName`, `#includeIpAddress`, `#publicIpAddress`, `#challengeType`, and `#autoUpdate` form controls. +- Produces: reactive visibility, required-field validation, and forced HTTP/renewal control state for IP-containing orders. + +- [x] **Step 1: Extend the Node harness and write failing tests** for domain/IP visibility, checkbox toggling, additional field validation, warning visibility, and forced/restored challenge/renewal controls. +- [x] **Step 2: Run `node --test tests/ip-address-warning.test.mjs`** and verify failures are caused by missing combined-certificate behavior. +- [x] **Step 3: Add native Semantic UI checkbox, field, and warning markup**, plus English/Russian labels and errors. +- [x] **Step 4: Implement the minimal reactive controller behavior** and integrate additional-IP validation into `cbBeforeSendForm`. +- [x] **Step 5: Rebuild the tracked browser asset with Babel** and re-run the Node and PHP tests. + +### Task 4: Final verification and commit + +**Files:** All files above and this plan. + +- [x] **Step 1: Run** `node --test tests/ip-address-warning.test.mjs`, both PHP tests, `node --check` on source/compiled JS, `php -l` on modified PHP files, and `git diff --check`. +- [x] **Step 2: Review the diff** for unrelated changes, credential exposure, unsafe shell concatenation, and migration compatibility. +- [x] **Step 3: Commit** with `git commit -m "feat: include public IP in domain certificates"`. diff --git a/public/assets/js/module-get-ssl-index.js b/public/assets/js/module-get-ssl-index.js index 1909a7a..d059ba7 100644 --- a/public/assets/js/module-get-ssl-index.js +++ b/public/assets/js/module-get-ssl-index.js @@ -15,6 +15,13 @@ const ModuleGetSsl = { $submitButton: $('#submitbutton'), $moduleStatus: $('#status'), $domainName: $('#domainName'), + $includeIpAddress: $('#includeIpAddress'), + $includeIpAddressField: $('#include-ip-address-field'), + $includeIpAddressCheckbox: $('#include-ip-address-checkbox'), + $publicIpAddress: $('#publicIpAddress'), + $publicIpAddressSettings: $('#public-ip-address-settings'), + $autoUpdate: $('#autoUpdate'), + $autoUpdateCheckbox: $('#auto-update-checkbox'), $challengeType: $('#challengeType'), $dnsProvider: $('#dnsProvider'), $httpChallengeInfo: $('#http-challenge-info'), @@ -55,6 +62,7 @@ const ModuleGetSsl = { // Initialize form with validation and submit handlers this.initializeForm(); this.bindIpAddressWarning(); + this.bindCertificateIdentifierControls(); moduleGetSSLStatusLoopWorker.$resultBlock.hide(); // Restore saved state @@ -85,6 +93,17 @@ const ModuleGetSsl = { return false; } }, + /** Return true only for a publicly routable IPv4 or IPv6 address. */ + isPublicIpAddress(value) { + if (!this.isIpAddress(value)) return false; + const address = String(value || '').trim().replace(/^\[|\]$/g, '').toLowerCase(); + const ipv4Parts = address.split('.').map(Number); + if (ipv4Parts.length === 4) { + const [a, b, c] = ipv4Parts; + return !(a === 0 || a === 10 || a === 127 || a >= 224 || a === 100 && b >= 64 && b <= 127 || a === 169 && b === 254 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 0 && (c === 0 || c === 2) || a === 192 && b === 168 || a === 198 && (b === 18 || b === 19) || a === 198 && b === 51 && c === 100 || a === 203 && b === 0 && c === 113); + } + return !(address === '::' || address === '::1' || address.startsWith('fc') || address.startsWith('fd') || /^fe[89ab]/.test(address) || address.startsWith('2001:db8:')); + }, /** Update warning visibility when the configured address changes. */ updateIpAddressWarning() { if (this.isIpAddress(this.$domainName.val())) { @@ -98,6 +117,45 @@ const ModuleGetSsl = { this.$domainName.on('input', () => this.updateIpAddressWarning()); this.updateIpAddressWarning(); }, + /** Apply visibility and compatibility rules for domain/IP certificate identifiers. */ + updateCertificateIdentifierControls() { + const primary = String(this.$domainName.val() || '').trim(); + const primaryIsIp = this.isIpAddress(primary); + const canIncludeIp = primary !== '' && !primaryIsIp; + if (primaryIsIp && this.$includeIpAddress.is(':checked')) { + this.$includeIpAddress.prop('checked', false); + this.$includeIpAddressCheckbox.checkbox('uncheck'); + } + const includeIp = canIncludeIp && this.$includeIpAddress.is(':checked'); + this.$includeIpAddressField.toggle(canIncludeIp); + this.$publicIpAddressSettings.toggle(includeIp); + if (primaryIsIp || includeIp) { + this.$challengeType.dropdown('set selected', 'http').dropdown('set disabled'); + this.$autoUpdate.prop('checked', true).prop('disabled', true); + this.$autoUpdateCheckbox.checkbox('check').checkbox('set disabled'); + } else { + this.$challengeType.dropdown('set enabled'); + this.$autoUpdate.prop('disabled', false); + this.$autoUpdateCheckbox.checkbox('set enabled'); + } + }, + /** Bind changes affecting the requested certificate identifier list. */ + bindCertificateIdentifierControls() { + this.$domainName.on('input', () => this.updateCertificateIdentifierControls()); + this.$includeIpAddressCheckbox.checkbox({ + onChange: () => this.updateCertificateIdentifierControls() + }); + this.updateCertificateIdentifierControls(); + }, + /** Validate the optional additional public IP address. */ + validatePublicIpAddress() { + const primaryIsIp = this.isIpAddress(this.$domainName.val()); + if (primaryIsIp || !this.$includeIpAddress.is(':checked')) return true; + if (this.isPublicIpAddress(this.$publicIpAddress.val())) return true; + UserMessage.showError(globalTranslate.module_getssl_PublicIpAddressInvalid); + this.$publicIpAddress.closest('.field').addClass('error'); + return false; + }, /** * Handle challenge type change: show/hide relevant sections. * @param {string} value - 'http' or 'dns' @@ -308,6 +366,9 @@ const ModuleGetSsl = { * @returns {Object} The modified Ajax request settings. */ cbBeforeSendForm(settings) { + if (!ModuleGetSsl.validatePublicIpAddress()) { + return false; + } if (!ModuleGetSsl.validateDnsFields()) { return false; } @@ -344,4 +405,4 @@ $(document).ready(() => { ModuleGetSsl.initialize(); }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kdWxlLWdldC1zc2wtaW5kZXguanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsic3JjL21vZHVsZS1nZXQtc3NsLWluZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGdsb2JhbCBnbG9iYWxSb290VXJsLCBnbG9iYWxUcmFuc2xhdGUsIEZvcm0sIENvbmZpZywgUGJ4QXBpLCBkbnNQcm92aWRlcnNNZXRhICovXG5cbi8vIENvbnN0YW50cyByZWxhdGVkIHRvIHRoZSBmb3JtIGFuZCBtb2R1bGVcbmNvbnN0IGlkVXJsICAgICA9ICdtb2R1bGUtZ2V0LXNzbCc7ICAgICAgICAgICAgICAvLyBBUEkgZW5kcG9pbnQgZm9yIFNTTCBtb2R1bGVcbmNvbnN0IGlkRm9ybSAgICA9ICdtb2R1bGUtZ2V0LXNzbC1mb3JtJzsgICAgICAgICAvLyBGb3JtIGVsZW1lbnQgSUQgZm9yIFNTTCBtb2R1bGVcbmNvbnN0IGNsYXNzTmFtZSA9ICdNb2R1bGVHZXRTc2wnOyAgICAgICAgICAgICAgICAvLyBDbGFzcyBuYW1lIGZvciB0aGlzIG1vZHVsZVxuXG4vLyBNYWluIE1vZHVsZUdldFNzbCBjbGFzcyBkZWZpbml0aW9uXG5jb25zdCBNb2R1bGVHZXRTc2wgPSB7XG5cdC8vIENhY2hlIGNvbW1vbmx5IHVzZWQgalF1ZXJ5IG9iamVjdHNcblx0JGZvcm1PYmo6ICQoJyMnICsgaWRGb3JtKSxcblx0JGNoZWNrQm94ZXM6ICQoJyMnICsgaWRGb3JtICsgJyAudWkuY2hlY2tib3gnKSxcblx0JGRpc2FiaWxpdHlGaWVsZHM6ICQoJyMnICsgaWRGb3JtICsgJyAuZGlzYWJpbGl0eScpLFxuXHQkc3RhdHVzVG9nZ2xlOiAkKCcjbW9kdWxlLXN0YXR1cy10b2dnbGUnKSxcblx0JHN1Ym1pdEJ1dHRvbjogJCgnI3N1Ym1pdGJ1dHRvbicpLFxuXHQkbW9kdWxlU3RhdHVzOiAkKCcjc3RhdHVzJyksXG5cdCRkb21haW5OYW1lOiAkKCcjZG9tYWluTmFtZScpLFxuXHQkY2hhbGxlbmdlVHlwZTogJCgnI2NoYWxsZW5nZVR5cGUnKSxcblx0JGRuc1Byb3ZpZGVyOiAkKCcjZG5zUHJvdmlkZXInKSxcblx0JGh0dHBDaGFsbGVuZ2VJbmZvOiAkKCcjaHR0cC1jaGFsbGVuZ2UtaW5mbycpLFxuXHQkZG5zU2V0dGluZ3NCbG9jazogJCgnI2Rucy1zZXR0aW5ncy1ibG9jaycpLFxuXHQkZG5zQ3JlZGVudGlhbHNGaWVsZHM6ICQoJyNkbnMtY3JlZGVudGlhbHMtZmllbGRzJyksXG5cdCRkbnNDcmVkZW50aWFsc0lucHV0OiAkKCdpbnB1dFtuYW1lPVwiZG5zQ3JlZGVudGlhbHNcIl0nKSxcblx0JGlwQWRkcmVzc1dhcm5pbmc6ICQoJyNpcC1hZGRyZXNzLWNlcnRpZmljYXRlLXdhcm5pbmcnKSxcblxuXHQvLyBWYWxpZGF0aW9uIHJ1bGVzIGZvciB0aGUgZm9ybVxuXHR2YWxpZGF0ZVJ1bGVzOiB7XG5cdFx0ZG9tYWluTmFtZToge1xuXHRcdFx0aWRlbnRpZmllcjogJ2RvbWFpbk5hbWUnLFxuXHRcdFx0cnVsZXM6IFtcblx0XHRcdFx0e1xuXHRcdFx0XHRcdHR5cGU6ICdlbXB0eScsXG5cdFx0XHRcdFx0cHJvbXB0OiBnbG9iYWxUcmFuc2xhdGUubW9kdWxlX2dldHNzbF9Eb21haW5OYW1lRW1wdHksXG5cdFx0XHRcdH0sXG5cdFx0XHRdLFxuXHRcdH0sXG5cdH0sXG5cblx0LyoqXG5cdCAqIEluaXRpYWxpemUgdGhlIG1vZHVsZSwgYmluZCBldmVudCBsaXN0ZW5lcnMsIGFuZCBzZXR1cCB0aGUgZm9ybS5cblx0ICovXG5cdGluaXRpYWxpemUoKSB7XG5cdFx0Ly8gSW5pdGlhbGl6ZSBTZW1hbnRpYyBVSSBjaGVja2JveGVzXG5cdFx0dGhpcy4kY2hlY2tCb3hlcy5jaGVja2JveCgpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBkcm9wZG93bnNcblx0XHR0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKHtcblx0XHRcdG9uQ2hhbmdlOiBNb2R1bGVHZXRTc2wub25DaGFuZ2VDaGFsbGVuZ2VUeXBlLFxuXHRcdH0pO1xuXHRcdHRoaXMuJGRuc1Byb3ZpZGVyLmRyb3Bkb3duKHtcblx0XHRcdGZ1bGxUZXh0U2VhcmNoOiB0cnVlLFxuXHRcdFx0b25DaGFuZ2U6IE1vZHVsZUdldFNzbC5vbkNoYW5nZURuc1Byb3ZpZGVyLFxuXHRcdH0pO1xuXG5cdFx0Ly8gQ2hlY2sgYW5kIHNldCBtb2R1bGUgc3RhdHVzIG9uIGxvYWQgYW5kIHdoZW4gdGhlIHN0YXR1cyBjaGFuZ2VzXG5cdFx0dGhpcy5jaGVja1N0YXR1c1RvZ2dsZSgpO1xuXHRcdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdNb2R1bGVTdGF0dXNDaGFuZ2VkJywgdGhpcy5jaGVja1N0YXR1c1RvZ2dsZSk7XG5cblx0XHQvLyBJbml0aWFsaXplIGZvcm0gd2l0aCB2YWxpZGF0aW9uIGFuZCBzdWJtaXQgaGFuZGxlcnNcblx0XHR0aGlzLmluaXRpYWxpemVGb3JtKCk7XG5cdFx0dGhpcy5iaW5kSXBBZGRyZXNzV2FybmluZygpO1xuXG5cdFx0bW9kdWxlR2V0U1NMU3RhdHVzTG9vcFdvcmtlci4kcmVzdWx0QmxvY2suaGlkZSgpO1xuXG5cdFx0Ly8gUmVzdG9yZSBzYXZlZCBzdGF0ZVxuXHRcdGNvbnN0IGN1cnJlbnRDaGFsbGVuZ2UgPSB0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdnZXQgdmFsdWUnKSB8fCAnaHR0cCc7XG5cdFx0dGhpcy5vbkNoYW5nZUNoYWxsZW5nZVR5cGUoY3VycmVudENoYWxsZW5nZSk7XG5cdFx0dGhpcy5yZXN0b3JlU2F2ZWRDcmVkZW50aWFscygpO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDaGVjayB3aGV0aGVyIGEgdmFsdWUgaXMgYSB2YWxpZCBJUHY0IG9yIElQdjYgYWRkcmVzcy5cblx0ICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlXG5cdCAqIEByZXR1cm5zIHtib29sZWFufVxuXHQgKi9cblx0aXNJcEFkZHJlc3ModmFsdWUpIHtcblx0XHRjb25zdCBhZGRyZXNzID0gU3RyaW5nKHZhbHVlIHx8ICcnKS50cmltKCk7XG5cdFx0Y29uc3QgaXB2NFBhcnRzID0gYWRkcmVzcy5zcGxpdCgnLicpO1xuXHRcdGlmIChpcHY0UGFydHMubGVuZ3RoID09PSA0KSB7XG5cdFx0XHRyZXR1cm4gaXB2NFBhcnRzLmV2ZXJ5KHBhcnQgPT4gL15cXGR7MSwzfSQvLnRlc3QocGFydClcblx0XHRcdFx0JiYgTnVtYmVyKHBhcnQpIDw9IDI1NVxuXHRcdFx0XHQmJiAocGFydCA9PT0gJzAnIHx8IHBhcnRbMF0gIT09ICcwJykpO1xuXHRcdH1cblxuXHRcdGNvbnN0IGhhc09wZW5pbmdCcmFja2V0ID0gYWRkcmVzcy5zdGFydHNXaXRoKCdbJyk7XG5cdFx0Y29uc3QgaGFzQ2xvc2luZ0JyYWNrZXQgPSBhZGRyZXNzLmVuZHNXaXRoKCddJyk7XG5cdFx0aWYgKGhhc09wZW5pbmdCcmFja2V0ICE9PSBoYXNDbG9zaW5nQnJhY2tldCkgcmV0dXJuIGZhbHNlO1xuXHRcdGNvbnN0IGlwdjYgPSBoYXNPcGVuaW5nQnJhY2tldCA/IGFkZHJlc3Muc2xpY2UoMSwgLTEpIDogYWRkcmVzcztcblx0XHRpZiAoIWlwdjYuaW5jbHVkZXMoJzonKSB8fCAvXFxzLy50ZXN0KGlwdjYpKSByZXR1cm4gZmFsc2U7XG5cdFx0dHJ5IHtcblx0XHRcdG5ldyBVUkwoYGh0dHA6Ly9bJHtpcHY2fV0vYCk7XG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKiBVcGRhdGUgd2FybmluZyB2aXNpYmlsaXR5IHdoZW4gdGhlIGNvbmZpZ3VyZWQgYWRkcmVzcyBjaGFuZ2VzLiAqL1xuXHR1cGRhdGVJcEFkZHJlc3NXYXJuaW5nKCkge1xuXHRcdGlmICh0aGlzLmlzSXBBZGRyZXNzKHRoaXMuJGRvbWFpbk5hbWUudmFsKCkpKSB7XG5cdFx0XHR0aGlzLiRpcEFkZHJlc3NXYXJuaW5nLnNob3coKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy4kaXBBZGRyZXNzV2FybmluZy5oaWRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKiBCaW5kIHJlYWN0aXZlIElQIHdhcm5pbmcgYmVoYXZpb3IgYW5kIGluaXRpYWxpemUgaXRzIHN0YXRlLiAqL1xuXHRiaW5kSXBBZGRyZXNzV2FybmluZygpIHtcblx0XHR0aGlzLiRkb21haW5OYW1lLm9uKCdpbnB1dCcsICgpID0+IHRoaXMudXBkYXRlSXBBZGRyZXNzV2FybmluZygpKTtcblx0XHR0aGlzLnVwZGF0ZUlwQWRkcmVzc1dhcm5pbmcoKTtcblx0fSxcblxuXHQvKipcblx0ICogSGFuZGxlIGNoYWxsZW5nZSB0eXBlIGNoYW5nZTogc2hvdy9oaWRlIHJlbGV2YW50IHNlY3Rpb25zLlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWUgLSAnaHR0cCcgb3IgJ2Rucydcblx0ICovXG5cdG9uQ2hhbmdlQ2hhbGxlbmdlVHlwZSh2YWx1ZSkge1xuXHRcdGlmICh2YWx1ZSA9PT0gJ2RucycpIHtcblx0XHRcdE1vZHVsZUdldFNzbC4kaHR0cENoYWxsZW5nZUluZm8uaGlkZSgpO1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRkbnNTZXR0aW5nc0Jsb2NrLnNob3coKTtcblx0XHRcdC8vIFRyaWdnZXIgcHJvdmlkZXIgY2hhbmdlIHRvIHJlbmRlciBjcmVkZW50aWFsIGZpZWxkc1xuXHRcdFx0Y29uc3QgY3VycmVudFByb3ZpZGVyID0gTW9kdWxlR2V0U3NsLiRkbnNQcm92aWRlci5kcm9wZG93bignZ2V0IHZhbHVlJyk7XG5cdFx0XHRpZiAoY3VycmVudFByb3ZpZGVyKSB7XG5cdFx0XHRcdE1vZHVsZUdldFNzbC5vbkNoYW5nZURuc1Byb3ZpZGVyKGN1cnJlbnRQcm92aWRlcik7XG5cdFx0XHR9XG5cdFx0fSBlbHNlIHtcblx0XHRcdE1vZHVsZUdldFNzbC4kaHR0cENoYWxsZW5nZUluZm8uc2hvdygpO1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRkbnNTZXR0aW5nc0Jsb2NrLmhpZGUoKTtcblx0XHR9XG5cdH0sXG5cblx0LyoqXG5cdCAqIEhhbmRsZSBETlMgcHJvdmlkZXIgY2hhbmdlOiBkeW5hbWljYWxseSByZW5kZXIgY3JlZGVudGlhbCBmaWVsZHMuXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZSAtIHByb3ZpZGVyIElEIChlLmcuICdkbnNfY2YnKVxuXHQgKi9cblx0b25DaGFuZ2VEbnNQcm92aWRlcih2YWx1ZSkge1xuXHRcdGNvbnN0ICRjb250YWluZXIgPSBNb2R1bGVHZXRTc2wuJGRuc0NyZWRlbnRpYWxzRmllbGRzO1xuXHRcdCRjb250YWluZXIuZW1wdHkoKTtcblxuXHRcdGlmICghdmFsdWUgfHwgdHlwZW9mIGRuc1Byb3ZpZGVyc01ldGEgPT09ICd1bmRlZmluZWQnKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gRmluZCBwcm92aWRlciBtZXRhZGF0YVxuXHRcdGNvbnN0IHByb3ZpZGVyID0gZG5zUHJvdmlkZXJzTWV0YS5maW5kKHAgPT4gcC5pZCA9PT0gdmFsdWUpO1xuXHRcdGlmICghcHJvdmlkZXIgfHwgIXByb3ZpZGVyLmZpZWxkcykge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIERlY29kZSBleGlzdGluZyBzYXZlZCBjcmVkZW50aWFscyBmb3IgcHJlLWZpbGxpbmdcblx0XHRsZXQgc2F2ZWRDcmVkcyA9IHt9O1xuXHRcdGNvbnN0IGVuY29kZWRWYWwgPSBNb2R1bGVHZXRTc2wuJGRuc0NyZWRlbnRpYWxzSW5wdXQudmFsKCk7XG5cdFx0aWYgKGVuY29kZWRWYWwpIHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdGNvbnN0IGRlY29kZWQgPSBhdG9iKGVuY29kZWRWYWwpO1xuXHRcdFx0XHRzYXZlZENyZWRzID0gSlNPTi5wYXJzZShkZWNvZGVkKTtcblx0XHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdFx0Ly8gaWdub3JlIGRlY29kZSBlcnJvcnNcblx0XHRcdH1cblx0XHR9XG5cblx0XHQvLyBSZW5kZXIgZmllbGRzXG5cdFx0cHJvdmlkZXIuZmllbGRzLmZvckVhY2goZmllbGQgPT4ge1xuXHRcdFx0Y29uc3QgaGFzU2F2ZWQgPSBCb29sZWFuKHNhdmVkQ3JlZHNbZmllbGQudmFyXSk7XG5cdFx0XHRjb25zdCBkaXNwbGF5VmFsdWUgPSBoYXNTYXZlZCA/ICfigKLigKLigKLigKLigKLigKLigKLigKInIDogJyc7XG5cdFx0XHRjb25zdCBtYXNrZWRBdHRyID0gaGFzU2F2ZWQgPyAnZGF0YS1tYXNrZWQ9XCJ0cnVlXCInIDogJyc7XG5cdFx0XHRjb25zdCBodG1sID0gYFxuXHRcdFx0XHQ8ZGl2IGNsYXNzPVwiZmllbGRcIj5cblx0XHRcdFx0XHQ8bGFiZWw+JHtmaWVsZC5sYWJlbH08L2xhYmVsPlxuXHRcdFx0XHRcdDxpbnB1dCB0eXBlPVwicGFzc3dvcmRcIlxuXHRcdFx0XHRcdFx0ICAgY2xhc3M9XCJkbnMtY3JlZC1pbnB1dFwiXG5cdFx0XHRcdFx0XHQgICBkYXRhLXZhcj1cIiR7ZmllbGQudmFyfVwiXG5cdFx0XHRcdFx0XHQgICAke21hc2tlZEF0dHJ9XG5cdFx0XHRcdFx0XHQgICB2YWx1ZT1cIiR7TW9kdWxlR2V0U3NsLmVzY2FwZUh0bWwoZGlzcGxheVZhbHVlKX1cIlxuXHRcdFx0XHRcdFx0ICAgcGxhY2Vob2xkZXI9XCIke2ZpZWxkLmxhYmVsfVwiPlxuXHRcdFx0XHQ8L2Rpdj5gO1xuXHRcdFx0JGNvbnRhaW5lci5hcHBlbmQoaHRtbCk7XG5cdFx0fSk7XG5cdFx0Ly8gT24gZm9jdXM6IGNsZWFyIG1hc2sgc28gdXNlciBjYW4gZW50ZXIgbmV3IHZhbHVlXG5cdFx0JGNvbnRhaW5lci5vbignZm9jdXMnLCAnLmRucy1jcmVkLWlucHV0W2RhdGEtbWFza2VkXScsIGZ1bmN0aW9uICgpIHtcblx0XHRcdCQodGhpcykudmFsKCcnKS5yZW1vdmVBdHRyKCdkYXRhLW1hc2tlZCcpO1xuXHRcdH0pO1xuXHRcdC8vIENsZWFyIGVycm9yIGhpZ2hsaWdodCB3aGVuIHVzZXIgc3RhcnRzIHR5cGluZ1xuXHRcdCRjb250YWluZXIub24oJ2lucHV0JywgJy5kbnMtY3JlZC1pbnB1dCcsIGZ1bmN0aW9uICgpIHtcblx0XHRcdCQodGhpcykuY2xvc2VzdCgnLmZpZWxkJykucmVtb3ZlQ2xhc3MoJ2Vycm9yJyk7XG5cdFx0fSk7XG5cdH0sXG5cblx0LyoqXG5cdCAqIENvbGxlY3QgRE5TIGNyZWRlbnRpYWwgZmllbGQgdmFsdWVzIGludG8gYmFzZTY0IEpTT04gYW5kIHdyaXRlIHRvIGhpZGRlbiBpbnB1dC5cblx0ICogTWFza2VkIGZpZWxkcyAodW5jaGFuZ2VkKSBhcmUgcmVzdG9yZWQgZnJvbSB0aGUgcHJldmlvdXNseSBzYXZlZCBjcmVkZW50aWFscy5cblx0ICovXG5cdGNvbGxlY3REbnNDcmVkZW50aWFscygpIHtcblx0XHRjb25zdCBjaGFsbGVuZ2VUeXBlID0gTW9kdWxlR2V0U3NsLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdnZXQgdmFsdWUnKTtcblx0XHRpZiAoY2hhbGxlbmdlVHlwZSAhPT0gJ2RucycpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cdFx0Ly8gRGVjb2RlIGN1cnJlbnRseSBzdG9yZWQgY3JlZGVudGlhbHMgKHNvdXJjZSBvZiB0cnV0aCBmb3IgbWFza2VkIGZpZWxkcylcblx0XHRsZXQgc2F2ZWRDcmVkcyA9IHt9O1xuXHRcdGNvbnN0IGVuY29kZWRWYWwgPSBNb2R1bGVHZXRTc2wuJGRuc0NyZWRlbnRpYWxzSW5wdXQudmFsKCk7XG5cdFx0aWYgKGVuY29kZWRWYWwpIHtcblx0XHRcdHRyeSB7XG5cdFx0XHRcdHNhdmVkQ3JlZHMgPSBKU09OLnBhcnNlKGF0b2IoZW5jb2RlZFZhbCkpO1xuXHRcdFx0fSBjYXRjaCAoZSkge1xuXHRcdFx0XHQvLyBpZ25vcmVcblx0XHRcdH1cblx0XHR9XG5cdFx0Y29uc3QgY3JlZHMgPSB7fTtcblx0XHQkKCcuZG5zLWNyZWQtaW5wdXQnKS5lYWNoKGZ1bmN0aW9uICgpIHtcblx0XHRcdGNvbnN0IHZhck5hbWUgPSAkKHRoaXMpLmRhdGEoJ3ZhcicpO1xuXHRcdFx0aWYgKCF2YXJOYW1lKSByZXR1cm47XG5cdFx0XHRpZiAoJCh0aGlzKS5pcygnW2RhdGEtbWFza2VkXScpKSB7XG5cdFx0XHRcdC8vIFVzZXIgZGlkbid0IGNoYW5nZSB0aGlzIGZpZWxkIOKAlCBrZWVwIHN0b3JlZCB2YWx1ZVxuXHRcdFx0XHRpZiAoc2F2ZWRDcmVkc1t2YXJOYW1lXSkge1xuXHRcdFx0XHRcdGNyZWRzW3Zhck5hbWVdID0gc2F2ZWRDcmVkc1t2YXJOYW1lXTtcblx0XHRcdFx0fVxuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0Y29uc3QgdmFsID0gJCh0aGlzKS52YWwoKTtcblx0XHRcdFx0aWYgKHZhbCkge1xuXHRcdFx0XHRcdGNyZWRzW3Zhck5hbWVdID0gdmFsO1xuXHRcdFx0XHR9XG5cdFx0XHR9XG5cdFx0fSk7XG5cdFx0TW9kdWxlR2V0U3NsLiRkbnNDcmVkZW50aWFsc0lucHV0LnZhbChidG9hKEpTT04uc3RyaW5naWZ5KGNyZWRzKSkpO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBSZXN0b3JlIHNhdmVkIGNyZWRlbnRpYWxzIGludG8gdGhlIEROUyBwcm92aWRlciBmaWVsZHMgb24gcGFnZSBsb2FkLlxuXHQgKi9cblx0cmVzdG9yZVNhdmVkQ3JlZGVudGlhbHMoKSB7XG5cdFx0Y29uc3QgY3VycmVudFByb3ZpZGVyID0gTW9kdWxlR2V0U3NsLiRkbnNQcm92aWRlci5kcm9wZG93bignZ2V0IHZhbHVlJyk7XG5cdFx0aWYgKGN1cnJlbnRQcm92aWRlcikge1xuXHRcdFx0TW9kdWxlR2V0U3NsLm9uQ2hhbmdlRG5zUHJvdmlkZXIoY3VycmVudFByb3ZpZGVyKTtcblx0XHR9XG5cdH0sXG5cblx0LyoqXG5cdCAqIEVzY2FwZSBIVE1MIHNwZWNpYWwgY2hhcmFjdGVycyBmb3Igc2FmZSBpbnNlcnRpb24gaW50byBhdHRyaWJ1dGVzLlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdGV4dFxuXHQgKiBAcmV0dXJucyB7c3RyaW5nfVxuXHQgKi9cblx0ZXNjYXBlSHRtbCh0ZXh0KSB7XG5cdFx0Y29uc3QgbWFwID0geyAnJic6ICcmYW1wOycsICc8JzogJyZsdDsnLCAnPic6ICcmZ3Q7JywgJ1wiJzogJyZxdW90OycsIFwiJ1wiOiAnJiMwMzk7JyB9O1xuXHRcdHJldHVybiBTdHJpbmcodGV4dCkucmVwbGFjZSgvWyY8PlwiJ10vZywgbSA9PiBtYXBbbV0pO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBSZXF1ZXN0IGFuIFNTTCBjZXJ0aWZpY2F0ZSBieSBjYWxsaW5nIHRoZSBzZXJ2ZXItc2lkZSBBUEkuXG5cdCAqL1xuXHRnZXRTc2woKSB7XG5cdFx0JC5hcGkoe1xuXHRcdFx0dXJsOiBgJHtDb25maWcucGJ4VXJsfS9wYnhjb3JlL2FwaS9tb2R1bGVzLyR7Y2xhc3NOYW1lfS9nZXQtY2VydGAsXG5cdFx0XHRvbjogJ25vdycsXG5cdFx0XHRtZXRob2Q6ICdQT1NUJyxcblx0XHRcdGJlZm9yZVhIUih4aHIpIHtcblx0XHRcdFx0eGhyLnNldFJlcXVlc3RIZWFkZXIgKCdYLUFzeW5jLVJlc3BvbnNlLUNoYW5uZWwtSWQnLCBtb2R1bGVHZXRTU0xTdGF0dXNMb29wV29ya2VyLmNoYW5uZWxJZCk7XG5cdFx0XHRcdHhoci5zZXRSZXF1ZXN0SGVhZGVyICgnWC1Qcm9jZXNzb3ItVGltZW91dCcsICcxMjAnKTtcblx0XHRcdFx0cmV0dXJuIHhocjtcblx0XHRcdH0sXG5cdFx0XHRiZWZvcmVTZW5kKHNldHRpbmdzKSB7XG5cdFx0XHRcdE1vZHVsZUdldFNzbC4kc3VibWl0QnV0dG9uLmFkZENsYXNzKCdsb2FkaW5nIGRpc2FibGVkJyk7XG5cdFx0XHRcdG1vZHVsZUdldFNTTFN0YXR1c0xvb3BXb3JrZXIuJHJlc3VsdEJsb2NrLnNob3coKTtcblx0XHRcdFx0bW9kdWxlR2V0U1NMU3RhdHVzTG9vcFdvcmtlci5lZGl0b3IuZ2V0U2Vzc2lvbigpLnNldFZhbHVlKFxuXHRcdFx0XHRnbG9iYWxUcmFuc2xhdGUubW9kdWxlX2dldHNzbF9HZXRTU0xQcm9jZXNzaW5nICsgJ1xcbidcblx0XHRcdCk7XG5cdFx0XHRcdHJldHVybiBzZXR0aW5ncztcblx0XHRcdH0sXG5cdFx0XHRzdWNjZXNzVGVzdDogUGJ4QXBpLnN1Y2Nlc3NUZXN0LFxuXHRcdFx0b25TdWNjZXNzOiBmdW5jdGlvbiAocmVzcG9uc2UpIHtcblx0XHRcdFx0TW9kdWxlR2V0U3NsLiRzdWJtaXRCdXR0b24ucmVtb3ZlQ2xhc3MoJ2xvYWRpbmcgZGlzYWJsZWQnKTtcblx0XHRcdH0sXG5cdFx0XHRvbkZhaWx1cmU6IGZ1bmN0aW9uKHJlc3BvbnNlKSB7XG5cdFx0XHRcdE1vZHVsZUdldFNzbC4kc3VibWl0QnV0dG9uLnJlbW92ZUNsYXNzKCdsb2FkaW5nIGRpc2FibGVkJyk7XG5cdFx0XHRcdFVzZXJNZXNzYWdlLnNob3dNdWx0aVN0cmluZyhyZXNwb25zZS5tZXNzYWdlKTtcblx0XHRcdH0sXG5cdFx0fSlcblx0fSxcblxuXHQvKipcblx0ICogVG9nZ2xlcyB0aGUgZm9ybSBmaWVsZHMgYW5kIHN0YXR1cyB2aXNpYmlsaXR5IGJhc2VkIG9uIHRoZSBtb2R1bGUncyBzdGF0dXMuXG5cdCAqL1xuXHRjaGVja1N0YXR1c1RvZ2dsZSgpIHtcblx0XHRpZiAoTW9kdWxlR2V0U3NsLiRzdGF0dXNUb2dnbGUuY2hlY2tib3goJ2lzIGNoZWNrZWQnKSkge1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRkaXNhYmlsaXR5RmllbGRzLnJlbW92ZUNsYXNzKCdkaXNhYmxlZCcpO1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRtb2R1bGVTdGF0dXMuc2hvdygpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGRpc2FiaWxpdHlGaWVsZHMuYWRkQ2xhc3MoJ2Rpc2FibGVkJyk7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJG1vZHVsZVN0YXR1cy5oaWRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKlxuXHQgKiBWYWxpZGF0ZSBETlMgcHJvdmlkZXIgYW5kIGNyZWRlbnRpYWxzIHdoZW4gRE5TLTAxIGlzIHNlbGVjdGVkLlxuXHQgKiBSZXR1cm5zIHRydWUgaWYgdmFsaWQsIGZhbHNlIG90aGVyd2lzZS5cblx0ICovXG5cdHZhbGlkYXRlRG5zRmllbGRzKCkge1xuXHRcdGlmIChNb2R1bGVHZXRTc2wuJGNoYWxsZW5nZVR5cGUuZHJvcGRvd24oJ2dldCB2YWx1ZScpICE9PSAnZG5zJykge1xuXHRcdFx0cmV0dXJuIHRydWU7XG5cdFx0fVxuXHRcdGlmICghTW9kdWxlR2V0U3NsLiRkbnNQcm92aWRlci5kcm9wZG93bignZ2V0IHZhbHVlJykpIHtcblx0XHRcdFVzZXJNZXNzYWdlLnNob3dFcnJvcihnbG9iYWxUcmFuc2xhdGUubW9kdWxlX2dldHNzbF9EbnNQcm92aWRlckVtcHR5KTtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdFx0bGV0IGhhc0VtcHR5ID0gZmFsc2U7XG5cdFx0JCgnLmRucy1jcmVkLWlucHV0JykuZWFjaChmdW5jdGlvbiAoKSB7XG5cdFx0XHRjb25zdCAkZmllbGQgPSAkKHRoaXMpLmNsb3Nlc3QoJy5maWVsZCcpO1xuXHRcdFx0Y29uc3QgaXNNYXNrZWQgPSAkKHRoaXMpLmlzKCdbZGF0YS1tYXNrZWRdJyk7XG5cdFx0XHRpZiAoIWlzTWFza2VkICYmICEkKHRoaXMpLnZhbCgpLnRyaW0oKSkge1xuXHRcdFx0XHQkZmllbGQuYWRkQ2xhc3MoJ2Vycm9yJyk7XG5cdFx0XHRcdGhhc0VtcHR5ID0gdHJ1ZTtcblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdCRmaWVsZC5yZW1vdmVDbGFzcygnZXJyb3InKTtcblx0XHRcdH1cblx0XHR9KTtcblx0XHRpZiAoaGFzRW1wdHkpIHtcblx0XHRcdFVzZXJNZXNzYWdlLnNob3dFcnJvcihnbG9iYWxUcmFuc2xhdGUubW9kdWxlX2dldHNzbF9EbnNDcmVkZW50aWFsc0VtcHR5KTtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdFx0cmV0dXJuIHRydWU7XG5cdH0sXG5cblx0LyoqXG5cdCAqIENhbGxiYWNrIGJlZm9yZSBzZW5kaW5nIHRoZSBmb3JtLlxuXHQgKiBAcGFyYW0ge09iamVjdH0gc2V0dGluZ3MgLSBBamF4IHJlcXVlc3Qgc2V0dGluZ3MuXG5cdCAqIEByZXR1cm5zIHtPYmplY3R9IFRoZSBtb2RpZmllZCBBamF4IHJlcXVlc3Qgc2V0dGluZ3MuXG5cdCAqL1xuXHRjYkJlZm9yZVNlbmRGb3JtKHNldHRpbmdzKSB7XG5cdFx0aWYgKCFNb2R1bGVHZXRTc2wudmFsaWRhdGVEbnNGaWVsZHMoKSkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0XHRjb25zdCByZXN1bHQgPSBzZXR0aW5ncztcblx0XHQvLyBDb2xsZWN0IEROUyBjcmVkZW50aWFscyBpbnRvIGhpZGRlbiBmaWVsZCBiZWZvcmUgZm9ybSBzdWJtaXNzaW9uXG5cdFx0TW9kdWxlR2V0U3NsLmNvbGxlY3REbnNDcmVkZW50aWFscygpO1xuXHRcdHJlc3VsdC5kYXRhID0gTW9kdWxlR2V0U3NsLiRmb3JtT2JqLmZvcm0oJ2dldCB2YWx1ZXMnKTtcblx0XHRyZXR1cm4gcmVzdWx0O1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDYWxsYmFjayBmdW5jdGlvbiBhZnRlciBzZW5kaW5nIHRoZSBmb3JtLlxuXHQgKi9cblx0Y2JBZnRlclNlbmRGb3JtKHJlc3BvbnNlKSB7XG5cdFx0aWYgKEZvcm0uY2hlY2tTdWNjZXNzKHJlc3BvbnNlKSl7XG5cdFx0XHRNb2R1bGVHZXRTc2wuZ2V0U3NsKCk7XG5cdFx0fVxuXHR9LFxuXG5cblx0LyoqXG5cdCAqIEluaXRpYWxpemVzIHRoZSBmb3JtIHZhbGlkYXRpb24gYW5kIHN1Ym1pc3Npb24gbG9naWMuXG5cdCAqL1xuXHRpbml0aWFsaXplRm9ybSgpIHtcblx0XHRGb3JtLiRmb3JtT2JqID0gTW9kdWxlR2V0U3NsLiRmb3JtT2JqO1xuXHRcdEZvcm0udXJsID0gYCR7Z2xvYmFsUm9vdFVybH0ke2lkVXJsfS8ke2lkVXJsfS9zYXZlYDtcblx0XHRGb3JtLnZhbGlkYXRlUnVsZXMgPSBNb2R1bGVHZXRTc2wudmFsaWRhdGVSdWxlcztcblx0XHRGb3JtLmVuYWJsZURpcnJpdHkgPSBmYWxzZTtcblx0XHRGb3JtLmNiQWZ0ZXJTZW5kRm9ybSA9IE1vZHVsZUdldFNzbC5jYkFmdGVyU2VuZEZvcm07XG5cdFx0Rm9ybS5jYkJlZm9yZVNlbmRGb3JtID0gTW9kdWxlR2V0U3NsLmNiQmVmb3JlU2VuZEZvcm07XG5cdFx0Rm9ybS5pbml0aWFsaXplKCk7XG5cdH0sXG59O1xuXG4vLyBJbml0aWFsaXplIHRoZSBNb2R1bGVHZXRTc2wgY2xhc3Mgd2hlbiB0aGUgZG9jdW1lbnQgaXMgcmVhZHlcbiQoZG9jdW1lbnQpLnJlYWR5KCgpID0+IHtcblx0TW9kdWxlR2V0U3NsLmluaXRpYWxpemUoKTtcbn0pO1xuIl0sIm1hcHBpbmdzIjoiQUFBQTs7QUFFQTtBQUNBLE1BQU0sS0FBSyxHQUFPLGdCQUFnQixDQUFDLENBQWM7QUFDakQsTUFBTSxNQUFNLEdBQU0scUJBQXFCLENBQUMsQ0FBUztBQUNqRCxNQUFNLFNBQVMsR0FBRyxjQUFjLENBQUMsQ0FBZ0I7O0FBRWpEO0FBQ0EsTUFBTSxZQUFZLEdBQUc7RUFDcEI7RUFDQSxRQUFRLEVBQUUsQ0FBQyxDQUFDLEdBQUcsR0FBRyxNQUFNLENBQUM7RUFDekIsV0FBVyxFQUFFLENBQUMsQ0FBQyxHQUFHLEdBQUcsTUFBTSxHQUFHLGVBQWUsQ0FBQztFQUM5QyxpQkFBaUIsRUFBRSxDQUFDLENBQUMsR0FBRyxHQUFHLE1BQU0sR0FBRyxjQUFjLENBQUM7RUFDbkQsYUFBYSxFQUFFLENBQUMsQ0FBQyx1QkFBdUIsQ0FBQztFQUN6QyxhQUFhLEVBQUUsQ0FBQyxDQUFDLGVBQWUsQ0FBQztFQUNqQyxhQUFhLEVBQUUsQ0FBQyxDQUFDLFNBQVMsQ0FBQztFQUMzQixXQUFXLEVBQUUsQ0FBQyxDQUFDLGFBQWEsQ0FBQztFQUM3QixjQUFjLEVBQUUsQ0FBQyxDQUFDLGdCQUFnQixDQUFDO0VBQ25DLFlBQVksRUFBRSxDQUFDLENBQUMsY0FBYyxDQUFDO0VBQy9CLGtCQUFrQixFQUFFLENBQUMsQ0FBQyxzQkFBc0IsQ0FBQztFQUM3QyxpQkFBaUIsRUFBRSxDQUFDLENBQUMscUJBQXFCLENBQUM7RUFD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 \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kdWxlLWdldC1zc2wtaW5kZXguanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsic3JjL21vZHVsZS1nZXQtc3NsLWluZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGdsb2JhbCBnbG9iYWxSb290VXJsLCBnbG9iYWxUcmFuc2xhdGUsIEZvcm0sIENvbmZpZywgUGJ4QXBpLCBkbnNQcm92aWRlcnNNZXRhICovXG5cbi8vIENvbnN0YW50cyByZWxhdGVkIHRvIHRoZSBmb3JtIGFuZCBtb2R1bGVcbmNvbnN0IGlkVXJsICAgICA9ICdtb2R1bGUtZ2V0LXNzbCc7ICAgICAgICAgICAgICAvLyBBUEkgZW5kcG9pbnQgZm9yIFNTTCBtb2R1bGVcbmNvbnN0IGlkRm9ybSAgICA9ICdtb2R1bGUtZ2V0LXNzbC1mb3JtJzsgICAgICAgICAvLyBGb3JtIGVsZW1lbnQgSUQgZm9yIFNTTCBtb2R1bGVcbmNvbnN0IGNsYXNzTmFtZSA9ICdNb2R1bGVHZXRTc2wnOyAgICAgICAgICAgICAgICAvLyBDbGFzcyBuYW1lIGZvciB0aGlzIG1vZHVsZVxuXG4vLyBNYWluIE1vZHVsZUdldFNzbCBjbGFzcyBkZWZpbml0aW9uXG5jb25zdCBNb2R1bGVHZXRTc2wgPSB7XG5cdC8vIENhY2hlIGNvbW1vbmx5IHVzZWQgalF1ZXJ5IG9iamVjdHNcblx0JGZvcm1PYmo6ICQoJyMnICsgaWRGb3JtKSxcblx0JGNoZWNrQm94ZXM6ICQoJyMnICsgaWRGb3JtICsgJyAudWkuY2hlY2tib3gnKSxcblx0JGRpc2FiaWxpdHlGaWVsZHM6ICQoJyMnICsgaWRGb3JtICsgJyAuZGlzYWJpbGl0eScpLFxuXHQkc3RhdHVzVG9nZ2xlOiAkKCcjbW9kdWxlLXN0YXR1cy10b2dnbGUnKSxcblx0JHN1Ym1pdEJ1dHRvbjogJCgnI3N1Ym1pdGJ1dHRvbicpLFxuXHQkbW9kdWxlU3RhdHVzOiAkKCcjc3RhdHVzJyksXG5cdCRkb21haW5OYW1lOiAkKCcjZG9tYWluTmFtZScpLFxuXHQkaW5jbHVkZUlwQWRkcmVzczogJCgnI2luY2x1ZGVJcEFkZHJlc3MnKSxcblx0JGluY2x1ZGVJcEFkZHJlc3NGaWVsZDogJCgnI2luY2x1ZGUtaXAtYWRkcmVzcy1maWVsZCcpLFxuXHQkaW5jbHVkZUlwQWRkcmVzc0NoZWNrYm94OiAkKCcjaW5jbHVkZS1pcC1hZGRyZXNzLWNoZWNrYm94JyksXG5cdCRwdWJsaWNJcEFkZHJlc3M6ICQoJyNwdWJsaWNJcEFkZHJlc3MnKSxcblx0JHB1YmxpY0lwQWRkcmVzc1NldHRpbmdzOiAkKCcjcHVibGljLWlwLWFkZHJlc3Mtc2V0dGluZ3MnKSxcblx0JGF1dG9VcGRhdGU6ICQoJyNhdXRvVXBkYXRlJyksXG5cdCRhdXRvVXBkYXRlQ2hlY2tib3g6ICQoJyNhdXRvLXVwZGF0ZS1jaGVja2JveCcpLFxuXHQkY2hhbGxlbmdlVHlwZTogJCgnI2NoYWxsZW5nZVR5cGUnKSxcblx0JGRuc1Byb3ZpZGVyOiAkKCcjZG5zUHJvdmlkZXInKSxcblx0JGh0dHBDaGFsbGVuZ2VJbmZvOiAkKCcjaHR0cC1jaGFsbGVuZ2UtaW5mbycpLFxuXHQkZG5zU2V0dGluZ3NCbG9jazogJCgnI2Rucy1zZXR0aW5ncy1ibG9jaycpLFxuXHQkZG5zQ3JlZGVudGlhbHNGaWVsZHM6ICQoJyNkbnMtY3JlZGVudGlhbHMtZmllbGRzJyksXG5cdCRkbnNDcmVkZW50aWFsc0lucHV0OiAkKCdpbnB1dFtuYW1lPVwiZG5zQ3JlZGVudGlhbHNcIl0nKSxcblx0JGlwQWRkcmVzc1dhcm5pbmc6ICQoJyNpcC1hZGRyZXNzLWNlcnRpZmljYXRlLXdhcm5pbmcnKSxcblxuXHQvLyBWYWxpZGF0aW9uIHJ1bGVzIGZvciB0aGUgZm9ybVxuXHR2YWxpZGF0ZVJ1bGVzOiB7XG5cdFx0ZG9tYWluTmFtZToge1xuXHRcdFx0aWRlbnRpZmllcjogJ2RvbWFpbk5hbWUnLFxuXHRcdFx0cnVsZXM6IFtcblx0XHRcdFx0e1xuXHRcdFx0XHRcdHR5cGU6ICdlbXB0eScsXG5cdFx0XHRcdFx0cHJvbXB0OiBnbG9iYWxUcmFuc2xhdGUubW9kdWxlX2dldHNzbF9Eb21haW5OYW1lRW1wdHksXG5cdFx0XHRcdH0sXG5cdFx0XHRdLFxuXHRcdH0sXG5cdH0sXG5cblx0LyoqXG5cdCAqIEluaXRpYWxpemUgdGhlIG1vZHVsZSwgYmluZCBldmVudCBsaXN0ZW5lcnMsIGFuZCBzZXR1cCB0aGUgZm9ybS5cblx0ICovXG5cdGluaXRpYWxpemUoKSB7XG5cdFx0Ly8gSW5pdGlhbGl6ZSBTZW1hbnRpYyBVSSBjaGVja2JveGVzXG5cdFx0dGhpcy4kY2hlY2tCb3hlcy5jaGVja2JveCgpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBkcm9wZG93bnNcblx0XHR0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKHtcblx0XHRcdG9uQ2hhbmdlOiBNb2R1bGVHZXRTc2wub25DaGFuZ2VDaGFsbGVuZ2VUeXBlLFxuXHRcdH0pO1xuXHRcdHRoaXMuJGRuc1Byb3ZpZGVyLmRyb3Bkb3duKHtcblx0XHRcdGZ1bGxUZXh0U2VhcmNoOiB0cnVlLFxuXHRcdFx0b25DaGFuZ2U6IE1vZHVsZUdldFNzbC5vbkNoYW5nZURuc1Byb3ZpZGVyLFxuXHRcdH0pO1xuXG5cdFx0Ly8gQ2hlY2sgYW5kIHNldCBtb2R1bGUgc3RhdHVzIG9uIGxvYWQgYW5kIHdoZW4gdGhlIHN0YXR1cyBjaGFuZ2VzXG5cdFx0dGhpcy5jaGVja1N0YXR1c1RvZ2dsZSgpO1xuXHRcdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdNb2R1bGVTdGF0dXNDaGFuZ2VkJywgdGhpcy5jaGVja1N0YXR1c1RvZ2dsZSk7XG5cblx0XHQvLyBJbml0aWFsaXplIGZvcm0gd2l0aCB2YWxpZGF0aW9uIGFuZCBzdWJtaXQgaGFuZGxlcnNcblx0XHR0aGlzLmluaXRpYWxpemVGb3JtKCk7XG5cdFx0dGhpcy5iaW5kSXBBZGRyZXNzV2FybmluZygpO1xuXHRcdHRoaXMuYmluZENlcnRpZmljYXRlSWRlbnRpZmllckNvbnRyb2xzKCk7XG5cblx0XHRtb2R1bGVHZXRTU0xTdGF0dXNMb29wV29ya2VyLiRyZXN1bHRCbG9jay5oaWRlKCk7XG5cblx0XHQvLyBSZXN0b3JlIHNhdmVkIHN0YXRlXG5cdFx0Y29uc3QgY3VycmVudENoYWxsZW5nZSA9IHRoaXMuJGNoYWxsZW5nZVR5cGUuZHJvcGRvd24oJ2dldCB2YWx1ZScpIHx8ICdodHRwJztcblx0XHR0aGlzLm9uQ2hhbmdlQ2hhbGxlbmdlVHlwZShjdXJyZW50Q2hhbGxlbmdlKTtcblx0XHR0aGlzLnJlc3RvcmVTYXZlZENyZWRlbnRpYWxzKCk7XG5cdH0sXG5cblx0LyoqXG5cdCAqIENoZWNrIHdoZXRoZXIgYSB2YWx1ZSBpcyBhIHZhbGlkIElQdjQgb3IgSVB2NiBhZGRyZXNzLlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWVcblx0ICogQHJldHVybnMge2Jvb2xlYW59XG5cdCAqL1xuXHRpc0lwQWRkcmVzcyh2YWx1ZSkge1xuXHRcdGNvbnN0IGFkZHJlc3MgPSBTdHJpbmcodmFsdWUgfHwgJycpLnRyaW0oKTtcblx0XHRjb25zdCBpcHY0UGFydHMgPSBhZGRyZXNzLnNwbGl0KCcuJyk7XG5cdFx0aWYgKGlwdjRQYXJ0cy5sZW5ndGggPT09IDQpIHtcblx0XHRcdHJldHVybiBpcHY0UGFydHMuZXZlcnkocGFydCA9PiAvXlxcZHsxLDN9JC8udGVzdChwYXJ0KVxuXHRcdFx0XHQmJiBOdW1iZXIocGFydCkgPD0gMjU1XG5cdFx0XHRcdCYmIChwYXJ0ID09PSAnMCcgfHwgcGFydFswXSAhPT0gJzAnKSk7XG5cdFx0fVxuXG5cdFx0Y29uc3QgaGFzT3BlbmluZ0JyYWNrZXQgPSBhZGRyZXNzLnN0YXJ0c1dpdGgoJ1snKTtcblx0XHRjb25zdCBoYXNDbG9zaW5nQnJhY2tldCA9IGFkZHJlc3MuZW5kc1dpdGgoJ10nKTtcblx0XHRpZiAoaGFzT3BlbmluZ0JyYWNrZXQgIT09IGhhc0Nsb3NpbmdCcmFja2V0KSByZXR1cm4gZmFsc2U7XG5cdFx0Y29uc3QgaXB2NiA9IGhhc09wZW5pbmdCcmFja2V0ID8gYWRkcmVzcy5zbGljZSgxLCAtMSkgOiBhZGRyZXNzO1xuXHRcdGlmICghaXB2Ni5pbmNsdWRlcygnOicpIHx8IC9cXHMvLnRlc3QoaXB2NikpIHJldHVybiBmYWxzZTtcblx0XHR0cnkge1xuXHRcdFx0bmV3IFVSTChgaHR0cDovL1ske2lwdjZ9XS9gKTtcblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdH0sXG5cblx0LyoqIFJldHVybiB0cnVlIG9ubHkgZm9yIGEgcHVibGljbHkgcm91dGFibGUgSVB2NCBvciBJUHY2IGFkZHJlc3MuICovXG5cdGlzUHVibGljSXBBZGRyZXNzKHZhbHVlKSB7XG5cdFx0aWYgKCF0aGlzLmlzSXBBZGRyZXNzKHZhbHVlKSkgcmV0dXJuIGZhbHNlO1xuXHRcdGNvbnN0IGFkZHJlc3MgPSBTdHJpbmcodmFsdWUgfHwgJycpLnRyaW0oKS5yZXBsYWNlKC9eXFxbfFxcXSQvZywgJycpLnRvTG93ZXJDYXNlKCk7XG5cdFx0Y29uc3QgaXB2NFBhcnRzID0gYWRkcmVzcy5zcGxpdCgnLicpLm1hcChOdW1iZXIpO1xuXHRcdGlmIChpcHY0UGFydHMubGVuZ3RoID09PSA0KSB7XG5cdFx0XHRjb25zdCBbYSwgYiwgY10gPSBpcHY0UGFydHM7XG5cdFx0XHRyZXR1cm4gIShhID09PSAwIHx8IGEgPT09IDEwIHx8IGEgPT09IDEyNyB8fCBhID49IDIyNFxuXHRcdFx0XHR8fCAoYSA9PT0gMTAwICYmIGIgPj0gNjQgJiYgYiA8PSAxMjcpXG5cdFx0XHRcdHx8IChhID09PSAxNjkgJiYgYiA9PT0gMjU0KVxuXHRcdFx0XHR8fCAoYSA9PT0gMTcyICYmIGIgPj0gMTYgJiYgYiA8PSAzMSlcblx0XHRcdFx0fHwgKGEgPT09IDE5MiAmJiBiID09PSAwICYmIChjID09PSAwIHx8IGMgPT09IDIpKVxuXHRcdFx0XHR8fCAoYSA9PT0gMTkyICYmIGIgPT09IDE2OClcblx0XHRcdFx0fHwgKGEgPT09IDE5OCAmJiAoYiA9PT0gMTggfHwgYiA9PT0gMTkpKVxuXHRcdFx0XHR8fCAoYSA9PT0gMTk4ICYmIGIgPT09IDUxICYmIGMgPT09IDEwMClcblx0XHRcdFx0fHwgKGEgPT09IDIwMyAmJiBiID09PSAwICYmIGMgPT09IDExMykpO1xuXHRcdH1cblx0XHRyZXR1cm4gIShhZGRyZXNzID09PSAnOjonIHx8IGFkZHJlc3MgPT09ICc6OjEnXG5cdFx0XHR8fCBhZGRyZXNzLnN0YXJ0c1dpdGgoJ2ZjJykgfHwgYWRkcmVzcy5zdGFydHNXaXRoKCdmZCcpXG5cdFx0XHR8fCAvXmZlWzg5YWJdLy50ZXN0KGFkZHJlc3MpIHx8IGFkZHJlc3Muc3RhcnRzV2l0aCgnMjAwMTpkYjg6JykpO1xuXHR9LFxuXG5cdC8qKiBVcGRhdGUgd2FybmluZyB2aXNpYmlsaXR5IHdoZW4gdGhlIGNvbmZpZ3VyZWQgYWRkcmVzcyBjaGFuZ2VzLiAqL1xuXHR1cGRhdGVJcEFkZHJlc3NXYXJuaW5nKCkge1xuXHRcdGlmICh0aGlzLmlzSXBBZGRyZXNzKHRoaXMuJGRvbWFpbk5hbWUudmFsKCkpKSB7XG5cdFx0XHR0aGlzLiRpcEFkZHJlc3NXYXJuaW5nLnNob3coKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy4kaXBBZGRyZXNzV2FybmluZy5oaWRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKiBCaW5kIHJlYWN0aXZlIElQIHdhcm5pbmcgYmVoYXZpb3IgYW5kIGluaXRpYWxpemUgaXRzIHN0YXRlLiAqL1xuXHRiaW5kSXBBZGRyZXNzV2FybmluZygpIHtcblx0XHR0aGlzLiRkb21haW5OYW1lLm9uKCdpbnB1dCcsICgpID0+IHRoaXMudXBkYXRlSXBBZGRyZXNzV2FybmluZygpKTtcblx0XHR0aGlzLnVwZGF0ZUlwQWRkcmVzc1dhcm5pbmcoKTtcblx0fSxcblxuXHQvKiogQXBwbHkgdmlzaWJpbGl0eSBhbmQgY29tcGF0aWJpbGl0eSBydWxlcyBmb3IgZG9tYWluL0lQIGNlcnRpZmljYXRlIGlkZW50aWZpZXJzLiAqL1xuXHR1cGRhdGVDZXJ0aWZpY2F0ZUlkZW50aWZpZXJDb250cm9scygpIHtcblx0XHRjb25zdCBwcmltYXJ5ID0gU3RyaW5nKHRoaXMuJGRvbWFpbk5hbWUudmFsKCkgfHwgJycpLnRyaW0oKTtcblx0XHRjb25zdCBwcmltYXJ5SXNJcCA9IHRoaXMuaXNJcEFkZHJlc3MocHJpbWFyeSk7XG5cdFx0Y29uc3QgY2FuSW5jbHVkZUlwID0gcHJpbWFyeSAhPT0gJycgJiYgIXByaW1hcnlJc0lwO1xuXHRcdGlmIChwcmltYXJ5SXNJcCAmJiB0aGlzLiRpbmNsdWRlSXBBZGRyZXNzLmlzKCc6Y2hlY2tlZCcpKSB7XG5cdFx0XHR0aGlzLiRpbmNsdWRlSXBBZGRyZXNzLnByb3AoJ2NoZWNrZWQnLCBmYWxzZSk7XG5cdFx0XHR0aGlzLiRpbmNsdWRlSXBBZGRyZXNzQ2hlY2tib3guY2hlY2tib3goJ3VuY2hlY2snKTtcblx0XHR9XG5cdFx0Y29uc3QgaW5jbHVkZUlwID0gY2FuSW5jbHVkZUlwICYmIHRoaXMuJGluY2x1ZGVJcEFkZHJlc3MuaXMoJzpjaGVja2VkJyk7XG5cblx0XHR0aGlzLiRpbmNsdWRlSXBBZGRyZXNzRmllbGQudG9nZ2xlKGNhbkluY2x1ZGVJcCk7XG5cdFx0dGhpcy4kcHVibGljSXBBZGRyZXNzU2V0dGluZ3MudG9nZ2xlKGluY2x1ZGVJcCk7XG5cblx0XHRpZiAocHJpbWFyeUlzSXAgfHwgaW5jbHVkZUlwKSB7XG5cdFx0XHR0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdzZXQgc2VsZWN0ZWQnLCAnaHR0cCcpLmRyb3Bkb3duKCdzZXQgZGlzYWJsZWQnKTtcblx0XHRcdHRoaXMuJGF1dG9VcGRhdGUucHJvcCgnY2hlY2tlZCcsIHRydWUpLnByb3AoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdFx0XHR0aGlzLiRhdXRvVXBkYXRlQ2hlY2tib3guY2hlY2tib3goJ2NoZWNrJykuY2hlY2tib3goJ3NldCBkaXNhYmxlZCcpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdzZXQgZW5hYmxlZCcpO1xuXHRcdFx0dGhpcy4kYXV0b1VwZGF0ZS5wcm9wKCdkaXNhYmxlZCcsIGZhbHNlKTtcblx0XHRcdHRoaXMuJGF1dG9VcGRhdGVDaGVja2JveC5jaGVja2JveCgnc2V0IGVuYWJsZWQnKTtcblx0XHR9XG5cdH0sXG5cblx0LyoqIEJpbmQgY2hhbmdlcyBhZmZlY3RpbmcgdGhlIHJlcXVlc3RlZCBjZXJ0aWZpY2F0ZSBpZGVudGlmaWVyIGxpc3QuICovXG5cdGJpbmRDZXJ0aWZpY2F0ZUlkZW50aWZpZXJDb250cm9scygpIHtcblx0XHR0aGlzLiRkb21haW5OYW1lLm9uKCdpbnB1dCcsICgpID0+IHRoaXMudXBkYXRlQ2VydGlmaWNhdGVJZGVudGlmaWVyQ29udHJvbHMoKSk7XG5cdFx0dGhpcy4kaW5jbHVkZUlwQWRkcmVzc0NoZWNrYm94LmNoZWNrYm94KHtcblx0XHRcdG9uQ2hhbmdlOiAoKSA9PiB0aGlzLnVwZGF0ZUNlcnRpZmljYXRlSWRlbnRpZmllckNvbnRyb2xzKCksXG5cdFx0fSk7XG5cdFx0dGhpcy51cGRhdGVDZXJ0aWZpY2F0ZUlkZW50aWZpZXJDb250cm9scygpO1xuXHR9LFxuXG5cdC8qKiBWYWxpZGF0ZSB0aGUgb3B0aW9uYWwgYWRkaXRpb25hbCBwdWJsaWMgSVAgYWRkcmVzcy4gKi9cblx0dmFsaWRhdGVQdWJsaWNJcEFkZHJlc3MoKSB7XG5cdFx0Y29uc3QgcHJpbWFyeUlzSXAgPSB0aGlzLmlzSXBBZGRyZXNzKHRoaXMuJGRvbWFpbk5hbWUudmFsKCkpO1xuXHRcdGlmIChwcmltYXJ5SXNJcCB8fCAhdGhpcy4kaW5jbHVkZUlwQWRkcmVzcy5pcygnOmNoZWNrZWQnKSkgcmV0dXJuIHRydWU7XG5cdFx0aWYgKHRoaXMuaXNQdWJsaWNJcEFkZHJlc3ModGhpcy4kcHVibGljSXBBZGRyZXNzLnZhbCgpKSkgcmV0dXJuIHRydWU7XG5cdFx0VXNlck1lc3NhZ2Uuc2hvd0Vycm9yKGdsb2JhbFRyYW5zbGF0ZS5tb2R1bGVfZ2V0c3NsX1B1YmxpY0lwQWRkcmVzc0ludmFsaWQpO1xuXHRcdHRoaXMuJHB1YmxpY0lwQWRkcmVzcy5jbG9zZXN0KCcuZmllbGQnKS5hZGRDbGFzcygnZXJyb3InKTtcblx0XHRyZXR1cm4gZmFsc2U7XG5cdH0sXG5cblx0LyoqXG5cdCAqIEhhbmRsZSBjaGFsbGVuZ2UgdHlwZSBjaGFuZ2U6IHNob3cvaGlkZSByZWxldmFudCBzZWN0aW9ucy5cblx0ICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gJ2h0dHAnIG9yICdkbnMnXG5cdCAqL1xuXHRvbkNoYW5nZUNoYWxsZW5nZVR5cGUodmFsdWUpIHtcblx0XHRpZiAodmFsdWUgPT09ICdkbnMnKSB7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGh0dHBDaGFsbGVuZ2VJbmZvLmhpZGUoKTtcblx0XHRcdE1vZHVsZUdldFNzbC4kZG5zU2V0dGluZ3NCbG9jay5zaG93KCk7XG5cdFx0XHQvLyBUcmlnZ2VyIHByb3ZpZGVyIGNoYW5nZSB0byByZW5kZXIgY3JlZGVudGlhbCBmaWVsZHNcblx0XHRcdGNvbnN0IGN1cnJlbnRQcm92aWRlciA9IE1vZHVsZUdldFNzbC4kZG5zUHJvdmlkZXIuZHJvcGRvd24oJ2dldCB2YWx1ZScpO1xuXHRcdFx0aWYgKGN1cnJlbnRQcm92aWRlcikge1xuXHRcdFx0XHRNb2R1bGVHZXRTc2wub25DaGFuZ2VEbnNQcm92aWRlcihjdXJyZW50UHJvdmlkZXIpO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGh0dHBDaGFsbGVuZ2VJbmZvLnNob3coKTtcblx0XHRcdE1vZHVsZUdldFNzbC4kZG5zU2V0dGluZ3NCbG9jay5oaWRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKlxuXHQgKiBIYW5kbGUgRE5TIHByb3ZpZGVyIGNoYW5nZTogZHluYW1pY2FsbHkgcmVuZGVyIGNyZWRlbnRpYWwgZmllbGRzLlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWUgLSBwcm92aWRlciBJRCAoZS5nLiAnZG5zX2NmJylcblx0ICovXG5cdG9uQ2hhbmdlRG5zUHJvdmlkZXIodmFsdWUpIHtcblx0XHRjb25zdCAkY29udGFpbmVyID0gTW9kdWxlR2V0U3NsLiRkbnNDcmVkZW50aWFsc0ZpZWxkcztcblx0XHQkY29udGFpbmVyLmVtcHR5KCk7XG5cblx0XHRpZiAoIXZhbHVlIHx8IHR5cGVvZiBkbnNQcm92aWRlcnNNZXRhID09PSAndW5kZWZpbmVkJykge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIEZpbmQgcHJvdmlkZXIgbWV0YWRhdGFcblx0XHRjb25zdCBwcm92aWRlciA9IGRuc1Byb3ZpZGVyc01ldGEuZmluZChwID0+IHAuaWQgPT09IHZhbHVlKTtcblx0XHRpZiAoIXByb3ZpZGVyIHx8ICFwcm92aWRlci5maWVsZHMpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvLyBEZWNvZGUgZXhpc3Rpbmcgc2F2ZWQgY3JlZGVudGlhbHMgZm9yIHByZS1maWxsaW5nXG5cdFx0bGV0IHNhdmVkQ3JlZHMgPSB7fTtcblx0XHRjb25zdCBlbmNvZGVkVmFsID0gTW9kdWxlR2V0U3NsLiRkbnNDcmVkZW50aWFsc0lucHV0LnZhbCgpO1xuXHRcdGlmIChlbmNvZGVkVmFsKSB7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRjb25zdCBkZWNvZGVkID0gYXRvYihlbmNvZGVkVmFsKTtcblx0XHRcdFx0c2F2ZWRDcmVkcyA9IEpTT04ucGFyc2UoZGVjb2RlZCk7XG5cdFx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRcdC8vIGlnbm9yZSBkZWNvZGUgZXJyb3JzXG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gUmVuZGVyIGZpZWxkc1xuXHRcdHByb3ZpZGVyLmZpZWxkcy5mb3JFYWNoKGZpZWxkID0+IHtcblx0XHRcdGNvbnN0IGhhc1NhdmVkID0gQm9vbGVhbihzYXZlZENyZWRzW2ZpZWxkLnZhcl0pO1xuXHRcdFx0Y29uc3QgZGlzcGxheVZhbHVlID0gaGFzU2F2ZWQgPyAn4oCi4oCi4oCi4oCi4oCi4oCi4oCi4oCiJyA6ICcnO1xuXHRcdFx0Y29uc3QgbWFza2VkQXR0ciA9IGhhc1NhdmVkID8gJ2RhdGEtbWFza2VkPVwidHJ1ZVwiJyA6ICcnO1xuXHRcdFx0Y29uc3QgaHRtbCA9IGBcblx0XHRcdFx0PGRpdiBjbGFzcz1cImZpZWxkXCI+XG5cdFx0XHRcdFx0PGxhYmVsPiR7ZmllbGQubGFiZWx9PC9sYWJlbD5cblx0XHRcdFx0XHQ8aW5wdXQgdHlwZT1cInBhc3N3b3JkXCJcblx0XHRcdFx0XHRcdCAgIGNsYXNzPVwiZG5zLWNyZWQtaW5wdXRcIlxuXHRcdFx0XHRcdFx0ICAgZGF0YS12YXI9XCIke2ZpZWxkLnZhcn1cIlxuXHRcdFx0XHRcdFx0ICAgJHttYXNrZWRBdHRyfVxuXHRcdFx0XHRcdFx0ICAgdmFsdWU9XCIke01vZHVsZUdldFNzbC5lc2NhcGVIdG1sKGRpc3BsYXlWYWx1ZSl9XCJcblx0XHRcdFx0XHRcdCAgIHBsYWNlaG9sZGVyPVwiJHtmaWVsZC5sYWJlbH1cIj5cblx0XHRcdFx0PC9kaXY+YDtcblx0XHRcdCRjb250YWluZXIuYXBwZW5kKGh0bWwpO1xuXHRcdH0pO1xuXHRcdC8vIE9uIGZvY3VzOiBjbGVhciBtYXNrIHNvIHVzZXIgY2FuIGVudGVyIG5ldyB2YWx1ZVxuXHRcdCRjb250YWluZXIub24oJ2ZvY3VzJywgJy5kbnMtY3JlZC1pbnB1dFtkYXRhLW1hc2tlZF0nLCBmdW5jdGlvbiAoKSB7XG5cdFx0XHQkKHRoaXMpLnZhbCgnJykucmVtb3ZlQXR0cignZGF0YS1tYXNrZWQnKTtcblx0XHR9KTtcblx0XHQvLyBDbGVhciBlcnJvciBoaWdobGlnaHQgd2hlbiB1c2VyIHN0YXJ0cyB0eXBpbmdcblx0XHQkY29udGFpbmVyLm9uKCdpbnB1dCcsICcuZG5zLWNyZWQtaW5wdXQnLCBmdW5jdGlvbiAoKSB7XG5cdFx0XHQkKHRoaXMpLmNsb3Nlc3QoJy5maWVsZCcpLnJlbW92ZUNsYXNzKCdlcnJvcicpO1xuXHRcdH0pO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDb2xsZWN0IEROUyBjcmVkZW50aWFsIGZpZWxkIHZhbHVlcyBpbnRvIGJhc2U2NCBKU09OIGFuZCB3cml0ZSB0byBoaWRkZW4gaW5wdXQuXG5cdCAqIE1hc2tlZCBmaWVsZHMgKHVuY2hhbmdlZCkgYXJlIHJlc3RvcmVkIGZyb20gdGhlIHByZXZpb3VzbHkgc2F2ZWQgY3JlZGVudGlhbHMuXG5cdCAqL1xuXHRjb2xsZWN0RG5zQ3JlZGVudGlhbHMoKSB7XG5cdFx0Y29uc3QgY2hhbGxlbmdlVHlwZSA9IE1vZHVsZUdldFNzbC4kY2hhbGxlbmdlVHlwZS5kcm9wZG93bignZ2V0IHZhbHVlJyk7XG5cdFx0aWYgKGNoYWxsZW5nZVR5cGUgIT09ICdkbnMnKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdC8vIERlY29kZSBjdXJyZW50bHkgc3RvcmVkIGNyZWRlbnRpYWxzIChzb3VyY2Ugb2YgdHJ1dGggZm9yIG1hc2tlZCBmaWVsZHMpXG5cdFx0bGV0IHNhdmVkQ3JlZHMgPSB7fTtcblx0XHRjb25zdCBlbmNvZGVkVmFsID0gTW9kdWxlR2V0U3NsLiRkbnNDcmVkZW50aWFsc0lucHV0LnZhbCgpO1xuXHRcdGlmIChlbmNvZGVkVmFsKSB7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRzYXZlZENyZWRzID0gSlNPTi5wYXJzZShhdG9iKGVuY29kZWRWYWwpKTtcblx0XHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdFx0Ly8gaWdub3JlXG5cdFx0XHR9XG5cdFx0fVxuXHRcdGNvbnN0IGNyZWRzID0ge307XG5cdFx0JCgnLmRucy1jcmVkLWlucHV0JykuZWFjaChmdW5jdGlvbiAoKSB7XG5cdFx0XHRjb25zdCB2YXJOYW1lID0gJCh0aGlzKS5kYXRhKCd2YXInKTtcblx0XHRcdGlmICghdmFyTmFtZSkgcmV0dXJuO1xuXHRcdFx0aWYgKCQodGhpcykuaXMoJ1tkYXRhLW1hc2tlZF0nKSkge1xuXHRcdFx0XHQvLyBVc2VyIGRpZG4ndCBjaGFuZ2UgdGhpcyBmaWVsZCDigJQga2VlcCBzdG9yZWQgdmFsdWVcblx0XHRcdFx0aWYgKHNhdmVkQ3JlZHNbdmFyTmFtZV0pIHtcblx0XHRcdFx0XHRjcmVkc1t2YXJOYW1lXSA9IHNhdmVkQ3JlZHNbdmFyTmFtZV07XG5cdFx0XHRcdH1cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGNvbnN0IHZhbCA9ICQodGhpcykudmFsKCk7XG5cdFx0XHRcdGlmICh2YWwpIHtcblx0XHRcdFx0XHRjcmVkc1t2YXJOYW1lXSA9IHZhbDtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0pO1xuXHRcdE1vZHVsZUdldFNzbC4kZG5zQ3JlZGVudGlhbHNJbnB1dC52YWwoYnRvYShKU09OLnN0cmluZ2lmeShjcmVkcykpKTtcblx0fSxcblxuXHQvKipcblx0ICogUmVzdG9yZSBzYXZlZCBjcmVkZW50aWFscyBpbnRvIHRoZSBETlMgcHJvdmlkZXIgZmllbGRzIG9uIHBhZ2UgbG9hZC5cblx0ICovXG5cdHJlc3RvcmVTYXZlZENyZWRlbnRpYWxzKCkge1xuXHRcdGNvbnN0IGN1cnJlbnRQcm92aWRlciA9IE1vZHVsZUdldFNzbC4kZG5zUHJvdmlkZXIuZHJvcGRvd24oJ2dldCB2YWx1ZScpO1xuXHRcdGlmIChjdXJyZW50UHJvdmlkZXIpIHtcblx0XHRcdE1vZHVsZUdldFNzbC5vbkNoYW5nZURuc1Byb3ZpZGVyKGN1cnJlbnRQcm92aWRlcik7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKlxuXHQgKiBFc2NhcGUgSFRNTCBzcGVjaWFsIGNoYXJhY3RlcnMgZm9yIHNhZmUgaW5zZXJ0aW9uIGludG8gYXR0cmlidXRlcy5cblx0ICogQHBhcmFtIHtzdHJpbmd9IHRleHRcblx0ICogQHJldHVybnMge3N0cmluZ31cblx0ICovXG5cdGVzY2FwZUh0bWwodGV4dCkge1xuXHRcdGNvbnN0IG1hcCA9IHsgJyYnOiAnJmFtcDsnLCAnPCc6ICcmbHQ7JywgJz4nOiAnJmd0OycsICdcIic6ICcmcXVvdDsnLCBcIidcIjogJyYjMDM5OycgfTtcblx0XHRyZXR1cm4gU3RyaW5nKHRleHQpLnJlcGxhY2UoL1smPD5cIiddL2csIG0gPT4gbWFwW21dKTtcblx0fSxcblxuXHQvKipcblx0ICogUmVxdWVzdCBhbiBTU0wgY2VydGlmaWNhdGUgYnkgY2FsbGluZyB0aGUgc2VydmVyLXNpZGUgQVBJLlxuXHQgKi9cblx0Z2V0U3NsKCkge1xuXHRcdCQuYXBpKHtcblx0XHRcdHVybDogYCR7Q29uZmlnLnBieFVybH0vcGJ4Y29yZS9hcGkvbW9kdWxlcy8ke2NsYXNzTmFtZX0vZ2V0LWNlcnRgLFxuXHRcdFx0b246ICdub3cnLFxuXHRcdFx0bWV0aG9kOiAnUE9TVCcsXG5cdFx0XHRiZWZvcmVYSFIoeGhyKSB7XG5cdFx0XHRcdHhoci5zZXRSZXF1ZXN0SGVhZGVyICgnWC1Bc3luYy1SZXNwb25zZS1DaGFubmVsLUlkJywgbW9kdWxlR2V0U1NMU3RhdHVzTG9vcFdvcmtlci5jaGFubmVsSWQpO1xuXHRcdFx0XHR4aHIuc2V0UmVxdWVzdEhlYWRlciAoJ1gtUHJvY2Vzc29yLVRpbWVvdXQnLCAnMTIwJyk7XG5cdFx0XHRcdHJldHVybiB4aHI7XG5cdFx0XHR9LFxuXHRcdFx0YmVmb3JlU2VuZChzZXR0aW5ncykge1xuXHRcdFx0XHRNb2R1bGVHZXRTc2wuJHN1Ym1pdEJ1dHRvbi5hZGRDbGFzcygnbG9hZGluZyBkaXNhYmxlZCcpO1xuXHRcdFx0XHRtb2R1bGVHZXRTU0xTdGF0dXNMb29wV29ya2VyLiRyZXN1bHRCbG9jay5zaG93KCk7XG5cdFx0XHRcdG1vZHVsZUdldFNTTFN0YXR1c0xvb3BXb3JrZXIuZWRpdG9yLmdldFNlc3Npb24oKS5zZXRWYWx1ZShcblx0XHRcdFx0Z2xvYmFsVHJhbnNsYXRlLm1vZHVsZV9nZXRzc2xfR2V0U1NMUHJvY2Vzc2luZyArICdcXG4nXG5cdFx0XHQpO1xuXHRcdFx0XHRyZXR1cm4gc2V0dGluZ3M7XG5cdFx0XHR9LFxuXHRcdFx0c3VjY2Vzc1Rlc3Q6IFBieEFwaS5zdWNjZXNzVGVzdCxcblx0XHRcdG9uU3VjY2VzczogZnVuY3Rpb24gKHJlc3BvbnNlKSB7XG5cdFx0XHRcdE1vZHVsZUdldFNzbC4kc3VibWl0QnV0dG9uLnJlbW92ZUNsYXNzKCdsb2FkaW5nIGRpc2FibGVkJyk7XG5cdFx0XHR9LFxuXHRcdFx0b25GYWlsdXJlOiBmdW5jdGlvbihyZXNwb25zZSkge1xuXHRcdFx0XHRNb2R1bGVHZXRTc2wuJHN1Ym1pdEJ1dHRvbi5yZW1vdmVDbGFzcygnbG9hZGluZyBkaXNhYmxlZCcpO1xuXHRcdFx0XHRVc2VyTWVzc2FnZS5zaG93TXVsdGlTdHJpbmcocmVzcG9uc2UubWVzc2FnZSk7XG5cdFx0XHR9LFxuXHRcdH0pXG5cdH0sXG5cblx0LyoqXG5cdCAqIFRvZ2dsZXMgdGhlIGZvcm0gZmllbGRzIGFuZCBzdGF0dXMgdmlzaWJpbGl0eSBiYXNlZCBvbiB0aGUgbW9kdWxlJ3Mgc3RhdHVzLlxuXHQgKi9cblx0Y2hlY2tTdGF0dXNUb2dnbGUoKSB7XG5cdFx0aWYgKE1vZHVsZUdldFNzbC4kc3RhdHVzVG9nZ2xlLmNoZWNrYm94KCdpcyBjaGVja2VkJykpIHtcblx0XHRcdE1vZHVsZUdldFNzbC4kZGlzYWJpbGl0eUZpZWxkcy5yZW1vdmVDbGFzcygnZGlzYWJsZWQnKTtcblx0XHRcdE1vZHVsZUdldFNzbC4kbW9kdWxlU3RhdHVzLnNob3coKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRkaXNhYmlsaXR5RmllbGRzLmFkZENsYXNzKCdkaXNhYmxlZCcpO1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRtb2R1bGVTdGF0dXMuaGlkZSgpO1xuXHRcdH1cblx0fSxcblxuXHQvKipcblx0ICogVmFsaWRhdGUgRE5TIHByb3ZpZGVyIGFuZCBjcmVkZW50aWFscyB3aGVuIEROUy0wMSBpcyBzZWxlY3RlZC5cblx0ICogUmV0dXJucyB0cnVlIGlmIHZhbGlkLCBmYWxzZSBvdGhlcndpc2UuXG5cdCAqL1xuXHR2YWxpZGF0ZURuc0ZpZWxkcygpIHtcblx0XHRpZiAoTW9kdWxlR2V0U3NsLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdnZXQgdmFsdWUnKSAhPT0gJ2RucycpIHtcblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdH1cblx0XHRpZiAoIU1vZHVsZUdldFNzbC4kZG5zUHJvdmlkZXIuZHJvcGRvd24oJ2dldCB2YWx1ZScpKSB7XG5cdFx0XHRVc2VyTWVzc2FnZS5zaG93RXJyb3IoZ2xvYmFsVHJhbnNsYXRlLm1vZHVsZV9nZXRzc2xfRG5zUHJvdmlkZXJFbXB0eSk7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHRcdGxldCBoYXNFbXB0eSA9IGZhbHNlO1xuXHRcdCQoJy5kbnMtY3JlZC1pbnB1dCcpLmVhY2goZnVuY3Rpb24gKCkge1xuXHRcdFx0Y29uc3QgJGZpZWxkID0gJCh0aGlzKS5jbG9zZXN0KCcuZmllbGQnKTtcblx0XHRcdGNvbnN0IGlzTWFza2VkID0gJCh0aGlzKS5pcygnW2RhdGEtbWFza2VkXScpO1xuXHRcdFx0aWYgKCFpc01hc2tlZCAmJiAhJCh0aGlzKS52YWwoKS50cmltKCkpIHtcblx0XHRcdFx0JGZpZWxkLmFkZENsYXNzKCdlcnJvcicpO1xuXHRcdFx0XHRoYXNFbXB0eSA9IHRydWU7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHQkZmllbGQucmVtb3ZlQ2xhc3MoJ2Vycm9yJyk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdFx0aWYgKGhhc0VtcHR5KSB7XG5cdFx0XHRVc2VyTWVzc2FnZS5zaG93RXJyb3IoZ2xvYmFsVHJhbnNsYXRlLm1vZHVsZV9nZXRzc2xfRG5zQ3JlZGVudGlhbHNFbXB0eSk7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHRcdHJldHVybiB0cnVlO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDYWxsYmFjayBiZWZvcmUgc2VuZGluZyB0aGUgZm9ybS5cblx0ICogQHBhcmFtIHtPYmplY3R9IHNldHRpbmdzIC0gQWpheCByZXF1ZXN0IHNldHRpbmdzLlxuXHQgKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgbW9kaWZpZWQgQWpheCByZXF1ZXN0IHNldHRpbmdzLlxuXHQgKi9cblx0Y2JCZWZvcmVTZW5kRm9ybShzZXR0aW5ncykge1xuXHRcdGlmICghTW9kdWxlR2V0U3NsLnZhbGlkYXRlUHVibGljSXBBZGRyZXNzKCkpIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdFx0aWYgKCFNb2R1bGVHZXRTc2wudmFsaWRhdGVEbnNGaWVsZHMoKSkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0XHRjb25zdCByZXN1bHQgPSBzZXR0aW5ncztcblx0XHQvLyBDb2xsZWN0IEROUyBjcmVkZW50aWFscyBpbnRvIGhpZGRlbiBmaWVsZCBiZWZvcmUgZm9ybSBzdWJtaXNzaW9uXG5cdFx0TW9kdWxlR2V0U3NsLmNvbGxlY3REbnNDcmVkZW50aWFscygpO1xuXHRcdHJlc3VsdC5kYXRhID0gTW9kdWxlR2V0U3NsLiRmb3JtT2JqLmZvcm0oJ2dldCB2YWx1ZXMnKTtcblx0XHRyZXR1cm4gcmVzdWx0O1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDYWxsYmFjayBmdW5jdGlvbiBhZnRlciBzZW5kaW5nIHRoZSBmb3JtLlxuXHQgKi9cblx0Y2JBZnRlclNlbmRGb3JtKHJlc3BvbnNlKSB7XG5cdFx0aWYgKEZvcm0uY2hlY2tTdWNjZXNzKHJlc3BvbnNlKSl7XG5cdFx0XHRNb2R1bGVHZXRTc2wuZ2V0U3NsKCk7XG5cdFx0fVxuXHR9LFxuXG5cblx0LyoqXG5cdCAqIEluaXRpYWxpemVzIHRoZSBmb3JtIHZhbGlkYXRpb24gYW5kIHN1Ym1pc3Npb24gbG9naWMuXG5cdCAqL1xuXHRpbml0aWFsaXplRm9ybSgpIHtcblx0XHRGb3JtLiRmb3JtT2JqID0gTW9kdWxlR2V0U3NsLiRmb3JtT2JqO1xuXHRcdEZvcm0udXJsID0gYCR7Z2xvYmFsUm9vdFVybH0ke2lkVXJsfS8ke2lkVXJsfS9zYXZlYDtcblx0XHRGb3JtLnZhbGlkYXRlUnVsZXMgPSBNb2R1bGVHZXRTc2wudmFsaWRhdGVSdWxlcztcblx0XHRGb3JtLmVuYWJsZURpcnJpdHkgPSBmYWxzZTtcblx0XHRGb3JtLmNiQWZ0ZXJTZW5kRm9ybSA9IE1vZHVsZUdldFNzbC5jYkFmdGVyU2VuZEZvcm07XG5cdFx0Rm9ybS5jYkJlZm9yZVNlbmRGb3JtID0gTW9kdWxlR2V0U3NsLmNiQmVmb3JlU2VuZEZvcm07XG5cdFx0Rm9ybS5pbml0aWFsaXplKCk7XG5cdH0sXG59O1xuXG4vLyBJbml0aWFsaXplIHRoZSBNb2R1bGVHZXRTc2wgY2xhc3Mgd2hlbiB0aGUgZG9jdW1lbnQgaXMgcmVhZHlcbiQoZG9jdW1lbnQpLnJlYWR5KCgpID0+IHtcblx0TW9kdWxlR2V0U3NsLmluaXRpYWxpemUoKTtcbn0pO1xuIl0sIm1hcHBpbmdzIjoiQUFBQTs7QUFFQTtBQUNBLE1BQU0sS0FBSyxHQUFPLGdCQUFnQixDQUFDLENBQWM7QUFDakQsTUFBTSxNQUFNLEdBQU0scUJBQXFCLENBQUMsQ0FBUztBQUNqRCxNQUFNLFNBQVMsR0FBRyxjQUFjLENBQUMsQ0FBZ0I7O0FBRWpEO0FBQ0EsTUFBTSxZQUFZLEdBQUc7RUFDcEI7RUFDQSxRQUFRLEVBQUUsQ0FBQyxDQUFDLEdBQUcsR0FBRyxNQUFNLENBQUM7RUFDekIsV0FBVyxFQUFFLENBQUMsQ0FBQyxHQUFHLEdBQUcsTUFBTSxHQUFHLGVBQWUsQ0FBQztFQUM5QyxpQkFBaUIsRUFBRSxDQUFDLENBQUMsR0FBRyxHQUFHLE1BQU0sR0FBRyxjQUFjLENBQUM7RUFDbkQsYUFBYSxFQUFFLENBQUMsQ0FBQyx1QkFBdUIsQ0FBQztFQUN6QyxhQUFhLEVBQUUsQ0FBQyxDQUFDLGVBQWUsQ0FBQztFQUNqQyxhQUFhLEVBQUUsQ0FBQyxDQUFDLFNBQVMsQ0FBQztFQUMzQixXQUFXLEVBQUUsQ0FBQyxDQUFDLGFBQWEsQ0FBQztFQUM3QixpQkFBaUIsRUFBRSxDQUFDLENBQUMsbUJBQW1CLENBQUM7RUFDekMsc0JBQXNCLEVBQUUsQ0FBQyxDQUFDLDJCQUEyQixDQUFDO0VBQ3RELHlCQUF5QixFQUFFLENBQUMsQ0FBQyw4QkFBOEIsQ0FBQztFQUM1RCxnQkFBZ0IsRUFBRSxDQUFDLENBQUMsa0JBQWtCLENBQUM7RUFDdkMsd0JBQXdCLEVBQUUsQ0FBQyxDQUFDLDZCQUE2QixDQUFDO0VBQzFELFdBQVcsRUFBRSxDQUFDLENBQUMsYUFBYSxDQUFDO0VBQzdCLG1CQUFtQixFQUFF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 \ No newline at end of file diff --git a/public/assets/js/src/module-get-ssl-index.js b/public/assets/js/src/module-get-ssl-index.js index 0bf5077..8045160 100644 --- a/public/assets/js/src/module-get-ssl-index.js +++ b/public/assets/js/src/module-get-ssl-index.js @@ -15,6 +15,13 @@ const ModuleGetSsl = { $submitButton: $('#submitbutton'), $moduleStatus: $('#status'), $domainName: $('#domainName'), + $includeIpAddress: $('#includeIpAddress'), + $includeIpAddressField: $('#include-ip-address-field'), + $includeIpAddressCheckbox: $('#include-ip-address-checkbox'), + $publicIpAddress: $('#publicIpAddress'), + $publicIpAddressSettings: $('#public-ip-address-settings'), + $autoUpdate: $('#autoUpdate'), + $autoUpdateCheckbox: $('#auto-update-checkbox'), $challengeType: $('#challengeType'), $dnsProvider: $('#dnsProvider'), $httpChallengeInfo: $('#http-challenge-info'), @@ -59,6 +66,7 @@ const ModuleGetSsl = { // Initialize form with validation and submit handlers this.initializeForm(); this.bindIpAddressWarning(); + this.bindCertificateIdentifierControls(); moduleGetSSLStatusLoopWorker.$resultBlock.hide(); @@ -95,6 +103,28 @@ const ModuleGetSsl = { } }, + /** Return true only for a publicly routable IPv4 or IPv6 address. */ + isPublicIpAddress(value) { + if (!this.isIpAddress(value)) return false; + const address = String(value || '').trim().replace(/^\[|\]$/g, '').toLowerCase(); + const ipv4Parts = address.split('.').map(Number); + if (ipv4Parts.length === 4) { + const [a, b, c] = ipv4Parts; + return !(a === 0 || a === 10 || a === 127 || a >= 224 + || (a === 100 && b >= 64 && b <= 127) + || (a === 169 && b === 254) + || (a === 172 && b >= 16 && b <= 31) + || (a === 192 && b === 0 && (c === 0 || c === 2)) + || (a === 192 && b === 168) + || (a === 198 && (b === 18 || b === 19)) + || (a === 198 && b === 51 && c === 100) + || (a === 203 && b === 0 && c === 113)); + } + return !(address === '::' || address === '::1' + || address.startsWith('fc') || address.startsWith('fd') + || /^fe[89ab]/.test(address) || address.startsWith('2001:db8:')); + }, + /** Update warning visibility when the configured address changes. */ updateIpAddressWarning() { if (this.isIpAddress(this.$domainName.val())) { @@ -110,6 +140,50 @@ const ModuleGetSsl = { this.updateIpAddressWarning(); }, + /** Apply visibility and compatibility rules for domain/IP certificate identifiers. */ + updateCertificateIdentifierControls() { + const primary = String(this.$domainName.val() || '').trim(); + const primaryIsIp = this.isIpAddress(primary); + const canIncludeIp = primary !== '' && !primaryIsIp; + if (primaryIsIp && this.$includeIpAddress.is(':checked')) { + this.$includeIpAddress.prop('checked', false); + this.$includeIpAddressCheckbox.checkbox('uncheck'); + } + const includeIp = canIncludeIp && this.$includeIpAddress.is(':checked'); + + this.$includeIpAddressField.toggle(canIncludeIp); + this.$publicIpAddressSettings.toggle(includeIp); + + if (primaryIsIp || includeIp) { + this.$challengeType.dropdown('set selected', 'http').dropdown('set disabled'); + this.$autoUpdate.prop('checked', true).prop('disabled', true); + this.$autoUpdateCheckbox.checkbox('check').checkbox('set disabled'); + } else { + this.$challengeType.dropdown('set enabled'); + this.$autoUpdate.prop('disabled', false); + this.$autoUpdateCheckbox.checkbox('set enabled'); + } + }, + + /** Bind changes affecting the requested certificate identifier list. */ + bindCertificateIdentifierControls() { + this.$domainName.on('input', () => this.updateCertificateIdentifierControls()); + this.$includeIpAddressCheckbox.checkbox({ + onChange: () => this.updateCertificateIdentifierControls(), + }); + this.updateCertificateIdentifierControls(); + }, + + /** Validate the optional additional public IP address. */ + validatePublicIpAddress() { + const primaryIsIp = this.isIpAddress(this.$domainName.val()); + if (primaryIsIp || !this.$includeIpAddress.is(':checked')) return true; + if (this.isPublicIpAddress(this.$publicIpAddress.val())) return true; + UserMessage.showError(globalTranslate.module_getssl_PublicIpAddressInvalid); + this.$publicIpAddress.closest('.field').addClass('error'); + return false; + }, + /** * Handle challenge type change: show/hide relevant sections. * @param {string} value - 'http' or 'dns' @@ -325,6 +399,9 @@ const ModuleGetSsl = { * @returns {Object} The modified Ajax request settings. */ cbBeforeSendForm(settings) { + if (!ModuleGetSsl.validatePublicIpAddress()) { + return false; + } if (!ModuleGetSsl.validateDnsFields()) { return false; } diff --git a/tests/CertificateIdentifierPolicyTest.php b/tests/CertificateIdentifierPolicyTest.php new file mode 100644 index 0000000..3b4230a --- /dev/null +++ b/tests/CertificateIdentifierPolicyTest.php @@ -0,0 +1,70 @@ + 'pbx.example.com', 'includeIpAddress' => 0, 'publicIpAddress' => '']; +assertSameValue(['pbx.example.com'], CertificateIdentifierPolicy::getIdentifiers($domainOnly), 'Domain-only identifiers'); +assertSameValue(false, CertificateIdentifierPolicy::containsIpAddress($domainOnly), 'Domain-only has no IP'); +assertSameValue('0 1 1,15 * *', CertificateIdentifierPolicy::getCronSchedule($domainOnly), 'Domain cron'); + +$ipOnly = ['domainName' => '8.8.8.8', 'includeIpAddress' => 0, 'publicIpAddress' => '']; +assertSameValue(['8.8.8.8'], CertificateIdentifierPolicy::getIdentifiers($ipOnly), 'IP-only identifiers'); +assertSameValue(true, CertificateIdentifierPolicy::requiresHttp01($ipOnly), 'IP-only requires HTTP'); +assertSameValue(true, CertificateIdentifierPolicy::requiresAutoUpdate($ipOnly), 'IP-only requires auto-update'); +assertSameValue('17 * * * *', CertificateIdentifierPolicy::getCronSchedule($ipOnly), 'IP cron'); + +$mixedV4 = ['domainName' => 'pbx.example.com', 'includeIpAddress' => 1, 'publicIpAddress' => '8.8.4.4']; +assertSameValue(['pbx.example.com', '8.8.4.4'], CertificateIdentifierPolicy::getIdentifiers($mixedV4), 'Mixed IPv4 identifiers'); +assertSameValue(true, CertificateIdentifierPolicy::containsIpAddress($mixedV4), 'Mixed order has IP'); +assertSameValue(true, CertificateIdentifierPolicy::requiresAutoUpdate($mixedV4), 'Mixed order requires auto-update'); + +$mixedV6 = ['domainName' => 'pbx.example.com', 'includeIpAddress' => 1, 'publicIpAddress' => '2606:4700:4700::1111']; +assertSameValue(['pbx.example.com', '2606:4700:4700::1111'], CertificateIdentifierPolicy::getIdentifiers($mixedV6), 'Mixed IPv6 identifiers'); + +$disabled = ['domainName' => 'pbx.example.com', 'includeIpAddress' => 0, 'publicIpAddress' => '8.8.4.4']; +assertSameValue(['pbx.example.com'], CertificateIdentifierPolicy::getIdentifiers($disabled), 'Disabled additional IP is ignored'); + +foreach (['192.168.1.10', '127.0.0.1', '999.1.1.1', 'pbx.example.com', '2001:db8::1'] as $invalidPublicIp) { + assertSameValue(false, CertificateIdentifierPolicy::isPublicIpAddress($invalidPublicIp), "Reject $invalidPublicIp"); +} + +foreach (['8.8.8.8', '2606:4700:4700::1111'] as $publicIp) { + assertSameValue(true, CertificateIdentifierPolicy::isPublicIpAddress($publicIp), "Accept $publicIp"); +} + +$thrown = false; +try { + CertificateIdentifierPolicy::getIdentifiers([ + 'domainName' => 'pbx.example.com', + 'includeIpAddress' => 1, + 'publicIpAddress' => '192.168.1.10', + ]); +} catch (InvalidArgumentException $e) { + $thrown = true; +} +assertSameValue(true, $thrown, 'Enabled invalid public IP must throw'); + +$thrown = false; +try { + CertificateIdentifierPolicy::getIdentifiers([ + 'domainName' => '192.168.1.10', + 'includeIpAddress' => 0, + 'publicIpAddress' => '', + ]); +} catch (InvalidArgumentException $e) { + $thrown = true; +} +assertSameValue(true, $thrown, 'Primary IP address must be public'); + +echo "CertificateIdentifierPolicyTest: PASS\n"; diff --git a/tests/GetSslPolicyIntegrationTest.php b/tests/GetSslPolicyIntegrationTest.php new file mode 100644 index 0000000..a6d3942 --- /dev/null +++ b/tests/GetSslPolicyIntegrationTest.php @@ -0,0 +1,47 @@ + 'pbx.example.com', + 'includeIpAddress' => 1, + 'publicIpAddress' => '8.8.4.4', +]); +assertContainsText(" -d 'pbx.example.com'", $mixed, 'Primary domain argument'); +assertContainsText(" -d '8.8.4.4'", $mixed, 'Additional IP argument'); +assertContainsText(' --cert-profile shortlived', $mixed, 'Short-lived profile'); + +$domainOnly = CertificateIdentifierPolicy::buildAcmeIdentifierArguments([ + 'domainName' => 'pbx.example.com', + 'includeIpAddress' => 0, + 'publicIpAddress' => '', +]); +assertContainsText(" -d 'pbx.example.com'", $domainOnly, 'Domain argument'); +assertNotContainsText('--cert-profile', $domainOnly, 'Domain-only order is not forced short-lived'); + +$escaped = CertificateIdentifierPolicy::buildAcmeIdentifierArguments([ + 'domainName' => "pbx'example.com", + 'includeIpAddress' => 0, + 'publicIpAddress' => '', +]); +assertContainsText(escapeshellarg("pbx'example.com"), $escaped, 'Identifier is escaped independently'); + +echo "GetSslPolicyIntegrationTest: PASS\n"; diff --git a/tests/ip-address-warning.test.mjs b/tests/ip-address-warning.test.mjs index 2576e4c..86d1696 100644 --- a/tests/ip-address-warning.test.mjs +++ b/tests/ip-address-warning.test.mjs @@ -11,6 +11,8 @@ function loadController() { selector, value: '', visible: false, + checked: false, + disabled: false, handlers: {}, val(value) { if (arguments.length === 0) return this.value; @@ -19,8 +21,37 @@ function loadController() { }, show() { this.visible = true; return this; }, hide() { this.visible = false; return this; }, + toggle(visible) { this.visible = Boolean(visible); return this; }, on(event, handler) { this.handlers[event] = handler; return this; }, trigger(event) { this.handlers[event]?.call(this); return this; }, + is(selector) { return selector === ':checked' ? this.checked : false; }, + prop(name, value) { + if (arguments.length === 1) return this[name]; + this[name] = value; + return this; + }, + checkbox(action) { + if (typeof action === 'object') { + this.handlers.change = action.onChange; + return this; + } + if (action === 'is checked') return this.checked; + if (action === 'check') this.checked = true; + if (action === 'uncheck') this.checked = false; + if (action === 'set disabled') this.disabled = true; + if (action === 'set enabled') this.disabled = false; + return this; + }, + dropdown(action, value) { + if (action === 'get value') return this.value; + if (action === 'set selected') this.value = value; + if (action === 'set disabled') this.disabled = true; + if (action === 'set enabled') this.disabled = false; + return this; + }, + closest() { return this; }, + addClass() { return this; }, + removeClass() { return this; }, }); const $ = selector => { if (!elements.has(selector)) elements.set(selector, makeElement(selector)); @@ -82,3 +113,65 @@ test('updates the warning immediately when the address input changes', () => { address.val('pbx.example.com').trigger('input'); assert.equal(warning.visible, false); }); + +test('shows the additional IP option only for a domain and reveals its settings when enabled', () => { + const { controller, elements } = loadController(); + const address = elements.get('#domainName'); + const option = elements.get('#include-ip-address-field'); + const checkbox = elements.get('#includeIpAddress'); + const settings = elements.get('#public-ip-address-settings'); + + address.val('pbx.example.com'); + controller.updateCertificateIdentifierControls(); + assert.equal(option.visible, true); + assert.equal(settings.visible, false); + + checkbox.checked = true; + controller.updateCertificateIdentifierControls(); + assert.equal(settings.visible, true); + + address.val('8.8.8.8'); + controller.updateCertificateIdentifierControls(); + assert.equal(option.visible, false); + assert.equal(settings.visible, false); + assert.equal(checkbox.checked, false); +}); + +test('forces HTTP-01 and automatic renewal whenever the requested certificate contains an IP', () => { + const { controller, elements } = loadController(); + const address = elements.get('#domainName'); + const challenge = elements.get('#challengeType'); + const autoUpdate = elements.get('#autoUpdate'); + + address.val('8.8.8.8'); + challenge.value = 'dns'; + autoUpdate.checked = false; + controller.updateCertificateIdentifierControls(); + assert.equal(challenge.value, 'http'); + assert.equal(challenge.disabled, true); + assert.equal(autoUpdate.checked, true); + assert.equal(autoUpdate.disabled, true); + + address.val('pbx.example.com'); + controller.updateCertificateIdentifierControls(); + assert.equal(challenge.disabled, false); + assert.equal(autoUpdate.disabled, false); +}); + +test('accepts only public addresses in the additional IP field', () => { + const { controller } = loadController(); + assert.equal(controller.isPublicIpAddress('8.8.8.8'), true); + assert.equal(controller.isPublicIpAddress('2606:4700:4700::1111'), true); + assert.equal(controller.isPublicIpAddress('203.1.1.1'), true); + assert.equal(controller.isPublicIpAddress('203.0.1.1'), true); + assert.equal(controller.isPublicIpAddress('198.50.1.1'), true); + assert.equal(controller.isPublicIpAddress('198.51.1.1'), true); + assert.equal(controller.isPublicIpAddress('192.1.1.1'), true); + assert.equal(controller.isPublicIpAddress('192.0.1.1'), true); + assert.equal(controller.isPublicIpAddress('192.168.1.10'), false); + assert.equal(controller.isPublicIpAddress('192.0.2.1'), false); + assert.equal(controller.isPublicIpAddress('198.51.100.1'), false); + assert.equal(controller.isPublicIpAddress('203.0.113.1'), false); + assert.equal(controller.isPublicIpAddress('2001:db8::1'), false); + assert.equal(controller.isPublicIpAddress('pbx.example.com'), false); +}); From 4067e899009730fd191d7ba620716ec5c94b35f8 Mon Sep 17 00:00:00 2001 From: boffart <5922739+boffart@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:35:21 +0300 Subject: [PATCH 7/8] fix: refresh translations and suggest public IP --- App/Controllers/ModuleGetSslController.php | 27 ++++++++++++++++++-- App/Views/ModuleGetSsl/index.volt | 5 +++- Lib/CertificateIdentifierPolicy.php | 27 ++++++++++++++++++++ public/assets/js/module-get-ssl-index.js | 7 +++-- public/assets/js/src/module-get-ssl-index.js | 6 ++++- tests/CertificateIdentifierPolicyTest.php | 21 +++++++++++++++ tests/ip-address-warning.test.mjs | 17 ++++++++++++ 7 files changed, 104 insertions(+), 6 deletions(-) diff --git a/App/Controllers/ModuleGetSslController.php b/App/Controllers/ModuleGetSslController.php index 58cc21b..58325ea 100644 --- a/App/Controllers/ModuleGetSslController.php +++ b/App/Controllers/ModuleGetSslController.php @@ -60,18 +60,41 @@ public function indexAction(): void $headerCollectionCSS = $this->assets->collection(AssetProvider::HEADER_CSS); $headerCollectionCSS->addCss("css/cache/{$this->moduleUniqueID}/module-get-ssl.css", true); + $internetInterface = LanInterfaces::findFirst("internet = '1'"); + $interfaceSettings = $internetInterface !== null ? $internetInterface->toArray() : []; $settings = ModuleGetSsl::findFirst(); if ($settings === null) { $settings = new ModuleGetSsl(); - $res = LanInterfaces::findFirst("internet = '1'")->toArray(); - $settings->domainName = $res['exthostname'] ?? ''; + $settings->domainName = $interfaceSettings['exthostname'] ?? ''; } + $resolvedIps = []; + $domainName = trim((string)($settings->domainName ?? '')); + if ($domainName !== '' && !CertificateIdentifierPolicy::isIpAddress($domainName)) { + $resolvedV4 = gethostbynamel($domainName); + if (is_array($resolvedV4)) { + $resolvedIps = array_merge($resolvedIps, $resolvedV4); + } + $resolvedV6 = dns_get_record($domainName, DNS_AAAA); + if (is_array($resolvedV6)) { + foreach ($resolvedV6 as $record) { + if (!empty($record['ipv6'])) { + $resolvedIps[] = $record['ipv6']; + } + } + } + } + $suggestedPublicIp = CertificateIdentifierPolicy::selectSuggestedPublicIp( + (string)($interfaceSettings['extipaddr'] ?? ''), + $resolvedIps + ); + $dnsProviderOptions = DnsProviderRegistry::getProviderSelectOptions(); $this->view->form = new ModuleGetSslForm($settings, [ 'dnsProviderOptions' => $dnsProviderOptions, ]); $this->view->dnsProvidersJson = json_encode(DnsProviderRegistry::getProviders()); + $this->view->suggestedPublicIpJson = json_encode($suggestedPublicIp); } /** diff --git a/App/Views/ModuleGetSsl/index.volt b/App/Views/ModuleGetSsl/index.volt index 76b157d..71df430 100644 --- a/App/Views/ModuleGetSsl/index.volt +++ b/App/Views/ModuleGetSsl/index.volt @@ -1,4 +1,7 @@ - +
{{ form.render('id') }} diff --git a/Lib/CertificateIdentifierPolicy.php b/Lib/CertificateIdentifierPolicy.php index c8f92cd..0793b20 100644 --- a/Lib/CertificateIdentifierPolicy.php +++ b/Lib/CertificateIdentifierPolicy.php @@ -102,6 +102,33 @@ public static function buildAcmeIdentifierArguments(array $settings): string return $arguments; } + public static function selectSuggestedPublicIp(string $configuredExternalIp, array $resolvedIps): string + { + $configuredExternalIp = self::normalizeIpWithOptionalPort($configuredExternalIp); + if (self::isPublicIpAddress($configuredExternalIp)) { + return $configuredExternalIp; + } + foreach ($resolvedIps as $resolvedIp) { + $resolvedIp = self::normalizeIpAddress((string)$resolvedIp); + if (self::isPublicIpAddress($resolvedIp)) { + return $resolvedIp; + } + } + return ''; + } + + private static function normalizeIpWithOptionalPort(string $value): string + { + $value = trim($value); + if (preg_match('/^\[([^]]+)](?::\d+)?$/', $value, $matches) === 1) { + return $matches[1]; + } + if (substr_count($value, ':') === 1 && preg_match('/^(.+):(\d+)$/', $value, $matches) === 1) { + return $matches[1]; + } + return self::normalizeIpAddress($value); + } + private static function isInCidr(string $ipAddress, string $cidr): bool { [$network, $prefixLength] = explode('/', $cidr, 2); diff --git a/public/assets/js/module-get-ssl-index.js b/public/assets/js/module-get-ssl-index.js index d059ba7..5ee8ce7 100644 --- a/public/assets/js/module-get-ssl-index.js +++ b/public/assets/js/module-get-ssl-index.js @@ -1,4 +1,4 @@ -/* global globalRootUrl, globalTranslate, Form, Config, PbxApi, dnsProvidersMeta */ +/* global globalRootUrl, globalTranslate, Form, Config, PbxApi, dnsProvidersMeta, suggestedPublicIp */ // Constants related to the form and module const idUrl = 'module-get-ssl'; // API endpoint for SSL module @@ -127,6 +127,9 @@ const ModuleGetSsl = { this.$includeIpAddressCheckbox.checkbox('uncheck'); } const includeIp = canIncludeIp && this.$includeIpAddress.is(':checked'); + if (includeIp && !String(this.$publicIpAddress.val() || '').trim() && typeof suggestedPublicIp === 'string' && this.isPublicIpAddress(suggestedPublicIp)) { + this.$publicIpAddress.val(suggestedPublicIp); + } this.$includeIpAddressField.toggle(canIncludeIp); this.$publicIpAddressSettings.toggle(includeIp); if (primaryIsIp || includeIp) { @@ -405,4 +408,4 @@ $(document).ready(() => { ModuleGetSsl.initialize(); }); -//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kdWxlLWdldC1zc2wtaW5kZXguanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsic3JjL21vZHVsZS1nZXQtc3NsLWluZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGdsb2JhbCBnbG9iYWxSb290VXJsLCBnbG9iYWxUcmFuc2xhdGUsIEZvcm0sIENvbmZpZywgUGJ4QXBpLCBkbnNQcm92aWRlcnNNZXRhICovXG5cbi8vIENvbnN0YW50cyByZWxhdGVkIHRvIHRoZSBmb3JtIGFuZCBtb2R1bGVcbmNvbnN0IGlkVXJsICAgICA9ICdtb2R1bGUtZ2V0LXNzbCc7ICAgICAgICAgICAgICAvLyBBUEkgZW5kcG9pbnQgZm9yIFNTTCBtb2R1bGVcbmNvbnN0IGlkRm9ybSAgICA9ICdtb2R1bGUtZ2V0LXNzbC1mb3JtJzsgICAgICAgICAvLyBGb3JtIGVsZW1lbnQgSUQgZm9yIFNTTCBtb2R1bGVcbmNvbnN0IGNsYXNzTmFtZSA9ICdNb2R1bGVHZXRTc2wnOyAgICAgICAgICAgICAgICAvLyBDbGFzcyBuYW1lIGZvciB0aGlzIG1vZHVsZVxuXG4vLyBNYWluIE1vZHVsZUdldFNzbCBjbGFzcyBkZWZpbml0aW9uXG5jb25zdCBNb2R1bGVHZXRTc2wgPSB7XG5cdC8vIENhY2hlIGNvbW1vbmx5IHVzZWQgalF1ZXJ5IG9iamVjdHNcblx0JGZvcm1PYmo6ICQoJyMnICsgaWRGb3JtKSxcblx0JGNoZWNrQm94ZXM6ICQoJyMnICsgaWRGb3JtICsgJyAudWkuY2hlY2tib3gnKSxcblx0JGRpc2FiaWxpdHlGaWVsZHM6ICQoJyMnICsgaWRGb3JtICsgJyAuZGlzYWJpbGl0eScpLFxuXHQkc3RhdHVzVG9nZ2xlOiAkKCcjbW9kdWxlLXN0YXR1cy10b2dnbGUnKSxcblx0JHN1Ym1pdEJ1dHRvbjogJCgnI3N1Ym1pdGJ1dHRvbicpLFxuXHQkbW9kdWxlU3RhdHVzOiAkKCcjc3RhdHVzJyksXG5cdCRkb21haW5OYW1lOiAkKCcjZG9tYWluTmFtZScpLFxuXHQkaW5jbHVkZUlwQWRkcmVzczogJCgnI2luY2x1ZGVJcEFkZHJlc3MnKSxcblx0JGluY2x1ZGVJcEFkZHJlc3NGaWVsZDogJCgnI2luY2x1ZGUtaXAtYWRkcmVzcy1maWVsZCcpLFxuXHQkaW5jbHVkZUlwQWRkcmVzc0NoZWNrYm94OiAkKCcjaW5jbHVkZS1pcC1hZGRyZXNzLWNoZWNrYm94JyksXG5cdCRwdWJsaWNJcEFkZHJlc3M6ICQoJyNwdWJsaWNJcEFkZHJlc3MnKSxcblx0JHB1YmxpY0lwQWRkcmVzc1NldHRpbmdzOiAkKCcjcHVibGljLWlwLWFkZHJlc3Mtc2V0dGluZ3MnKSxcblx0JGF1dG9VcGRhdGU6ICQoJyNhdXRvVXBkYXRlJyksXG5cdCRhdXRvVXBkYXRlQ2hlY2tib3g6ICQoJyNhdXRvLXVwZGF0ZS1jaGVja2JveCcpLFxuXHQkY2hhbGxlbmdlVHlwZTogJCgnI2NoYWxsZW5nZVR5cGUnKSxcblx0JGRuc1Byb3ZpZGVyOiAkKCcjZG5zUHJvdmlkZXInKSxcblx0JGh0dHBDaGFsbGVuZ2VJbmZvOiAkKCcjaHR0cC1jaGFsbGVuZ2UtaW5mbycpLFxuXHQkZG5zU2V0dGluZ3NCbG9jazogJCgnI2Rucy1zZXR0aW5ncy1ibG9jaycpLFxuXHQkZG5zQ3JlZGVudGlhbHNGaWVsZHM6ICQoJyNkbnMtY3JlZGVudGlhbHMtZmllbGRzJyksXG5cdCRkbnNDcmVkZW50aWFsc0lucHV0OiAkKCdpbnB1dFtuYW1lPVwiZG5zQ3JlZGVudGlhbHNcIl0nKSxcblx0JGlwQWRkcmVzc1dhcm5pbmc6ICQoJyNpcC1hZGRyZXNzLWNlcnRpZmljYXRlLXdhcm5pbmcnKSxcblxuXHQvLyBWYWxpZGF0aW9uIHJ1bGVzIGZvciB0aGUgZm9ybVxuXHR2YWxpZGF0ZVJ1bGVzOiB7XG5cdFx0ZG9tYWluTmFtZToge1xuXHRcdFx0aWRlbnRpZmllcjogJ2RvbWFpbk5hbWUnLFxuXHRcdFx0cnVsZXM6IFtcblx0XHRcdFx0e1xuXHRcdFx0XHRcdHR5cGU6ICdlbXB0eScsXG5cdFx0XHRcdFx0cHJvbXB0OiBnbG9iYWxUcmFuc2xhdGUubW9kdWxlX2dldHNzbF9Eb21haW5OYW1lRW1wdHksXG5cdFx0XHRcdH0sXG5cdFx0XHRdLFxuXHRcdH0sXG5cdH0sXG5cblx0LyoqXG5cdCAqIEluaXRpYWxpemUgdGhlIG1vZHVsZSwgYmluZCBldmVudCBsaXN0ZW5lcnMsIGFuZCBzZXR1cCB0aGUgZm9ybS5cblx0ICovXG5cdGluaXRpYWxpemUoKSB7XG5cdFx0Ly8gSW5pdGlhbGl6ZSBTZW1hbnRpYyBVSSBjaGVja2JveGVzXG5cdFx0dGhpcy4kY2hlY2tCb3hlcy5jaGVja2JveCgpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBkcm9wZG93bnNcblx0XHR0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKHtcblx0XHRcdG9uQ2hhbmdlOiBNb2R1bGVHZXRTc2wub25DaGFuZ2VDaGFsbGVuZ2VUeXBlLFxuXHRcdH0pO1xuXHRcdHRoaXMuJGRuc1Byb3ZpZGVyLmRyb3Bkb3duKHtcblx0XHRcdGZ1bGxUZXh0U2VhcmNoOiB0cnVlLFxuXHRcdFx0b25DaGFuZ2U6IE1vZHVsZUdldFNzbC5vbkNoYW5nZURuc1Byb3ZpZGVyLFxuXHRcdH0pO1xuXG5cdFx0Ly8gQ2hlY2sgYW5kIHNldCBtb2R1bGUgc3RhdHVzIG9uIGxvYWQgYW5kIHdoZW4gdGhlIHN0YXR1cyBjaGFuZ2VzXG5cdFx0dGhpcy5jaGVja1N0YXR1c1RvZ2dsZSgpO1xuXHRcdHdpbmRvdy5hZGRFdmVudExpc3RlbmVyKCdNb2R1bGVTdGF0dXNDaGFuZ2VkJywgdGhpcy5jaGVja1N0YXR1c1RvZ2dsZSk7XG5cblx0XHQvLyBJbml0aWFsaXplIGZvcm0gd2l0aCB2YWxpZGF0aW9uIGFuZCBzdWJtaXQgaGFuZGxlcnNcblx0XHR0aGlzLmluaXRpYWxpemVGb3JtKCk7XG5cdFx0dGhpcy5iaW5kSXBBZGRyZXNzV2FybmluZygpO1xuXHRcdHRoaXMuYmluZENlcnRpZmljYXRlSWRlbnRpZmllckNvbnRyb2xzKCk7XG5cblx0XHRtb2R1bGVHZXRTU0xTdGF0dXNMb29wV29ya2VyLiRyZXN1bHRCbG9jay5oaWRlKCk7XG5cblx0XHQvLyBSZXN0b3JlIHNhdmVkIHN0YXRlXG5cdFx0Y29uc3QgY3VycmVudENoYWxsZW5nZSA9IHRoaXMuJGNoYWxsZW5nZVR5cGUuZHJvcGRvd24oJ2dldCB2YWx1ZScpIHx8ICdodHRwJztcblx0XHR0aGlzLm9uQ2hhbmdlQ2hhbGxlbmdlVHlwZShjdXJyZW50Q2hhbGxlbmdlKTtcblx0XHR0aGlzLnJlc3RvcmVTYXZlZENyZWRlbnRpYWxzKCk7XG5cdH0sXG5cblx0LyoqXG5cdCAqIENoZWNrIHdoZXRoZXIgYSB2YWx1ZSBpcyBhIHZhbGlkIElQdjQgb3IgSVB2NiBhZGRyZXNzLlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWVcblx0ICogQHJldHVybnMge2Jvb2xlYW59XG5cdCAqL1xuXHRpc0lwQWRkcmVzcyh2YWx1ZSkge1xuXHRcdGNvbnN0IGFkZHJlc3MgPSBTdHJpbmcodmFsdWUgfHwgJycpLnRyaW0oKTtcblx0XHRjb25zdCBpcHY0UGFydHMgPSBhZGRyZXNzLnNwbGl0KCcuJyk7XG5cdFx0aWYgKGlwdjRQYXJ0cy5sZW5ndGggPT09IDQpIHtcblx0XHRcdHJldHVybiBpcHY0UGFydHMuZXZlcnkocGFydCA9PiAvXlxcZHsxLDN9JC8udGVzdChwYXJ0KVxuXHRcdFx0XHQmJiBOdW1iZXIocGFydCkgPD0gMjU1XG5cdFx0XHRcdCYmIChwYXJ0ID09PSAnMCcgfHwgcGFydFswXSAhPT0gJzAnKSk7XG5cdFx0fVxuXG5cdFx0Y29uc3QgaGFzT3BlbmluZ0JyYWNrZXQgPSBhZGRyZXNzLnN0YXJ0c1dpdGgoJ1snKTtcblx0XHRjb25zdCBoYXNDbG9zaW5nQnJhY2tldCA9IGFkZHJlc3MuZW5kc1dpdGgoJ10nKTtcblx0XHRpZiAoaGFzT3BlbmluZ0JyYWNrZXQgIT09IGhhc0Nsb3NpbmdCcmFja2V0KSByZXR1cm4gZmFsc2U7XG5cdFx0Y29uc3QgaXB2NiA9IGhhc09wZW5pbmdCcmFja2V0ID8gYWRkcmVzcy5zbGljZSgxLCAtMSkgOiBhZGRyZXNzO1xuXHRcdGlmICghaXB2Ni5pbmNsdWRlcygnOicpIHx8IC9cXHMvLnRlc3QoaXB2NikpIHJldHVybiBmYWxzZTtcblx0XHR0cnkge1xuXHRcdFx0bmV3IFVSTChgaHR0cDovL1ske2lwdjZ9XS9gKTtcblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdH0sXG5cblx0LyoqIFJldHVybiB0cnVlIG9ubHkgZm9yIGEgcHVibGljbHkgcm91dGFibGUgSVB2NCBvciBJUHY2IGFkZHJlc3MuICovXG5cdGlzUHVibGljSXBBZGRyZXNzKHZhbHVlKSB7XG5cdFx0aWYgKCF0aGlzLmlzSXBBZGRyZXNzKHZhbHVlKSkgcmV0dXJuIGZhbHNlO1xuXHRcdGNvbnN0IGFkZHJlc3MgPSBTdHJpbmcodmFsdWUgfHwgJycpLnRyaW0oKS5yZXBsYWNlKC9eXFxbfFxcXSQvZywgJycpLnRvTG93ZXJDYXNlKCk7XG5cdFx0Y29uc3QgaXB2NFBhcnRzID0gYWRkcmVzcy5zcGxpdCgnLicpLm1hcChOdW1iZXIpO1xuXHRcdGlmIChpcHY0UGFydHMubGVuZ3RoID09PSA0KSB7XG5cdFx0XHRjb25zdCBbYSwgYiwgY10gPSBpcHY0UGFydHM7XG5cdFx0XHRyZXR1cm4gIShhID09PSAwIHx8IGEgPT09IDEwIHx8IGEgPT09IDEyNyB8fCBhID49IDIyNFxuXHRcdFx0XHR8fCAoYSA9PT0gMTAwICYmIGIgPj0gNjQgJiYgYiA8PSAxMjcpXG5cdFx0XHRcdHx8IChhID09PSAxNjkgJiYgYiA9PT0gMjU0KVxuXHRcdFx0XHR8fCAoYSA9PT0gMTcyICYmIGIgPj0gMTYgJiYgYiA8PSAzMSlcblx0XHRcdFx0fHwgKGEgPT09IDE5MiAmJiBiID09PSAwICYmIChjID09PSAwIHx8IGMgPT09IDIpKVxuXHRcdFx0XHR8fCAoYSA9PT0gMTkyICYmIGIgPT09IDE2OClcblx0XHRcdFx0fHwgKGEgPT09IDE5OCAmJiAoYiA9PT0gMTggfHwgYiA9PT0gMTkpKVxuXHRcdFx0XHR8fCAoYSA9PT0gMTk4ICYmIGIgPT09IDUxICYmIGMgPT09IDEwMClcblx0XHRcdFx0fHwgKGEgPT09IDIwMyAmJiBiID09PSAwICYmIGMgPT09IDExMykpO1xuXHRcdH1cblx0XHRyZXR1cm4gIShhZGRyZXNzID09PSAnOjonIHx8IGFkZHJlc3MgPT09ICc6OjEnXG5cdFx0XHR8fCBhZGRyZXNzLnN0YXJ0c1dpdGgoJ2ZjJykgfHwgYWRkcmVzcy5zdGFydHNXaXRoKCdmZCcpXG5cdFx0XHR8fCAvXmZlWzg5YWJdLy50ZXN0KGFkZHJlc3MpIHx8IGFkZHJlc3Muc3RhcnRzV2l0aCgnMjAwMTpkYjg6JykpO1xuXHR9LFxuXG5cdC8qKiBVcGRhdGUgd2FybmluZyB2aXNpYmlsaXR5IHdoZW4gdGhlIGNvbmZpZ3VyZWQgYWRkcmVzcyBjaGFuZ2VzLiAqL1xuXHR1cGRhdGVJcEFkZHJlc3NXYXJuaW5nKCkge1xuXHRcdGlmICh0aGlzLmlzSXBBZGRyZXNzKHRoaXMuJGRvbWFpbk5hbWUudmFsKCkpKSB7XG5cdFx0XHR0aGlzLiRpcEFkZHJlc3NXYXJuaW5nLnNob3coKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy4kaXBBZGRyZXNzV2FybmluZy5oaWRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKiBCaW5kIHJlYWN0aXZlIElQIHdhcm5pbmcgYmVoYXZpb3IgYW5kIGluaXRpYWxpemUgaXRzIHN0YXRlLiAqL1xuXHRiaW5kSXBBZGRyZXNzV2FybmluZygpIHtcblx0XHR0aGlzLiRkb21haW5OYW1lLm9uKCdpbnB1dCcsICgpID0+IHRoaXMudXBkYXRlSXBBZGRyZXNzV2FybmluZygpKTtcblx0XHR0aGlzLnVwZGF0ZUlwQWRkcmVzc1dhcm5pbmcoKTtcblx0fSxcblxuXHQvKiogQXBwbHkgdmlzaWJpbGl0eSBhbmQgY29tcGF0aWJpbGl0eSBydWxlcyBmb3IgZG9tYWluL0lQIGNlcnRpZmljYXRlIGlkZW50aWZpZXJzLiAqL1xuXHR1cGRhdGVDZXJ0aWZpY2F0ZUlkZW50aWZpZXJDb250cm9scygpIHtcblx0XHRjb25zdCBwcmltYXJ5ID0gU3RyaW5nKHRoaXMuJGRvbWFpbk5hbWUudmFsKCkgfHwgJycpLnRyaW0oKTtcblx0XHRjb25zdCBwcmltYXJ5SXNJcCA9IHRoaXMuaXNJcEFkZHJlc3MocHJpbWFyeSk7XG5cdFx0Y29uc3QgY2FuSW5jbHVkZUlwID0gcHJpbWFyeSAhPT0gJycgJiYgIXByaW1hcnlJc0lwO1xuXHRcdGlmIChwcmltYXJ5SXNJcCAmJiB0aGlzLiRpbmNsdWRlSXBBZGRyZXNzLmlzKCc6Y2hlY2tlZCcpKSB7XG5cdFx0XHR0aGlzLiRpbmNsdWRlSXBBZGRyZXNzLnByb3AoJ2NoZWNrZWQnLCBmYWxzZSk7XG5cdFx0XHR0aGlzLiRpbmNsdWRlSXBBZGRyZXNzQ2hlY2tib3guY2hlY2tib3goJ3VuY2hlY2snKTtcblx0XHR9XG5cdFx0Y29uc3QgaW5jbHVkZUlwID0gY2FuSW5jbHVkZUlwICYmIHRoaXMuJGluY2x1ZGVJcEFkZHJlc3MuaXMoJzpjaGVja2VkJyk7XG5cblx0XHR0aGlzLiRpbmNsdWRlSXBBZGRyZXNzRmllbGQudG9nZ2xlKGNhbkluY2x1ZGVJcCk7XG5cdFx0dGhpcy4kcHVibGljSXBBZGRyZXNzU2V0dGluZ3MudG9nZ2xlKGluY2x1ZGVJcCk7XG5cblx0XHRpZiAocHJpbWFyeUlzSXAgfHwgaW5jbHVkZUlwKSB7XG5cdFx0XHR0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdzZXQgc2VsZWN0ZWQnLCAnaHR0cCcpLmRyb3Bkb3duKCdzZXQgZGlzYWJsZWQnKTtcblx0XHRcdHRoaXMuJGF1dG9VcGRhdGUucHJvcCgnY2hlY2tlZCcsIHRydWUpLnByb3AoJ2Rpc2FibGVkJywgdHJ1ZSk7XG5cdFx0XHR0aGlzLiRhdXRvVXBkYXRlQ2hlY2tib3guY2hlY2tib3goJ2NoZWNrJykuY2hlY2tib3goJ3NldCBkaXNhYmxlZCcpO1xuXHRcdH0gZWxzZSB7XG5cdFx0XHR0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdzZXQgZW5hYmxlZCcpO1xuXHRcdFx0dGhpcy4kYXV0b1VwZGF0ZS5wcm9wKCdkaXNhYmxlZCcsIGZhbHNlKTtcblx0XHRcdHRoaXMuJGF1dG9VcGRhdGVDaGVja2JveC5jaGVja2JveCgnc2V0IGVuYWJsZWQnKTtcblx0XHR9XG5cdH0sXG5cblx0LyoqIEJpbmQgY2hhbmdlcyBhZmZlY3RpbmcgdGhlIHJlcXVlc3RlZCBjZXJ0aWZpY2F0ZSBpZGVudGlmaWVyIGxpc3QuICovXG5cdGJpbmRDZXJ0aWZpY2F0ZUlkZW50aWZpZXJDb250cm9scygpIHtcblx0XHR0aGlzLiRkb21haW5OYW1lLm9uKCdpbnB1dCcsICgpID0+IHRoaXMudXBkYXRlQ2VydGlmaWNhdGVJZGVudGlmaWVyQ29udHJvbHMoKSk7XG5cdFx0dGhpcy4kaW5jbHVkZUlwQWRkcmVzc0NoZWNrYm94LmNoZWNrYm94KHtcblx0XHRcdG9uQ2hhbmdlOiAoKSA9PiB0aGlzLnVwZGF0ZUNlcnRpZmljYXRlSWRlbnRpZmllckNvbnRyb2xzKCksXG5cdFx0fSk7XG5cdFx0dGhpcy51cGRhdGVDZXJ0aWZpY2F0ZUlkZW50aWZpZXJDb250cm9scygpO1xuXHR9LFxuXG5cdC8qKiBWYWxpZGF0ZSB0aGUgb3B0aW9uYWwgYWRkaXRpb25hbCBwdWJsaWMgSVAgYWRkcmVzcy4gKi9cblx0dmFsaWRhdGVQdWJsaWNJcEFkZHJlc3MoKSB7XG5cdFx0Y29uc3QgcHJpbWFyeUlzSXAgPSB0aGlzLmlzSXBBZGRyZXNzKHRoaXMuJGRvbWFpbk5hbWUudmFsKCkpO1xuXHRcdGlmIChwcmltYXJ5SXNJcCB8fCAhdGhpcy4kaW5jbHVkZUlwQWRkcmVzcy5pcygnOmNoZWNrZWQnKSkgcmV0dXJuIHRydWU7XG5cdFx0aWYgKHRoaXMuaXNQdWJsaWNJcEFkZHJlc3ModGhpcy4kcHVibGljSXBBZGRyZXNzLnZhbCgpKSkgcmV0dXJuIHRydWU7XG5cdFx0VXNlck1lc3NhZ2Uuc2hvd0Vycm9yKGdsb2JhbFRyYW5zbGF0ZS5tb2R1bGVfZ2V0c3NsX1B1YmxpY0lwQWRkcmVzc0ludmFsaWQpO1xuXHRcdHRoaXMuJHB1YmxpY0lwQWRkcmVzcy5jbG9zZXN0KCcuZmllbGQnKS5hZGRDbGFzcygnZXJyb3InKTtcblx0XHRyZXR1cm4gZmFsc2U7XG5cdH0sXG5cblx0LyoqXG5cdCAqIEhhbmRsZSBjaGFsbGVuZ2UgdHlwZSBjaGFuZ2U6IHNob3cvaGlkZSByZWxldmFudCBzZWN0aW9ucy5cblx0ICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gJ2h0dHAnIG9yICdkbnMnXG5cdCAqL1xuXHRvbkNoYW5nZUNoYWxsZW5nZVR5cGUodmFsdWUpIHtcblx0XHRpZiAodmFsdWUgPT09ICdkbnMnKSB7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGh0dHBDaGFsbGVuZ2VJbmZvLmhpZGUoKTtcblx0XHRcdE1vZHVsZUdldFNzbC4kZG5zU2V0dGluZ3NCbG9jay5zaG93KCk7XG5cdFx0XHQvLyBUcmlnZ2VyIHByb3ZpZGVyIGNoYW5nZSB0byByZW5kZXIgY3JlZGVudGlhbCBmaWVsZHNcblx0XHRcdGNvbnN0IGN1cnJlbnRQcm92aWRlciA9IE1vZHVsZUdldFNzbC4kZG5zUHJvdmlkZXIuZHJvcGRvd24oJ2dldCB2YWx1ZScpO1xuXHRcdFx0aWYgKGN1cnJlbnRQcm92aWRlcikge1xuXHRcdFx0XHRNb2R1bGVHZXRTc2wub25DaGFuZ2VEbnNQcm92aWRlcihjdXJyZW50UHJvdmlkZXIpO1xuXHRcdFx0fVxuXHRcdH0gZWxzZSB7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGh0dHBDaGFsbGVuZ2VJbmZvLnNob3coKTtcblx0XHRcdE1vZHVsZUdldFNzbC4kZG5zU2V0dGluZ3NCbG9jay5oaWRlKCk7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKlxuXHQgKiBIYW5kbGUgRE5TIHByb3ZpZGVyIGNoYW5nZTogZHluYW1pY2FsbHkgcmVuZGVyIGNyZWRlbnRpYWwgZmllbGRzLlxuXHQgKiBAcGFyYW0ge3N0cmluZ30gdmFsdWUgLSBwcm92aWRlciBJRCAoZS5nLiAnZG5zX2NmJylcblx0ICovXG5cdG9uQ2hhbmdlRG5zUHJvdmlkZXIodmFsdWUpIHtcblx0XHRjb25zdCAkY29udGFpbmVyID0gTW9kdWxlR2V0U3NsLiRkbnNDcmVkZW50aWFsc0ZpZWxkcztcblx0XHQkY29udGFpbmVyLmVtcHR5KCk7XG5cblx0XHRpZiAoIXZhbHVlIHx8IHR5cGVvZiBkbnNQcm92aWRlcnNNZXRhID09PSAndW5kZWZpbmVkJykge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblxuXHRcdC8vIEZpbmQgcHJvdmlkZXIgbWV0YWRhdGFcblx0XHRjb25zdCBwcm92aWRlciA9IGRuc1Byb3ZpZGVyc01ldGEuZmluZChwID0+IHAuaWQgPT09IHZhbHVlKTtcblx0XHRpZiAoIXByb3ZpZGVyIHx8ICFwcm92aWRlci5maWVsZHMpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvLyBEZWNvZGUgZXhpc3Rpbmcgc2F2ZWQgY3JlZGVudGlhbHMgZm9yIHByZS1maWxsaW5nXG5cdFx0bGV0IHNhdmVkQ3JlZHMgPSB7fTtcblx0XHRjb25zdCBlbmNvZGVkVmFsID0gTW9kdWxlR2V0U3NsLiRkbnNDcmVkZW50aWFsc0lucHV0LnZhbCgpO1xuXHRcdGlmIChlbmNvZGVkVmFsKSB7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRjb25zdCBkZWNvZGVkID0gYXRvYihlbmNvZGVkVmFsKTtcblx0XHRcdFx0c2F2ZWRDcmVkcyA9IEpTT04ucGFyc2UoZGVjb2RlZCk7XG5cdFx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRcdC8vIGlnbm9yZSBkZWNvZGUgZXJyb3JzXG5cdFx0XHR9XG5cdFx0fVxuXG5cdFx0Ly8gUmVuZGVyIGZpZWxkc1xuXHRcdHByb3ZpZGVyLmZpZWxkcy5mb3JFYWNoKGZpZWxkID0+IHtcblx0XHRcdGNvbnN0IGhhc1NhdmVkID0gQm9vbGVhbihzYXZlZENyZWRzW2ZpZWxkLnZhcl0pO1xuXHRcdFx0Y29uc3QgZGlzcGxheVZhbHVlID0gaGFzU2F2ZWQgPyAn4oCi4oCi4oCi4oCi4oCi4oCi4oCi4oCiJyA6ICcnO1xuXHRcdFx0Y29uc3QgbWFza2VkQXR0ciA9IGhhc1NhdmVkID8gJ2RhdGEtbWFza2VkPVwidHJ1ZVwiJyA6ICcnO1xuXHRcdFx0Y29uc3QgaHRtbCA9IGBcblx0XHRcdFx0PGRpdiBjbGFzcz1cImZpZWxkXCI+XG5cdFx0XHRcdFx0PGxhYmVsPiR7ZmllbGQubGFiZWx9PC9sYWJlbD5cblx0XHRcdFx0XHQ8aW5wdXQgdHlwZT1cInBhc3N3b3JkXCJcblx0XHRcdFx0XHRcdCAgIGNsYXNzPVwiZG5zLWNyZWQtaW5wdXRcIlxuXHRcdFx0XHRcdFx0ICAgZGF0YS12YXI9XCIke2ZpZWxkLnZhcn1cIlxuXHRcdFx0XHRcdFx0ICAgJHttYXNrZWRBdHRyfVxuXHRcdFx0XHRcdFx0ICAgdmFsdWU9XCIke01vZHVsZUdldFNzbC5lc2NhcGVIdG1sKGRpc3BsYXlWYWx1ZSl9XCJcblx0XHRcdFx0XHRcdCAgIHBsYWNlaG9sZGVyPVwiJHtmaWVsZC5sYWJlbH1cIj5cblx0XHRcdFx0PC9kaXY+YDtcblx0XHRcdCRjb250YWluZXIuYXBwZW5kKGh0bWwpO1xuXHRcdH0pO1xuXHRcdC8vIE9uIGZvY3VzOiBjbGVhciBtYXNrIHNvIHVzZXIgY2FuIGVudGVyIG5ldyB2YWx1ZVxuXHRcdCRjb250YWluZXIub24oJ2ZvY3VzJywgJy5kbnMtY3JlZC1pbnB1dFtkYXRhLW1hc2tlZF0nLCBmdW5jdGlvbiAoKSB7XG5cdFx0XHQkKHRoaXMpLnZhbCgnJykucmVtb3ZlQXR0cignZGF0YS1tYXNrZWQnKTtcblx0XHR9KTtcblx0XHQvLyBDbGVhciBlcnJvciBoaWdobGlnaHQgd2hlbiB1c2VyIHN0YXJ0cyB0eXBpbmdcblx0XHQkY29udGFpbmVyLm9uKCdpbnB1dCcsICcuZG5zLWNyZWQtaW5wdXQnLCBmdW5jdGlvbiAoKSB7XG5cdFx0XHQkKHRoaXMpLmNsb3Nlc3QoJy5maWVsZCcpLnJlbW92ZUNsYXNzKCdlcnJvcicpO1xuXHRcdH0pO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDb2xsZWN0IEROUyBjcmVkZW50aWFsIGZpZWxkIHZhbHVlcyBpbnRvIGJhc2U2NCBKU09OIGFuZCB3cml0ZSB0byBoaWRkZW4gaW5wdXQuXG5cdCAqIE1hc2tlZCBmaWVsZHMgKHVuY2hhbmdlZCkgYXJlIHJlc3RvcmVkIGZyb20gdGhlIHByZXZpb3VzbHkgc2F2ZWQgY3JlZGVudGlhbHMuXG5cdCAqL1xuXHRjb2xsZWN0RG5zQ3JlZGVudGlhbHMoKSB7XG5cdFx0Y29uc3QgY2hhbGxlbmdlVHlwZSA9IE1vZHVsZUdldFNzbC4kY2hhbGxlbmdlVHlwZS5kcm9wZG93bignZ2V0IHZhbHVlJyk7XG5cdFx0aWYgKGNoYWxsZW5nZVR5cGUgIT09ICdkbnMnKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXHRcdC8vIERlY29kZSBjdXJyZW50bHkgc3RvcmVkIGNyZWRlbnRpYWxzIChzb3VyY2Ugb2YgdHJ1dGggZm9yIG1hc2tlZCBmaWVsZHMpXG5cdFx0bGV0IHNhdmVkQ3JlZHMgPSB7fTtcblx0XHRjb25zdCBlbmNvZGVkVmFsID0gTW9kdWxlR2V0U3NsLiRkbnNDcmVkZW50aWFsc0lucHV0LnZhbCgpO1xuXHRcdGlmIChlbmNvZGVkVmFsKSB7XG5cdFx0XHR0cnkge1xuXHRcdFx0XHRzYXZlZENyZWRzID0gSlNPTi5wYXJzZShhdG9iKGVuY29kZWRWYWwpKTtcblx0XHRcdH0gY2F0Y2ggKGUpIHtcblx0XHRcdFx0Ly8gaWdub3JlXG5cdFx0XHR9XG5cdFx0fVxuXHRcdGNvbnN0IGNyZWRzID0ge307XG5cdFx0JCgnLmRucy1jcmVkLWlucHV0JykuZWFjaChmdW5jdGlvbiAoKSB7XG5cdFx0XHRjb25zdCB2YXJOYW1lID0gJCh0aGlzKS5kYXRhKCd2YXInKTtcblx0XHRcdGlmICghdmFyTmFtZSkgcmV0dXJuO1xuXHRcdFx0aWYgKCQodGhpcykuaXMoJ1tkYXRhLW1hc2tlZF0nKSkge1xuXHRcdFx0XHQvLyBVc2VyIGRpZG4ndCBjaGFuZ2UgdGhpcyBmaWVsZCDigJQga2VlcCBzdG9yZWQgdmFsdWVcblx0XHRcdFx0aWYgKHNhdmVkQ3JlZHNbdmFyTmFtZV0pIHtcblx0XHRcdFx0XHRjcmVkc1t2YXJOYW1lXSA9IHNhdmVkQ3JlZHNbdmFyTmFtZV07XG5cdFx0XHRcdH1cblx0XHRcdH0gZWxzZSB7XG5cdFx0XHRcdGNvbnN0IHZhbCA9ICQodGhpcykudmFsKCk7XG5cdFx0XHRcdGlmICh2YWwpIHtcblx0XHRcdFx0XHRjcmVkc1t2YXJOYW1lXSA9IHZhbDtcblx0XHRcdFx0fVxuXHRcdFx0fVxuXHRcdH0pO1xuXHRcdE1vZHVsZUdldFNzbC4kZG5zQ3JlZGVudGlhbHNJbnB1dC52YWwoYnRvYShKU09OLnN0cmluZ2lmeShjcmVkcykpKTtcblx0fSxcblxuXHQvKipcblx0ICogUmVzdG9yZSBzYXZlZCBjcmVkZW50aWFscyBpbnRvIHRoZSBETlMgcHJvdmlkZXIgZmllbGRzIG9uIHBhZ2UgbG9hZC5cblx0ICovXG5cdHJlc3RvcmVTYXZlZENyZWRlbnRpYWxzKCkge1xuXHRcdGNvbnN0IGN1cnJlbnRQcm92aWRlciA9IE1vZHVsZUdldFNzbC4kZG5zUHJvdmlkZXIuZHJvcGRvd24oJ2dldCB2YWx1ZScpO1xuXHRcdGlmIChjdXJyZW50UHJvdmlkZXIpIHtcblx0XHRcdE1vZHVsZUdldFNzbC5vbkNoYW5nZURuc1Byb3ZpZGVyKGN1cnJlbnRQcm92aWRlcik7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKlxuXHQgKiBFc2NhcGUgSFRNTCBzcGVjaWFsIGNoYXJhY3RlcnMgZm9yIHNhZmUgaW5zZXJ0aW9uIGludG8gYXR0cmlidXRlcy5cblx0ICogQHBhcmFtIHtzdHJpbmd9IHRleHRcblx0ICogQHJldHVybnMge3N0cmluZ31cblx0ICovXG5cdGVzY2FwZUh0bWwodGV4dCkge1xuXHRcdGNvbnN0IG1hcCA9IHsgJyYnOiAnJmFtcDsnLCAnPCc6ICcmbHQ7JywgJz4nOiAnJmd0OycsICdcIic6ICcmcXVvdDsnLCBcIidcIjogJyYjMDM5OycgfTtcblx0XHRyZXR1cm4gU3RyaW5nKHRleHQpLnJlcGxhY2UoL1smPD5cIiddL2csIG0gPT4gbWFwW21dKTtcblx0fSxcblxuXHQvKipcblx0ICogUmVxdWVzdCBhbiBTU0wgY2VydGlmaWNhdGUgYnkgY2FsbGluZyB0aGUgc2VydmVyLXNpZGUgQVBJLlxuXHQgKi9cblx0Z2V0U3NsKCkge1xuXHRcdCQuYXBpKHtcblx0XHRcdHVybDogYCR7Q29uZmlnLnBieFVybH0vcGJ4Y29yZS9hcGkvbW9kdWxlcy8ke2NsYXNzTmFtZX0vZ2V0LWNlcnRgLFxuXHRcdFx0b246ICdub3cnLFxuXHRcdFx0bWV0aG9kOiAnUE9TVCcsXG5cdFx0XHRiZWZvcmVYSFIoeGhyKSB7XG5cdFx0XHRcdHhoci5zZXRSZXF1ZXN0SGVhZGVyICgnWC1Bc3luYy1SZXNwb25zZS1DaGFubmVsLUlkJywgbW9kdWxlR2V0U1NMU3RhdHVzTG9vcFdvcmtlci5jaGFubmVsSWQpO1xuXHRcdFx0XHR4aHIuc2V0UmVxdWVzdEhlYWRlciAoJ1gtUHJvY2Vzc29yLVRpbWVvdXQnLCAnMTIwJyk7XG5cdFx0XHRcdHJldHVybiB4aHI7XG5cdFx0XHR9LFxuXHRcdFx0YmVmb3JlU2VuZChzZXR0aW5ncykge1xuXHRcdFx0XHRNb2R1bGVHZXRTc2wuJHN1Ym1pdEJ1dHRvbi5hZGRDbGFzcygnbG9hZGluZyBkaXNhYmxlZCcpO1xuXHRcdFx0XHRtb2R1bGVHZXRTU0xTdGF0dXNMb29wV29ya2VyLiRyZXN1bHRCbG9jay5zaG93KCk7XG5cdFx0XHRcdG1vZHVsZUdldFNTTFN0YXR1c0xvb3BXb3JrZXIuZWRpdG9yLmdldFNlc3Npb24oKS5zZXRWYWx1ZShcblx0XHRcdFx0Z2xvYmFsVHJhbnNsYXRlLm1vZHVsZV9nZXRzc2xfR2V0U1NMUHJvY2Vzc2luZyArICdcXG4nXG5cdFx0XHQpO1xuXHRcdFx0XHRyZXR1cm4gc2V0dGluZ3M7XG5cdFx0XHR9LFxuXHRcdFx0c3VjY2Vzc1Rlc3Q6IFBieEFwaS5zdWNjZXNzVGVzdCxcblx0XHRcdG9uU3VjY2VzczogZnVuY3Rpb24gKHJlc3BvbnNlKSB7XG5cdFx0XHRcdE1vZHVsZUdldFNzbC4kc3VibWl0QnV0dG9uLnJlbW92ZUNsYXNzKCdsb2FkaW5nIGRpc2FibGVkJyk7XG5cdFx0XHR9LFxuXHRcdFx0b25GYWlsdXJlOiBmdW5jdGlvbihyZXNwb25zZSkge1xuXHRcdFx0XHRNb2R1bGVHZXRTc2wuJHN1Ym1pdEJ1dHRvbi5yZW1vdmVDbGFzcygnbG9hZGluZyBkaXNhYmxlZCcpO1xuXHRcdFx0XHRVc2VyTWVzc2FnZS5zaG93TXVsdGlTdHJpbmcocmVzcG9uc2UubWVzc2FnZSk7XG5cdFx0XHR9LFxuXHRcdH0pXG5cdH0sXG5cblx0LyoqXG5cdCAqIFRvZ2dsZXMgdGhlIGZvcm0gZmllbGRzIGFuZCBzdGF0dXMgdmlzaWJpbGl0eSBiYXNlZCBvbiB0aGUgbW9kdWxlJ3Mgc3RhdHVzLlxuXHQgKi9cblx0Y2hlY2tTdGF0dXNUb2dnbGUoKSB7XG5cdFx0aWYgKE1vZHVsZUdldFNzbC4kc3RhdHVzVG9nZ2xlLmNoZWNrYm94KCdpcyBjaGVja2VkJykpIHtcblx0XHRcdE1vZHVsZUdldFNzbC4kZGlzYWJpbGl0eUZpZWxkcy5yZW1vdmVDbGFzcygnZGlzYWJsZWQnKTtcblx0XHRcdE1vZHVsZUdldFNzbC4kbW9kdWxlU3RhdHVzLnNob3coKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRkaXNhYmlsaXR5RmllbGRzLmFkZENsYXNzKCdkaXNhYmxlZCcpO1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRtb2R1bGVTdGF0dXMuaGlkZSgpO1xuXHRcdH1cblx0fSxcblxuXHQvKipcblx0ICogVmFsaWRhdGUgRE5TIHByb3ZpZGVyIGFuZCBjcmVkZW50aWFscyB3aGVuIEROUy0wMSBpcyBzZWxlY3RlZC5cblx0ICogUmV0dXJucyB0cnVlIGlmIHZhbGlkLCBmYWxzZSBvdGhlcndpc2UuXG5cdCAqL1xuXHR2YWxpZGF0ZURuc0ZpZWxkcygpIHtcblx0XHRpZiAoTW9kdWxlR2V0U3NsLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdnZXQgdmFsdWUnKSAhPT0gJ2RucycpIHtcblx0XHRcdHJldHVybiB0cnVlO1xuXHRcdH1cblx0XHRpZiAoIU1vZHVsZUdldFNzbC4kZG5zUHJvdmlkZXIuZHJvcGRvd24oJ2dldCB2YWx1ZScpKSB7XG5cdFx0XHRVc2VyTWVzc2FnZS5zaG93RXJyb3IoZ2xvYmFsVHJhbnNsYXRlLm1vZHVsZV9nZXRzc2xfRG5zUHJvdmlkZXJFbXB0eSk7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHRcdGxldCBoYXNFbXB0eSA9IGZhbHNlO1xuXHRcdCQoJy5kbnMtY3JlZC1pbnB1dCcpLmVhY2goZnVuY3Rpb24gKCkge1xuXHRcdFx0Y29uc3QgJGZpZWxkID0gJCh0aGlzKS5jbG9zZXN0KCcuZmllbGQnKTtcblx0XHRcdGNvbnN0IGlzTWFza2VkID0gJCh0aGlzKS5pcygnW2RhdGEtbWFza2VkXScpO1xuXHRcdFx0aWYgKCFpc01hc2tlZCAmJiAhJCh0aGlzKS52YWwoKS50cmltKCkpIHtcblx0XHRcdFx0JGZpZWxkLmFkZENsYXNzKCdlcnJvcicpO1xuXHRcdFx0XHRoYXNFbXB0eSA9IHRydWU7XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHQkZmllbGQucmVtb3ZlQ2xhc3MoJ2Vycm9yJyk7XG5cdFx0XHR9XG5cdFx0fSk7XG5cdFx0aWYgKGhhc0VtcHR5KSB7XG5cdFx0XHRVc2VyTWVzc2FnZS5zaG93RXJyb3IoZ2xvYmFsVHJhbnNsYXRlLm1vZHVsZV9nZXRzc2xfRG5zQ3JlZGVudGlhbHNFbXB0eSk7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHRcdHJldHVybiB0cnVlO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDYWxsYmFjayBiZWZvcmUgc2VuZGluZyB0aGUgZm9ybS5cblx0ICogQHBhcmFtIHtPYmplY3R9IHNldHRpbmdzIC0gQWpheCByZXF1ZXN0IHNldHRpbmdzLlxuXHQgKiBAcmV0dXJucyB7T2JqZWN0fSBUaGUgbW9kaWZpZWQgQWpheCByZXF1ZXN0IHNldHRpbmdzLlxuXHQgKi9cblx0Y2JCZWZvcmVTZW5kRm9ybShzZXR0aW5ncykge1xuXHRcdGlmICghTW9kdWxlR2V0U3NsLnZhbGlkYXRlUHVibGljSXBBZGRyZXNzKCkpIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdFx0aWYgKCFNb2R1bGVHZXRTc2wudmFsaWRhdGVEbnNGaWVsZHMoKSkge1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0XHRjb25zdCByZXN1bHQgPSBzZXR0aW5ncztcblx0XHQvLyBDb2xsZWN0IEROUyBjcmVkZW50aWFscyBpbnRvIGhpZGRlbiBmaWVsZCBiZWZvcmUgZm9ybSBzdWJtaXNzaW9uXG5cdFx0TW9kdWxlR2V0U3NsLmNvbGxlY3REbnNDcmVkZW50aWFscygpO1xuXHRcdHJlc3VsdC5kYXRhID0gTW9kdWxlR2V0U3NsLiRmb3JtT2JqLmZvcm0oJ2dldCB2YWx1ZXMnKTtcblx0XHRyZXR1cm4gcmVzdWx0O1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDYWxsYmFjayBmdW5jdGlvbiBhZnRlciBzZW5kaW5nIHRoZSBmb3JtLlxuXHQgKi9cblx0Y2JBZnRlclNlbmRGb3JtKHJlc3BvbnNlKSB7XG5cdFx0aWYgKEZvcm0uY2hlY2tTdWNjZXNzKHJlc3BvbnNlKSl7XG5cdFx0XHRNb2R1bGVHZXRTc2wuZ2V0U3NsKCk7XG5cdFx0fVxuXHR9LFxuXG5cblx0LyoqXG5cdCAqIEluaXRpYWxpemVzIHRoZSBmb3JtIHZhbGlkYXRpb24gYW5kIHN1Ym1pc3Npb24gbG9naWMuXG5cdCAqL1xuXHRpbml0aWFsaXplRm9ybSgpIHtcblx0XHRGb3JtLiRmb3JtT2JqID0gTW9kdWxlR2V0U3NsLiRmb3JtT2JqO1xuXHRcdEZvcm0udXJsID0gYCR7Z2xvYmFsUm9vdFVybH0ke2lkVXJsfS8ke2lkVXJsfS9zYXZlYDtcblx0XHRGb3JtLnZhbGlkYXRlUnVsZXMgPSBNb2R1bGVHZXRTc2wudmFsaWRhdGVSdWxlcztcblx0XHRGb3JtLmVuYWJsZURpcnJpdHkgPSBmYWxzZTtcblx0XHRGb3JtLmNiQWZ0ZXJTZW5kRm9ybSA9IE1vZHVsZUdldFNzbC5jYkFmdGVyU2VuZEZvcm07XG5cdFx0Rm9ybS5jYkJlZm9yZVNlbmRGb3JtID0gTW9kdWxlR2V0U3NsLmNiQmVmb3JlU2VuZEZvcm07XG5cdFx0Rm9ybS5pbml0aWFsaXplKCk7XG5cdH0sXG59O1xuXG4vLyBJbml0aWFsaXplIHRoZSBNb2R1bGVHZXRTc2wgY2xhc3Mgd2hlbiB0aGUgZG9jdW1lbnQgaXMgcmVhZHlcbiQoZG9jdW1lbnQpLnJlYWR5KCgpID0+IHtcblx0TW9kdWxlR2V0U3NsLmluaXRpYWxpemUoKTtcbn0pO1xuIl0sIm1hcHBpbmdzIjoiQUFBQTs7QUFFQTtBQUNBLE1BQU0sS0FBSyxHQUFPLGdCQUFnQixDQUFDLENBQWM7QUFDakQsTUFBTSxNQUFNLEdBQU0scUJBQXFCLENBQUMsQ0FBUztBQUNqRCxNQUFNLFNBQVMsR0FBRyxjQUFjLENBQUMsQ0FBZ0I7O0FBRWpEO0FBQ0EsTUFBTSxZQUFZLEdBQUc7RUFDcEI7RUFDQSxRQUFRLEVBQUUsQ0FBQyxDQUFDLEdBQUcsR0FBRyxNQUFNLENBQUM7RUFDekIsV0FBVyxFQUFFLENBQUMsQ0FBQyxHQUFHLEdBQUcsTUFBTSxHQUFHLGVBQWUsQ0FBQztFQUM5QyxpQkFBaUIsRUFBRSxDQUFDLENBQUMsR0FBRyxHQUFHLE1BQU0sR0FBRyxjQUFjLENBQUM7RUFDbkQsYUFBYSxFQUFFLENBQUMsQ0FBQyx1QkFBdUIsQ0FBQztFQUN6QyxhQUFhLEVBQUUsQ0FBQyxDQUFDLGVBQWUsQ0FBQztFQUNqQyxhQUFhLEVBQUUsQ0FBQyxDQUFDLFNBQVMsQ0FBQztFQUMzQixXQUFXLEVBQUUsQ0FBQyxDQUFDLGFBQWEsQ0FBQztFQUM3QixpQkFBaUIsRUFBRSxDQUFDLENBQUMsbUJBQW1CLENBQUM7RUFDekMsc0JBQXNCLEVBQUUsQ0FBQyxDQUFDLDJCQUEyQixDQUFDO0VBQ3RELHlCQUF5QixFQUFFLENBQUMsQ0FBQyw4QkFBOEIsQ0FBQztFQUM1RCxnQkFBZ0IsRUFBRSxDQUFDLENBQUMsa0JBQWtCLENBQUM7RUFDdkMsd0JBQXdCLEVBQUUsQ0FBQyxDQUFDLDZCQUE2QixDQUFDO0VBQzFELFdBQVcsRUFBRSxDQUFDLENBQUMsYUFBYSxDQUFDO0VBQzdCLG1CQUFtQixFQUFF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 \ No newline at end of file +//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibW9kdWxlLWdldC1zc2wtaW5kZXguanMiLCJuYW1lcyI6W10sInNvdXJjZXMiOlsic3JjL21vZHVsZS1nZXQtc3NsLWluZGV4LmpzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qIGdsb2JhbCBnbG9iYWxSb290VXJsLCBnbG9iYWxUcmFuc2xhdGUsIEZvcm0sIENvbmZpZywgUGJ4QXBpLCBkbnNQcm92aWRlcnNNZXRhLCBzdWdnZXN0ZWRQdWJsaWNJcCAqL1xuXG4vLyBDb25zdGFudHMgcmVsYXRlZCB0byB0aGUgZm9ybSBhbmQgbW9kdWxlXG5jb25zdCBpZFVybCAgICAgPSAnbW9kdWxlLWdldC1zc2wnOyAgICAgICAgICAgICAgLy8gQVBJIGVuZHBvaW50IGZvciBTU0wgbW9kdWxlXG5jb25zdCBpZEZvcm0gICAgPSAnbW9kdWxlLWdldC1zc2wtZm9ybSc7ICAgICAgICAgLy8gRm9ybSBlbGVtZW50IElEIGZvciBTU0wgbW9kdWxlXG5jb25zdCBjbGFzc05hbWUgPSAnTW9kdWxlR2V0U3NsJzsgICAgICAgICAgICAgICAgLy8gQ2xhc3MgbmFtZSBmb3IgdGhpcyBtb2R1bGVcblxuLy8gTWFpbiBNb2R1bGVHZXRTc2wgY2xhc3MgZGVmaW5pdGlvblxuY29uc3QgTW9kdWxlR2V0U3NsID0ge1xuXHQvLyBDYWNoZSBjb21tb25seSB1c2VkIGpRdWVyeSBvYmplY3RzXG5cdCRmb3JtT2JqOiAkKCcjJyArIGlkRm9ybSksXG5cdCRjaGVja0JveGVzOiAkKCcjJyArIGlkRm9ybSArICcgLnVpLmNoZWNrYm94JyksXG5cdCRkaXNhYmlsaXR5RmllbGRzOiAkKCcjJyArIGlkRm9ybSArICcgLmRpc2FiaWxpdHknKSxcblx0JHN0YXR1c1RvZ2dsZTogJCgnI21vZHVsZS1zdGF0dXMtdG9nZ2xlJyksXG5cdCRzdWJtaXRCdXR0b246ICQoJyNzdWJtaXRidXR0b24nKSxcblx0JG1vZHVsZVN0YXR1czogJCgnI3N0YXR1cycpLFxuXHQkZG9tYWluTmFtZTogJCgnI2RvbWFpbk5hbWUnKSxcblx0JGluY2x1ZGVJcEFkZHJlc3M6ICQoJyNpbmNsdWRlSXBBZGRyZXNzJyksXG5cdCRpbmNsdWRlSXBBZGRyZXNzRmllbGQ6ICQoJyNpbmNsdWRlLWlwLWFkZHJlc3MtZmllbGQnKSxcblx0JGluY2x1ZGVJcEFkZHJlc3NDaGVja2JveDogJCgnI2luY2x1ZGUtaXAtYWRkcmVzcy1jaGVja2JveCcpLFxuXHQkcHVibGljSXBBZGRyZXNzOiAkKCcjcHVibGljSXBBZGRyZXNzJyksXG5cdCRwdWJsaWNJcEFkZHJlc3NTZXR0aW5nczogJCgnI3B1YmxpYy1pcC1hZGRyZXNzLXNldHRpbmdzJyksXG5cdCRhdXRvVXBkYXRlOiAkKCcjYXV0b1VwZGF0ZScpLFxuXHQkYXV0b1VwZGF0ZUNoZWNrYm94OiAkKCcjYXV0by11cGRhdGUtY2hlY2tib3gnKSxcblx0JGNoYWxsZW5nZVR5cGU6ICQoJyNjaGFsbGVuZ2VUeXBlJyksXG5cdCRkbnNQcm92aWRlcjogJCgnI2Ruc1Byb3ZpZGVyJyksXG5cdCRodHRwQ2hhbGxlbmdlSW5mbzogJCgnI2h0dHAtY2hhbGxlbmdlLWluZm8nKSxcblx0JGRuc1NldHRpbmdzQmxvY2s6ICQoJyNkbnMtc2V0dGluZ3MtYmxvY2snKSxcblx0JGRuc0NyZWRlbnRpYWxzRmllbGRzOiAkKCcjZG5zLWNyZWRlbnRpYWxzLWZpZWxkcycpLFxuXHQkZG5zQ3JlZGVudGlhbHNJbnB1dDogJCgnaW5wdXRbbmFtZT1cImRuc0NyZWRlbnRpYWxzXCJdJyksXG5cdCRpcEFkZHJlc3NXYXJuaW5nOiAkKCcjaXAtYWRkcmVzcy1jZXJ0aWZpY2F0ZS13YXJuaW5nJyksXG5cblx0Ly8gVmFsaWRhdGlvbiBydWxlcyBmb3IgdGhlIGZvcm1cblx0dmFsaWRhdGVSdWxlczoge1xuXHRcdGRvbWFpbk5hbWU6IHtcblx0XHRcdGlkZW50aWZpZXI6ICdkb21haW5OYW1lJyxcblx0XHRcdHJ1bGVzOiBbXG5cdFx0XHRcdHtcblx0XHRcdFx0XHR0eXBlOiAnZW1wdHknLFxuXHRcdFx0XHRcdHByb21wdDogZ2xvYmFsVHJhbnNsYXRlLm1vZHVsZV9nZXRzc2xfRG9tYWluTmFtZUVtcHR5LFxuXHRcdFx0XHR9LFxuXHRcdFx0XSxcblx0XHR9LFxuXHR9LFxuXG5cdC8qKlxuXHQgKiBJbml0aWFsaXplIHRoZSBtb2R1bGUsIGJpbmQgZXZlbnQgbGlzdGVuZXJzLCBhbmQgc2V0dXAgdGhlIGZvcm0uXG5cdCAqL1xuXHRpbml0aWFsaXplKCkge1xuXHRcdC8vIEluaXRpYWxpemUgU2VtYW50aWMgVUkgY2hlY2tib3hlc1xuXHRcdHRoaXMuJGNoZWNrQm94ZXMuY2hlY2tib3goKTtcblxuXHRcdC8vIEluaXRpYWxpemUgZHJvcGRvd25zXG5cdFx0dGhpcy4kY2hhbGxlbmdlVHlwZS5kcm9wZG93bih7XG5cdFx0XHRvbkNoYW5nZTogTW9kdWxlR2V0U3NsLm9uQ2hhbmdlQ2hhbGxlbmdlVHlwZSxcblx0XHR9KTtcblx0XHR0aGlzLiRkbnNQcm92aWRlci5kcm9wZG93bih7XG5cdFx0XHRmdWxsVGV4dFNlYXJjaDogdHJ1ZSxcblx0XHRcdG9uQ2hhbmdlOiBNb2R1bGVHZXRTc2wub25DaGFuZ2VEbnNQcm92aWRlcixcblx0XHR9KTtcblxuXHRcdC8vIENoZWNrIGFuZCBzZXQgbW9kdWxlIHN0YXR1cyBvbiBsb2FkIGFuZCB3aGVuIHRoZSBzdGF0dXMgY2hhbmdlc1xuXHRcdHRoaXMuY2hlY2tTdGF0dXNUb2dnbGUoKTtcblx0XHR3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcignTW9kdWxlU3RhdHVzQ2hhbmdlZCcsIHRoaXMuY2hlY2tTdGF0dXNUb2dnbGUpO1xuXG5cdFx0Ly8gSW5pdGlhbGl6ZSBmb3JtIHdpdGggdmFsaWRhdGlvbiBhbmQgc3VibWl0IGhhbmRsZXJzXG5cdFx0dGhpcy5pbml0aWFsaXplRm9ybSgpO1xuXHRcdHRoaXMuYmluZElwQWRkcmVzc1dhcm5pbmcoKTtcblx0XHR0aGlzLmJpbmRDZXJ0aWZpY2F0ZUlkZW50aWZpZXJDb250cm9scygpO1xuXG5cdFx0bW9kdWxlR2V0U1NMU3RhdHVzTG9vcFdvcmtlci4kcmVzdWx0QmxvY2suaGlkZSgpO1xuXG5cdFx0Ly8gUmVzdG9yZSBzYXZlZCBzdGF0ZVxuXHRcdGNvbnN0IGN1cnJlbnRDaGFsbGVuZ2UgPSB0aGlzLiRjaGFsbGVuZ2VUeXBlLmRyb3Bkb3duKCdnZXQgdmFsdWUnKSB8fCAnaHR0cCc7XG5cdFx0dGhpcy5vbkNoYW5nZUNoYWxsZW5nZVR5cGUoY3VycmVudENoYWxsZW5nZSk7XG5cdFx0dGhpcy5yZXN0b3JlU2F2ZWRDcmVkZW50aWFscygpO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBDaGVjayB3aGV0aGVyIGEgdmFsdWUgaXMgYSB2YWxpZCBJUHY0IG9yIElQdjYgYWRkcmVzcy5cblx0ICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlXG5cdCAqIEByZXR1cm5zIHtib29sZWFufVxuXHQgKi9cblx0aXNJcEFkZHJlc3ModmFsdWUpIHtcblx0XHRjb25zdCBhZGRyZXNzID0gU3RyaW5nKHZhbHVlIHx8ICcnKS50cmltKCk7XG5cdFx0Y29uc3QgaXB2NFBhcnRzID0gYWRkcmVzcy5zcGxpdCgnLicpO1xuXHRcdGlmIChpcHY0UGFydHMubGVuZ3RoID09PSA0KSB7XG5cdFx0XHRyZXR1cm4gaXB2NFBhcnRzLmV2ZXJ5KHBhcnQgPT4gL15cXGR7MSwzfSQvLnRlc3QocGFydClcblx0XHRcdFx0JiYgTnVtYmVyKHBhcnQpIDw9IDI1NVxuXHRcdFx0XHQmJiAocGFydCA9PT0gJzAnIHx8IHBhcnRbMF0gIT09ICcwJykpO1xuXHRcdH1cblxuXHRcdGNvbnN0IGhhc09wZW5pbmdCcmFja2V0ID0gYWRkcmVzcy5zdGFydHNXaXRoKCdbJyk7XG5cdFx0Y29uc3QgaGFzQ2xvc2luZ0JyYWNrZXQgPSBhZGRyZXNzLmVuZHNXaXRoKCddJyk7XG5cdFx0aWYgKGhhc09wZW5pbmdCcmFja2V0ICE9PSBoYXNDbG9zaW5nQnJhY2tldCkgcmV0dXJuIGZhbHNlO1xuXHRcdGNvbnN0IGlwdjYgPSBoYXNPcGVuaW5nQnJhY2tldCA/IGFkZHJlc3Muc2xpY2UoMSwgLTEpIDogYWRkcmVzcztcblx0XHRpZiAoIWlwdjYuaW5jbHVkZXMoJzonKSB8fCAvXFxzLy50ZXN0KGlwdjYpKSByZXR1cm4gZmFsc2U7XG5cdFx0dHJ5IHtcblx0XHRcdG5ldyBVUkwoYGh0dHA6Ly9bJHtpcHY2fV0vYCk7XG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKiBSZXR1cm4gdHJ1ZSBvbmx5IGZvciBhIHB1YmxpY2x5IHJvdXRhYmxlIElQdjQgb3IgSVB2NiBhZGRyZXNzLiAqL1xuXHRpc1B1YmxpY0lwQWRkcmVzcyh2YWx1ZSkge1xuXHRcdGlmICghdGhpcy5pc0lwQWRkcmVzcyh2YWx1ZSkpIHJldHVybiBmYWxzZTtcblx0XHRjb25zdCBhZGRyZXNzID0gU3RyaW5nKHZhbHVlIHx8ICcnKS50cmltKCkucmVwbGFjZSgvXlxcW3xcXF0kL2csICcnKS50b0xvd2VyQ2FzZSgpO1xuXHRcdGNvbnN0IGlwdjRQYXJ0cyA9IGFkZHJlc3Muc3BsaXQoJy4nKS5tYXAoTnVtYmVyKTtcblx0XHRpZiAoaXB2NFBhcnRzLmxlbmd0aCA9PT0gNCkge1xuXHRcdFx0Y29uc3QgW2EsIGIsIGNdID0gaXB2NFBhcnRzO1xuXHRcdFx0cmV0dXJuICEoYSA9PT0gMCB8fCBhID09PSAxMCB8fCBhID09PSAxMjcgfHwgYSA+PSAyMjRcblx0XHRcdFx0fHwgKGEgPT09IDEwMCAmJiBiID49IDY0ICYmIGIgPD0gMTI3KVxuXHRcdFx0XHR8fCAoYSA9PT0gMTY5ICYmIGIgPT09IDI1NClcblx0XHRcdFx0fHwgKGEgPT09IDE3MiAmJiBiID49IDE2ICYmIGIgPD0gMzEpXG5cdFx0XHRcdHx8IChhID09PSAxOTIgJiYgYiA9PT0gMCAmJiAoYyA9PT0gMCB8fCBjID09PSAyKSlcblx0XHRcdFx0fHwgKGEgPT09IDE5MiAmJiBiID09PSAxNjgpXG5cdFx0XHRcdHx8IChhID09PSAxOTggJiYgKGIgPT09IDE4IHx8IGIgPT09IDE5KSlcblx0XHRcdFx0fHwgKGEgPT09IDE5OCAmJiBiID09PSA1MSAmJiBjID09PSAxMDApXG5cdFx0XHRcdHx8IChhID09PSAyMDMgJiYgYiA9PT0gMCAmJiBjID09PSAxMTMpKTtcblx0XHR9XG5cdFx0cmV0dXJuICEoYWRkcmVzcyA9PT0gJzo6JyB8fCBhZGRyZXNzID09PSAnOjoxJ1xuXHRcdFx0fHwgYWRkcmVzcy5zdGFydHNXaXRoKCdmYycpIHx8IGFkZHJlc3Muc3RhcnRzV2l0aCgnZmQnKVxuXHRcdFx0fHwgL15mZVs4OWFiXS8udGVzdChhZGRyZXNzKSB8fCBhZGRyZXNzLnN0YXJ0c1dpdGgoJzIwMDE6ZGI4OicpKTtcblx0fSxcblxuXHQvKiogVXBkYXRlIHdhcm5pbmcgdmlzaWJpbGl0eSB3aGVuIHRoZSBjb25maWd1cmVkIGFkZHJlc3MgY2hhbmdlcy4gKi9cblx0dXBkYXRlSXBBZGRyZXNzV2FybmluZygpIHtcblx0XHRpZiAodGhpcy5pc0lwQWRkcmVzcyh0aGlzLiRkb21haW5OYW1lLnZhbCgpKSkge1xuXHRcdFx0dGhpcy4kaXBBZGRyZXNzV2FybmluZy5zaG93KCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdHRoaXMuJGlwQWRkcmVzc1dhcm5pbmcuaGlkZSgpO1xuXHRcdH1cblx0fSxcblxuXHQvKiogQmluZCByZWFjdGl2ZSBJUCB3YXJuaW5nIGJlaGF2aW9yIGFuZCBpbml0aWFsaXplIGl0cyBzdGF0ZS4gKi9cblx0YmluZElwQWRkcmVzc1dhcm5pbmcoKSB7XG5cdFx0dGhpcy4kZG9tYWluTmFtZS5vbignaW5wdXQnLCAoKSA9PiB0aGlzLnVwZGF0ZUlwQWRkcmVzc1dhcm5pbmcoKSk7XG5cdFx0dGhpcy51cGRhdGVJcEFkZHJlc3NXYXJuaW5nKCk7XG5cdH0sXG5cblx0LyoqIEFwcGx5IHZpc2liaWxpdHkgYW5kIGNvbXBhdGliaWxpdHkgcnVsZXMgZm9yIGRvbWFpbi9JUCBjZXJ0aWZpY2F0ZSBpZGVudGlmaWVycy4gKi9cblx0dXBkYXRlQ2VydGlmaWNhdGVJZGVudGlmaWVyQ29udHJvbHMoKSB7XG5cdFx0Y29uc3QgcHJpbWFyeSA9IFN0cmluZyh0aGlzLiRkb21haW5OYW1lLnZhbCgpIHx8ICcnKS50cmltKCk7XG5cdFx0Y29uc3QgcHJpbWFyeUlzSXAgPSB0aGlzLmlzSXBBZGRyZXNzKHByaW1hcnkpO1xuXHRcdGNvbnN0IGNhbkluY2x1ZGVJcCA9IHByaW1hcnkgIT09ICcnICYmICFwcmltYXJ5SXNJcDtcblx0XHRpZiAocHJpbWFyeUlzSXAgJiYgdGhpcy4kaW5jbHVkZUlwQWRkcmVzcy5pcygnOmNoZWNrZWQnKSkge1xuXHRcdFx0dGhpcy4kaW5jbHVkZUlwQWRkcmVzcy5wcm9wKCdjaGVja2VkJywgZmFsc2UpO1xuXHRcdFx0dGhpcy4kaW5jbHVkZUlwQWRkcmVzc0NoZWNrYm94LmNoZWNrYm94KCd1bmNoZWNrJyk7XG5cdFx0fVxuXHRcdGNvbnN0IGluY2x1ZGVJcCA9IGNhbkluY2x1ZGVJcCAmJiB0aGlzLiRpbmNsdWRlSXBBZGRyZXNzLmlzKCc6Y2hlY2tlZCcpO1xuXHRcdGlmIChpbmNsdWRlSXAgJiYgIVN0cmluZyh0aGlzLiRwdWJsaWNJcEFkZHJlc3MudmFsKCkgfHwgJycpLnRyaW0oKVxuXHRcdFx0JiYgdHlwZW9mIHN1Z2dlc3RlZFB1YmxpY0lwID09PSAnc3RyaW5nJyAmJiB0aGlzLmlzUHVibGljSXBBZGRyZXNzKHN1Z2dlc3RlZFB1YmxpY0lwKSkge1xuXHRcdFx0dGhpcy4kcHVibGljSXBBZGRyZXNzLnZhbChzdWdnZXN0ZWRQdWJsaWNJcCk7XG5cdFx0fVxuXG5cdFx0dGhpcy4kaW5jbHVkZUlwQWRkcmVzc0ZpZWxkLnRvZ2dsZShjYW5JbmNsdWRlSXApO1xuXHRcdHRoaXMuJHB1YmxpY0lwQWRkcmVzc1NldHRpbmdzLnRvZ2dsZShpbmNsdWRlSXApO1xuXG5cdFx0aWYgKHByaW1hcnlJc0lwIHx8IGluY2x1ZGVJcCkge1xuXHRcdFx0dGhpcy4kY2hhbGxlbmdlVHlwZS5kcm9wZG93bignc2V0IHNlbGVjdGVkJywgJ2h0dHAnKS5kcm9wZG93bignc2V0IGRpc2FibGVkJyk7XG5cdFx0XHR0aGlzLiRhdXRvVXBkYXRlLnByb3AoJ2NoZWNrZWQnLCB0cnVlKS5wcm9wKCdkaXNhYmxlZCcsIHRydWUpO1xuXHRcdFx0dGhpcy4kYXV0b1VwZGF0ZUNoZWNrYm94LmNoZWNrYm94KCdjaGVjaycpLmNoZWNrYm94KCdzZXQgZGlzYWJsZWQnKTtcblx0XHR9IGVsc2Uge1xuXHRcdFx0dGhpcy4kY2hhbGxlbmdlVHlwZS5kcm9wZG93bignc2V0IGVuYWJsZWQnKTtcblx0XHRcdHRoaXMuJGF1dG9VcGRhdGUucHJvcCgnZGlzYWJsZWQnLCBmYWxzZSk7XG5cdFx0XHR0aGlzLiRhdXRvVXBkYXRlQ2hlY2tib3guY2hlY2tib3goJ3NldCBlbmFibGVkJyk7XG5cdFx0fVxuXHR9LFxuXG5cdC8qKiBCaW5kIGNoYW5nZXMgYWZmZWN0aW5nIHRoZSByZXF1ZXN0ZWQgY2VydGlmaWNhdGUgaWRlbnRpZmllciBsaXN0LiAqL1xuXHRiaW5kQ2VydGlmaWNhdGVJZGVudGlmaWVyQ29udHJvbHMoKSB7XG5cdFx0dGhpcy4kZG9tYWluTmFtZS5vbignaW5wdXQnLCAoKSA9PiB0aGlzLnVwZGF0ZUNlcnRpZmljYXRlSWRlbnRpZmllckNvbnRyb2xzKCkpO1xuXHRcdHRoaXMuJGluY2x1ZGVJcEFkZHJlc3NDaGVja2JveC5jaGVja2JveCh7XG5cdFx0XHRvbkNoYW5nZTogKCkgPT4gdGhpcy51cGRhdGVDZXJ0aWZpY2F0ZUlkZW50aWZpZXJDb250cm9scygpLFxuXHRcdH0pO1xuXHRcdHRoaXMudXBkYXRlQ2VydGlmaWNhdGVJZGVudGlmaWVyQ29udHJvbHMoKTtcblx0fSxcblxuXHQvKiogVmFsaWRhdGUgdGhlIG9wdGlvbmFsIGFkZGl0aW9uYWwgcHVibGljIElQIGFkZHJlc3MuICovXG5cdHZhbGlkYXRlUHVibGljSXBBZGRyZXNzKCkge1xuXHRcdGNvbnN0IHByaW1hcnlJc0lwID0gdGhpcy5pc0lwQWRkcmVzcyh0aGlzLiRkb21haW5OYW1lLnZhbCgpKTtcblx0XHRpZiAocHJpbWFyeUlzSXAgfHwgIXRoaXMuJGluY2x1ZGVJcEFkZHJlc3MuaXMoJzpjaGVja2VkJykpIHJldHVybiB0cnVlO1xuXHRcdGlmICh0aGlzLmlzUHVibGljSXBBZGRyZXNzKHRoaXMuJHB1YmxpY0lwQWRkcmVzcy52YWwoKSkpIHJldHVybiB0cnVlO1xuXHRcdFVzZXJNZXNzYWdlLnNob3dFcnJvcihnbG9iYWxUcmFuc2xhdGUubW9kdWxlX2dldHNzbF9QdWJsaWNJcEFkZHJlc3NJbnZhbGlkKTtcblx0XHR0aGlzLiRwdWJsaWNJcEFkZHJlc3MuY2xvc2VzdCgnLmZpZWxkJykuYWRkQ2xhc3MoJ2Vycm9yJyk7XG5cdFx0cmV0dXJuIGZhbHNlO1xuXHR9LFxuXG5cdC8qKlxuXHQgKiBIYW5kbGUgY2hhbGxlbmdlIHR5cGUgY2hhbmdlOiBzaG93L2hpZGUgcmVsZXZhbnQgc2VjdGlvbnMuXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSB2YWx1ZSAtICdodHRwJyBvciAnZG5zJ1xuXHQgKi9cblx0b25DaGFuZ2VDaGFsbGVuZ2VUeXBlKHZhbHVlKSB7XG5cdFx0aWYgKHZhbHVlID09PSAnZG5zJykge1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRodHRwQ2hhbGxlbmdlSW5mby5oaWRlKCk7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGRuc1NldHRpbmdzQmxvY2suc2hvdygpO1xuXHRcdFx0Ly8gVHJpZ2dlciBwcm92aWRlciBjaGFuZ2UgdG8gcmVuZGVyIGNyZWRlbnRpYWwgZmllbGRzXG5cdFx0XHRjb25zdCBjdXJyZW50UHJvdmlkZXIgPSBNb2R1bGVHZXRTc2wuJGRuc1Byb3ZpZGVyLmRyb3Bkb3duKCdnZXQgdmFsdWUnKTtcblx0XHRcdGlmIChjdXJyZW50UHJvdmlkZXIpIHtcblx0XHRcdFx0TW9kdWxlR2V0U3NsLm9uQ2hhbmdlRG5zUHJvdmlkZXIoY3VycmVudFByb3ZpZGVyKTtcblx0XHRcdH1cblx0XHR9IGVsc2Uge1xuXHRcdFx0TW9kdWxlR2V0U3NsLiRodHRwQ2hhbGxlbmdlSW5mby5zaG93KCk7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGRuc1NldHRpbmdzQmxvY2suaGlkZSgpO1xuXHRcdH1cblx0fSxcblxuXHQvKipcblx0ICogSGFuZGxlIEROUyBwcm92aWRlciBjaGFuZ2U6IGR5bmFtaWNhbGx5IHJlbmRlciBjcmVkZW50aWFsIGZpZWxkcy5cblx0ICogQHBhcmFtIHtzdHJpbmd9IHZhbHVlIC0gcHJvdmlkZXIgSUQgKGUuZy4gJ2Ruc19jZicpXG5cdCAqL1xuXHRvbkNoYW5nZURuc1Byb3ZpZGVyKHZhbHVlKSB7XG5cdFx0Y29uc3QgJGNvbnRhaW5lciA9IE1vZHVsZUdldFNzbC4kZG5zQ3JlZGVudGlhbHNGaWVsZHM7XG5cdFx0JGNvbnRhaW5lci5lbXB0eSgpO1xuXG5cdFx0aWYgKCF2YWx1ZSB8fCB0eXBlb2YgZG5zUHJvdmlkZXJzTWV0YSA9PT0gJ3VuZGVmaW5lZCcpIHtcblx0XHRcdHJldHVybjtcblx0XHR9XG5cblx0XHQvLyBGaW5kIHByb3ZpZGVyIG1ldGFkYXRhXG5cdFx0Y29uc3QgcHJvdmlkZXIgPSBkbnNQcm92aWRlcnNNZXRhLmZpbmQocCA9PiBwLmlkID09PSB2YWx1ZSk7XG5cdFx0aWYgKCFwcm92aWRlciB8fCAhcHJvdmlkZXIuZmllbGRzKSB7XG5cdFx0XHRyZXR1cm47XG5cdFx0fVxuXG5cdFx0Ly8gRGVjb2RlIGV4aXN0aW5nIHNhdmVkIGNyZWRlbnRpYWxzIGZvciBwcmUtZmlsbGluZ1xuXHRcdGxldCBzYXZlZENyZWRzID0ge307XG5cdFx0Y29uc3QgZW5jb2RlZFZhbCA9IE1vZHVsZUdldFNzbC4kZG5zQ3JlZGVudGlhbHNJbnB1dC52YWwoKTtcblx0XHRpZiAoZW5jb2RlZFZhbCkge1xuXHRcdFx0dHJ5IHtcblx0XHRcdFx0Y29uc3QgZGVjb2RlZCA9IGF0b2IoZW5jb2RlZFZhbCk7XG5cdFx0XHRcdHNhdmVkQ3JlZHMgPSBKU09OLnBhcnNlKGRlY29kZWQpO1xuXHRcdFx0fSBjYXRjaCAoZSkge1xuXHRcdFx0XHQvLyBpZ25vcmUgZGVjb2RlIGVycm9yc1xuXHRcdFx0fVxuXHRcdH1cblxuXHRcdC8vIFJlbmRlciBmaWVsZHNcblx0XHRwcm92aWRlci5maWVsZHMuZm9yRWFjaChmaWVsZCA9PiB7XG5cdFx0XHRjb25zdCBoYXNTYXZlZCA9IEJvb2xlYW4oc2F2ZWRDcmVkc1tmaWVsZC52YXJdKTtcblx0XHRcdGNvbnN0IGRpc3BsYXlWYWx1ZSA9IGhhc1NhdmVkID8gJ+KAouKAouKAouKAouKAouKAouKAouKAoicgOiAnJztcblx0XHRcdGNvbnN0IG1hc2tlZEF0dHIgPSBoYXNTYXZlZCA/ICdkYXRhLW1hc2tlZD1cInRydWVcIicgOiAnJztcblx0XHRcdGNvbnN0IGh0bWwgPSBgXG5cdFx0XHRcdDxkaXYgY2xhc3M9XCJmaWVsZFwiPlxuXHRcdFx0XHRcdDxsYWJlbD4ke2ZpZWxkLmxhYmVsfTwvbGFiZWw+XG5cdFx0XHRcdFx0PGlucHV0IHR5cGU9XCJwYXNzd29yZFwiXG5cdFx0XHRcdFx0XHQgICBjbGFzcz1cImRucy1jcmVkLWlucHV0XCJcblx0XHRcdFx0XHRcdCAgIGRhdGEtdmFyPVwiJHtmaWVsZC52YXJ9XCJcblx0XHRcdFx0XHRcdCAgICR7bWFza2VkQXR0cn1cblx0XHRcdFx0XHRcdCAgIHZhbHVlPVwiJHtNb2R1bGVHZXRTc2wuZXNjYXBlSHRtbChkaXNwbGF5VmFsdWUpfVwiXG5cdFx0XHRcdFx0XHQgICBwbGFjZWhvbGRlcj1cIiR7ZmllbGQubGFiZWx9XCI+XG5cdFx0XHRcdDwvZGl2PmA7XG5cdFx0XHQkY29udGFpbmVyLmFwcGVuZChodG1sKTtcblx0XHR9KTtcblx0XHQvLyBPbiBmb2N1czogY2xlYXIgbWFzayBzbyB1c2VyIGNhbiBlbnRlciBuZXcgdmFsdWVcblx0XHQkY29udGFpbmVyLm9uKCdmb2N1cycsICcuZG5zLWNyZWQtaW5wdXRbZGF0YS1tYXNrZWRdJywgZnVuY3Rpb24gKCkge1xuXHRcdFx0JCh0aGlzKS52YWwoJycpLnJlbW92ZUF0dHIoJ2RhdGEtbWFza2VkJyk7XG5cdFx0fSk7XG5cdFx0Ly8gQ2xlYXIgZXJyb3IgaGlnaGxpZ2h0IHdoZW4gdXNlciBzdGFydHMgdHlwaW5nXG5cdFx0JGNvbnRhaW5lci5vbignaW5wdXQnLCAnLmRucy1jcmVkLWlucHV0JywgZnVuY3Rpb24gKCkge1xuXHRcdFx0JCh0aGlzKS5jbG9zZXN0KCcuZmllbGQnKS5yZW1vdmVDbGFzcygnZXJyb3InKTtcblx0XHR9KTtcblx0fSxcblxuXHQvKipcblx0ICogQ29sbGVjdCBETlMgY3JlZGVudGlhbCBmaWVsZCB2YWx1ZXMgaW50byBiYXNlNjQgSlNPTiBhbmQgd3JpdGUgdG8gaGlkZGVuIGlucHV0LlxuXHQgKiBNYXNrZWQgZmllbGRzICh1bmNoYW5nZWQpIGFyZSByZXN0b3JlZCBmcm9tIHRoZSBwcmV2aW91c2x5IHNhdmVkIGNyZWRlbnRpYWxzLlxuXHQgKi9cblx0Y29sbGVjdERuc0NyZWRlbnRpYWxzKCkge1xuXHRcdGNvbnN0IGNoYWxsZW5nZVR5cGUgPSBNb2R1bGVHZXRTc2wuJGNoYWxsZW5nZVR5cGUuZHJvcGRvd24oJ2dldCB2YWx1ZScpO1xuXHRcdGlmIChjaGFsbGVuZ2VUeXBlICE9PSAnZG5zJykge1xuXHRcdFx0cmV0dXJuO1xuXHRcdH1cblx0XHQvLyBEZWNvZGUgY3VycmVudGx5IHN0b3JlZCBjcmVkZW50aWFscyAoc291cmNlIG9mIHRydXRoIGZvciBtYXNrZWQgZmllbGRzKVxuXHRcdGxldCBzYXZlZENyZWRzID0ge307XG5cdFx0Y29uc3QgZW5jb2RlZFZhbCA9IE1vZHVsZUdldFNzbC4kZG5zQ3JlZGVudGlhbHNJbnB1dC52YWwoKTtcblx0XHRpZiAoZW5jb2RlZFZhbCkge1xuXHRcdFx0dHJ5IHtcblx0XHRcdFx0c2F2ZWRDcmVkcyA9IEpTT04ucGFyc2UoYXRvYihlbmNvZGVkVmFsKSk7XG5cdFx0XHR9IGNhdGNoIChlKSB7XG5cdFx0XHRcdC8vIGlnbm9yZVxuXHRcdFx0fVxuXHRcdH1cblx0XHRjb25zdCBjcmVkcyA9IHt9O1xuXHRcdCQoJy5kbnMtY3JlZC1pbnB1dCcpLmVhY2goZnVuY3Rpb24gKCkge1xuXHRcdFx0Y29uc3QgdmFyTmFtZSA9ICQodGhpcykuZGF0YSgndmFyJyk7XG5cdFx0XHRpZiAoIXZhck5hbWUpIHJldHVybjtcblx0XHRcdGlmICgkKHRoaXMpLmlzKCdbZGF0YS1tYXNrZWRdJykpIHtcblx0XHRcdFx0Ly8gVXNlciBkaWRuJ3QgY2hhbmdlIHRoaXMgZmllbGQg4oCUIGtlZXAgc3RvcmVkIHZhbHVlXG5cdFx0XHRcdGlmIChzYXZlZENyZWRzW3Zhck5hbWVdKSB7XG5cdFx0XHRcdFx0Y3JlZHNbdmFyTmFtZV0gPSBzYXZlZENyZWRzW3Zhck5hbWVdO1xuXHRcdFx0XHR9XG5cdFx0XHR9IGVsc2Uge1xuXHRcdFx0XHRjb25zdCB2YWwgPSAkKHRoaXMpLnZhbCgpO1xuXHRcdFx0XHRpZiAodmFsKSB7XG5cdFx0XHRcdFx0Y3JlZHNbdmFyTmFtZV0gPSB2YWw7XG5cdFx0XHRcdH1cblx0XHRcdH1cblx0XHR9KTtcblx0XHRNb2R1bGVHZXRTc2wuJGRuc0NyZWRlbnRpYWxzSW5wdXQudmFsKGJ0b2EoSlNPTi5zdHJpbmdpZnkoY3JlZHMpKSk7XG5cdH0sXG5cblx0LyoqXG5cdCAqIFJlc3RvcmUgc2F2ZWQgY3JlZGVudGlhbHMgaW50byB0aGUgRE5TIHByb3ZpZGVyIGZpZWxkcyBvbiBwYWdlIGxvYWQuXG5cdCAqL1xuXHRyZXN0b3JlU2F2ZWRDcmVkZW50aWFscygpIHtcblx0XHRjb25zdCBjdXJyZW50UHJvdmlkZXIgPSBNb2R1bGVHZXRTc2wuJGRuc1Byb3ZpZGVyLmRyb3Bkb3duKCdnZXQgdmFsdWUnKTtcblx0XHRpZiAoY3VycmVudFByb3ZpZGVyKSB7XG5cdFx0XHRNb2R1bGVHZXRTc2wub25DaGFuZ2VEbnNQcm92aWRlcihjdXJyZW50UHJvdmlkZXIpO1xuXHRcdH1cblx0fSxcblxuXHQvKipcblx0ICogRXNjYXBlIEhUTUwgc3BlY2lhbCBjaGFyYWN0ZXJzIGZvciBzYWZlIGluc2VydGlvbiBpbnRvIGF0dHJpYnV0ZXMuXG5cdCAqIEBwYXJhbSB7c3RyaW5nfSB0ZXh0XG5cdCAqIEByZXR1cm5zIHtzdHJpbmd9XG5cdCAqL1xuXHRlc2NhcGVIdG1sKHRleHQpIHtcblx0XHRjb25zdCBtYXAgPSB7ICcmJzogJyZhbXA7JywgJzwnOiAnJmx0OycsICc+JzogJyZndDsnLCAnXCInOiAnJnF1b3Q7JywgXCInXCI6ICcmIzAzOTsnIH07XG5cdFx0cmV0dXJuIFN0cmluZyh0ZXh0KS5yZXBsYWNlKC9bJjw+XCInXS9nLCBtID0+IG1hcFttXSk7XG5cdH0sXG5cblx0LyoqXG5cdCAqIFJlcXVlc3QgYW4gU1NMIGNlcnRpZmljYXRlIGJ5IGNhbGxpbmcgdGhlIHNlcnZlci1zaWRlIEFQSS5cblx0ICovXG5cdGdldFNzbCgpIHtcblx0XHQkLmFwaSh7XG5cdFx0XHR1cmw6IGAke0NvbmZpZy5wYnhVcmx9L3BieGNvcmUvYXBpL21vZHVsZXMvJHtjbGFzc05hbWV9L2dldC1jZXJ0YCxcblx0XHRcdG9uOiAnbm93Jyxcblx0XHRcdG1ldGhvZDogJ1BPU1QnLFxuXHRcdFx0YmVmb3JlWEhSKHhocikge1xuXHRcdFx0XHR4aHIuc2V0UmVxdWVzdEhlYWRlciAoJ1gtQXN5bmMtUmVzcG9uc2UtQ2hhbm5lbC1JZCcsIG1vZHVsZUdldFNTTFN0YXR1c0xvb3BXb3JrZXIuY2hhbm5lbElkKTtcblx0XHRcdFx0eGhyLnNldFJlcXVlc3RIZWFkZXIgKCdYLVByb2Nlc3Nvci1UaW1lb3V0JywgJzEyMCcpO1xuXHRcdFx0XHRyZXR1cm4geGhyO1xuXHRcdFx0fSxcblx0XHRcdGJlZm9yZVNlbmQoc2V0dGluZ3MpIHtcblx0XHRcdFx0TW9kdWxlR2V0U3NsLiRzdWJtaXRCdXR0b24uYWRkQ2xhc3MoJ2xvYWRpbmcgZGlzYWJsZWQnKTtcblx0XHRcdFx0bW9kdWxlR2V0U1NMU3RhdHVzTG9vcFdvcmtlci4kcmVzdWx0QmxvY2suc2hvdygpO1xuXHRcdFx0XHRtb2R1bGVHZXRTU0xTdGF0dXNMb29wV29ya2VyLmVkaXRvci5nZXRTZXNzaW9uKCkuc2V0VmFsdWUoXG5cdFx0XHRcdGdsb2JhbFRyYW5zbGF0ZS5tb2R1bGVfZ2V0c3NsX0dldFNTTFByb2Nlc3NpbmcgKyAnXFxuJ1xuXHRcdFx0KTtcblx0XHRcdFx0cmV0dXJuIHNldHRpbmdzO1xuXHRcdFx0fSxcblx0XHRcdHN1Y2Nlc3NUZXN0OiBQYnhBcGkuc3VjY2Vzc1Rlc3QsXG5cdFx0XHRvblN1Y2Nlc3M6IGZ1bmN0aW9uIChyZXNwb25zZSkge1xuXHRcdFx0XHRNb2R1bGVHZXRTc2wuJHN1Ym1pdEJ1dHRvbi5yZW1vdmVDbGFzcygnbG9hZGluZyBkaXNhYmxlZCcpO1xuXHRcdFx0fSxcblx0XHRcdG9uRmFpbHVyZTogZnVuY3Rpb24ocmVzcG9uc2UpIHtcblx0XHRcdFx0TW9kdWxlR2V0U3NsLiRzdWJtaXRCdXR0b24ucmVtb3ZlQ2xhc3MoJ2xvYWRpbmcgZGlzYWJsZWQnKTtcblx0XHRcdFx0VXNlck1lc3NhZ2Uuc2hvd011bHRpU3RyaW5nKHJlc3BvbnNlLm1lc3NhZ2UpO1xuXHRcdFx0fSxcblx0XHR9KVxuXHR9LFxuXG5cdC8qKlxuXHQgKiBUb2dnbGVzIHRoZSBmb3JtIGZpZWxkcyBhbmQgc3RhdHVzIHZpc2liaWxpdHkgYmFzZWQgb24gdGhlIG1vZHVsZSdzIHN0YXR1cy5cblx0ICovXG5cdGNoZWNrU3RhdHVzVG9nZ2xlKCkge1xuXHRcdGlmIChNb2R1bGVHZXRTc2wuJHN0YXR1c1RvZ2dsZS5jaGVja2JveCgnaXMgY2hlY2tlZCcpKSB7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJGRpc2FiaWxpdHlGaWVsZHMucmVtb3ZlQ2xhc3MoJ2Rpc2FibGVkJyk7XG5cdFx0XHRNb2R1bGVHZXRTc2wuJG1vZHVsZVN0YXR1cy5zaG93KCk7XG5cdFx0fSBlbHNlIHtcblx0XHRcdE1vZHVsZUdldFNzbC4kZGlzYWJpbGl0eUZpZWxkcy5hZGRDbGFzcygnZGlzYWJsZWQnKTtcblx0XHRcdE1vZHVsZUdldFNzbC4kbW9kdWxlU3RhdHVzLmhpZGUoKTtcblx0XHR9XG5cdH0sXG5cblx0LyoqXG5cdCAqIFZhbGlkYXRlIEROUyBwcm92aWRlciBhbmQgY3JlZGVudGlhbHMgd2hlbiBETlMtMDEgaXMgc2VsZWN0ZWQuXG5cdCAqIFJldHVybnMgdHJ1ZSBpZiB2YWxpZCwgZmFsc2Ugb3RoZXJ3aXNlLlxuXHQgKi9cblx0dmFsaWRhdGVEbnNGaWVsZHMoKSB7XG5cdFx0aWYgKE1vZHVsZUdldFNzbC4kY2hhbGxlbmdlVHlwZS5kcm9wZG93bignZ2V0IHZhbHVlJykgIT09ICdkbnMnKSB7XG5cdFx0XHRyZXR1cm4gdHJ1ZTtcblx0XHR9XG5cdFx0aWYgKCFNb2R1bGVHZXRTc2wuJGRuc1Byb3ZpZGVyLmRyb3Bkb3duKCdnZXQgdmFsdWUnKSkge1xuXHRcdFx0VXNlck1lc3NhZ2Uuc2hvd0Vycm9yKGdsb2JhbFRyYW5zbGF0ZS5tb2R1bGVfZ2V0c3NsX0Ruc1Byb3ZpZGVyRW1wdHkpO1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0XHRsZXQgaGFzRW1wdHkgPSBmYWxzZTtcblx0XHQkKCcuZG5zLWNyZWQtaW5wdXQnKS5lYWNoKGZ1bmN0aW9uICgpIHtcblx0XHRcdGNvbnN0ICRmaWVsZCA9ICQodGhpcykuY2xvc2VzdCgnLmZpZWxkJyk7XG5cdFx0XHRjb25zdCBpc01hc2tlZCA9ICQodGhpcykuaXMoJ1tkYXRhLW1hc2tlZF0nKTtcblx0XHRcdGlmICghaXNNYXNrZWQgJiYgISQodGhpcykudmFsKCkudHJpbSgpKSB7XG5cdFx0XHRcdCRmaWVsZC5hZGRDbGFzcygnZXJyb3InKTtcblx0XHRcdFx0aGFzRW1wdHkgPSB0cnVlO1xuXHRcdFx0fSBlbHNlIHtcblx0XHRcdFx0JGZpZWxkLnJlbW92ZUNsYXNzKCdlcnJvcicpO1xuXHRcdFx0fVxuXHRcdH0pO1xuXHRcdGlmIChoYXNFbXB0eSkge1xuXHRcdFx0VXNlck1lc3NhZ2Uuc2hvd0Vycm9yKGdsb2JhbFRyYW5zbGF0ZS5tb2R1bGVfZ2V0c3NsX0Ruc0NyZWRlbnRpYWxzRW1wdHkpO1xuXHRcdFx0cmV0dXJuIGZhbHNlO1xuXHRcdH1cblx0XHRyZXR1cm4gdHJ1ZTtcblx0fSxcblxuXHQvKipcblx0ICogQ2FsbGJhY2sgYmVmb3JlIHNlbmRpbmcgdGhlIGZvcm0uXG5cdCAqIEBwYXJhbSB7T2JqZWN0fSBzZXR0aW5ncyAtIEFqYXggcmVxdWVzdCBzZXR0aW5ncy5cblx0ICogQHJldHVybnMge09iamVjdH0gVGhlIG1vZGlmaWVkIEFqYXggcmVxdWVzdCBzZXR0aW5ncy5cblx0ICovXG5cdGNiQmVmb3JlU2VuZEZvcm0oc2V0dGluZ3MpIHtcblx0XHRpZiAoIU1vZHVsZUdldFNzbC52YWxpZGF0ZVB1YmxpY0lwQWRkcmVzcygpKSB7XG5cdFx0XHRyZXR1cm4gZmFsc2U7XG5cdFx0fVxuXHRcdGlmICghTW9kdWxlR2V0U3NsLnZhbGlkYXRlRG5zRmllbGRzKCkpIHtcblx0XHRcdHJldHVybiBmYWxzZTtcblx0XHR9XG5cdFx0Y29uc3QgcmVzdWx0ID0gc2V0dGluZ3M7XG5cdFx0Ly8gQ29sbGVjdCBETlMgY3JlZGVudGlhbHMgaW50byBoaWRkZW4gZmllbGQgYmVmb3JlIGZvcm0gc3VibWlzc2lvblxuXHRcdE1vZHVsZUdldFNzbC5jb2xsZWN0RG5zQ3JlZGVudGlhbHMoKTtcblx0XHRyZXN1bHQuZGF0YSA9IE1vZHVsZUdldFNzbC4kZm9ybU9iai5mb3JtKCdnZXQgdmFsdWVzJyk7XG5cdFx0cmV0dXJuIHJlc3VsdDtcblx0fSxcblxuXHQvKipcblx0ICogQ2FsbGJhY2sgZnVuY3Rpb24gYWZ0ZXIgc2VuZGluZyB0aGUgZm9ybS5cblx0ICovXG5cdGNiQWZ0ZXJTZW5kRm9ybShyZXNwb25zZSkge1xuXHRcdGlmIChGb3JtLmNoZWNrU3VjY2VzcyhyZXNwb25zZSkpe1xuXHRcdFx0TW9kdWxlR2V0U3NsLmdldFNzbCgpO1xuXHRcdH1cblx0fSxcblxuXG5cdC8qKlxuXHQgKiBJbml0aWFsaXplcyB0aGUgZm9ybSB2YWxpZGF0aW9uIGFuZCBzdWJtaXNzaW9uIGxvZ2ljLlxuXHQgKi9cblx0aW5pdGlhbGl6ZUZvcm0oKSB7XG5cdFx0Rm9ybS4kZm9ybU9iaiA9IE1vZHVsZUdldFNzbC4kZm9ybU9iajtcblx0XHRGb3JtLnVybCA9IGAke2dsb2JhbFJvb3RVcmx9JHtpZFVybH0vJHtpZFVybH0vc2F2ZWA7XG5cdFx0Rm9ybS52YWxpZGF0ZVJ1bGVzID0gTW9kdWxlR2V0U3NsLnZhbGlkYXRlUnVsZXM7XG5cdFx0Rm9ybS5lbmFibGVEaXJyaXR5ID0gZmFsc2U7XG5cdFx0Rm9ybS5jYkFmdGVyU2VuZEZvcm0gPSBNb2R1bGVHZXRTc2wuY2JBZnRlclNlbmRGb3JtO1xuXHRcdEZvcm0uY2JCZWZvcmVTZW5kRm9ybSA9IE1vZHVsZUdldFNzbC5jYkJlZm9yZVNlbmRGb3JtO1xuXHRcdEZvcm0uaW5pdGlhbGl6ZSgpO1xuXHR9LFxufTtcblxuLy8gSW5pdGlhbGl6ZSB0aGUgTW9kdWxlR2V0U3NsIGNsYXNzIHdoZW4gdGhlIGRvY3VtZW50IGlzIHJlYWR5XG4kKGRvY3VtZW50KS5yZWFkeSgoKSA9PiB7XG5cdE1vZHVsZUdldFNzbC5pbml0aWFsaXplKCk7XG59KTtcbiJdLCJtYXBwaW5ncyI6IkFBQUE7O0FBRUE7QUFDQSxNQUFNLEtBQUssR0FBTyxnQkFBZ0IsQ0FBQyxDQUFjO0FBQ2pELE1BQU0sTUFBTSxHQUFNLHFCQUFxQixDQUFDLENBQVM7QUFDakQsTUFBTSxTQUFTLEdBQUcsY0FBYyxDQUFDLENBQWdCOztBQUVqRDtBQUNBLE1BQU0sWUFBWSxHQUFHO0VBQ3BCO0VBQ0EsUUFBUSxFQUFFLENBQUMsQ0FBQyxHQUFHLEdBQUcsTUFBTSxDQUFDO0VBQ3pCLFdBQVcsRUFBRSxDQUFDLENBQUMsR0FBRyxHQUFHLE1BQU0sR0FBRyxlQUFlLENBQUM7RUFDOUMsaUJBQWlCLEVBQUUsQ0FBQyxDQUFDLEdBQUcsR0FBRyxNQUFNLEdBQUcsY0FBYyxDQUFDO0VBQ25ELGFBQWEsRUFBRSxDQUFDLENBQUMsdUJBQXVCLENBQUM7RUFDekMsYUFBYSxFQUFFLENBQUMsQ0FBQyxlQUFlLENBQUM7RUFDakMsYUFBYSxFQUFFLENBQUMsQ0FBQyxTQUFTLENBQUM7RUFDM0IsV0FBVyxFQUFFLENBQUMsQ0FBQyxhQUFhLENBQUM7RUFDN0Is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 \ No newline at end of file diff --git a/public/assets/js/src/module-get-ssl-index.js b/public/assets/js/src/module-get-ssl-index.js index 8045160..75ddce7 100644 --- a/public/assets/js/src/module-get-ssl-index.js +++ b/public/assets/js/src/module-get-ssl-index.js @@ -1,4 +1,4 @@ -/* global globalRootUrl, globalTranslate, Form, Config, PbxApi, dnsProvidersMeta */ +/* global globalRootUrl, globalTranslate, Form, Config, PbxApi, dnsProvidersMeta, suggestedPublicIp */ // Constants related to the form and module const idUrl = 'module-get-ssl'; // API endpoint for SSL module @@ -150,6 +150,10 @@ const ModuleGetSsl = { this.$includeIpAddressCheckbox.checkbox('uncheck'); } const includeIp = canIncludeIp && this.$includeIpAddress.is(':checked'); + if (includeIp && !String(this.$publicIpAddress.val() || '').trim() + && typeof suggestedPublicIp === 'string' && this.isPublicIpAddress(suggestedPublicIp)) { + this.$publicIpAddress.val(suggestedPublicIp); + } this.$includeIpAddressField.toggle(canIncludeIp); this.$publicIpAddressSettings.toggle(includeIp); diff --git a/tests/CertificateIdentifierPolicyTest.php b/tests/CertificateIdentifierPolicyTest.php index 3b4230a..4438ddd 100644 --- a/tests/CertificateIdentifierPolicyTest.php +++ b/tests/CertificateIdentifierPolicyTest.php @@ -67,4 +67,25 @@ function assertSameValue($expected, $actual, string $message): void } assertSameValue(true, $thrown, 'Primary IP address must be public'); +assertSameValue( + '178.154.243.193', + CertificateIdentifierPolicy::selectSuggestedPublicIp('178.154.243.193', ['158.160.170.198']), + 'Configured external IP has priority' +); +assertSameValue( + '178.154.243.193', + CertificateIdentifierPolicy::selectSuggestedPublicIp('178.154.243.193:5060', ['158.160.170.198']), + 'Configured external IPv4 port is removed' +); +assertSameValue( + '158.160.170.198', + CertificateIdentifierPolicy::selectSuggestedPublicIp('10.0.0.24', ['192.168.1.10', '158.160.170.198']), + 'Domain resolution is the fallback' +); +assertSameValue( + '', + CertificateIdentifierPolicy::selectSuggestedPublicIp('', ['192.168.1.10']), + 'No public candidate produces no suggestion' +); + echo "CertificateIdentifierPolicyTest: PASS\n"; diff --git a/tests/ip-address-warning.test.mjs b/tests/ip-address-warning.test.mjs index 86d1696..d6b3a15 100644 --- a/tests/ip-address-warning.test.mjs +++ b/tests/ip-address-warning.test.mjs @@ -71,6 +71,7 @@ function loadController() { dnsProvidersMeta: [], moduleGetSSLStatusLoopWorker: {}, UserMessage: {}, + suggestedPublicIp: '178.154.243.193', }; vm.createContext(context); const source = fs.readFileSync(sourcePath, 'utf8') @@ -175,3 +176,19 @@ test('accepts only public addresses in the additional IP field', () => { assert.equal(controller.isPublicIpAddress('2001:db8::1'), false); assert.equal(controller.isPublicIpAddress('pbx.example.com'), false); }); + +test('fills an empty additional IP field from the server suggestion when enabled', () => { + const { controller, elements } = loadController(); + const address = elements.get('#domainName'); + const checkbox = elements.get('#includeIpAddress'); + const publicIp = elements.get('#publicIpAddress'); + + address.val('pbx.example.com'); + checkbox.checked = true; + controller.updateCertificateIdentifierControls(); + assert.equal(publicIp.val(), '178.154.243.193'); + + publicIp.val('8.8.8.8'); + controller.updateCertificateIdentifierControls(); + assert.equal(publicIp.val(), '8.8.8.8'); +}); From 0b6f0503ff421dd933295035e7c0c8529c033747 Mon Sep 17 00:00:00 2001 From: boffart <5922739+boffart@users.noreply.github.com> Date: Fri, 31 Jul 2026 16:54:49 +0300 Subject: [PATCH 8/8] fix: renew short-lived certificates before expiry --- Lib/CertificateIdentifierPolicy.php | 5 ++++- tests/GetSslPolicyIntegrationTest.php | 2 ++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Lib/CertificateIdentifierPolicy.php b/Lib/CertificateIdentifierPolicy.php index 0793b20..47e6bee 100644 --- a/Lib/CertificateIdentifierPolicy.php +++ b/Lib/CertificateIdentifierPolicy.php @@ -97,7 +97,10 @@ public static function buildAcmeIdentifierArguments(array $settings): string $arguments .= ' -d ' . escapeshellarg($identifier); } if (self::containsIpAddress($settings)) { - $arguments .= ' --cert-profile shortlived'; + // acme.sh otherwise retains its default ~60-day renewal window, + // which is longer than Let's Encrypt short-lived certificates. + // acme.sh subtracts one day from this value, so 5 renews after 4 days. + $arguments .= ' --cert-profile shortlived --days 5'; } return $arguments; } diff --git a/tests/GetSslPolicyIntegrationTest.php b/tests/GetSslPolicyIntegrationTest.php index a6d3942..481a835 100644 --- a/tests/GetSslPolicyIntegrationTest.php +++ b/tests/GetSslPolicyIntegrationTest.php @@ -28,6 +28,7 @@ function assertNotContainsText(string $needle, string $haystack, string $message assertContainsText(" -d 'pbx.example.com'", $mixed, 'Primary domain argument'); assertContainsText(" -d '8.8.4.4'", $mixed, 'Additional IP argument'); assertContainsText(' --cert-profile shortlived', $mixed, 'Short-lived profile'); +assertContainsText(' --days 5', $mixed, 'Short-lived certificate renews before expiry'); $domainOnly = CertificateIdentifierPolicy::buildAcmeIdentifierArguments([ 'domainName' => 'pbx.example.com', @@ -36,6 +37,7 @@ function assertNotContainsText(string $needle, string $haystack, string $message ]); assertContainsText(" -d 'pbx.example.com'", $domainOnly, 'Domain argument'); assertNotContainsText('--cert-profile', $domainOnly, 'Domain-only order is not forced short-lived'); +assertNotContainsText('--days', $domainOnly, 'Domain-only order keeps the default renewal interval'); $escaped = CertificateIdentifierPolicy::buildAcmeIdentifierArguments([ 'domainName' => "pbx'example.com",