%PDF- %PDF-
| Direktori : /proc/thread-self/root/backups/router/usr/local/www/ |
| Current File : //proc/thread-self/root/backups/router/usr/local/www/diag_backup.php |
<?php
/*
* Copyright (C) 2015-2023 Franco Fichtner <franco@opnsense.org>
* Copyright (C) 2014 Deciso B.V.
* Copyright (C) 2004-2009 Scott Ullrich <sullrich@gmail.com>
* Copyright (C) 2008 Shrew Soft Inc. <mgrooms@shrew.net>
* Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>
* 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.
*/
require_once("guiconfig.inc");
require_once("interfaces.inc");
require_once("console.inc");
require_once("filter.inc");
require_once("rrd.inc");
require_once("system.inc");
use OPNsense\Backup\Local;
/**
* restore config section
* @param array $section_sets config section sets
* @param string $new_contents xml content
* @return bool status
*/
function restore_config_section($section_sets, $new_contents)
{
global $config;
$tmpxml = '/tmp/tmpxml';
$xml = null;
try {
file_put_contents($tmpxml, $new_contents);
$xml = load_config_from_file($tmpxml);
} catch (Exception $e) { }
@unlink($tmpxml);
if (!is_array($xml)) {
return false;
}
$restored = [];
$failed = [];
foreach ($section_sets as $section_set) {
$sections = explode(',', $section_set);
$found = [];
/* first find the existing sections from the set to be imported */
foreach ($sections as $section) {
$new = &$xml;
$path = explode('.', $section);
$target = array_pop($path);
foreach ($path as $node) {
if (!isset($new[$node])) {
continue 2;
}
$new = &$new[$node];
}
if (isset($new[$target])) {
$found[] = $section;
}
}
/* keep current config and skip to next one considering this set (and subsequently the import) failed */
if (!count($found)) {
$failed[] = $section_set;
continue;
}
/* secondly delete every old config section to be able to force a migration too */
foreach (array_diff($sections, $found) as $section) {
$old = &$config;
$path = explode('.', $section);
$target = array_pop($path);
foreach ($path as $node) {
if (!isset($old[$node])) {
continue 2;
}
$old = &$old[$node];
}
if (isset($old[$target])) {
unset($old[$target]);
$restored[] = $section;
}
}
/* thirdly and lastly import the found sections */
foreach ($found as $section) {
$old = &$config;
$new = &$xml;
$path = explode('.', $section);
$target = array_pop($path);
foreach ($path as $node) {
if (!isset($new[$node])) {
continue 2;
}
$new = &$new[$node];
if (!isset($old[$node])) {
$old[$node] = [];
}
$old = &$old[$node];
}
if (isset($new[$target])) {
$old[$target] = $new[$target];
$restored[] = $section;
}
}
}
if (count($restored) && !count($failed)) {
/* restored but may not have been modified at all */
write_config(sprintf('Restored sections (%s) of config file', join(',', $restored)));
convert_config();
}
return $failed;
}
/* config areas that are not suitable for config sync live here */
$areas = [
'bridges' => gettext('Bridge Devices'),
'gifs' => gettext('GIF Devices'),
'interfaces' => gettext('Interfaces'),
'laggs' => gettext('LAGG Devices'),
'ppps' => gettext('Point-to-Point Devices'),
'rrddata' => gettext('RRD Data'),
'vlans' => gettext('VLAN Devices'),
'wireless' => gettext('Wireless Devices'),
];
foreach (plugins_xmlrpc_sync() as $area) {
if (!empty($area['section'])) {
$areas[$area['section']] = $area['description'];
}
}
natcasesort($areas);
$backupFactory = new OPNsense\Backup\BackupFactory();
$do_reboot = false;
if ($_SERVER['REQUEST_METHOD'] === 'GET') {
$pconfig = [];
$pconfig['backupcount'] = isset($config['system']['backupcount']) ? $config['system']['backupcount'] : null;
$pconfig['rebootafterrestore'] = true;
$pconfig['keepconsole'] = true;
$pconfig['flush_history'] = true;
$pconfig['decrypt'] = false;
foreach ($backupFactory->listProviders() as $providerId => $provider) {
foreach ($provider['handle']->getConfigurationFields() as $field) {
$fieldId = $providerId . "_" .$field['name'];
$pconfig[$fieldId] = $field['value'];
}
}
} elseif ($_SERVER['REQUEST_METHOD'] === 'POST') {
$input_errors = [];
$pconfig = $_POST;
$mode = null;
foreach (array_keys($backupFactory->listProviders()) as $providerName) {
if (!empty($pconfig["setup_{$providerName}"])) {
$mode = "setup_{$providerName}";
}
}
if (empty($mode)) {
if (!empty($pconfig['restore'])) {
$mode = "restore";
} elseif (!empty($pconfig['download'])) {
$mode = "download";
}
}
if ($mode == "download") {
if (!empty($_POST['encrypt']) && (empty($_POST['encrypt_password']) || empty($_POST['encrypt_passconf']))) {
$input_errors[] = gettext("You must supply and confirm the password for encryption.");
} elseif (!empty($_POST['encrypt']) && $_POST['encrypt_password'] != $_POST['encrypt_passconf']) {
$input_errors[] = gettext('The passwords do not match.');
}
if (count($input_errors) == 0) {
$host = "{$config['system']['hostname']}.{$config['system']['domain']}";
$name = "config-{$host}-".date("YmdHis").".xml";
$data = "";
/* backup entire configuration */
$data = file_get_contents('/conf/config.xml');
/* backup RRD data */
if (empty($_POST['donotbackuprrd'])) {
$rrd_data_xml = rrd_export();
$closing_tag = "</opnsense>";
$data = str_replace($closing_tag, $rrd_data_xml . $closing_tag, $data);
}
if (!empty($_POST['encrypt'])) {
$crypter = new Local();
/* XXX this *could* fail, not handled */
$data = $crypter->encrypt($data, $_POST['encrypt_password']);
}
$size = strlen($data);
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment; filename={$name}");
header("Content-Length: $size");
if (isset($_SERVER['HTTPS'])) {
header('Pragma: ');
header('Cache-Control: ');
} else {
header("Pragma: private");
header("Cache-Control: private, must-revalidate");
}
echo $data;
exit;
}
} elseif ($mode == "restore") {
// unpack data and perform validation
$data = null;
if (!empty($_POST['decrypt']) && empty($_POST['decrypt_password'])) {
$input_errors[] = gettext('You must supply the password for decryption.');
}
$user = getUserEntry($_SESSION['Username']);
if (userHasPrivilege($user, 'user-config-readonly')) {
$input_errors[] = gettext('You do not have the permission to perform this action.');
}
/* read the file contents */
if (is_uploaded_file($_FILES['conffile']['tmp_name'])) {
$data = file_get_contents($_FILES['conffile']['tmp_name']);
if(empty($data)) {
$input_errors[] = sprintf(gettext("Warning, could not read file %s"), $_FILES['conffile']['tmp_name']);
}
} else {
$input_errors[] = gettext("The configuration could not be restored (file upload error).");
}
if (!empty($_POST['decrypt'])) {
$crypter = new Local();
$data = $crypter->decrypt($data, $_POST['decrypt_password']);
if (empty($data)) {
$input_errors[] = gettext('The uploaded file could not be decrypted.');
}
}
if (count($input_errors) == 0) {
if (!empty($pconfig['restorearea'])) {
$ret = restore_config_section($pconfig['restorearea'], $data);
if ($ret === false) {
$input_errors[] = gettext('The selected config file could not be parsed.');
} elseif (count($ret)) {
$descr = [];
foreach ($ret as $area) {
$descr[] = $areas[$area];
}
$input_errors[] = sprintf(gettext('At least one requested restore area could not be found: %s.'), join(', ', $descr));
} else {
if (!empty($config['rrddata'])) {
/* XXX we should point to the data... */
rrd_import();
unset($config['rrddata']);
write_config();
convert_config();
}
if (!empty($pconfig['rebootafterrestore'])) {
$do_reboot = true;
}
$savemsg = gettext("The configuration area has been restored.");
}
} else {
/* restore the entire configuration */
$cfieldnames = [
'usevirtualterminal',
'primaryconsole',
'secondaryconsole',
'serialspeed',
'serialusb',
'disableconsolemenu'
];
$csettings = [];
foreach ($cfieldnames as $fieldname) {
$csettings[$fieldname] = $config['system'][$fieldname] ?? null;
}
$filename = $_FILES['conffile']['tmp_name'];
file_put_contents($filename, $data);
$cnf = OPNsense\Core\Config::getInstance();
if ($cnf->restoreBackup($filename)) {
if (!empty($pconfig['rebootafterrestore'])) {
$do_reboot = true;
}
$config = parse_config();
$flush = false;
if (!empty($pconfig['keepconsole'])) {
// restore existing console settings
foreach ($csettings as $fieldname => $fieldcontent) {
if ($fieldcontent === null && isset($config[$fieldname])) {
unset($config[$fieldname]);
} else {
$config['system'][$fieldname] = $fieldcontent;
}
}
$flush = true;
}
if (!empty($config['rrddata'])) {
/* XXX we should point to the data... */
rrd_import();
unset($config['rrddata']);
$flush = true;
}
if ($flush) {
write_config();
convert_config();
}
$savemsg = gettext("The configuration has been restored.");
} else {
$input_errors[] = gettext("The configuration could not be restored.");
}
}
if (is_interface_mismatch(false)) {
$savemsg .= ' ' . sprintf(
gettext(
"Interfaces do not seem to match, please check the %sassignments%s now for missing devices."
),
'<a href="/interfaces_assign.php">',
'</a>'
);
if ($do_reboot) {
$savemsg .= ' ' . gettext('Postponing reboot.');
$do_reboot = false;
}
}
if ($do_reboot) {
$savemsg .= ' ' . gettext("The system is rebooting now. This may take one minute.");
}
if (empty($input_errors) && !empty($pconfig['flush_history'])) {
configd_run('system flush config_history');
write_config('System restore flushed local history');
}
}
} elseif (!empty($mode)){
// setup backup provider, collect provider settings and save/validate
$providerId = substr($mode, 6);
$provider = $backupFactory->getProvider($providerId);
$providerSet = array();
foreach ($provider['handle']->getConfigurationFields() as $field) {
$fieldId = $providerId . "_" .$field['name'];
if ($field['type'] == 'file') {
// extract file to sent to setConfiguration()
if (is_uploaded_file($_FILES[$fieldId]['tmp_name'])) {
$providerSet[$field['name']] = file_get_contents($_FILES[$fieldId]['tmp_name']);
} else {
$providerSet[$field['name']] = null;
}
} else {
$providerSet[$field['name']] = $pconfig[$fieldId];
}
}
$input_errors = $provider['handle']->setConfiguration($providerSet);
if (count($input_errors) == 0) {
if ($provider['handle']->isEnabled()) {
try {
$filesInBackup = $provider['handle']->backup();
} catch (Exception $e) {
$filesInBackup = array();
$input_errors[] = $e->getMessage();
}
if (count($filesInBackup) == 0) {
$input_errors[] = gettext('Saved settings, but remote backup failed.');
} else {
$input_messages = gettext("Backup successful, current file list:") . "<br>";
foreach ($filesInBackup as $filename) {
$input_messages .= "<br>" . $filename;
}
}
}
system_cron_configure();
}
} elseif (!empty($pconfig['save'])) {
if ($pconfig['backupcount'] != null && (!is_numeric($pconfig['backupcount']) || $pconfig['backupcount'] <= 0)) {
$input_errors[] = gettext('Backup count must be greater than zero.');
}
if (count($input_errors) == 0) {
if ($pconfig['backupcount'] != null) {
$config['system']['backupcount'] = $pconfig['backupcount'];
} elseif (isset($config['system']['backupcount'])) {
unset($config['system']['backupcount']);
}
write_config('Changed backup revision count');
$savemsg = get_std_save_message();
}
}
}
include("head.inc");
legacy_html_escape_form_data($pconfig);
?>
<body>
<?php include("fbegin.inc"); ?>
<script>
function show_value(key) {
$('#show-' + key + '-btn').html('');
$('#show-' + key + '-val').show();
$("[name='" + key + "']").focus();
}
//<![CDATA[
$( document ).ready(function() {
// show encryption password
$("#encryptconf").change(function(event){
event.preventDefault();
if ($("#encryptconf").prop('checked')) {
$("#encrypt_opts").removeClass("hidden");
} else {
$("#encrypt_opts").addClass("hidden");
}
});
// show decryption password
$("#decryptconf").change(function(event){
event.preventDefault();
if ($("#decryptconf").prop('checked')) {
$("#decrypt_opts").removeClass("hidden");
} else {
$("#decrypt_opts").addClass("hidden");
}
});
$("#decryptconf").change();
$('#restorearea').change(function () {
$("#flush_history").attr('checked', false);
if ($('#restorearea option:selected').text() == '') {
$.restorearea_warned = 0;
$("#flush_history").attr('checked', true);
} else if ($.restorearea_warned != 1) {
$.restorearea_warned = 1;
BootstrapDialog.confirm({
title: '<?= html_safe(gettext('Warning!')) ?>',
message: '<?= html_safe(gettext('Selecting specific restore areas during a configuration import may ' .
'cause loss of configuration integrity due to external references not being restored. It is ' .
'recommended to keep this set to the default unless you know what you are doing.')) ?>',
type: BootstrapDialog.TYPE_WARNING,
btnOKClass: 'btn-warning',
btnOKLabel: '<?= html_safe(gettext('I know what I am doing')) ?>',
btnCancelLabel: '<?= html_safe(gettext('Use the default')) ?>',
callback: function(result) {
if (!result) {
$('#restorearea option:selected').prop('selected', false);
$('#restorearea').selectpicker('refresh');
$.restorearea_warned = 0;
}
}
});
}
});
$.restorearea_warned = $('#restorearea option:selected').length ? 1 : 0;
});
//]]>
</script>
<section class="page-content-main">
<div class="container-fluid">
<div class="row">
<?php if (isset($savemsg)) print_info_box($savemsg); ?>
<?php if (isset($input_messages)) print_info_box($input_messages); ?>
<?php if (isset($input_errors) && count($input_errors) > 0) print_input_errors($input_errors); ?>
<form method="post" enctype="multipart/form-data">
<section class="col-xs-12">
<div class="content-box tab-content table-responsive __mb">
<table class="table table-striped">
<div class="content-box tab-content table-responsive __mb">
<table class="table table-striped">
<tbody>
<tr>
<td colspan="2"><strong><?= gettext('Backup Count') ?></strong></td>
</tr>
<tr>
<td><input name="backupcount" type="text" size="5" value="<?= html_safe($pconfig['backupcount']) ?>"/></td>
<td><?= gettext("Enter the number of older configurations to keep in the local backup cache."); ?></td>
</tr>
<tr>
<td><input name="save" type="submit" class="btn btn-primary" value="<?= html_safe(gettext('Save')) ?>"/></td>
<td>
<?= gettext('Be aware of how much space is consumed by backups before adjusting this value.'); ?>
<?php if (count(OPNsense\Core\Config::getInstance()->getBackups(true)) > 0): ?>
<?= gettext('Current space used:') . ' ' . exec("/usr/bin/du -sh /conf/backup | /usr/bin/awk '{print $1;}'") ?>
<?php endif ?>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<section class="col-xs-12">
<div class="content-box tab-content table-responsive __mb">
<table class="table table-striped">
<tr>
<td><strong><?= gettext('Download') ?></strong></td>
</tr>
<tr>
<td>
<input name="donotbackuprrd" type="checkbox" id="dotnotbackuprrd" checked="checked" />
<?=gettext("Do not backup RRD data."); ?><br/>
<input name="encrypt" type="checkbox" id="encryptconf" />
<?=gettext("Encrypt this configuration file."); ?><br/>
<div class="hidden table-responsive __mt" id="encrypt_opts">
<table class="table table-condensed">
<tr>
<td><?= gettext('Password') ?></td>
<td><input name="encrypt_password" type="password" autocomplete="new-password"/></td>
</tr>
<tr>
<td><?= gettext('Confirmation') ?></td>
<td><input name="encrypt_passconf" type="password" autocomplete="new-password"/> </td>
</tr>
</table>
</div>
</td>
</tr>
<tr>
<td>
<input name="download" type="submit" class="btn btn-primary" value="<?= html_safe(gettext('Download configuration')) ?>" />
</td>
</tr>
<tr>
<td>
<?=gettext("Click this button to download the system configuration in XML format."); ?>
</td>
</tr>
</table>
</div>
<div class="content-box tab-content table-responsive __mb">
<table class="table table-striped">
<tr>
<td><strong><?= gettext('Restore') ?></strong></td>
</tr>
<tr>
<td>
<?= gettext('Restore areas:') ?>
<div>
<select name="restorearea[]" id="restorearea" class="selectpicker" multiple="multiple" size="5" title="<?= html_safe(gettext('All (recommended)')) ?>" data-live-search="true" data-size="10">
<?php foreach ($areas as $area => $areaname): ?>
<option value="<?= html_safe($area) ?>" <?= in_array($area, $pconfig['restorearea'] ?? []) ? 'selected="selected"' : '' ?>><?= $areaname ?></option>
<?php endforeach ?>
</select>
</div>
<br/><input name="conffile" type="file" id="conffile" /><br/>
<input name="rebootafterrestore" type="checkbox" id="rebootafterrestore" <?= !empty($pconfig['rebootafterrestore']) ? 'checked="checked"' : '' ?>/>
<?=gettext("Reboot after a successful restore."); ?><br/>
<input name="keepconsole" type="checkbox" id="keepconsole" <?= !empty($pconfig['keepconsole']) ? 'checked="checked"' : '' ?>/>
<?=gettext("Exclude console settings from import."); ?><br/>
<input name="flush_history" type="checkbox" id="flush_history" <?= !empty($pconfig['flush_history']) ? 'checked="checked"' : '' ?>/>
<?=gettext("Flush (full) local configuration history."); ?><br/>
<input name="decrypt" type="checkbox" id="decryptconf" <?= !empty($pconfig['decrypt']) ? 'checked="checked"' : '' ?>/>
<?=gettext("Configuration file is encrypted."); ?>
<div class="hidden table-responsive __mt" id="decrypt_opts">
<table class="table table-condensed">
<tr>
<td><?= gettext('Password') ?></td>
<td><input name="decrypt_password" type="password" autocomplete="new-password"/></td>
</tr>
</table>
</div>
</td>
</tr>
<tr>
<td>
<input name="restore" type="submit" class="btn btn-primary" id="restore" value="<?= html_safe(gettext('Restore configuration')) ?>" />
</td>
</tr>
<tr>
<td>
<?=gettext("Open a configuration XML file and click the button below to restore the configuration."); ?><br/>
</td>
</tr>
</table>
</div>
<?php
foreach ($backupFactory->listProviders() as $providerId => $provider):?>
<div class="content-box tab-content table-responsive __mb">
<table class="table table-striped opnsense_standard_table_form">
<tr>
<td colspan="2"><strong><?= $provider['handle']->getName() ?></strong></td>
</tr>
<?php
foreach ($provider['handle']->getConfigurationFields() as $field):
$fieldId = $providerId . "_" .$field['name'];?>
<tr>
<td style="width:22%">
<?php if (!empty($field['help'])): ?>
<a id="help_for_<?=$fieldId;?>" href="#" class="showhelp"><i class="fa fa-info-circle"></i></a>
<?php else: ?>
<i class="fa fa-info-circle text-muted"></i>
<?php endif ?>
<?=$field['label'];?>
</td>
<td style="width:78%">
<?php if ($field['type'] == 'checkbox'): ?>
<input name="<?=$fieldId;?>" type="checkbox" <?=!empty($pconfig[$fieldId]) ? "checked" : "";?> >
<?php elseif ($field['type'] == 'text'): ?>
<input name="<?=$fieldId;?>" value="<?=$pconfig[$fieldId];?>" type="text">
<?php elseif ($field['type'] == 'file'): ?>
<input name="<?=$fieldId;?>" type="file">
<?php elseif ($field['type'] == 'password'):?>
<input name="<?=$fieldId;?>" type="password" autocomplete="new-password" value="<?=$pconfig[$fieldId];?>" />
<?php elseif ($field['type'] == 'textarea'): ?>
<textarea name="<?=$fieldId;?>" rows="10"><?=$pconfig[$fieldId];?></textarea>
<?php elseif ($field['type'] == 'passwordarea'): ?>
<div id="show-<?=$fieldId;?>-btn">
<button onclick="event.preventDefault();show_value('<?= html_safe($fieldId) ?>');" class="btn btn-default"><?= html_safe(gettext('Click to edit')) ?></button>
</div>
<div id="show-<?=$fieldId;?>-val" style="display:none">
<textarea name="<?=$fieldId;?>" rows="10"><?=$pconfig[$fieldId];?></textarea>
</div>
<?php endif ?>
<div class="hidden" data-for="help_for_<?=$fieldId;?>">
<?=!empty($field['help']) ? $field['help'] : "";?>
</div>
</td>
</tr>
<?php
endforeach;?>
<tr>
<td></td>
<td>
<button type="submit" name="setup_<?=$providerId;?>" value="yes" class="btn btn-primary">
<?= sprintf(gettext("Setup/Test %s"), $provider['handle']->getName()) ?>
</button>
</td>
</tr>
</table>
</div>
<?php
endforeach;?>
</section>
</form>
</div>
</div>
</section>
<?php
include("foot.inc");
if ($do_reboot) {
configd_run('system reboot', true);
}