%PDF- %PDF-
Direktori : /backups/router/usr/local/opnsense/mvc/app/controllers/OPNsense/Core/Api/ |
Current File : //backups/router/usr/local/opnsense/mvc/app/controllers/OPNsense/Core/Api/FirmwareController.php |
<?php /* * Copyright (c) 2015-2023 Franco Fichtner <franco@opnsense.org> * Copyright (c) 2015-2018 Deciso B.V. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ namespace OPNsense\Core\Api; use OPNsense\Base\ApiMutableModelControllerBase; use OPNsense\Core\Backend; use OPNsense\Core\Config; use OPNsense\Core\Firmware; use OPNsense\Core\SanitizeFilter; /** * Class FirmwareController * @package OPNsense\Core */ class FirmwareController extends ApiMutableModelControllerBase { protected static $internalModelName = 'firmware'; protected static $internalModelClass = 'OPNsense\Core\Firmware'; /** * return bytes in human-readable form * @param integer $bytes bytes to convert * @return string */ protected function formatBytes($bytes) { if (preg_match('/[^0-9]/', $bytes)) { /* already processed */ return $bytes; } if ($bytes >= 1024 ** 5) { return sprintf('%.1F%s', $bytes / (1024 ** 5), 'PiB'); } elseif ($bytes >= 1024 ** 4) { return sprintf('%.1F%s', $bytes / (1024 ** 4), 'TiB'); } elseif ($bytes >= 1024 ** 3) { return sprintf('%.1F%s', $bytes / (1024 ** 3), 'GiB'); } elseif ($bytes >= 1024 ** 2) { return sprintf('%.1F%s', $bytes / (1024 ** 2), 'MiB'); } elseif ($bytes >= 1024) { return sprintf('%.1F%s', $bytes / 1024, 'KiB'); } else { return sprintf('%d%s', $bytes, 'B'); } } /** * Run check for updates * @return array */ public function checkAction() { $response = []; if ($this->request->isPost()) { $backend = new Backend(); $response['msg_uuid'] = trim($backend->configdRun('firmware check', true)); $response['status'] = 'ok'; } else { $response['status'] = 'failure'; } return $response; } /** * retrieve available updates * @return array */ public function statusAction() { $active_array = []; $active_count = 0; $active_size = ''; $active_status = ''; $backend = new Backend(); if ($this->request->isPost()) { /* run a synchronous check prior to the result fetch */ $backend->configdRun('firmware probe'); } $product = json_decode(trim($backend->configdRun('firmware product')), true); if ($product == null) { $response = [ 'status_msg' => gettext('Firmware status check was aborted internally. Please try again.'), 'status' => 'error', ]; } elseif ($product['product_check'] == null) { $response = [ 'product' => $product, 'status_msg' => gettext('Firmware status requires to check for update first to provide more information.'), 'status' => 'none', ]; } else { $response = $product['product_check']; $response['product'] = $product; $download_size = !empty($response['download_size']) ? $response['download_size'] : 0; $upgrade_size = 0; $update_size = 0; if (!empty($response['upgrade_packages'])) { foreach ($response['upgrade_packages'] as $listing) { if (!empty($listing['size']) && is_numeric($listing['size'])) { $update_size += $listing['size']; } } } foreach (explode(',', $download_size) as $size) { if (preg_match('/\s*(\d+)\s*([a-z])/i', $size, $matches)) { $factor = 1; switch (isset($matches[2]) ? strtolower($matches[2]) : 'b') { case 'p': $factor *= 1024; /* FALLTHROUGH */ case 't': $factor *= 1024; /* FALLTHROUGH */ case 'g': $factor *= 1024; /* FALLTHROUGH */ case 'm': $factor *= 1024; /* FALLTHROUGH */ case 'k': $factor *= 1024; /* FALLTHROUGH */ default: break; } $update_size += $factor * $matches[1]; } } $sorted = []; foreach ( array('new_packages', 'reinstall_packages', 'upgrade_packages', 'downgrade_packages', 'remove_packages') as $pkg_type ) { if (isset($response[$pkg_type])) { foreach ($response[$pkg_type] as $value) { switch ($pkg_type) { case 'downgrade_packages': $sorted[$value['name']] = array( 'reason' => gettext('downgrade'), 'old' => $value['current_version'], 'new' => $value['new_version'], 'name' => $value['name'], 'repository' => $value['repository'], ); break; case 'new_packages': $sorted[$value['name']] = array( 'new' => $value['version'], 'reason' => gettext('new'), 'name' => $value['name'], 'repository' => $value['repository'], 'old' => gettext('N/A'), ); break; case 'reinstall_packages': $sorted[$value['name']] = array( 'reason' => gettext('reinstall'), 'new' => $value['version'], 'old' => $value['version'], 'repository' => $value['repository'], 'name' => $value['name'], ); break; case 'remove_packages': $sorted[$value['name']] = array( 'reason' => gettext('obsolete'), 'new' => gettext('N/A'), 'old' => $value['version'], 'name' => $value['name'], 'repository' => $value['repository'], ); break; case 'upgrade_packages': $sorted[$value['name']] = array( 'reason' => gettext('upgrade'), 'old' => empty($value['current_version']) ? gettext('N/A') : $value['current_version'], 'new' => $value['new_version'], 'repository' => $value['repository'], 'name' => $value['name'], ); break; default: /* undefined */ break; } } } } uksort($sorted, function ($a, $b) { return strnatcasecmp($a, $b); }); $response['all_packages'] = $sorted; $active_count = count($response['all_packages']); $active_array = &$response['all_packages']; $active_size = $update_size; $active_status = 'update'; $active_reboot = '0'; $sorted = []; if (isset($response['upgrade_sets'])) { foreach ($response['upgrade_sets'] as $value) { if (!empty($value['size']) && is_numeric($value['size'])) { $upgrade_size += $value['size']; } $sorted[$value['name']] = array( 'reason' => gettext('upgrade'), 'old' => empty($value['current_version']) ? gettext('N/A') : $value['current_version'], 'new' => $value['new_version'], 'repository' => $value['repository'], 'name' => $value['name'], ); } } uksort($sorted, function ($a, $b) { return strnatcasecmp($a, $b); }); $response['all_sets'] = $sorted; if ($active_count == 0) { $active_count = count($response['all_sets']); $active_array = &$response['all_sets']; $active_size = $upgrade_size; $active_status = 'upgrade'; } $subscription = strpos($response['product']['product_mirror'], '${SUBSCRIPTION}') !== false; if (array_key_exists('connection', $response) && $response['connection'] == 'unresolved') { $response['status_msg'] = gettext('No address record found for the selected mirror.'); $response['status'] = 'error'; } elseif (array_key_exists('connection', $response) && $response['connection'] == 'unauthenticated') { $response['status_msg'] = gettext('Could not authenticate the selected mirror.'); $response['status'] = 'error'; } elseif (array_key_exists('connection', $response) && $response['connection'] == 'misconfigured') { $response['status_msg'] = gettext('The current package configuration is invalid.'); $response['status'] = 'error'; } elseif (array_key_exists('connection', $response) && $response['connection'] != 'ok') { $response['status_msg'] = gettext('An error occurred while connecting to the selected mirror.'); $response['status'] = 'error'; } elseif (array_key_exists('repository', $response) && $response['repository'] == 'untrusted') { $response['status_msg'] = gettext('Could not verify the repository fingerprint.'); $response['status'] = 'error'; } elseif (array_key_exists('repository', $response) && $response['repository'] == 'forbidden') { $response['status_msg'] = $subscription ? gettext('The provided subscription is either invalid or expired. Please make sure the input is correct. Otherwise contact sales or visit the online shop to obtain a valid one.') : gettext('The repository did not grant access.'); $response['status'] = 'error'; } elseif (array_key_exists('repository', $response) && $response['repository'] == 'revoked') { $response['status_msg'] = gettext('The repository fingerprint has been revoked.'); $response['status'] = 'error'; } elseif (array_key_exists('repository', $response) && $response['repository'] == 'unsigned') { $response['status_msg'] = gettext('The repository has no fingerprint.'); $response['status'] = 'error'; } elseif (array_key_exists('repository', $response) && $response['repository'] == 'incomplete') { $response['status_msg'] = sprintf(gettext('The release type "%s" is not available on this repository.'), $response['product_target']); $response['status'] = 'error'; } elseif (array_key_exists('repository', $response) && $response['repository'] != 'ok') { $response['status_msg'] = $subscription ? sprintf(gettext('The matching %s %s series does not yet exist. Images are available to switch this installation to the latest business edition.'), $response['product']['product_name'], $response['product_abi']) : gettext('Could not find the repository on the selected mirror.'); $response['status'] = 'error'; } elseif ($active_count) { if ($active_count == 1) { /* keep this dynamic for template translation even though %s is always '1' */ $response['status_msg'] = sprintf( gettext('There is %s update available, total download size is %s.'), $active_count, $this->formatBytes($active_size) ); } else { $response['status_msg'] = sprintf( gettext('There are %s updates available, total download size is %s.'), $active_count, $this->formatBytes($active_size) ); } if ( ($active_status == 'update' && $response['needs_reboot'] == 1) || ($active_status == 'upgrade' && $response['upgrade_needs_reboot'] == 1) ) { $active_reboot = '1'; $response['status_msg'] = sprintf( '%s %s', $response['status_msg'], gettext('This update requires a reboot.') ); } $response['status_reboot'] = $active_reboot; $response['status'] = $active_status; } elseif (!$active_count) { $response['status_msg'] = gettext('There are no updates available on the selected mirror.'); $response['status'] = 'none'; } else { $response['status_msg'] = gettext('Unknown firmware status encountered.'); $response['status'] = 'error'; } } return $response; } /** * Retrieve specific changelog in text and html format * @param string $version changelog to retrieve * @return array corresponding changelog in both formats * @throws \Exception */ public function changelogAction($version) { $response = ['status' => 'failure']; if (!$this->request->isPost()) { return $response; } $version = (new SanitizeFilter())->sanitize($version, 'version'); $backend = new Backend(); $html = trim($backend->configdRun(sprintf('firmware changelog html %s', $version))); if (!empty($html)) { $response['status'] = 'ok'; $response['html'] = $html; } return $response; } /** * Retrieve upgrade log hidden in system * @return string with upgrade log * @throws \Exception */ public function logAction($clear) { $backend = new Backend(); $response = ['status' => 'failure']; if ($this->request->isPost()) { $text = trim($backend->configdRun('firmware log ' . (empty($clear) ? 'show' : 'clear'))); $response['status'] = 'ok'; if (!empty($text)) { $response['log'] = $text; } } return $response; } /** * Retrieve specific license for package in text format * @param string $package package to retrieve * @return array with all possible licenses * @throws \Exception */ public function licenseAction($package) { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { // sanitize package name $package = (new SanitizeFilter())->sanitize($package, 'pkgname'); $text = trim($backend->configdRun(sprintf('firmware license %s', $package))); if (!empty($text)) { $response['license'] = $text; } } return $response; } /** * perform reboot * @return array status * @throws \Exception */ public function rebootAction() { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $this->getLogger('audit')->notice(sprintf("[Firmware] User %s executed a reboot", $this->getUserName())); $response['status'] = 'ok'; $response['msg_uuid'] = trim($backend->configdRun('firmware reboot', true)); } else { $response['status'] = 'failure'; } return $response; } /** * perform poweroff * @return array status * @throws \Exception */ public function poweroffAction() { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $this->getLogger('audit')->notice(sprintf("[Firmware] User %s executed a poweroff", $this->getUserName())); $response['status'] = 'ok'; $response['msg_uuid'] = trim($backend->configdRun('firmware poweroff', true)); } else { $response['status'] = 'failure'; } return $response; } /** * perform (stable) update * @return array status * @throws \Exception */ public function updateAction() { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $this->getLogger('audit')->notice(sprintf("[Firmware] User %s executed a firmware update", $this->getUserName())); $backend->configdRun('firmware flush'); $response['msg_uuid'] = trim($backend->configdRun('firmware update', true)); $response['status'] = 'ok'; } else { $response['status'] = 'failure'; } return $response; } /** * perform (major) upgrade * @return array status * @throws \Exception */ public function upgradeAction() { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $this->getLogger('audit')->notice(sprintf("[Firmware] User %s executed a firmware upgrade", $this->getUserName())); $backend->configdRun('firmware flush'); $response['msg_uuid'] = trim($backend->configdRun('firmware upgrade', true)); $response['status'] = 'ok'; } else { $response['status'] = 'failure'; } return $response; } /** * run a connection check * @return array status * @throws \Exception */ public function connectionAction() { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $response['status'] = 'ok'; $response['msg_uuid'] = trim($backend->configdRun("firmware connection", true)); } else { $response['status'] = 'failure'; } return $response; } /** * run a health check * @return array status * @throws \Exception */ public function healthAction() { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $response['status'] = 'ok'; $response['msg_uuid'] = trim($backend->configdRun("firmware health", true)); } else { $response['status'] = 'failure'; } return $response; } /* * run a security audit * @return array status * @throws \Exception */ public function auditAction() { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $this->getLogger('audit')->notice(sprintf("[Firmware] User %s executed a security audit", $this->getUserName())); $response['status'] = 'ok'; $response['msg_uuid'] = trim($backend->configdRun("firmware audit", true)); } else { $response['status'] = 'failure'; } return $response; } /** * reinstall package * @param string $pkg_name package name to reinstall * @return array status * @throws \Exception */ public function reinstallAction($pkg_name) { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $this->getLogger('audit')->notice( sprintf("[Firmware] User %s executed a reinstall of package %s", $this->getUserName(), $pkg_name) ); $response['status'] = 'ok'; $pkg_name = (new SanitizeFilter())->sanitize($pkg_name, "pkgname"); // execute action $response['msg_uuid'] = trim($backend->configdpRun("firmware reinstall", array($pkg_name), true)); } else { $response['status'] = 'failure'; } return $response; } /** * install missing configured plugins * @return array status * @throws \Exception */ public function syncPluginsAction() { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $this->getLogger('audit')->notice(sprintf("[Firmware] User %s executed a plugins sync", $this->getUserName())); $response['status'] = strtolower(trim($backend->configdRun('firmware sync'))); } else { $response['status'] = 'failure'; } return $response; } /** * reset missing configured plugins * @return array status * @throws \Exception */ public function resyncPluginsAction() { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $response['status'] = strtolower(trim($backend->configdRun('firmware resync'))); } else { $response['status'] = 'failure'; } return $response; } /** * install package * @param string $pkg_name package name to install * @return array status * @throws \Exception */ public function installAction($pkg_name) { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $this->getLogger('audit')->notice( sprintf("[Firmware] User %s executed an install of package %s", $this->getUserName(), $pkg_name) ); $response['status'] = 'ok'; $pkg_name = (new SanitizeFilter())->sanitize($pkg_name, "pkgname"); // execute action $response['msg_uuid'] = trim($backend->configdpRun("firmware install", array($pkg_name), true)); } else { $response['status'] = 'failure'; } return $response; } /** * remove package * @param string $pkg_name package name to remove * @return array status * @throws \Exception */ public function removeAction($pkg_name) { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $this->getLogger('audit')->notice( sprintf("[Firmware] User %s executed an remove of package %s", $this->getUserName(), $pkg_name) ); $response['status'] = 'ok'; // sanitize package name $pkg_name = (new SanitizeFilter())->sanitize($pkg_name, "pkgname"); // execute action $response['msg_uuid'] = trim($backend->configdpRun("firmware remove", array($pkg_name), true)); } else { $response['status'] = 'failure'; } return $response; } /** * lock package * @param string $pkg_name package name to lock * @return array status * @throws \Exception */ public function lockAction($pkg_name) { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $this->getLogger('audit')->notice( sprintf("[Firmware] User %s locked package %s", $this->getUserName(), $pkg_name) ); $pkg_name = (new SanitizeFilter())->sanitize($pkg_name, "pkgname"); } else { $pkg_name = null; } if (!empty($pkg_name)) { $response['msg_uuid'] = trim($backend->configdpRun("firmware lock", array($pkg_name), true)); $response['status'] = 'ok'; } else { $response['status'] = 'failure'; } return $response; } /** * unlock package * @param string $pkg_name package name to unlock * @return array status * @throws \Exception */ public function unlockAction($pkg_name) { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $this->getLogger('audit')->notice( sprintf("[Firmware] User %s unlocked package %s", $this->getUserName(), $pkg_name) ); $pkg_name = (new SanitizeFilter())->sanitize($pkg_name, "pkgname"); } else { $pkg_name = null; } if (!empty($pkg_name)) { $response['msg_uuid'] = trim($backend->configdpRun("firmware unlock", array($pkg_name), true)); $response['status'] = 'ok'; } else { $response['status'] = 'failure'; } return $response; } /** * retrieve execution status */ public function runningAction() { $backend = new Backend(); $result = array( 'status' => trim($backend->configdRun('firmware running')) ); return $result; } /** * retrieve upgrade status (and log file of current process) */ public function upgradestatusAction() { $backend = new Backend(); $result = array('status' => 'running'); $cmd_result = trim($backend->configdRun('firmware status') ?? ''); $result['log'] = $cmd_result; if ($cmd_result == null) { $result['status'] = 'error'; } elseif (strpos($cmd_result, '***DONE***') !== false) { $result['status'] = 'done'; } elseif (strpos($cmd_result, '***REBOOT***') !== false) { $result['status'] = 'reboot'; } return $result; } /** * query package details * @return array * @throws \Exception */ public function detailsAction($package) { $backend = new Backend(); $response = array(); if ($this->request->isPost()) { $package = (new SanitizeFilter())->sanitize($package, 'pkgname'); $text = trim($backend->configdRun(sprintf('firmware details %s', $package)) ?? ''); if (!empty($text)) { $response['details'] = $text; } } return $response; } /** * list local and remote packages * @return array */ public function infoAction() { $config = Config::getInstance()->object(); $configPlugins = array(); if (!empty($config->system->firmware->plugins)) { $configPlugins = explode(",", $config->system->firmware->plugins); } $keys = array('name', 'version', 'comment', 'flatsize', 'locked', 'automatic', 'license', 'repository', 'origin'); $backend = new Backend(); $response = array(); $version = explode(' ', trim(shell_exec('opnsense-version -nv') ?? '')); foreach (array('product_id' => 0, 'product_version' => 1) as $result => $index) { $response[$result] = !empty($version[$index]) ? $version[$index] : 'unknown'; } /* allows us to select UI features based on product state */ $devel = explode('-', $response['product_id']); $devel = count($devel) == 2 ? $devel[1] == 'devel' : false; /* need both remote and local, create array earlier */ $packages = []; $plugins = []; $tiers = []; $current = $backend->configdRun('firmware tiers'); $current = explode("\n", trim($current ?? '')); foreach ($current as $line) { $expanded = explode('|||', $line); if (count($expanded) == 3) { $tiers[$expanded[0]] = $expanded[2]; } } /* package infos are flat lists with 3 pipes as delimiter */ foreach (array('remote', 'local') as $type) { $current = $backend->configdRun("firmware {$type}"); $current = explode("\n", trim($current ?? '')); foreach ($current as $line) { $expanded = explode('|||', $line); $translated = array(); $index = 0; if (count($expanded) != count($keys)) { continue; } foreach ($keys as $key) { $translated[$key] = $expanded[$index++]; if (empty($translated[$key])) { $translated[$key] = gettext('N/A'); } elseif ($key == 'flatsize') { $translated[$key] = $this->formatBytes($translated[$key]); } } /* mark remote packages as "provided", local as "installed" */ $translated['provided'] = $type == 'remote' ? '1' : '0'; $translated['installed'] = $type == 'local' ? '1' : '0'; if (isset($packages[$translated['name']])) { /* local iteration, mark package provided */ $translated['provided'] = '1'; } $translated['path'] = "{$translated['repository']}/{$translated['origin']}"; $translated['configured'] = in_array($translated['name'], $configPlugins) || $translated['automatic'] == '1' ? '1' : '0'; $packages[$translated['name']] = $translated; /* figure out local and remote plugins */ $plugin = explode('-', $translated['name']); if (count($plugin)) { if ($plugin[0] == 'os') { if ( $type == 'local' || ($type == 'remote' && ($devel || end($plugin) != 'devel')) ) { $plugins[$translated['name']] = $translated; } } } } } uksort($packages, function ($a, $b) { return strnatcasecmp($a, $b); }); $response['package'] = array(); foreach ($packages as $package) { $response['package'][] = $package; } foreach ($configPlugins as $missing) { if (!array_key_exists($missing, $plugins)) { $plugins[$missing] = []; foreach ($keys as $key) { $plugins[$missing][$key] = gettext('N/A'); } $plugins[$missing]['path'] = gettext('N/A'); $plugins[$missing]['configured'] = '1'; $plugins[$missing]['installed'] = '0'; $plugins[$missing]['provided'] = '0'; $plugins[$missing]['name'] = $missing; } } uasort($plugins, function ($a, $b) { return strnatcasecmp( ($a['configured'] && !$a['installed'] ? '0' : '1') . ($a['installed'] ? '0' : '1') . $a['name'], ($b['configured'] && !$b['installed'] ? '0' : '1') . ($b['installed'] ? '0' : '1') . $b['name'] ); }); $response['plugin'] = array(); foreach ($plugins as $plugin) { $plugin['tier'] = isset($tiers[$plugin['name']]) ? $tiers[$plugin['name']] : gettext('N/A'); $response['plugin'][] = $plugin; } /* also pull in changelogs from here */ $changelogs = json_decode(trim($backend->configdRun('firmware changelog list')), true); if ($changelogs == null) { $changelogs = []; } else { /* development strategy for changelog slightly differs from above */ $devel = preg_match('/^\d+\.\d+\.[a-z]/i', $response['product_version']) ? true : false; foreach ($changelogs as $index => &$changelog) { /* skip development items */ if (!$devel && preg_match('/^\d+\.\d+\.[a-z]/i', $changelog['version'])) { unset($changelogs[$index]); continue; } /* rewrite dates as ISO */ $date = date_parse($changelog['date']); $changelog['date'] = sprintf('%04d-%02d-%02d', $date['year'], $date['month'], $date['day']); } /* sort in reverse */ usort($changelogs, function ($a, $b) { return strcmp($b['date'], $a['date']); }); } $response['changelog'] = $changelogs; $product = json_decode(trim($backend->configdRun('firmware product')), true); if ($product == null) { $product = []; } $response['product'] = $product; return $response; } /** * list firmware mirror and flavour options * @return array */ public function getOptionsAction() { return $this->getModel()->getRepositoryOptions(); } /** * set firmware configuration options * @return array status */ public function setAction() { $response = ['status' => 'failure']; if (!$this->request->isPost()) { return $response; } $values = $this->request->getPost(static::$internalModelName); foreach ($values as $key => &$value) { if ($key == 'plugins') { /* discards plugins on purpose for the time being */ unset($values[$key]); } else { $value = filter_var($value, FILTER_SANITIZE_URL); } } $mdl = $this->getModel(); $mdl->setNodes($values); $ret = $this->validate(); if (!empty($ret['result'])) { $response['status_msg'] = array_values($ret['validations'] ?? [gettext('Unkown firmware validation error')]); return $response; } $response['status'] = 'ok'; $this->save(); $backend = new Backend(); $backend->configdRun('firmware flush'); $backend->configdRun('firmware reload'); return $response; } }