Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/nextcloudpi.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornachoparker <nacho@ownyourbits.com>2019-04-30 05:06:58 +0300
committernachoparker <nacho@ownyourbits.com>2019-05-01 03:02:58 +0300
commitf34354c336614bc74df41381dafda6cea90642cc (patch)
tree8688f54a710657e25d4a820098c32913f0c54568
parent01cd4215a530aeff3a62e3a38f07d4cc734cb1bb (diff)
ncp-web: add backups panelv1.12.0
-rw-r--r--bin/ncp/BACKUPS/nc-backup.sh3
-rw-r--r--changelog.md10
-rw-r--r--ncp-web/backups.php159
-rw-r--r--ncp-web/css/ncp.css56
-rw-r--r--ncp-web/download.php75
-rw-r--r--ncp-web/elements.php2
-rw-r--r--ncp-web/img/defaults-white.svg54
-rw-r--r--ncp-web/img/delete.svg1
-rw-r--r--ncp-web/img/download.svg1
-rw-r--r--ncp-web/index.php29
-rw-r--r--ncp-web/js/ncp.js218
-rw-r--r--ncp-web/ncp-launcher.php43
-rw-r--r--ncp-web/upload.php47
-rw-r--r--ncp.sh32
-rwxr-xr-xupdate.sh41
15 files changed, 746 insertions, 25 deletions
diff --git a/bin/ncp/BACKUPS/nc-backup.sh b/bin/ncp/BACKUPS/nc-backup.sh
index 03c49566..b4cb457b 100644
--- a/bin/ncp/BACKUPS/nc-backup.sh
+++ b/bin/ncp/BACKUPS/nc-backup.sh
@@ -100,7 +100,8 @@ tar $compress_arg -cf "$destfile" \
exit 1
}
rm "$dbbackup"
-chmod 600 "$destfile"
+chmod 640 "$destfile"
+chown :www-data "$destfile"
echo "backup $destfile generated"
EOF
diff --git a/changelog.md b/changelog.md
index 5eb76739..c73e18ca 100644
--- a/changelog.md
+++ b/changelog.md
@@ -1,9 +1,13 @@
-[v1.11.4](https://github.com/nextcloud/nextcloudpi/commit/62a7f45) (2019-04-28) letsencrypt: switch to apt version
+[v1.12.0](https://github.com/nextcloud/nextcloudpi/commit/703ff6f) (2019-04-29) ncp-web: add backups panel
-[v1.11.3 ](https://github.com/nextcloud/nextcloudpi/commit/71d8f52) (2019-04-09) nc-restore: check btrfs command
+[v1.11.5](https://github.com/nextcloud/nextcloudpi/commit/01cd421) (2019-04-29) letsencrypt: force renewal by default
-[v1.11.2, master](https://github.com/nextcloud/nextcloudpi/commit/3754609) (2019-04-06) armbian: fix uu
+[v1.11.4 ](https://github.com/nextcloud/nextcloudpi/commit/b3c7d13) (2019-04-28) letsencrypt: switch to apt version
+
+[v1.11.3 ](https://github.com/nextcloud/nextcloudpi/commit/02efd61) (2019-04-09) nc-restore: check btrfs command
+
+[v1.11.2 ](https://github.com/nextcloud/nextcloudpi/commit/3754609) (2019-04-06) armbian: fix uu
[v1.11.1 ](https://github.com/nextcloud/nextcloudpi/commit/a712935) (2019-04-05) nc-backup: fix space calculation
diff --git a/ncp-web/backups.php b/ncp-web/backups.php
new file mode 100644
index 00000000..2bdb08e3
--- /dev/null
+++ b/ncp-web/backups.php
@@ -0,0 +1,159 @@
+<!--
+ NextCloudPi Web Backups Panel
+
+ Copyleft 2019 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
+ GPL licensed (see end of file) * Use at your own risk!
+
+ More at https://nextcloudpi.com
+-->
+<?php
+
+$bkp_cfg = file_get_contents('/usr/local/etc/ncp-config.d/nc-backup.cfg') or exit('backup config not found');
+$bkp_auto_cfg = file_get_contents('/usr/local/etc/ncp-config.d/nc-backup-auto.cfg') or exit('backup config not found');
+
+$bkp_json = json_decode($bkp_cfg , true) or exit('invalid format');
+$bkp_auto_json = json_decode($bkp_auto_cfg, true) or exit('invalid format');
+
+$bkp_dir = $bkp_json['params'][0]['value'];
+$bkp_auto_dir = $bkp_auto_json['params'][1]['value'];
+
+$bkps = array();
+$bkps_auto = array();
+
+if (file_exists($bkp_dir))
+{
+ $bkps = array_diff(scandir($bkp_dir), array('.', '..'));
+ $bkps = preg_filter('/^/', $bkp_dir. '/', $bkps);
+}
+
+if (file_exists($bkp_auto_dir))
+{
+ $bkps_auto = array_diff(scandir($bkp_auto_dir), array('.', '..'));
+ $bkps_auto = preg_filter('/^/', $bkp_auto_dir . '/', $bkps_auto);
+}
+
+$bkps = array_unique(array_merge($bkps, $bkps_auto));
+
+if (!empty($bkps))
+{
+echo <<<HTML
+ <div id="backups-table">
+ <table class="dashtable backuptable">
+ <th>Date</th><th>Size</th><th>Compressed</th><th>Data</th><th></th>
+HTML;
+ foreach ($bkps as $bkp)
+ {
+ $extension = pathinfo($bkp, PATHINFO_EXTENSION);
+ if ($extension === "tar" || $extension === "gz")
+ {
+ $compressed = "";
+ if ($extension === "gz")
+ $compressed = '✓';
+
+ $date = date("Y M d @ H:i", filemtime($bkp));
+ $size = round(filesize($bkp)/1024/1024) . " MiB";
+
+ $has_data = '';
+ exec("sudo /home/www/ncp-backup-launcher.sh bkp " . escapeshellarg($bkp) . " \"$compressed\"", $output, $ret);
+ if ($ret == 0)
+ $has_data = '✓';
+
+ echo <<<HTML
+ <tr id="$bkp">
+ <td class="long-field" title="$bkp">$date&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
+ <td class="val-field">$size</td>
+ <td class="ok-field align-center">$compressed</td>
+ <td class="ok-field align-center">$has_data</td>
+ <td>
+ <img class="hidden-btn default-btn download-bkp" title="download" src="../img/download.svg">
+ <img class="hidden-btn default-btn delete-bkp" title="delete" src="../img/delete.svg">
+ <img class="hidden-btn default-btn restore-bkp" title="restore" src="../img/defaults.svg">
+ </td>
+ </tr>
+HTML;
+ echo '<input type="hidden" name="csrf-token" value="' . getCSRFToken() . '"/>';
+ }
+ }
+echo <<<HTML
+ </table>
+ </div>
+HTML;
+} else {
+ echo "<div>No backups found.</div>";
+}
+?>
+
+</br></br>
+<h2 class="text-title">Restore from file</h2>
+<form action="upload.php" method="POST" enctype="multipart/form-data">
+ <div class="restore-upload-btn-wrapper">
+ <input type="file" name="backup" id="restore-upload" accept=".tar,.tar.gz"/>
+ <input id="restore-upload-btn" type="submit" value="Restore"/>
+ </div>
+</form>
+</br></br>
+
+<h2 class="text-title"><?php echo $l->__("Snapshots"); ?></h2>
+
+<?php
+
+include( '/var/www/nextcloud/config/config.php' );
+
+$snap_dir = realpath($CONFIG['datadirectory'] . '/../ncp-snapshots');
+$snaps = array();
+if (file_exists($snap_dir))
+{
+ $snaps = array_diff(scandir($snap_dir), array('.', '..'));
+ $snaps = preg_filter('/^/', $snap_dir . '/', $snaps);
+}
+
+if (!empty($snaps))
+{
+echo <<<HTML
+ <div id="snapshots-table">
+ <table class="dashtable backuptable">
+HTML;
+ foreach ($snaps as $snap)
+ {
+ exec("sudo /home/www/ncp-backup-launcher.sh chksnp " . escapeshellarg($snap), $out, $ret);
+ if ($ret == 0)
+ {
+ $snap_name = basename($snap);
+ echo <<<HTML
+ <tr id="$snap">
+ <td class="text-align-left" title="$snap">$snap_name</td>
+ <td>
+ <img class="hidden-btn default-btn delete-snap" title="delete" src="../img/delete.svg">
+ <img class="hidden-btn default-btn restore-snap" title="restore" src="../img/defaults.svg">
+ </td>
+ </tr>
+HTML;
+ }
+ }
+echo <<<HTML
+ </table>
+ </div>
+HTML;
+} else {
+ echo "<div>No snapshots found.</div>";
+}
+?>
+
+<!--
+ License
+
+ This script is free software; you can redistribute it and/or modify it
+ under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This script is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this script; if not, write to the
+ Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ Boston, MA 02111-1307 USA
+-->
diff --git a/ncp-web/css/ncp.css b/ncp-web/css/ncp.css
index 17231bcd..9ed3140d 100644
--- a/ncp-web/css/ncp.css
+++ b/ncp-web/css/ncp.css
@@ -1076,7 +1076,7 @@ select {
display: none;
}
-#loading-info-gif {
+.loading-section-gif {
display: flex;
justify-content: center;
align-items: center;
@@ -1150,6 +1150,9 @@ select {
.icon-search {
background-image: url('../img/search.svg');
}
+.icon-backups {
+ background-image: url('../img/defaults-white.svg');
+}
.icon-config {
background-image: url('../img/settings-white.svg');
}
@@ -1223,6 +1226,18 @@ a#versionlink:hover {
max-width: 210px;
}
+#confirmation-dialog {
+ position:fixed;
+ top:0;
+ bottom:0;
+ height:100%;
+ width:100%;
+ background-color:rgba(0, 0, 0, 0.5);
+ z-index:9000;
+ text-align:center;
+ cursor:pointer;
+}
+
.dialog {
display:block;
background: white;
@@ -1249,7 +1264,7 @@ a#versionlink:hover {
opacity:0.75
}
-#close-wizard {
+.close-dialog-x {
position: absolute;
top: 5px;
right: 5px;
@@ -1319,6 +1334,23 @@ a#versionlink:hover {
border-bottom: 1px solid #ebebeb;
}
+.backuptable td {
+ text-align: right;
+}
+.text-align-left {
+ text-align: left !important;
+ padding-left: 1em;
+}
+.backuptable th {
+ text-align: center;
+}
+#backups-content div {
+ text-align: center;
+}
+.align-center {
+ text-align: center !important;
+}
+
#dashboard-suggestions {
margin-bottom: 1em;
}
@@ -1353,7 +1385,23 @@ a#versionlink:hover {
-ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)';
opacity: 0.5;
}
+
+.hidden-btn {
+ cursor: pointer;
+ -ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=0)';
+ opacity: 0;
+}
+
+.backuptable tr:hover img {
+ -ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=50)';
+ opacity: 0.5;
+}
.pwd-btn:hover, .default-btn:hover {
- -ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)';
- opacity: 1;
+ -ms-filter:'progid:DXImageTransform.Microsoft.Alpha(Opacity=100)' !important;
+ opacity: 1 !important;
+}
+.restore-upload-btn-wrapper {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
}
diff --git a/ncp-web/download.php b/ncp-web/download.php
new file mode 100644
index 00000000..195ad818
--- /dev/null
+++ b/ncp-web/download.php
@@ -0,0 +1,75 @@
+<?php
+///
+// NextCloudPi Web Panel backend
+//
+// Copyleft 2019 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
+// GPL licensed (see end of file) * Use at your own risk!
+//
+// More at https://nextcloudpi.com
+///
+
+include ('csrf.php');
+session_start();
+
+// CSRF check
+$token = isset($_REQUEST['token']) ? $_REQUEST['token'] : '';
+if ( empty($token) || !validateCSRFToken($token) )
+ exit('Unauthorized download');
+
+if (!isset($_REQUEST["bkp"]))
+ die();
+
+$file = $_REQUEST["bkp"];
+
+if (!file_exists($file))
+ die('File not found');
+
+if (!is_readable($file))
+ die('NCP does not have read permissions on this file');
+
+$size = filesize($file);
+
+$extension = pathinfo($file, PATHINFO_EXTENSION);
+if ($extension === "tar" )
+ $mime_type = 'application/x-tar';
+else if( $extension === "gz")
+ $mime_type = 'application/x-gzip';
+else
+ die();
+
+ob_start();
+ob_clean();
+header('Content-Description: File Transfer');
+header('Content-Type: ' . $mime_type);
+header("Content-Transfer-Encoding: Binary");
+header("Content-disposition: attachment; filename=\"" . basename($file) . "\"");
+header('Content-Length: ' . $size);
+header('Expires: 0');
+header('Cache-Control: must-revalidate');
+header('Pragma: public');
+
+$chunksize = 8 * (1024 * 1024);
+if($size > $chunksize)
+{
+ $handle = fopen($file, 'rb') or die("Error opening file");
+
+ while (!feof($handle))
+ {
+ $buffer = fread($handle, $chunksize);
+ echo $buffer;
+
+ ob_flush();
+ flush();
+ }
+
+ fclose($handle);
+}
+else
+ readfile($file);
+
+ob_flush();
+flush();
+
+exit();
+
+?>
diff --git a/ncp-web/elements.php b/ncp-web/elements.php
index 7e7b176d..afe4b2ba 100644
--- a/ncp-web/elements.php
+++ b/ncp-web/elements.php
@@ -123,7 +123,6 @@ function print_config_forms( $l /* translations l10n object */ )
$cfg_dir = '/usr/local/etc/ncp-config.d/';
$d_iterator = new RecursiveDirectoryIterator($bin_dir);
$iterator = new RecursiveIteratorIterator($d_iterator);
- $objects = new RegexIterator($iterator, '/^.+\.sh$/i', RecursiveRegexIterator::GET_MATCH);
$ret = "";
$sections = array_diff(scandir($bin_dir), array('.', '..', 'l10n'));
@@ -167,7 +166,6 @@ function print_sidebar( $l /* translations l10n object */, $ticks /* wether to c
$cfg_dir = '/usr/local/etc/ncp-config.d/';
$d_iterator = new RecursiveDirectoryIterator($bin_dir);
$iterator = new RecursiveIteratorIterator($d_iterator);
- $objects = new RegexIterator($iterator, '/^.+\.sh$/i', RecursiveRegexIterator::GET_MATCH);
$ret = "";
$sections = array_diff(scandir($bin_dir), array('.', '..', 'l10n'));
diff --git a/ncp-web/img/defaults-white.svg b/ncp-web/img/defaults-white.svg
new file mode 100644
index 00000000..0c01a2aa
--- /dev/null
+++ b/ncp-web/img/defaults-white.svg
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="16"
+ height="16"
+ version="1.1"
+ viewbox="0 0 16 16"
+ id="svg4"
+ sodipodi:docname="defaults-white.svg"
+ inkscape:version="0.92.4 5da689c313, 2019-01-14">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="640"
+ inkscape:window-height="480"
+ id="namedview6"
+ showgrid="false"
+ inkscape:zoom="14.75"
+ inkscape:cx="8"
+ inkscape:cy="8"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ inkscape:window-maximized="0"
+ inkscape:current-layer="svg4" />
+ <path
+ d="m7.9319 2.4252c-3.4324 0-5.6787 2.9953-5.5301 5.8394h-1.8778l3.3924 3.4063 3.5454-3.3664h-1.8657c-0.20594-1.4772 1.0106-2.706 2.3366-2.6868 1.386 0.020855 2.4331 1.0688 2.4331 2.3758 0.07821 1.3851-1.4164 2.9788-3.4463 2.1985 0 1.0688 0.00261 2.2115 0 3.2717 3.641 0.72124 6.6389-2.1811 6.6389-5.431 0-3.0961-2.5374-5.6074-5.6266-5.6074z"
+ id="path2"
+ style="fill:#ffffff" />
+</svg>
diff --git a/ncp-web/img/delete.svg b/ncp-web/img/delete.svg
new file mode 100644
index 00000000..53f0b020
--- /dev/null
+++ b/ncp-web/img/delete.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 16 16" width="16" height="16"><path d="M6.5 1L6 2H3c-.554 0-1 .446-1 1v1h12V3c0-.554-.446-1-1-1h-3l-.5-1zM3 5l.875 9c.06.55.573 1 1.125 1h6c.552 0 1.064-.45 1.125-1L13 5z"/></svg>
diff --git a/ncp-web/img/download.svg b/ncp-web/img/download.svg
new file mode 100644
index 00000000..dd2389b2
--- /dev/null
+++ b/ncp-web/img/download.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewbox="0 0 16 16" width="16" height="16"><path d="M6 1h4v7h5l-7 7-7-7h5z"/></svg>
diff --git a/ncp-web/index.php b/ncp-web/index.php
index c823f5a9..b2d21760 100644
--- a/ncp-web/index.php
+++ b/ncp-web/index.php
@@ -104,7 +104,7 @@ if ($ret == 0) {
<br>
<a href="wizard"> <button type="button" class="wizard-btn" id="go-wizard">{$l->__("run")} </button></a>
<button type="button" class="first-run-close" id="skip-wizard" >{$l->__("skip")} </button>
- <button type="button" class="first-run-close" id="close-wizard">{$l->__("close")}</button>
+ <button type="button" class="first-run-close close-dialog-x">{$l->__("close")}</button>
<br><br>
</div>
</div>
@@ -113,6 +113,20 @@ HTML;
}
?>
+ <div id="confirmation-dialog" class="hidden">
+ <div class='dialog'>
+ <br><br>
+ <h2 id="config-box-title">Are you sure?</h2>
+ <br>
+ <p>Click OK to confirm this operation</p>
+ <br>
+ <button type="button" id="confirmation-dialog-ok"> OK </button>
+ <button type="button" class="confirmation-dialog-close"> Cancel </button>
+ <button type="button" class="confirmation-dialog-close close-dialog-x">Close</button>
+ <br><br>
+ </div>
+ </div>
+
<header role="banner"><div id="header">
<div id="header-left">
<a href="https://ownyourbits.com" id="nextcloudpi" target="_blank" tabindex="1">
@@ -166,6 +180,11 @@ HTML;
<div class="icon-dashboard"></div>
</div>
</div>
+ <div id="backups-btn" title="<?php echo $l->__("Backups and snapshots"); ?>">
+ <div class="expand">
+ <div class="icon-backups"></div>
+ </div>
+ </div>
<div id="config-btn" title="<?php echo $l->__("Nextcloud Configuration"); ?>">
<div class="expand">
<div class="icon-config"></div>
@@ -213,7 +232,13 @@ HTML;
<h2 class="text-title"><?php echo $l->__("System Info"); ?></h2>
<div id="dashboard-suggestions" class="table-wrapper"></div>
<div id="dashboard-table" class="outputbox table-wrapper"></div>
- <div id="loading-info-gif"> <img src="img/loading-small.gif"> </div>
+ <div id="loading-info-gif" class="loading-section-gif"> <img src="img/loading-small.gif"> </div>
+ </div>
+
+ <div id="backups-wrapper" class="content-box <?php if($_GET['app'] != 'backups') echo 'hidden';?>">
+ <h2 class="text-title"><?php echo $l->__("Backups"); ?></h2>
+ <div id="backups-content" class="table-wrapper"></div>
+ <div id="loading-backups-gif" class="loading-section-gif"> <img src="img/loading-small.gif"> </div>
</div>
<div id="nc-config-wrapper" class="content-box <?php if($_GET['app'] != 'config') echo 'hidden';?>">
diff --git a/ncp-web/js/ncp.js b/ncp-web/js/ncp.js
index 556bc90e..bd0dc108 100644
--- a/ncp-web/js/ncp.js
+++ b/ncp-web/js/ncp.js
@@ -4,20 +4,23 @@
// Copyleft 2017 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
// GPL licensed (see end of file) * Use at your own risk!
//
-// More at https://ownyourbits.com/2017/02/13/nextcloud-ready-raspberry-pi-image/
+// More at https://nextcloudpi.com
///
var MINI = require('minified');
var $ = MINI.$, $$ = MINI.$$, EE = MINI.EE;
-var selectedID = null;
+var selectedID = null;
var ncp_app_list = null;
-var search_box = null;
-var lock = false;
+var search_box = null;
+var lock = false;
// URL based navigation
+// TODO unify repeating code
window.onpopstate = function(event) {
selectedID = location.search.split('=')[1];
- if (selectedID == 'config')
+ if (selectedID == 'backups')
+ switch_to_section('backups');
+ else if (selectedID == 'config')
switch_to_section('nc-config');
else if (selectedID == 'dashboard')
switch_to_section('dashboard');
@@ -27,14 +30,16 @@ window.onpopstate = function(event) {
function errorMsg()
{
- $('#config-box').fill( "Something went wrong. Try refreshing the page" );
+ $('#app-content').fill( "Something went wrong. Try refreshing the page" );
}
function switch_to_section(section)
{
+ // TODO unify repeating code
$( '#config-wrapper > div' ).hide();
$( '#dashboard-wrapper' ).hide();
$( '#nc-config-wrapper' ).hide();
+ $( '#backups-wrapper' ).hide();
$( '#' + section + '-wrapper' ).show();
$( '#app-navigation ul' ).set('-active');
selectedID = null;
@@ -123,10 +128,169 @@ function print_dashboard()
$('#loading-info-gif').hide();
$('#dashboard-table').ht( ret.table );
$('#dashboard-suggestions').ht( ret.suggestions );
- reload_sidebar();
+ print_backups();
} ).error( errorMsg );
}
+function del_bkp(button)
+{
+ var tr = button.up().up();
+ var path = tr.get('.id');
+ $.request('post', 'ncp-launcher.php', { action:'del-bkp',
+ value: path,
+ csrf_token: $( '#csrf-token' ).get( '.value' ) }).then(
+ function success( result )
+ {
+ var ret = $.parseJSON( result );
+ if ( ret.token )
+ $('#csrf-token').set( { value: ret.token } );
+ if ( ret.ret && ret.ret == '0' ) // means that the process was launched
+ tr.remove();
+ else
+ console.log('failed removing ' + path);
+ }
+ ).error( errorMsg )
+}
+
+function restore_bkp(button)
+{
+ var tr = button.up().up();
+ var path = tr.get('.id');
+ click_app($('#nc-restore'));
+ history.pushState(null, selectedID, "?app=" + selectedID);
+ $('#nc-restore-BACKUPFILE').set({ value: path });
+ $('#nc-restore-config-button').trigger('click');
+}
+
+function restore_snap(button)
+{
+ var tr = button.up().up();
+ var path = tr.get('.id');
+ click_app($('#nc-restore-snapshot'));
+ history.pushState(null, selectedID, "?app=" + selectedID);
+ $('#nc-restore-snapshot-SNAPSHOT').set({ value: path });
+ $('#nc-restore-snapshot-config-button').trigger('click');
+}
+
+function del_snap(button)
+{
+ var tr = button.up().up();
+ var path = tr.get('.id');
+ $.request('post', 'ncp-launcher.php', { action:'del-snap',
+ value: path,
+ csrf_token: $('#csrf-token').get('.value') }).then(
+ function success( result )
+ {
+ var ret = $.parseJSON( result );
+ if ( ret.token )
+ $('#csrf-token').set( { value: ret.token } );
+ if ( ret.ret && ret.ret == '0' ) // means that the process was launched
+ tr.remove();
+ else
+ console.log('failed removing ' + path);
+ }
+ ).error( errorMsg )
+}
+
+function restore_upload(button)
+{
+ var file = $$('#restore-upload').files[0];
+ if (!file) return;
+ var upload_token = $('#csrf-token').get('.value');
+ var form_data = new FormData();
+ form_data.append('backup', file);
+ form_data.append('csrf_token', upload_token);
+ $.request('post', 'upload.php', form_data).then(
+ function success( result )
+ {
+ var ret = $.parseJSON( result );
+ if ( ret.token )
+ $('#csrf-token').set( { value: ret.token } );
+ if ( ret.ret && ret.ret == '0' ) // means that the process was launched
+ {
+ click_app($('#nc-restore'));
+ history.pushState(null, selectedID, "?app=" + selectedID);
+ $('#nc-restore-BACKUPFILE').set({ value: '/tmp/' + upload_token.replace('/', '') + file.name });
+ $('#nc-restore-config-button').trigger('click');
+ }
+ else
+ console.log('error uploading ' + file);
+ }
+ ).error( errorMsg )
+}
+
+clicked_dialog_button = null;
+clicked_dialog_action = null;
+
+function dialog_action(button)
+{
+ if ( clicked_dialog_action && clicked_dialog_button)
+ clicked_dialog_action(clicked_dialog_button);
+}
+
+// backups
+function set_backup_handlers()
+{
+ $( '.download-bkp' ).on('click', function(e)
+ {
+ var tr = this.up().up();
+ var path = tr.get('.id');
+ window.location.replace('download.php?bkp=' + encodeURIComponent(path) + '&token=' + encodeURIComponent(tr.next().get('.value')));
+ });
+ $( '.delete-bkp' ).on('click', function(e)
+ {
+ $('#confirmation-dialog').show();
+ clicked_dialog_action = del_bkp;
+ clicked_dialog_button = this;
+ });
+ $( '.restore-bkp' ).on('click', function(e)
+ {
+ $('#confirmation-dialog').show();
+ clicked_dialog_action = restore_bkp;
+ clicked_dialog_button = this;
+ });
+ $( '#restore-upload-btn' ).on('click', function(e)
+ {
+ var file = $$('#restore-upload').files[0];
+ if (!file) return;
+ $('#confirmation-dialog').show();
+ clicked_dialog_action = restore_upload;
+ clicked_dialog_button = this;
+ });
+ $( '.restore-snap' ).on('click', function(e)
+ {
+ $('#confirmation-dialog').show();
+ clicked_dialog_action = restore_snap;
+ clicked_dialog_button = this;
+ });
+ $( '.delete-snap' ).on('click', function(e)
+ {
+ $('#confirmation-dialog').show();
+ clicked_dialog_action = del_snap;
+ clicked_dialog_button = this;
+ });
+}
+
+function print_backups()
+{
+ // request
+ $.request('post', 'ncp-launcher.php', { action:'backups',
+ csrf_token: $('#csrf-token-ui').get('.value') }
+ ).then(
+ function success( result )
+ {
+ var ret = $.parseJSON( result );
+ if (ret.token)
+ $('#csrf-token-ui').set({ value: ret.token });
+ if (ret.ret && ret.ret == '0') {
+ $('#loading-backups-gif').hide();
+ $('#backups-content').ht(ret.output);
+ set_backup_handlers();
+ reload_sidebar();
+ }
+ }).error( errorMsg );
+}
+
function reload_sidebar()
{
// request
@@ -272,9 +436,16 @@ $(function()
{
if ( ret.ret == '0' )
{
- if( ret.ref && ret.ref == 'nc-update' )
- window.location.reload( true );
- reload_sidebar();
+ if (ret.ref)
+ {
+ if (ret.ref == 'nc-update')
+ window.location.reload( true );
+ else if(ret.ref == 'nc-backup')
+ print_backups();
+ if(ret.ref != 'nc-restore' && ret.ref != 'nc-backup') // FIXME PHP is reloaded asynchronously after nc-restore
+ reload_sidebar();
+ }
+
$('.circle-retstatus').set('+icon-green-circle');
}
else
@@ -354,7 +525,6 @@ $(function()
function dirname(path) { return path.replace(/\\/g,'/').replace(/\/[^\/]*$/, ''); }
var span = this.up().select('span', true);
- console.log(span);
var path = dirname(this.get('.value'));
// request
@@ -470,6 +640,22 @@ $(function()
$( '#first-run-wizard' ).hide();
} );
+ // dialog confirmation
+ $( '#confirmation-dialog-ok' ).on('click', function(e)
+ {
+ $( '#confirmation-dialog' ).hide();
+ dialog_action();
+ } );
+ $( '.confirmation-dialog-close' ).on('click', function(e)
+ {
+ $( '#confirmation-dialog' ).hide();
+ } );
+ $( '#confirmation-dialog' ).on('|click', function(e)
+ {
+ if( e.target.id == 'confirmation-dialog' )
+ $( '#confirmation-dialog' ).hide();
+ } );
+
// click to nextcloud button
$('#nextcloud-btn').set( '@href', window.location.protocol + '//' + window.location.hostname );
@@ -482,6 +668,7 @@ $(function()
history.pushState(null, selectedID, "?app=dashboard");
} );
+ // TODO unify repeating code
// config button
$( '#config-btn' ).on('click', function(e)
{
@@ -491,6 +678,15 @@ $(function()
history.pushState(null, selectedID, "?app=config");
} );
+ // backups button
+ $( '#backups-btn' ).on('click', function(e)
+ {
+ if ( lock ) return;
+ close_menu();
+ switch_to_section( 'backups' );
+ history.pushState(null, selectedID, "?app=backups");
+ } );
+
// language selection
var langold = $( '#language-selection' ).get( '.value' );
$( '#language-selection' ).on( 'change', function(e)
diff --git a/ncp-web/ncp-launcher.php b/ncp-web/ncp-launcher.php
index 60f22e65..122a43ba 100644
--- a/ncp-web/ncp-launcher.php
+++ b/ncp-web/ncp-launcher.php
@@ -137,6 +137,21 @@ else if ( $_POST['action'] == "info" )
}
//
+// backups
+//
+else if ( $_POST['action'] == "backups" )
+{
+ ob_start();
+ include('backups.php');
+ $backups_page = ob_get_clean();
+
+ // return JSON
+ echo '{ "token": "' . getCSRFToken() . '",'; // Get new token
+ echo ' "output": ' . json_encode($backups_page) . ' , ';
+ echo ' "ret": "0" }';
+}
+
+//
// sidebar
//
else if ( $_POST['action'] == "sidebar" )
@@ -176,6 +191,34 @@ else if ( $_POST['action'] == "path-exists" )
}
//
+// del backup
+//
+else if ( $_POST['action'] == "del-bkp" )
+{
+ $file = escapeshellarg($_POST['value']);
+ $ret = 1;
+ exec("sudo /home/www/ncp-backup-launcher.sh del $file", $out, $ret);
+
+ // return JSON
+ echo '{ "token": "' . getCSRFToken() . '",'; // Get new token
+ echo ' "ret": "' . $ret . '" }';
+}
+
+//
+// del snapshot
+//
+else if ( $_POST['action'] == "del-snap" )
+{
+ $file = escapeshellarg($_POST['value']);
+ $ret = 1;
+ exec("sudo /home/www/ncp-backup-launcher.sh delsnp $file", $out, $ret);
+
+ // return JSON
+ echo '{ "token": "' . getCSRFToken() . '",'; // Get new token
+ echo ' "ret": "' . $ret . '" }';
+}
+
+//
// poweroff
//
else if ( $_POST['action'] == "poweroff" )
diff --git a/ncp-web/upload.php b/ncp-web/upload.php
new file mode 100644
index 00000000..38b47cb7
--- /dev/null
+++ b/ncp-web/upload.php
@@ -0,0 +1,47 @@
+<?php
+///
+// NextCloudPi Web Panel backend
+//
+// Copyleft 2019 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
+// GPL licensed (see end of file) * Use at your own risk!
+//
+// More at https://nextcloudpi.com
+///
+
+include ('csrf.php');
+session_start();
+
+// CSRF check
+$token = isset($_POST['csrf_token']) ? $_POST['csrf_token'] : '';
+if ( empty($token) || !validateCSRFToken($token) )
+ exit( '{ "output": "Unauthorized request. Try reloading the page" }' );
+
+isset($_FILES['backup']) or exit( '{ "output": "no upload" }' );
+
+$error=$_FILES['backup']['error'];
+if ($error !== 0)
+ exit( '{ "output": "upload error ' . $error . '" }' );
+
+$file_name = $_POST['csrf_token'] . basename($_FILES['backup']['name']);
+$file_name = str_replace('/', '', $file_name);
+$file_size = $_FILES['backup']['size'];
+$file_tmp = $_FILES['backup']['tmp_name'];
+$file_type = $_FILES['backup']['type'];
+
+preg_match( '/\.\./' , $file_name, $matches )
+ and exit( '{ "output": "Invalid input" , "token": "' . getCSRFToken() . '" }' );
+
+if($file_size === 0)
+ $errors[]='No file';
+
+$extension = pathinfo($file_name, PATHINFO_EXTENSION);
+if ($extension !== "tar" and $extension !== "gz")
+ exit( '{ "output": "invalid file" }' );
+
+if (!move_uploaded_file($file_tmp, sys_get_temp_dir() . '/' . $file_name))
+ exit('{ "output": "upload denied" }');
+
+// return JSON
+echo '{ "token": "' . getCSRFToken() . '",'; // Get new token
+echo ' "ret": "0" }';
+?>
diff --git a/ncp.sh b/ncp.sh
index 71872fa3..7aacc190 100644
--- a/ncp.sh
+++ b/ncp.sh
@@ -22,7 +22,7 @@ install()
{
# NCP-CONFIG
apt-get update
- $APTINSTALL git dialog whiptail jq
+ $APTINSTALL git dialog whiptail jq file
mkdir -p "$CONFDIR" "$BINDIR"
# include option in raspi-config (only Raspbian)
@@ -131,11 +131,39 @@ EOF
cat > /home/www/ncp-launcher.sh <<'EOF'
#!/bin/bash
+grep -q '[\\&#;`|*?~<>^()[{}$&[:space:]]' <<< "$*" && exit 1
source /usr/local/etc/library.sh
run_app $1
EOF
chmod 700 /home/www/ncp-launcher.sh
- echo "www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /sbin/halt, /sbin/reboot" >> /etc/sudoers
+
+ cat > /home/www/ncp-backup-launcher.sh <<'EOF'
+#!/bin/bash
+action="${1}"
+file="${2}"
+compressed="${3}"
+grep -q '[\\&#;`|*?~<>^()[{}$&]' <<< "$*" && exit 1
+[[ "$file" =~ ".." ]] && exit 1
+[[ "${action}" == "chksnp" ]] && {
+ btrfs subvolume show "$file" &>/dev/null || exit 1
+ exit
+}
+[[ "${action}" == "delsnp" ]] && {
+ btrfs subvolume delete "$file" || exit 1
+ exit
+}
+[[ -f "$file" ]] || exit 1
+[[ "$file" =~ ".tar" ]] || exit 1
+[[ "${action}" == "del" ]] && {
+ [[ "$(file "$file")" =~ "tar archive" ]] || [[ "$(file "$file")" =~ "gzip compressed data" ]] || exit 1
+ rm "$file" || exit 1
+ exit
+}
+[[ "$compressed" != "" ]] && pigz="-I pigz"
+tar $pigz -tf "$file" data &>/dev/null
+EOF
+ chmod 700 /home/www/ncp-backup-launcher.sh
+ echo "www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /home/www/ncp-backup-launcher.sh, /sbin/halt, /sbin/reboot" >> /etc/sudoers
# NCP AUTO TRUSTED DOMAIN
mkdir -p /usr/lib/systemd/system
diff --git a/update.sh b/update.sh
index 4921e675..fdc012ce 100755
--- a/update.sh
+++ b/update.sh
@@ -27,6 +27,8 @@ nc-init
UFW
nc-snapshot
nc-snapshot-auto
+nc-snapshot-sync
+nc-restore-snapshot
nc-audit
nc-hdd-monitor
nc-zram
@@ -187,6 +189,45 @@ EOF
# switch back to the apt LE version
which letsencrypt &>/dev/null || install_app letsencrypt
+ # update launchers
+ apt-get update
+ apt-get install -y --no-install-recommends file
+ cat > /home/www/ncp-launcher.sh <<'EOF'
+#!/bin/bash
+grep -q '[\\&#;`|*?~<>^()[{}$&[:space:]]' <<< "$*" && exit 1
+source /usr/local/etc/library.sh
+run_app $1
+EOF
+ chmod 700 /home/www/ncp-launcher.sh
+
+ cat > /home/www/ncp-backup-launcher.sh <<'EOF'
+#!/bin/bash
+action="${1}"
+file="${2}"
+compressed="${3}"
+grep -q '[\\&#;`|*?~<>^()[{}$&]' <<< "$*" && exit 1
+[[ "$file" =~ ".." ]] && exit 1
+[[ "${action}" == "chksnp" ]] && {
+ btrfs subvolume show "$file" &>/dev/null || exit 1
+ exit
+}
+[[ "${action}" == "delsnp" ]] && {
+ btrfs subvolume delete "$file" || exit 1
+ exit
+}
+[[ -f "$file" ]] || exit 1
+[[ "$file" =~ ".tar" ]] || exit 1
+[[ "${action}" == "del" ]] && {
+ [[ "$(file "$file")" =~ "tar archive" ]] || [[ "$(file "$file")" =~ "gzip compressed data" ]] || exit 1
+ rm "$file" || exit 1
+ exit
+}
+[[ "$compressed" != "" ]] && pigz="-I pigz"
+tar $pigz -tf "$file" data &>/dev/null
+EOF
+ chmod 700 /home/www/ncp-backup-launcher.sh
+ sed -i 's|www-data ALL = NOPASSWD: .*|www-data ALL = NOPASSWD: /home/www/ncp-launcher.sh , /home/www/ncp-backup-launcher.sh, /sbin/halt, /sbin/reboot|' /etc/sudoers
+
# remove redundant opcache configuration. Leave until update bug is fixed -> https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=815968
# Bug #416 reappeared after we moved to php7.2 and debian buster packages. (keep last)
[[ "$( ls -l /etc/php/7.2/fpm/conf.d/*-opcache.ini | wc -l )" -gt 1 ]] && rm "$( ls /etc/php/7.2/fpm/conf.d/*-opcache.ini | tail -1 )"