%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/BackupController.php |
<?php
/*
* Copyright (C) 2023 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\ApiControllerBase;
use OPNsense\Core\ACL;
use OPNsense\Core\Backend;
use OPNsense\Core\Config;
/**
* Class BackupController
* @package OPNsense\Core\Api
*/
class BackupController extends ApiControllerBase
{
/**
* when the user-config-readonly privilege is set, raise an error
*/
private function throwReadOnly()
{
if ((new ACL())->hasPrivilege($this->getUserName(), 'user-config-readonly')) {
throw new UserException(
sprintf("User %s denied for write access (user-config-readonly set)", $this->getUserName())
);
}
}
/**
* return available providers and their backup locations
* @return array
*/
private function providers()
{
$result = [];
$result['this'] = ['description' => gettext('This Firewall'), 'dirname' => '/conf/backup'];
if (class_exists('\Deciso\OPNcentral\Central')) {
$central = new \Deciso\OPNcentral\Central();
$central->setUserScope($this->getUserName());
$ctrHosts = [];
foreach ($central->hosts->host->getNodes() as $itemid => $item) {
$ctrHosts[$itemid] = ['description' => $item['description']];
}
foreach (glob('/conf/remote.backups/*') as $filename) {
$dirname = basename($filename);
if (isset($ctrHosts[$dirname])) {
$result[$dirname] = $ctrHosts[$dirname];
$result[$dirname]['dirname'] = $filename;
}
}
}
return $result;
}
/**
* list available providers
* @return array
*/
public function providersAction()
{
return ['items' => $this->providers()];
}
/**
* list available backups for selected host
*/
public function backupsAction($host)
{
$result = ['items' => []];
$providers = $this->providers();
if (!empty($providers[$host])) {
foreach (glob($providers[$host]['dirname'] . "/config-*.xml") as $filename) {
$xmlNode = @simplexml_load_file($filename, "SimpleXMLElement", LIBXML_NOERROR | LIBXML_ERR_NONE);
if (isset($xmlNode->revision)) {
$cfg_item = [
'time' => (string)$xmlNode->revision->time,
'time_iso' => date('c', (int)$xmlNode->revision->time),
'description' => (string)$xmlNode->revision->description,
'username' => (string)$xmlNode->revision->username,
'filesize' => filesize($filename),
'id' => basename($filename)
];
$result['items'][] = $cfg_item;
}
}
// sort newest first
usort($result['items'], function ($item1, $item2) {
return ($item1['time'] < $item2['time']) ? 1 : -1;
});
}
return $result;
}
/**
* diff two backups for selected host
*/
public function diffAction($host, $backup1, $backup2)
{
$result = ['items' => []];
$providers = $this->providers();
if (!empty($providers[$host])) {
$bckfilename1 = null;
$bckfilename2 = null;
foreach (glob($providers[$host]['dirname'] . "/config-*.xml") as $filename) {
$bckfilename = basename($filename);
if ($backup1 == $bckfilename) {
$bckfilename1 = $filename;
} elseif ($backup2 == $bckfilename) {
$bckfilename2 = $filename;
}
}
if (!empty($bckfilename1) && !empty($bckfilename2)) {
$diff = [];
exec("/usr/bin/diff -u " . escapeshellarg($bckfilename2) . " " . escapeshellarg($bckfilename1), $diff);
if (!empty($diff)) {
foreach ($diff as $line) {
$result['items'][] = htmlspecialchars($line, ENT_QUOTES | ENT_HTML401);
}
}
}
}
return $result;
}
/**
* delete local backup
*/
public function deleteBackupAction($backup)
{
$this->throwReadOnly();
foreach (glob("/conf/backup/config-*.xml") as $filename) {
$bckfilename = basename($filename);
if ($backup === $bckfilename) {
@unlink($filename);
return ['status' => 'deleted'];
}
}
return ['status' => 'not_found'];
}
/**
* revert to local backup from history
*/
public function revertBackupAction($backup)
{
$this->throwReadOnly();
foreach (glob("/conf/backup/config-*.xml") as $filename) {
$bckfilename = basename($filename);
if ($backup === $bckfilename) {
$cnf = Config::getInstance();
$cnf->restoreBackup($filename);
$cnf->save();
return ['status' => 'reverted'];
}
}
return ['status' => 'not_found'];
}
/**
* download specified backup, when left empty the latest is offered
*/
public function downloadAction($host, $backup = null)
{
$providers = $this->providers();
if (!empty($providers[$host])) {
foreach (array_reverse(glob($providers[$host]['dirname'] . "/config-*.xml")) as $filename) {
if (empty($backup) || $backup == basename($filename)) {
$payload = @simplexml_load_file($filename);
$hostname = '';
if ($payload !== false && isset($payload->system) && isset($payload->system->hostname)) {
$hostname = $payload->system->hostname . "." . $payload->system->domain;
}
$target_filename = urlencode('config-' . $hostname . '-' . explode('/config-', $filename, 2)[1]);
$this->response->setContentType('application/octet-stream');
$this->response->setRawHeader("Content-Disposition: attachment; filename=" . $target_filename);
$this->response->setRawHeader("Content-length: " . filesize($filename));
$this->response->setRawHeader("Pragma: no-cache");
$this->response->setRawHeader("Expires: 0");
$this->response->setContent(fopen($filename, 'r'));
break;
}
}
}
}
}