#!/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 # # 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 your " . "own 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 () { ($du, undef) = split(/\t/, $_); } close INF; print $q->p ("When last checked with the link above your configuration " . "and data files totaled $du. 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 "\n"; print '

'; 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 ''; print ''; 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 () { 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 "

"; 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 ""; 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, ""); }