From bd640b1f620e3f66b458cf7f00b16929cce3358e Mon Sep 17 00:00:00 2001 From: Zack Spear Date: Tue, 18 Mar 2025 15:51:08 -0700 Subject: [PATCH 1/6] feat: ReplaceKey 7 day window around regExp & new forceCheck param (#10) * feat: ReplaceKey 7 day window around regExp & new forceCheck param * feat: Enhance writeJsonFile to support appending data --- emhttp/plugins/dynamix/include/ReplaceKey.php | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/emhttp/plugins/dynamix/include/ReplaceKey.php b/emhttp/plugins/dynamix/include/ReplaceKey.php index b260d4ed9..4071c6b33 100644 --- a/emhttp/plugins/dynamix/include/ReplaceKey.php +++ b/emhttp/plugins/dynamix/include/ReplaceKey.php @@ -147,28 +147,35 @@ class ReplaceKey $KeyInstaller->installKey($key); } - private function writeJsonFile($file, $data) + private function writeJsonFile($file, $data, $append = false) { - if (!is_dir(dirname($file))) { // prevents errors when directory doesn't exist + if (!is_dir(dirname($file))) { mkdir(dirname($file)); } + + if ($append && file_exists($file)) { + $existing = json_decode(file_get_contents($file), true) ?: []; + $data = array_merge_recursive($existing, $data); + } + file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } - public function check() + public function check(bool $forceCheck = false) { // we don't need to check if (empty($this->guid) || empty($this->keyfile) || empty($this->regExp)) { return; } - // set $isAlreadyExpired to true if regExp is in the past - $isAlreadyExpired = $this->regExp <= time(); - // if regExp is seven days or less from now, we need to check - $isWithinSevenDays = $this->regExp <= strtotime('+7 days'); + // Check if we're within the 7-day window before and after regExp + $now = time(); + $sevenDaysBefore = strtotime('-7 days', $this->regExp); + $sevenDaysAfter = strtotime('+7 days', $this->regExp); - $shouldCheck = $isAlreadyExpired || $isWithinSevenDays; - if (!$shouldCheck) { + $isWithinWindow = ($now >= $sevenDaysBefore && $now <= $sevenDaysAfter); + + if ($forceCheck || $isWithinWindow) { return; } @@ -187,7 +194,9 @@ class ReplaceKey '/tmp/ReplaceKey/error.json', [ 'error' => 'Failed to retrieve latest key after getting a `hasNewerKeyfile` in the validation response.', - ] + 'ts' => time(), + ], + true, ); return; } From 80869a032cea028628aa7865059e482107058158 Mon Sep 17 00:00:00 2001 From: Zack Spear Date: Thu, 20 Mar 2025 12:31:04 -0700 Subject: [PATCH 2/6] refactor: Simplify writeJsonFile method and adjust forceCheck logic in ReplaceKey * Removed appending functionality from writeJsonFile for cleaner implementation. * Inverted condition for forceCheck to improve logic clarity. --- emhttp/plugins/dynamix/include/ReplaceKey.php | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/emhttp/plugins/dynamix/include/ReplaceKey.php b/emhttp/plugins/dynamix/include/ReplaceKey.php index 4071c6b33..b6d7439ab 100644 --- a/emhttp/plugins/dynamix/include/ReplaceKey.php +++ b/emhttp/plugins/dynamix/include/ReplaceKey.php @@ -147,17 +147,12 @@ class ReplaceKey $KeyInstaller->installKey($key); } - private function writeJsonFile($file, $data, $append = false) + private function writeJsonFile($file, $data) { if (!is_dir(dirname($file))) { mkdir(dirname($file)); } - if ($append && file_exists($file)) { - $existing = json_decode(file_get_contents($file), true) ?: []; - $data = array_merge_recursive($existing, $data); - } - file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); } @@ -175,7 +170,7 @@ class ReplaceKey $isWithinWindow = ($now >= $sevenDaysBefore && $now <= $sevenDaysAfter); - if ($forceCheck || $isWithinWindow) { + if (!$forceCheck && !$isWithinWindow) { return; } @@ -195,8 +190,7 @@ class ReplaceKey [ 'error' => 'Failed to retrieve latest key after getting a `hasNewerKeyfile` in the validation response.', 'ts' => time(), - ], - true, + ] ); return; } From 58bee5744de5e2df562e5e84746d7af18d4abcbd Mon Sep 17 00:00:00 2001 From: Zack Spear Date: Thu, 20 Mar 2025 12:41:38 -0700 Subject: [PATCH 3/6] fix: KeyInstaller class responseComplete usage --- emhttp/plugins/dynamix/include/InstallKey.php | 21 +++++++++++-------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/emhttp/plugins/dynamix/include/InstallKey.php b/emhttp/plugins/dynamix/include/InstallKey.php index 62b80a912..467a90322 100644 --- a/emhttp/plugins/dynamix/include/InstallKey.php +++ b/emhttp/plugins/dynamix/include/InstallKey.php @@ -32,20 +32,20 @@ class KeyInstaller * @param int $httpcode https://developer.mozilla.org/en-US/docs/Web/HTTP/Status * @param string|array $result - strings are assumed to be encoded JSON. Arrays will be encoded to JSON. */ - private function responseComplete($httpcode, $result) + private function responseComplete($httpcode, $result): string { $mutatedResult = is_array($result) ? json_encode($result) : $result; if ($this->isGetRequest && $this->getHasUrlParam) { // return JSON to the caller - header('Content-Type: application/json'); - http_response_code($httpcode); - exit((string)$mutatedResult); + header('Content-Type: application/json'); + http_response_code($httpcode); + exit((string)$mutatedResult); } else { // return the result to the caller - return $mutatedResult; + return $mutatedResult; } } - public function installKey($keyUrl = null) + public function installKey($keyUrl = null): string { $url = unscript($keyUrl ?? _var($_GET, 'url')); $host = parse_url($url)['host'] ?? ''; @@ -61,9 +61,12 @@ class KeyInstaller if ($returnVar === 0) { $var = (array)@parse_ini_file('/var/local/emhttp/var.ini'); if (_var($var, 'mdState') == "STARTED") { - return $this->responseComplete(200, ['status' => _('Please Stop array to complete key installation')], _('success') . ', ' . _('Please Stop array to complete key installation')); + return $this->responseComplete(200, [ + 'status' => 'success', + 'message' => _('Please Stop array to complete key installation'), + ]); } else { - return $this->responseComplete(200, ['status' => ''], _('success')); + return $this->responseComplete(200, ['status' => 'success']); } } else { @unlink(escapeshellarg("/boot/config/$keyFile")); @@ -81,4 +84,4 @@ $getHasUrlParam = $_GET !== null && !empty($_GET) && isset($_GET['url']); if ($isGetRequest && $getHasUrlParam) { $keyInstaller = new KeyInstaller(); $keyInstaller->installKey(); -} +} \ No newline at end of file From 1768d77c3107f495a33ff974f675dcb4fd0ae4b5 Mon Sep 17 00:00:00 2001 From: Zack Spear Date: Thu, 20 Mar 2025 14:48:29 -0700 Subject: [PATCH 4/6] feat: Enhance ReplaceKey to handle installation response and notifications * Added logic to process the response from KeyInstaller's installKey method. * Implemented error handling and notification system based on installation success or failure. * Notifications include event, subject, and description for better user feedback. --- emhttp/plugins/dynamix/include/ReplaceKey.php | 44 +++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/emhttp/plugins/dynamix/include/ReplaceKey.php b/emhttp/plugins/dynamix/include/ReplaceKey.php index b6d7439ab..502064efb 100644 --- a/emhttp/plugins/dynamix/include/ReplaceKey.php +++ b/emhttp/plugins/dynamix/include/ReplaceKey.php @@ -1,5 +1,4 @@ docroot/webGui/include/InstallKey.php"; $KeyInstaller = new KeyInstaller(); - $KeyInstaller->installKey($key); + $installResponse = $KeyInstaller->installKey($key); + + $installSuccess = false; + + if (!empty($installResponse)) { + $decodedResponse = json_decode($installResponse, true); + if (isset($decodedResponse['error'])) { + $this->writeJsonFile( + '/tmp/ReplaceKey/error.json', + [ + 'error' => $decodedResponse['error'], + 'ts' => time(), + ] + ); + $installSuccess = false; + } else { + $installSuccess = true; + } + } + + $keyType = basename($key, '.key'); + $output = isset($GLOBALS['notify']) ? _var($GLOBALS['notify'],'plugin') : ''; + $script = '/usr/local/emhttp/webGui/scripts/notify'; + + if ($installSuccess) { + $event = "Installed New $keyType License"; + $subject = "Your new $keyType license key has been automatically installed"; + $description = ""; + $importance = "normal $output"; + } else { + $event = "Failed to Install New $keyType License"; + $subject = "Failed to automatically install your new $keyType license key"; + $description = isset($decodedResponse['error']) ? $decodedResponse['error'] : "Unknown error occurred"; + $importance = "alert $output"; + } + + exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg($importance)." -l '/Tools/Registration' -x"); + + return $installSuccess; } private function writeJsonFile($file, $data) @@ -194,6 +231,7 @@ class ReplaceKey ); return; } + $this->installNewKey($latestKey); } -} +} \ No newline at end of file From f6aa74c264bc40bea1641bf8ff2ef29afe541ff4 Mon Sep 17 00:00:00 2001 From: Zack Spear Date: Thu, 20 Mar 2025 14:52:29 -0700 Subject: [PATCH 5/6] feat: Integrate ReplaceKey functionality across multiple pages * Added `ReplaceKey` initialization and check to `Registration.page`, `Downgrade.page`, `Update.page`, and `unraidcheck` script. * Ensures consistent handling of key replacement across relevant components. --- emhttp/plugins/dynamix.my.servers/Registration.page | 5 ++++- emhttp/plugins/dynamix.plugin.manager/Downgrade.page | 5 +++-- emhttp/plugins/dynamix.plugin.manager/Update.page | 4 ++++ emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck | 7 +++++-- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/emhttp/plugins/dynamix.my.servers/Registration.page b/emhttp/plugins/dynamix.my.servers/Registration.page index 8aa41be25..812ed720b 100644 --- a/emhttp/plugins/dynamix.my.servers/Registration.page +++ b/emhttp/plugins/dynamix.my.servers/Registration.page @@ -14,7 +14,10 @@ Tag="pencil" * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. */ +require_once "$docroot/plugins/dynamix/include/ReplaceKey.php"; +$replaceKey = new ReplaceKey(); +$replaceKey->check(true); ?> - + \ No newline at end of file diff --git a/emhttp/plugins/dynamix.plugin.manager/Downgrade.page b/emhttp/plugins/dynamix.plugin.manager/Downgrade.page index 0019b5167..037740ac6 100644 --- a/emhttp/plugins/dynamix.plugin.manager/Downgrade.page +++ b/emhttp/plugins/dynamix.plugin.manager/Downgrade.page @@ -16,11 +16,12 @@ Tag="upload" /** * @note icon-update is rotated via CSS in myservers1.php */ +require_once "$docroot/plugins/dynamix/include/ReplaceKey.php"; +$replaceKey = new ReplaceKey(); +$replaceKey->check(); require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php"; -// Create an instance of the RebootDetails class $rebootDetails = new RebootDetails(); -// Get the current reboot details if there are any $rebootDetails->setPrevious(); $serverNameEscaped = htmlspecialchars(str_replace(' ', '_', strtolower($var['NAME']))); diff --git a/emhttp/plugins/dynamix.plugin.manager/Update.page b/emhttp/plugins/dynamix.plugin.manager/Update.page index 82d0d337c..c3a5e52e6 100644 --- a/emhttp/plugins/dynamix.plugin.manager/Update.page +++ b/emhttp/plugins/dynamix.plugin.manager/Update.page @@ -13,6 +13,10 @@ Tag="upload" * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. */ +require_once "$docroot/plugins/dynamix/include/ReplaceKey.php"; +$replaceKey = new ReplaceKey(); +$replaceKey->check(); + require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php"; $rebootDetails = new RebootDetails(); ?> diff --git a/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck b/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck index bb87f6343..787c9b49c 100755 --- a/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck +++ b/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck @@ -13,7 +13,10 @@ ?> check(); +require_once "$docroot/plugins/dynamix.plugin.manager/include/UnraidCheck.php"; $unraidOsCheck = new UnraidOsCheck(); -$unraidOsCheck->checkForUpdate(); +$unraidOsCheck->checkForUpdate(); \ No newline at end of file From 116732fab7284212462037a0098cc2402f848d42 Mon Sep 17 00:00:00 2001 From: Zack Spear Date: Thu, 20 Mar 2025 17:34:24 -0700 Subject: [PATCH 6/6] feat: UnraidCheckExec for calling unraidcheck from web components * Added `UnraidCheckExec` class to handle execution of the unraidcheck script and return JSON responses. * Updated the unraidcheck script to parse query parameters for better integration with web components. * This change maintains separation of concerns between UnraidCheck and ReplaceKey functionalities. --- .../include/UnraidCheckExec.php | 42 +++++++++++++++++++ .../scripts/unraidcheck | 3 ++ 2 files changed, 45 insertions(+) create mode 100644 emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php diff --git a/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php b/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php new file mode 100644 index 000000000..15523e19a --- /dev/null +++ b/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php @@ -0,0 +1,42 @@ + 'true', + ]; + // allows the web components to determine the OS_RELEASES url + if (isset($_GET['altUrl']) && filter_var($_GET['altUrl'], FILTER_VALIDATE_URL)) { + $params['altUrl'] = $_GET['altUrl']; + } + // pass the params to the unraidcheck script for usage in UnraidCheck.php + putenv('QUERY_STRING=' . http_build_query($params)); + } + + public function execute(): string + { + $this->setupEnvironment(); + $output = []; + exec(self::SCRIPT_PATH, $output); + return implode("\n", $output); + } +} + +// Usage +$checker = new UnraidCheckExec(); +echo $checker->execute(); \ No newline at end of file diff --git a/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck b/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck index 787c9b49c..71766db8f 100755 --- a/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck +++ b/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck @@ -17,6 +17,9 @@ require_once "$docroot/plugins/dynamix/include/ReplaceKey.php"; $replaceKey = new ReplaceKey(); $replaceKey->check(); +// utilized by UnraidCheckExec.php to have UnraidCheck.php return a json response when this script is called directly +parse_str(getenv('QUERY_STRING') ?? '', $_GET); + require_once "$docroot/plugins/dynamix.plugin.manager/include/UnraidCheck.php"; $unraidOsCheck = new UnraidOsCheck(); $unraidOsCheck->checkForUpdate(); \ No newline at end of file