diff --git a/emhttp/plugins/dynamix.plugin.manager/Downgrade.page b/emhttp/plugins/dynamix.plugin.manager/Downgrade.page deleted file mode 100644 index 037740ac6..000000000 --- a/emhttp/plugins/dynamix.plugin.manager/Downgrade.page +++ /dev/null @@ -1,154 +0,0 @@ -Menu="About:20" -Title="Downgrade OS" -Icon="icon-update" -Tag="upload" ---- -check(); - -require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php"; -$rebootDetails = new RebootDetails(); -$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 deleted file mode 100644 index c3a5e52e6..000000000 --- a/emhttp/plugins/dynamix.plugin.manager/Update.page +++ /dev/null @@ -1,54 +0,0 @@ -Menu="About:10" -Title="Update OS" -Icon="icon-update" -Tag="upload" ---- -check(); - -require_once "$docroot/plugins/dynamix.my.servers/include/reboot-details.php"; -$rebootDetails = new RebootDetails(); -?> - - - - - diff --git a/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheck.php b/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheck.php deleted file mode 100644 index a474ea9ba..000000000 --- a/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheck.php +++ /dev/null @@ -1,271 +0,0 @@ -checkForUpdate(); - * - * 2. Unraid webgui web components can GET this file with action params to get updates, ignore updates, etc. - * - EX: Unraid webgui web components can check for updates via a GET request and receive a response with the json file directly - * - this is useful for the UPC to check for updates and display a model based on the value - * - `/plugins/dynamix.plugin.manager/scripts/unraidcheck.php?json=true` - * - note the json=true query param to receive a json response - * - * @param action {'check'|'removeAllIgnored'|'removeIgnoredVersion'|'ignoreVersion'} - the action to perform - * @param version {string} - the version to ignore or remove - * @param json {string} - if set to true, will return the json response from the external request - * @param altUrl {URL} - if set, will use this url instead of the default - */ -$docroot ??= ($_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp'); -require_once "$docroot/webGui/include/Wrappers.php"; -require_once "$docroot/plugins/dynamix.plugin.manager/include/PluginHelpers.php"; - -class UnraidOsCheck -{ - private const BASE_RELEASES_URL = 'https://releases.unraid.net/os'; - private const JSON_FILE_IGNORED = '/tmp/unraidcheck/ignored.json'; - private const JSON_FILE_IGNORED_KEY = 'updateOsIgnoredReleases'; - private const JSON_FILE_RESULT = '/tmp/unraidcheck/result.json'; - private const PLG_PATH = '/usr/local/emhttp/plugins/unRAIDServer/unRAIDServer.plg'; - - public function __construct() - { - $isGetRequest = !empty($_SERVER) && isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'GET'; - $getHasAction = $_GET !== null && !empty($_GET) && isset($_GET['action']); - - if ($isGetRequest && $getHasAction) { - $this->handleGetRequestWithActions(); - } - } - - private function handleGetRequestWithActions() - { - switch ($_GET['action']) { - case 'check': - $this->checkForUpdate(); - break; - - case 'removeAllIgnored': - $this->removeAllIgnored(); - break; - - case 'removeIgnoredVersion': - if (isset($_GET['version'])) { - $this->removeIgnoredVersion($_GET['version']); - } - break; - - case 'ignoreVersion': - if (isset($_GET['version'])) { - $this->ignoreVersion($_GET['version']); - } - break; - - default: - $this->respondWithError(400, "Unhandled action"); - break; - } - } - - public function getUnraidOSCheckResult() - { - if (file_exists(self::JSON_FILE_RESULT)) { - return $this->readJsonFile(self::JSON_FILE_RESULT); - } - } - - public function getIgnoredReleases() - { - if (!file_exists(self::JSON_FILE_IGNORED)) { - return []; - } - - $ignoredData = $this->readJsonFile(self::JSON_FILE_IGNORED); - - if (is_array($ignoredData) && array_key_exists(self::JSON_FILE_IGNORED_KEY, $ignoredData)) { - return $ignoredData[self::JSON_FILE_IGNORED_KEY]; - } - - return []; - } - - /** @todo clean up this method to be more extensible */ - public function checkForUpdate() - { - // Multi-language support - if (!function_exists('_')) { - function _($text) {return $text;} - } - - // this command will set the $notify array - extract(parse_plugin_cfg('dynamix', true)); - - $var = (array)@parse_ini_file('/var/local/emhttp/var.ini'); - - $params = []; - $params['branch'] = plugin('category', self::PLG_PATH, 'stable'); - // Get current version from patches.json if it exists, otherwise fall back to plugin version or var.ini - $patcherVersion = null; - if (file_exists('/tmp/Patcher/patches.json')) { - $patcherData = @json_decode(file_get_contents('/tmp/Patcher/patches.json'), true); - $unraidVersionInfo = parse_ini_file('/etc/unraid-version'); - if ($patcherData['unraidVersion'] === $unraidVersionInfo['version']) { - $patcherVersion = $patcherData['combinedVersion'] ?? null; - } - } - - $params['current_version'] = $patcherVersion ?: plugin('version', self::PLG_PATH) ?: _var($var, 'version'); - if (_var($var,'regExp')) $params['update_exp'] = date('Y-m-d', _var($var,'regExp')*1); - $defaultUrl = self::BASE_RELEASES_URL; - // pass a param of altUrl to use the provided url instead of the default - $parsedAltUrl = (array_key_exists('altUrl',$_GET) && $_GET['altUrl']) ? $_GET['altUrl'] : null; - // if $parsedAltUrl pass to params - if ($parsedAltUrl) $params['altUrl'] = $parsedAltUrl; - - $urlbase = $parsedAltUrl ?? $defaultUrl; - $url = $urlbase.'?'.http_build_query($params); - $curlinfo = []; - $response = http_get_contents($url,[],$curlinfo); - if (array_key_exists('error', $curlinfo)) { - $response = json_encode(array('error' => $curlinfo['error']), JSON_PRETTY_PRINT); - } - $responseMutated = json_decode($response, true); - if (!$responseMutated) { - $response = json_encode(array('error' => 'Invalid response from '.$urlbase), JSON_PRETTY_PRINT); - $responseMutated = json_decode($response, true); - } - - // add params that were used for debugging - $responseMutated['params'] = $params; - - // store locally for UPC to access - $this->writeJsonFile(self::JSON_FILE_RESULT, $responseMutated); - - // if we have a query param of json=true then just output the json - if (array_key_exists('json',$_GET) && $_GET['json']) { - header('Content-Type: application/json'); - echo $response; - exit(0); - } - - // send notification if a newer version is available and not ignored - $isNewerVersion = array_key_exists('isNewer',$responseMutated) ? $responseMutated['isNewer'] : false; - $isReleaseIgnored = array_key_exists('version',$responseMutated) ? in_array($responseMutated['version'], $this->getIgnoredReleases()) : false; - - if ($responseMutated && $isNewerVersion && !$isReleaseIgnored) { - $output = _var($notify,'plugin'); - $server = strtoupper(_var($var,'NAME','server')); - $newver = (array_key_exists('version',$responseMutated) && $responseMutated['version']) ? $responseMutated['version'] : 'unknown'; - $script = '/usr/local/emhttp/webGui/scripts/notify'; - $event = "System - Unraid [$newver]"; - $subject = "Notice [$server] - Version update $newver"; - $description = "A new version of Unraid is available"; - exec("$script -e ".escapeshellarg($event)." -s ".escapeshellarg($subject)." -d ".escapeshellarg($description)." -i ".escapeshellarg("normal $output")." -l '/Tools/Update' -x"); - } - - exit(0); - } - - private function removeAllIgnored() - { - if (file_exists(self::JSON_FILE_IGNORED)) { - $this->deleteJsonFile(self::JSON_FILE_IGNORED); - $this->respondWithSuccess([]); - } - // fail silently if file doesn't exist - } - - private function removeIgnoredVersion($removeVersion) - { - if ($this->isValidSemVerFormat($removeVersion)) { - if (file_exists(self::JSON_FILE_IGNORED)) { - $existingData = $this->readJsonFile(self::JSON_FILE_IGNORED); - - if (isset($existingData[self::JSON_FILE_IGNORED_KEY])) { - $existingData[self::JSON_FILE_IGNORED_KEY] = array_diff($existingData[self::JSON_FILE_IGNORED_KEY], [$removeVersion]); - $this->writeJsonFile(self::JSON_FILE_IGNORED, $existingData); - $this->respondWithSuccess($existingData); - } else { - $this->respondWithError(400, "No versions to remove in the JSON file"); - } - } else { - $this->respondWithError(400, "No JSON file found"); - } - } else { - $this->respondWithError(400, "Invalid removeVersion format"); - } - } - - private function ignoreVersion($version) - { - if ($this->isValidSemVerFormat($version)) { - $newData = [$this::JSON_FILE_IGNORED_KEY => [$version]]; - $existingData = file_exists(self::JSON_FILE_IGNORED) ? $this->readJsonFile(self::JSON_FILE_IGNORED) : []; - - if (isset($existingData[self::JSON_FILE_IGNORED_KEY])) { - $existingData[self::JSON_FILE_IGNORED_KEY][] = $version; - } else { - $existingData[self::JSON_FILE_IGNORED_KEY] = [$version]; - } - - $this->writeJsonFile(self::JSON_FILE_IGNORED, $existingData); - $this->respondWithSuccess($existingData); - } else { - $this->respondWithError(400, "Invalid version format"); - } - } - - private function isValidSemVerFormat($version) - { - return preg_match('/^\d+\.\d+(\.\d+)?(-.+)?$/', $version); - } - - private function readJsonFile($file) - { - return @json_decode(@file_get_contents($file), true) ?? []; - } - - private function writeJsonFile($file, $data) - { - if (!is_dir(dirname($file))) { // prevents errors when directory doesn't exist - mkdir(dirname($file)); - } - file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); - } - - private function deleteJsonFile($file) - { - unlink($file); - } - - private function respondWithError($statusCode, $message) - { - http_response_code($statusCode); - echo $message; - } - - private function respondWithSuccess($data) - { - http_response_code(200); - header('Content-Type: application/json'); - echo json_encode($data, JSON_PRETTY_PRINT); - } -} - -// Instantiate and handle the request for GET requests with actions – vars are duplicated here for multi-use of this file -$isGetRequest = !empty($_SERVER) && isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'GET'; -$getHasAction = $_GET !== null && !empty($_GET) && isset($_GET['action']); -if ($isGetRequest && $getHasAction) { - new UnraidOsCheck(); -} diff --git a/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php b/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php deleted file mode 100644 index 7b2881cd2..000000000 --- a/emhttp/plugins/dynamix.plugin.manager/include/UnraidCheckExec.php +++ /dev/null @@ -1,68 +0,0 @@ - 'true', - ]; - - if (isset($_GET['altUrl'])) { - $url = filter_var($_GET['altUrl'], FILTER_VALIDATE_URL); - if ($url !== false) { - $host = parse_url($url, PHP_URL_HOST); - $scheme = parse_url($url, PHP_URL_SCHEME); - - if ($host && $scheme === 'https' && ( - $host === self::ALLOWED_DOMAIN || - str_ends_with($host, '.' . self::ALLOWED_DOMAIN) - )) { - $params['altUrl'] = $url; - } - } - } - - putenv('QUERY_STRING=' . http_build_query($params)); - } - - public function execute(): string - { - // Validate script with all necessary permissions - if (!is_file(self::SCRIPT_PATH) || - !is_readable(self::SCRIPT_PATH) || - !is_executable(self::SCRIPT_PATH)) { - throw new RuntimeException('Script not found or not executable'); - } - - $this->setupEnvironment(); - $output = []; - $command = escapeshellcmd(self::SCRIPT_PATH); - if (exec($command, $output) === false) { - throw new RuntimeException('Script execution failed'); - } - - return implode("\n", $output); - } -} - -// Usage -$checker = new UnraidCheckExec(); -echo $checker->execute(); diff --git a/emhttp/plugins/dynamix.plugin.manager/include/UnraidUpdateCancel.php b/emhttp/plugins/dynamix.plugin.manager/include/UnraidUpdateCancel.php deleted file mode 100644 index 6180dd041..000000000 --- a/emhttp/plugins/dynamix.plugin.manager/include/UnraidUpdateCancel.php +++ /dev/null @@ -1,60 +0,0 @@ -PLG_FILENAME = "unRAIDServer.plg"; - $this->PLG_BOOT = "/boot/config/plugins/{$this->PLG_FILENAME}"; - $this->PLG_VAR = "/var/log/plugins/{$this->PLG_FILENAME}"; - $this->USR_LOCAL_PLUGIN_UNRAID_PATH = "/usr/local/emhttp/plugins/unRAIDServer"; - - // Handle the cancellation - $revertResult = $this->revertFiles(); - // Return JSON response for front-end client - $statusCode = $revertResult['success'] ? 200 : 500; - http_response_code($statusCode); - header('Content-Type: application/json'); - echo json_encode($revertResult); - } - - public function revertFiles() { - try { - $command = '/sbin/mount | grep -q "/boot/previous/bz"'; - exec($command, $output, $returnCode); - - if ($returnCode !== 0) { - return ['success' => true]; // Nothing to revert - } - - // Clear the results of previous unraidcheck run - @unlink("/tmp/unraidcheck/result.json"); - - // Revert changes made by unRAIDServer.plg - shell_exec("mv -f /boot/previous/* /boot"); - unlink($this->PLG_BOOT); - unlink($this->PLG_VAR); - symlink("{$this->USR_LOCAL_PLUGIN_UNRAID_PATH}/{$this->PLG_FILENAME}", $this->PLG_VAR); - - // Restore README.md by echoing the content into the file - $readmeFile = "{$this->USR_LOCAL_PLUGIN_UNRAID_PATH}/README.md"; - $readmeContent = "**Unraid OS**\n\n"; - $readmeContent .= "Unraid OS by [Lime Technology, Inc.](https://lime-technology.com).\n"; - file_put_contents($readmeFile, $readmeContent); - - return ['success' => true]; // Upgrade handled successfully - } catch (\Throwable $th) { - return [ - 'success' => false, - 'message' => $th->getMessage(), - ]; - } - } -} - -// Self instantiate the class and handle the cancellation -new UnraidUpdateCancel(); diff --git a/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck b/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck deleted file mode 100755 index 81cb3ea58..000000000 --- a/emhttp/plugins/dynamix.plugin.manager/scripts/unraidcheck +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/php -q - -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(); diff --git a/emhttp/plugins/dynamix/include/ReplaceKey.php b/emhttp/plugins/dynamix/include/ReplaceKey.php deleted file mode 100644 index d180d92f2..000000000 --- a/emhttp/plugins/dynamix/include/ReplaceKey.php +++ /dev/null @@ -1,237 +0,0 @@ -docroot = $GLOBALS['docroot'] ?? $_SERVER['DOCUMENT_ROOT'] ?: '/usr/local/emhttp'; - - $this->var = (array)@parse_ini_file('/var/local/emhttp/var.ini'); - $this->guid = @$this->var['regGUID'] ?? null; - - $keyfileBase64 = empty($this->var['regFILE']) ? null : @file_get_contents($this->var['regFILE']); - if ($keyfileBase64 !== false) { - $keyfileBase64 = @base64_encode($keyfileBase64); - $this->keyfile = str_replace(['+', '/', '='], ['-', '_', ''], trim($keyfileBase64)); - } - - $this->regExp = @$this->var['regExp'] ?? null; - } - - private function request($url, $method, $payload = null, $headers = null) - { - $ch = curl_init($url); - - // Set the request method - curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method); - // store the response in a variable instead of printing it - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - - // Set the payload if present - if ($payload !== null) { - curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); - } - - if ($headers !== null) { - // Set the headers - curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); - } - - // Set additional options as needed - - // Execute the request - $response = curl_exec($ch); - - // Check for errors - if (curl_errno($ch)) { - $error = [ - 'heading' => 'CurlError', - 'message' => curl_error($ch), - 'level' => 'error', - 'ref' => 'curlError', - 'type' => 'request', - ]; - // @todo store error - } - - // Close the cURL session - curl_close($ch); - - return $response; - } - - private function validateGuid() - { - $headers = [ - 'Content-Type: application/x-www-form-urlencoded', - ]; - - $params = [ - 'guid' => $this->guid, - 'keyfile' => $this->keyfile, - ]; - - /** - * returns {JSON} - * hasNewerKeyfile : boolean; - * purchaseable: true; - * registered: false; - * replaceable: false; - * upgradeable: false; - * upgradeAllowed: string[]; - * updatesRenewable: false; - */ - $response = $this->request( - self::KEY_SERVER_URL . '/validate/guid', - 'POST', - http_build_query($params), - $headers, - ); - - // Handle the response as needed (parsing JSON, etc.) - $decodedResponse = json_decode($response, true); - - if (!empty($decodedResponse)) { - return $decodedResponse; - } - - // @todo save error response somewhere - return []; - } - - private function getLatestKey() - { - $headers = [ - 'Content-Type: application/x-www-form-urlencoded', - ]; - - $params = [ - 'keyfile' => $this->keyfile, - ]; - - /** - * returns {JSON} - * license: string; - */ - $response = $this->request( - self::KEY_SERVER_URL . '/key/latest', - 'POST', - http_build_query($params), - $headers, - ); - - // Handle the response as needed (parsing JSON, etc.) - $decodedResponse = json_decode($response, true); - - if (!empty($decodedResponse) && !empty($decodedResponse['license'])) { - return $decodedResponse['license']; - } - return null; - } - - private function installNewKey($key) - { - require_once "$this->docroot/webGui/include/InstallKey.php"; - - $KeyInstaller = new KeyInstaller(); - $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) - { - if (!is_dir(dirname($file))) { - mkdir(dirname($file)); - } - - file_put_contents($file, json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES)); - } - - public function check(bool $forceCheck = false) - { - // we don't need to check - if (empty($this->guid) || empty($this->keyfile) || empty($this->regExp)) { - return; - } - - // 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); - - $isWithinWindow = ($now >= $sevenDaysBefore && $now <= $sevenDaysAfter); - - if (!$forceCheck && !$isWithinWindow) { - return; - } - - // see if we have a new key - $validateGuidResponse = $this->validateGuid(); - - $hasNewerKeyfile = @$validateGuidResponse['hasNewerKeyfile'] ?? false; - if (!$hasNewerKeyfile) { - return; // if there is no newer keyfile, we don't need to do anything - } - - $latestKey = $this->getLatestKey(); - if (!$latestKey) { - // we supposedly have a new key, but didn't get it back… - $this->writeJsonFile( - '/tmp/ReplaceKey/error.json', - [ - 'error' => 'Failed to retrieve latest key after getting a `hasNewerKeyfile` in the validation response.', - 'ts' => time(), - ] - ); - return; - } - - $this->installNewKey($latestKey); - } -} diff --git a/emhttp/plugins/dynamix/include/UpdateDNS.php b/emhttp/plugins/dynamix/include/UpdateDNS.php deleted file mode 100644 index c33893561..000000000 --- a/emhttp/plugins/dynamix/include/UpdateDNS.php +++ /dev/null @@ -1,22 +0,0 @@ - -