smeserver-userpanels/root/etc/e-smith/web/functions/userpanel-userbackup

553 lines
13 KiB
Perl

#!/usr/bin/perl -wT
#----------------------------------------------------------------------
# heading : Your Settings
# description : Data Backup
# longdesc : backup user data
# navigation : 100 200
#
# Copyright (c) 2001 Mitel Networks Corporation
# 2002-5 Stephen Noble <support@dungog.net>
#
# This program 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 program 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 program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#----------------------------------------------------------------------
package esmith;
use strict;
use CGI ':all';
use CGI::Carp qw(fatalsToBrowser);
use esmith::cgi;
use esmith::config;
use esmith::util;
use esmith::db;
use esmith::lockfile;
sub showInitial ($$);
sub performAndShowResult ($);
sub desktopBackup ();
sub desktopRestore ();
sub desktopVerify ();
sub performDesktopRestore ($);
sub performDesktopVerify ($);
sub performDiskUsage ($);
BEGIN
{
# Clear PATH and related environment variables so that calls to
# external programs do not cause results to be tainted. See
# "perlsec" manual page for details.
$ENV {'PATH'} = '';
$ENV {'SHELL'} = '/bin/bash';
delete $ENV {'ENV'};
}
esmith::util::setRealToEffective ();
my %conf;
tie %conf, 'esmith::config';
my %accounts;
tie %accounts, 'esmith::config', '/home/e-smith/db/accounts';
my $acctName = $ENV{'REMOTE_USER'};
if ($acctName =~ /^([a-z][\-\_\.a-z0-9]*)$/)
{
$acctName = $1;
}
my @directories = ("/home/e-smith/files/users/$acctName/");
# Unbuffer standard output so that files and directories are listed as
# they are restored
$| = 1;
# Store away current gid of 'www' group.
my $www_gid = getgrnam("www");
#------------------------------------------------------------
# examine state parameter and display the appropriate form
#------------------------------------------------------------
my $q = new CGI;
if (! grep (/^state$/, $q->param))
{
showInitial ($q, '');
}
elsif ($q->param ('state') eq "perform")
{
performAndShowResult ($q);
}
elsif ($q->param ('state') eq "desktop-restore")
{
performDesktopRestore($q);
}
elsif ($q->param ('state') eq "desktop-verify")
{
performDesktopVerify($q);
}
elsif ($q->param ('state') eq "diskUsage")
{
performDiskUsage ($q);
}
else
{
esmith::cgi::genStateError ($q, \%conf);
}
exit (0);
#------------------------------------------------------------
# subroutine to display initial form
#------------------------------------------------------------
sub showInitial ($$)
{
my ($q, $msg) = @_;
if ($msg eq '')
{
esmith::cgi::genHeaderNonCacheable ($q, \%conf,
'Backup or restore user data');
}
else
{
esmith::cgi::genHeaderNonCacheable
($q, \%conf, 'Operation status report');
print $q->p ($msg);
print $q->hr;
}
print $q->p (
"This page allows you to create a copy of <b>your "
. "own</b> user data files, from your home directory "
. "and downloads it to your local desktop via your web "
. "browser. The Verify desktop backup file option can be used to\n"
. "view a list of files in a desktop backup file.\n");
print $q->p ($q->a ({href => $q->url (-absolute => 1) . "?state=diskUsage"},
'Click here'),
'to check/refresh the disk usage for this user shown below, it may take
a minute the first time but should be faster afterwards.');
#show usage
if ( -e "/tmp/du-$acctName" )
{
my $du = '0';
open (INF,"/tmp/du-$acctName")
or die ("can't open du to read: $1. ");
while (<INF>)
{
($du, undef) = split(/\t/, $_);
}
close INF;
print $q->p ("When last checked with the link above your configuration "
. "and data files totaled <b>$du</b>. Your backup will be compressed to approx. "
. "half that size.\n");
}
my %labels = (
"desktop-backup" => "Backup to desktop",
"desktop-restore" => "Restore from desktop",
"desktop-verify" => "Verify desktop backup file",
);
my @labels = (
'desktop-backup',
'desktop-restore',
'desktop-verify',
);
my $default_action = 'desktop-backup';
print $q->startform (-method => 'POST',
-action => $q->url (-absolute => 1));
print $q->table ({border => 0, cellspacing => 0, cellpadding => 4});
print $q->Tr (esmith::cgi::genWidgetRow($q,"Select an action",
$q->popup_menu (-name => 'function',
-values => [ @labels ],
-default => $default_action,
-labels => \%labels)));
print "</table>\n";
print '<p>';
print $q->Tr (
esmith::cgi::genButtonRow(
$q,
$q->submit (-name => 'action', -value => 'Perform')
)
);
print $q->hidden (
-name => 'state',
-override => 1,
-default => 'perform'
);
print $q->endform;
# print $q->p ($q->hr, $q->font ({size => "-1"}, "www.dungog.net/sme"));
esmith::cgi::genFooter($q);
print '</FONT>';
print '</DIV>';
print $q->end_html;
}
sub performAndShowResult ($)
{
my ($q) = @_;
my $function = $q->param ('function');
if ($function eq 'refresh')
{
showInitial ($q, '');
}
elsif ($function eq 'desktop-backup')
{
desktopBackup();
}
elsif ($function eq 'desktop-restore')
{
desktopRestore();
}
elsif ($function eq 'desktop-verify')
{
desktopVerify();
}
else
{
# Unknown function - refresh the screen anyway
showInitial ($q, 'unknown');
}
return;
esmith::cgi::genHeaderNonCacheable ($q, \%conf,
'X Backup or restore server data');
print $q->p ( $function );
esmith::cgi::genFooter ($q);
}
sub desktopBackup ()
{
my $directories = $q->param ('directories');
# Generate a header that will trigger a download and send data as
# an octet stream.
print "Expires: 0\n";
print "Content-type: application/octet-stream\n";
print "Content-disposition: inline; filename=$acctName.tgz\n";
print "\n";
open(RD,
"/bin/tar --directory / --create @directories --file=-"
. " | /usr/bin/gzip |");
while (<RD>)
{
print;
}
close RD;
}
sub desktopRestore ()
{
esmith::cgi::genHeaderNonCacheable ($q, \%conf,
'Restore server configuration');
print $q->p (
"This process will upload a SME Server "
. " backup file from your "
. "local desktop to your server and "
. "restore the configuration and user data files.\n");
print $q->p (
"Be very sure you are uploading the correct file, "
. "files with the same name will be overwritten.\n");
print $q->start_multipart_form(
-method => 'POST',
-action => $q->url (-absolute => 1));
print $q->table (
{border => 0, cellspacing => 0, cellpadding => 4},
esmith::cgi::genWidgetRow(
$q,
"Backup file to restore from",
$q->filefield(
-name => 'backupFile',
-default => "$acctName.tgz",
-size => 32)),
esmith::cgi::genButtonRow(
$q,
$q->submit(
-name => 'action',
-value => 'Restore')));
print $q->hidden(
-name => 'state',
-override => 1,
-default => 'desktop-restore');
print $q->endform;
esmith::cgi::genFooter ($q);
}
sub desktopVerify ()
{
esmith::cgi::genHeaderNonCacheable ($q, \%conf,
'Verify desktop backup file');
print $q->p (
"This option will display the names of all files\n"
. "in a previously created desktop backup file. You\n"
. "can use this option to verify the contents of the\n"
. "backup file.\n"
);
print $q->start_multipart_form(
-method => 'POST',
-action => $q->url (-absolute => 1)
);
print $q->table (
{border => 0, cellspacing => 0, cellpadding => 4},
esmith::cgi::genWidgetRow(
$q,
"Select backup file",
$q->filefield(
-name => 'backupFile',
-default => "$acctName.tgz",
-size => 32
)
),
esmith::cgi::genButtonRow(
$q,
$q->submit(
-name => 'action',
-value => 'Verify'
)
)
);
print $q->hidden(
-name => 'state',
-override => 1,
-default => 'desktop-verify'
);
print $q->endform;
esmith::cgi::genFooter ($q);
}
sub performDesktopRestore ($)
{
my ($q) = @_;
# Need to validate this here: $q->param ('backupFile');
my $lock_file = "/var/lock/subsys/e-smith-restore";
my $file_handle = &esmith::lockfile::LockFileOrReturn($lock_file);
unless ($file_handle)
{
esmith::cgi::genHeaderNonCacheable(
$q,
\%conf,
"Unable to proceed with restore of server configuration"
);
print $q->p (
$q->b (
"Another restore is in progress.",
"Please try again later."
)
);
esmith::cgi::genFooter ($q);
return;
}
# no need to reboot after restoring users data
#db_set_prop(\%restore, 'restore', 'state', 'running');
#db_set_prop(\%restore, 'restore', 'start', time);
#db_set_prop(\%conf, 'bootstrap-console', 'Run', 'yes');
if (open(RD, "-|"))
{
#----------------------------------------
# restore system from uploaded backup file
#----------------------------------------
esmith::cgi::genHeaderNonCacheable ($q, \%conf, "Restore in progress");
print $q->p ("The following files and directories have been restored:");
print "<UL>";
my $complete = 0;
while (<RD>)
{
print "<li>$_</li>\n";
}
print "</UL>";
my $message;
if (!close RD)
{
$message = "Restore failed! " .
"There was an error in reading the backup file";
}
else
{
$message = "Restore complete";
}
#db_set_prop(\%restore, 'restore', 'state', 'complete');
#db_set_prop(\%restore, 'restore', 'finish', time);
&esmith::lockfile::UnlockFile($file_handle);
print $q->p ($q->b ($message));
esmith::cgi::genFooter ($q);
}
else
{
select(STDOUT);
$| = 1;
my $backupFile = $q->param ('backupFile');
my $decodeCommand = ( $backupFile =~ /\.bak$/ ) ?
"|/usr/bin/uudecode -o /dev/stdout" :
"|/usr/bin/gzip -d";
open(WR,
$decodeCommand
. " | /bin/tar --directory / --extract --verbose --file=-"
) || die ("Could not execute backup pipeline: $!\n");
while (<$backupFile>)
{
print WR;
}
close WR || die("Could not decode backup file: $!\n");
exit(0);
}
return;
}
sub performDesktopVerify ($)
{
my ($q) = @_;
if (open(RD, "-|"))
{
esmith::cgi::genHeaderNonCacheable ($q,
\%conf, "Verify desktop backup file");
print $q->p
("The following files are contained in the backup file:");
print "<UL>";
my $complete = 0;
while (<RD>)
{
#$complete++ if /etc\/smbpasswd/;
print "<li>$_</li>\n";
}
print "</UL>";
my $status = close RD ?
($complete ?
"Verify complete" :
"End of list")
: "There was an error in reading the backup file";
print $q->p ($q->b ($status));
esmith::cgi::genFooter ($q);
}
else
{
select(STDOUT);
$| = 1;
my $backupFile = $q->param ('backupFile');
my $decodeCommand = ( $backupFile =~ /\.bak$/ ) ?
"|/usr/bin/uudecode -o /dev/stdout" :
"|/usr/bin/gzip -d";
open(WR,
$decodeCommand
. " | /bin/tar --directory / --list --file=-"
) || die ("Could not decode backup file: $!\n");
while (<$backupFile>)
{
print WR;
}
close WR;
exit 0;
}
return;
}
sub performDiskUsage ($)
{
my ($q) = @_;
my $acctName = $ENV{'REMOTE_USER'};
if ($acctName =~ /^([a-z][\-a-z0-9]*)$/)
{
$acctName = $1;
}
system ("/sbin/e-smith/signal-event", "calc-du", "$acctName") == 0
or die ("Error occurred while calculating disk usage.\n");
showInitial ($q, "");
}