X-Git-Url: http://git.rot13.org/?p=BackupPC.git;a=blobdiff_plain;f=cgi-bin%2FBackupPC_Admin;h=cac9707afff973e4723701bf96737f5013401b53;hp=c0c2af059522a504070c7393705e84ba154430c5;hb=2fb1e8b5cee74c72bbb8ecc7722c05256cbdc6bf;hpb=1ce7d1541ea1279aaa0a75c16986a3fd40b608ec diff --git a/cgi-bin/BackupPC_Admin b/cgi-bin/BackupPC_Admin index c0c2af0..cac9707 100755 --- a/cgi-bin/BackupPC_Admin +++ b/cgi-bin/BackupPC_Admin @@ -14,7 +14,8 @@ # user name. # # Also, this script needs to run as the BackupPC user. To accomplish -# this the script is typically installed as setuid to the BackupPC user. +# this the script is typically installed as setuid to the BackupPC user, +# or it can run under mod_perl with httpd running as the BackupPC user. # # AUTHOR # Craig Barratt @@ -38,7 +39,7 @@ # #======================================================================== # -# Version 1.5.0, released 2 Aug 2002. +# Version 1.6.0_CVS, released 10 Dec 2002. # # See http://backuppc.sourceforge.net. # @@ -46,10 +47,11 @@ use strict; use CGI; -use lib "__INSTALLDIR__/lib"; +use lib "/usr/local/BackupPC/lib"; use BackupPC::Lib; use BackupPC::FileZIO; use BackupPC::Attrib qw(:all); +use BackupPC::View; use Data::Dumper; use vars qw($Cgi %In $MyURL $User %Conf $TopDir $BinDir $bpc); @@ -58,6 +60,8 @@ use vars qw(%Status %Info %Jobs @BgQueue @UserQueue @CmdQueue use vars qw($Hosts $HostsMTime $ConfigMTime $PrivAdmin); use vars qw(%UserEmailInfo $UserEmailInfoMTime %RestoreReq); +use vars qw ($Lang); + $Cgi = new CGI; %In = $Cgi->Vars; @@ -71,16 +75,18 @@ $MyURL = $ENV{SCRIPT_NAME}; $User = $ENV{REMOTE_USER}; if ( !defined($bpc) ) { - ErrorExit("BackupPC::Lib->new failed: check apache error_log\n") - if ( !($bpc = BackupPC::Lib->new) ); + ErrorExit($Lang->{BackupPC__Lib__new_failed__check_apache_error_log}) + if ( !($bpc = BackupPC::Lib->new) ); $TopDir = $bpc->TopDir(); $BinDir = $bpc->BinDir(); %Conf = $bpc->Conf(); + $Lang = $bpc->Lang(); $ConfigMTime = $bpc->ConfigMTime(); } elsif ( $bpc->ConfigMTime() != $ConfigMTime ) { $bpc->ConfigRead(); %Conf = $bpc->Conf(); $ConfigMTime = $bpc->ConfigMTime(); + $Lang = $bpc->Lang(); } # @@ -94,36 +100,41 @@ $ENV{PATH} = $Conf{MyPath}; # if ( $Conf{BackupPCUserVerify} && $> != (my $uid = (getpwnam($Conf{BackupPCUser}))[2]) ) { - ErrorExit("Wrong user: my userid is $>, instead of $uid" - . " ($Conf{BackupPCUser})\n"); + ErrorExit(eval("qq{$Lang->{Wrong_user__my_userid_is___}}")); } if ( !defined($Hosts) || $bpc->HostsMTime() != $HostsMTime ) { $HostsMTime = $bpc->HostsMTime(); $Hosts = $bpc->HostInfoRead(); + + # turn operators list into a hash for quick lookups + foreach my $host (keys %$Hosts) { + $Hosts->{$host}{moreUsers} = + {map {$_, 1} split(",", $Hosts->{$host}{moreUsers}) } + } } my %ActionDispatch = ( - "summary" => \&Action_Summary, - "Start Incr Backup" => \&Action_StartStopBackup, - "Start Full Backup" => \&Action_StartStopBackup, - "Stop/Dequeue Backup" => \&Action_StartStopBackup, - "queue" => \&Action_Queue, - "view" => \&Action_View, - "LOGlist" => \&Action_LOGlist, - "emailSummary" => \&Action_EmailSummary, - "browse" => \&Action_Browse, - "Restore" => \&Action_Restore, - "RestoreFile" => \&Action_RestoreFile, - "hostInfo" => \&Action_HostInfo, - "generalInfo" => \&Action_GeneralInfo, - "restoreInfo" => \&Action_RestoreInfo, + "summary" => \&Action_Summary, + $Lang->{Start_Incr_Backup} => \&Action_StartStopBackup, + $Lang->{Start_Full_Backup} => \&Action_StartStopBackup, + $Lang->{Stop_Dequeue_Backup} => \&Action_StartStopBackup, + "queue" => \&Action_Queue, + "view" => \&Action_View, + "LOGlist" => \&Action_LOGlist, + "emailSummary" => \&Action_EmailSummary, + "browse" => \&Action_Browse, + $Lang->{Restore} => \&Action_Restore, + "RestoreFile" => \&Action_RestoreFile, + $Lang->{hostInfo} => \&Action_HostInfo, + "generalInfo" => \&Action_GeneralInfo, + "restoreInfo" => \&Action_RestoreInfo, ); # # Set default actions, then call sub handler # -$In{action} ||= "hostInfo" if ( defined($In{host}) ); +$In{action} ||= $Lang->{hostInfo} if ( defined($In{host}) ); $In{action} = "generalInfo" if ( !defined($ActionDispatch{$In{action}}) ); $ActionDispatch{$In{action}}(); exit(0); @@ -142,7 +153,7 @@ sub Action_Summary my $Privileged = CheckPermission(); if ( !$Privileged ) { - ErrorExit("Only privileged users can view PC summaries." ); + ErrorExit($Lang->{Only_privileged_users_can_view_PC_summaries} ); } foreach my $host ( sort(keys(%Status)) ) { my($fullDur, $incrCnt, $incrAge, $fullSize, $fullRate); @@ -182,6 +193,7 @@ sub Action_Summary $fullTot += $fullCnt; $incrTot += $incrCnt; $fullSize = sprintf("%.2f", $fullSize / 1000); + if (! $incrAge) { $incrAge = " "; } $str = < ${HostLink($host)} ${UserLink($Hosts->{$host}{user})} @@ -191,8 +203,8 @@ sub Action_Summary $fullRate $incrCnt $incrAge - $Status{$host}{state} - $Status{$host}{reason} + $Lang->{$Status{$host}{state}} + $Lang->{$Status{$host}{reason}} EOF if ( @Backups == 0 ) { $hostCntNone++; @@ -206,73 +218,25 @@ EOF $incrSizeTot = sprintf("%.2f", $incrSizeTot / 1000); my $now = timeStamp2(time); - Header("BackupPC: Server Summary"); - - print < -This status was generated at $now. -

+ Header($Lang->{BackupPC__Server_Summary}); + print eval ("qq{$Lang->{BackupPC_Summary}}"); -${h2("Hosts with good Backups")} -

-There are $hostCntGood hosts that have been backed up, for a total of: -

    -
  • $fullTot full backups of total size ${fullSizeTot}GB - (prior to pooling and compression), -
  • $incrTot incr backups of total size ${incrSizeTot}GB - (prior to pooling and compression). -
- - - - - - - - - - - -$strGood -
Host User #Full Full Age/days Full Size/GB Speed MB/sec #Incr Incr Age/days State Last attempt
-

- -${h2("Hosts with no Backups")} -

-There are $hostCntNone hosts with no backups. -

- - - - - - - - - - - -$strNone -
Host User #Full Full Age/days Full Size/GB Speed MB/sec #Incr Incr Age/days Current State Last backup attempt
-EOF Trailer(); } sub Action_StartStopBackup { my($str, $reply); - my $start = 1 if ( $In{action} eq "Start Incr Backup" - || $In{action} eq "Start Full Backup" ); - my $doFull = $In{action} eq "Start Full Backup" ? 1 : 0; + + my $start = 1 if ( $In{action} eq $Lang->{Start_Incr_Backup} + || $In{action} eq $Lang->{Start_Full_Backup} ); + my $doFull = $In{action} eq $Lang->{Start_Full_Backup} ? 1 : 0; my $type = $doFull ? "full" : "incremental"; my $host = $In{host}; my $Privileged = CheckPermission($host); if ( !$Privileged ) { - ErrorExit("Only privileged users can stop or start backups on" - . " ${EscapeHTML($host)}."); + ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_stop_or_start_backups}}")); } ServerConnect(); @@ -280,45 +244,27 @@ sub Action_StartStopBackup if ( $start ) { if ( $Hosts->{$host}{dhcp} ) { $reply = $bpc->ServerMesg("backup $In{hostIP} $host" - . " $User $doFull"); - $str = "Backup requested on DHCP $host ($In{hostIP}) by" - . " $User from $ENV{REMOTE_ADDR}"; + . " $User $doFull"); + $str = eval("qq{$Lang->{Backup_requested_on_DHCP__host}}"); } else { $reply = $bpc->ServerMesg("backup $host $host $User $doFull"); - $str = "Backup requested on $host by $User"; + $str = eval("qq{$Lang->{Backup_requested_on__host_by__User}}"); } } else { $reply = $bpc->ServerMesg("stop $host $User $In{backoff}"); - $str = "Backup stopped/dequeued on $host by $User"; + $str = eval("qq{$Lang->{Backup_stopped_dequeued_on__host_by__User}}"); } - Header("BackupPC: Backup Requested on $host"); - print < -Reply from server was: $reply -

-Go back to $host home page. -EOF + + Header(eval ("qq{$Lang->{BackupPC__Backup_Requested_on__host}}") ); + print (eval ("qq{$Lang->{REPLY_FROM_SERVER}}")); + Trailer(); } else { if ( $start ) { my $ipAddr = ConfirmIPAddress($host); - Header("BackupPC: Start Backup Confirm on $host"); - print < -You are about to start a $type backup on $host. - -

- - - -Do you really want to do this? - - -
-EOF + Header(eval("qq{$Lang->{BackupPC__Start_Backup_Confirm_on__host}}")); + print (eval("qq{$Lang->{Are_you_sure_start}}")); } else { my $backoff = ""; GetStatusInfo("host($host)"); @@ -326,23 +272,8 @@ EOF $backoff = sprintf("%.1f", ($StatusHost{backoffTime} - time) / 3600); } - Header("BackupPC: Stop Backup Confirm on $host"); - print < -You are about to stop/dequeue backups on $host; - -
- - -Also, please don't start another backup for - hours. -

-Do you really want to do this? - - -

-EOF + Header($Lang->{BackupPC__Stop_Backup_Confirm_on__host}); + print (eval ("qq{$Lang->{Are_you_sure_stop}}")); } Trailer(); } @@ -356,7 +287,7 @@ sub Action_Queue my $Privileged = CheckPermission(); if ( !$Privileged ) { - ErrorExit("Only privileged users can view queues." ); + ErrorExit($Lang->{Only_privileged_users_can_view_queues_}); } while ( @BgQueue ) { @@ -388,43 +319,10 @@ EOF $cmd EOF } - Header("BackupPC: Queue Summary"); - print < -${h2("User Queue Summary")} -

-The following user requests are currently queued: - - - - -$strUser -
Host Req Time User
-

- -${h2("Background Queue Summary")} -

-The following background requests are currently queued: - - - - -$strBg -
Host Req Time User
-

- -${h2("Command Queue Summary")} -

-The following command requests are currently queued: - - - - - -$strCmd -
Host Req Time User Command
-EOF + Header($Lang->{BackupPC__Queue_Summary}); + + print ( eval ( "qq{$Lang->{Backup_Queue_Summary}}") ); + Trailer(); } @@ -440,7 +338,7 @@ sub Action_View my($file, $comment); my $ext = $num ne "" ? ".$num" : ""; - ErrorExit("Invalid number $num") if ( $num ne "" && $num !~ /^\d+$/ ); + ErrorExit(eval("qq{$Lang->{Invalid_number__num}}")) if ( $num ne "" && $num !~ /^\d+$/ ); if ( $type eq "XferLOG" ) { $file = "$TopDir/pc/$host/SmbLOG$ext"; $file = "$TopDir/pc/$host/XferLOG$ext" if ( !-f $file && !-f "$file.z"); @@ -465,12 +363,12 @@ sub Action_View } elsif ( $type eq "docs" ) { $file = "$BinDir/../doc/BackupPC.html"; if ( open(LOG, $file) ) { - Header("BackupPC: Documentation"); + Header($Lang->{BackupPC__Documentation}); print while ( ); close(LOG); Trailer(); } else { - ErrorExit("Unable to open $file: configuration problem?"); + ErrorExit(eval("qq{$Lang->{Unable_to_open__file__configuration_problem}}")); } return; } elsif ( $type eq "config" ) { @@ -484,22 +382,19 @@ sub Action_View $linkHosts = 1; } if ( !$Privileged ) { - ErrorExit("Only privileged users can view log or config files." ); + ErrorExit($Lang->{Only_privileged_users_can_view_log_or_config_files}); } if ( !-f $file && -f "$file.z" ) { $file .= ".z"; $compress = 1; } - Header("BackupPC: Log File $file"); - print < -EOF + Header(eval("qq{$Lang->{Backup_PC__Log_File__file}}") ); + print( eval ("qq{$Lang->{Log_File__file__comment}}")); if ( defined($fh = BackupPC::FileZIO->open($file, 0, $compress)) ) { my $mtimeStr = $bpc->timeStamp((stat($file))[9], 1); - print <$file, modified $mtimeStr $comment -EOF + + print ( eval ("qq{$Lang->{Contents_of_log_file}}")); + print "

";
         if ( $type eq "XferErr" || $type eq "XferErrbad"
 				|| $type eq "RestoreErr" ) {
@@ -507,7 +402,7 @@ EOF
             while ( 1 ) {
                 $_ = $fh->readLine();
                 if ( $_ eq "" ) {
-		    print("[ skipped $skipped lines ]\n") if ( $skipped );
+		    print(eval ("qq{$Lang->{skipped__skipped_lines}}"));
 		    last;
 		}
                 if ( /smb: \\>/
@@ -525,11 +420,12 @@ EOF
                         || /^\s+directory \\/
                         || /^Timezone is/
                         || /^\.\//
+                        || /^  /
 			    ) {
 		    $skipped++;
 		    next;
 		}
-		print("[ skipped $skipped lines ]\n") if ( $skipped );
+		print(eval("qq{$Lang->{skipped__skipped_lines}}")) if ( $skipped );
 		$skipped = 0;
                 print ${EscapeHTML($_)};
             }
@@ -561,7 +457,7 @@ EOF
         }
         $fh->close();
     } else {
-        printf("
\nCan't open log file $file\n");
+	printf( eval("qq{$Lang->{_pre___Can_t_open_log_file__file}}"));
     }
     print <
@@ -574,7 +470,7 @@ sub Action_LOGlist
     my $Privileged = CheckPermission($In{host});
 
     if ( !$Privileged ) {
-        ErrorExit("Only privileged users can view log files.");
+        ErrorExit($Lang->{Only_privileged_users_can_view_log_files});
     }
     my $host = $In{host};
     my($url0, $hdr, $root, $str);
@@ -604,18 +500,8 @@ sub Action_LOGlist
      $mtimeStr 
 EOF
     }
-    Header("BackupPC: Log File History");
-    print <
-
-
-    
-    
-$str
-
File Size Modification time
-EOF + Header($Lang->{BackupPC__Log_File_History}); + print (eval("qq{$Lang->{Log_File_History__hdr}}")); Trailer(); } @@ -624,7 +510,7 @@ sub Action_EmailSummary my $Privileged = CheckPermission(); if ( !$Privileged ) { - ErrorExit("Only privileged users can view email summaries." ); + ErrorExit($Lang->{Only_privileged_users_can_view_email_summaries}); } GetStatusInfo("hosts"); ReadUserEmailInfo(); @@ -642,170 +528,99 @@ EOF foreach my $t ( sort({$b <=> $a} keys(%EmailStr)) ) { $str .= $EmailStr{$t}; } - Header("BackupPC: Email Summary"); - print < - - - - - -$str -
Recipient Host Time Subject
-EOF + Header($Lang->{Email_Summary}); + print (eval("qq{$Lang->{Recent_Email_Summary}}")); Trailer(); } sub Action_Browse { my $Privileged = CheckPermission($In{host}); - my($i, $dirStr, $fileStr, $mangle); - my($numF, $compressF, $mangleF, $fullDirF); - my $checkBoxCnt = 0; # checkbox counter + my($i, $dirStr, $fileStr, $attr); + my $checkBoxCnt = 0; if ( !$Privileged ) { - ErrorExit("Only privileged users can browse backup files" - . " for host ${EscapeHTML($In{host})}." ); - } - my $host = $In{host}; - my $num = $In{num}; - my $dir = $In{dir}; - if ( $host eq "" ) { - ErrorExit("Empty host name."); + ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_browse_backup_files}}")); } + my $host = $In{host}; + my $num = $In{num}; + my $share = $In{share}; + my $dir = $In{dir}; + + ErrorExit($Lang->{Empty_host_name}) if ( $host eq "" ); # # Find the requested backup and the previous filled backup # my @Backups = $bpc->BackupInfoRead($host); for ( $i = 0 ; $i < @Backups ; $i++ ) { - if ( !$Backups[$i]{noFill} ) { - $numF = $Backups[$i]{num}; - $mangleF = $Backups[$i]{mangle}; - $compressF = $Backups[$i]{compress}; - } last if ( $Backups[$i]{num} == $num ); } if ( $i >= @Backups ) { ErrorExit("Backup number $num for host ${EscapeHTML($host)} does" . " not exist."); } - if ( !$Backups[$i]{noFill} ) { - # no need to back-fill a filled backup - $numF = $mangleF = $compressF = undef; - } my $backupTime = timeStamp2($Backups[$i]{startTime}); my $backupAge = sprintf("%.1f", (time - $Backups[$i]{startTime}) / (24 * 3600)); - $mangle = $Backups[$i]{mangle}; + my $view = BackupPC::View->new($bpc, $host, \@Backups); + if ( $dir eq "" || $dir eq "." || $dir eq ".." ) { - if ( !opendir(DIR, "$TopDir/pc/$host/$num") ) { - ErrorExit("Can't browse bad directory name" - . " ${EscapeHTML(\"$TopDir/pc/$host/$num\")}"); - } - # - # Read this directory and find the first directory - # - foreach my $f ( readdir(DIR) ) { - next if ( $f eq "." || $f eq ".." ); - if ( -d "$TopDir/pc/$host/$num/$f" ) { - $dir = "/$f"; - last; - } - } - closedir(DIR); - if ( $dir eq "" || $dir eq "." || $dir eq ".." ) { - ErrorExit("Directory ${EscapeHTML(\"$TopDir/pc/$host/$num\")}" - . " is empty"); - } - } - my $relDir = $dir; - my $fullDir = "$TopDir/pc/$host/$num/$relDir"; - if ( defined($numF) ) { - # get full path to filled backup - if ( $mangle && !$mangleF ) { - $fullDirF = "$TopDir/pc/$host/$numF/" - . $bpc->fileNameUnmangle($relDir); + $attr = $view->dirAttrib($num, "", ""); + if ( keys(%$attr) > 0 ) { + $share = (sort(keys(%$attr)))[0]; + $dir = '/'; } else { - $fullDirF = "$TopDir/pc/$host/$numF/$relDir"; + ErrorExit(eval("qq{$Lang->{Directory___EscapeHTML}}")); } } - my $currDir = undef; - # - # Read attributes for the directory and optionally for the filled backup - # - my $attr = BackupPC::Attrib->new({ compress => $Backups[$i]{compress}}); - my $attrF = BackupPC::Attrib->new({ compress => $compressF}) - if ( defined($numF) ); - $attr->read($fullDir) if ( -f $attr->fileName($fullDir) ); - if ( defined($numF) && -f $attrF->fileName($fullDirF) - && $attrF->read($fullDirF) ) { - $attr->merge($attrF); - } + my $relDir = $dir; + my $currDir = undef; + # # Loop up the directory tree until we hit the top. # my(@DirStrPrev); while ( 1 ) { - my($fLast, $fum, $fLastum, @DirStr); + my($fLast, $fLastum, @DirStr); - if ( $fullDir =~ m{(^|/)\.\.(/|$)} || !opendir(DIR, $fullDir) ) { - ErrorExit("Can't browse bad directory name" - . " ${EscapeHTML($fullDir)}"); - } - # - # Read this directory and optionally the corresponding filled directory - # - my @Dir = readdir(DIR); - closedir(DIR); - if ( defined($numF) && opendir(DIR, $fullDirF) ) { - if ( $mangle == $mangleF ) { - @Dir = (@Dir, readdir(DIR)); - } else { - foreach my $f ( readdir(DIR) ) { - next if ( $f eq "." || $f eq ".." ); - push(@Dir, $bpc->fileNameMangle($f)); - } - } - closedir(DIR); + $attr = $view->dirAttrib($num, $share, $relDir); + if ( !defined($attr) ) { + ErrorExit(eval("qq{$Lang->{Can_t_browse_bad_directory_name2}}")); } + my $fileCnt = 0; # file counter $fLast = $dirStr = ""; + # # Loop over each of the files in this directory # - my(@DirUniq); - foreach my $f ( sort({uc($a) cmp uc($b)} @Dir) ) { - next if ( $f eq "." || $f eq ".." - || $f eq $fLast || ($mangle && $f eq "attrib") ); - $fLast = $f; - push(@DirUniq, $f); - } - while ( defined(my $f = shift(@DirUniq)) ) { - my $path = "$relDir/$f"; + foreach my $f ( sort(keys(%$attr)) ) { my($dirOpen, $gotDir, $imgStr, $img); - $fum = $mangle ? $bpc->fileNameUnmangle($f) : $f; # unmangled $f - my $fumURI = $fum; # URI escaped $f + my $fURI = $f; # URI escaped $f + my $shareURI = $share; # URI escaped $share + my $path = "$relDir/$f"; + if ( $shareURI eq "" ) { + $shareURI = $path; + $path = ""; + } $path =~ s{^/+}{/}; - $path =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; - $fumURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; - $dirOpen = 1 if ( defined($currDir) && $f eq $currDir ); - if ( -d "$fullDir/$f" ) { + $path =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; + $fURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; + $shareURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; + $dirOpen = 1 if ( defined($currDir) && $f eq $currDir ); + if ( $attr->{$f}{type} == BPC_FTYPE_DIR ) { # # Display directory if it exists in current backup. # First find out if there are subdirs # - my @s = (defined($numF) && -d "$fullDirF/$f") - ? stat("$fullDirF/$f") - : stat("$fullDir/$f"); my($bold, $unbold, $BGcolor); $img |= 1 << 6; - $img |= 1 << 5 if ( $s[3] > 2 ); + $img |= 1 << 5 if ( $attr->{$f}{nlink} > 2 ); if ( $dirOpen ) { $bold = ""; $unbold = ""; $img |= 1 << 2; - $img |= 1 << 3 if ( $s[3] > 2 ); + $img |= 1 << 3 if ( $attr->{$f}{nlink} > 2 ); } my $imgFileName = sprintf("%07b.gif", $img); $imgStr = ""; @@ -814,12 +629,12 @@ sub Action_Browse } else { $BGcolor = ""; } - my $dirName = $fum; + my $dirName = $f; $dirName =~ s/ / /g; push(@DirStr, {needTick => 1, tdArgs => $BGcolor, link => <$imgStr $bold$dirName$unbold +$imgStr $bold$dirName$unbold EOF $fileCnt++; $gotDir = 1; @@ -854,29 +669,32 @@ EOF # This is the selected directory, so display all the files # my $attrStr; - if ( defined($a = $attr->get($fum)) ) { + if ( defined($a = $attr->{$f}) ) { my $mtimeStr = $bpc->timeStamp($a->{mtime}); - my $typeStr = $attr->fileType2Text($a->{type}); + # UGH -> fix this + my $typeStr = BackupPC::Attrib::fileType2Text(undef, + $a->{type}); my $modeStr = sprintf("0%o", $a->{mode} & 07777); $attrStr .= <$typeStr - $modeStr + $modeStr + $a->{backupNum} $a->{size} $mtimeStr EOF } else { - $attrStr .= " \n"; + $attrStr .= " \n"; } if ( $gotDir ) { $fileStr .= < ${EscapeHTML($fum)} + ${EscapeHTML($f)} $attrStr EOF } else { $fileStr .= < ${EscapeHTML($fum)} + ${EscapeHTML($f)} $attrStr EOF @@ -885,25 +703,31 @@ EOF } } @DirStrPrev = @DirStr; - last if ( $relDir eq "" ); + last if ( $relDir eq "" && $share eq "" ); # - # Prune the last directory off $relDir + # Prune the last directory off $relDir, or at the very end + # do the top-level directory. # - $relDir =~ s/(.*)\/(.*)/$1/; - $currDir = $2; - $fullDir = "$TopDir/pc/$host/$num/$relDir"; - $fullDirF = "$TopDir/pc/$host/$numF/$relDir" if ( defined($numF) ); + if ( $relDir eq "" ) { + $currDir = $share; + $share = ""; + } else { + $relDir =~ s/(.*)\/(.*)/$1/; + $currDir = $2; + } } - my $dirDisplay = $mangle ? $bpc->fileNameUnmangle($dir) : $dir; - $dirDisplay =~ s{//}{/}g; + $share = $currDir; + my $dirDisplay = "$share/$dir"; + $dirDisplay =~ s{//+}{/}g; + $dirDisplay =~ s{/+$}{}g; my $filledBackup; - if ( defined($numF) ) { - $filledBackup = < This display is merged with backup #$numF, the most recent prior - filled (full) dump. -EOF + + if ( (my @mergeNums = @{$view->mergeNums}) > 1 ) { + shift(@mergeNums); + my $numF = join(", #", @mergeNums); + $filledBackup = eval("qq{$Lang->{This_display_is_merged_with_backup}}"); } - Header("BackupPC: Browse backup $num for $host"); + Header(eval("qq{$Lang->{Browse_backup__num_for__host}}")); foreach my $d ( @DirStrPrev ) { $dirStr .= "{tdArgs}>$d->{link}\n"; @@ -912,145 +736,67 @@ EOF ### hide checkall button if there are no files my ($topCheckAll, $checkAll, $fileHeader); if ( $fileStr ) { - $fileHeader = < Name - Type - Mode - Size - Mod time - -EOF - $checkAll = < - Select all - - - -EOF + $fileHeader = eval("qq{$Lang->{fileHeader}}"); + + $checkAll = $Lang->{checkAll}; + # and put a checkall box on top if there are at least 20 files if ( $checkBoxCnt >= 20 ) { $topCheckAll = $checkAll; $topCheckAll =~ s{allFiles}{allFilestop}g; } } else { - $fileStr = <The directory ${EscapeHTML($dirDisplay)} is empty - -EOF + $fileStr = eval("qq{$Lang->{The_directory_is_empty}}"); } - - print < - - - -
    -
  • You are browsing backup #$num, which started around $backupTime - ($backupAge days ago), -$filledBackup -
  • Click on a directory below to navigate into that directory, -
  • Click on a file below to restore that file. -
- -${h2("Contents of ${EscapeHTML($dirDisplay)}")} -
- - - - -
- -
- -
- $dirStr -
-
- - -
-
- - $fileHeader - $topCheckAll - $fileStr - $checkAll -
-
-
- -
-
-EOF + + print (eval("qq{$Lang->{Backup_browse_for__host}}")); Trailer(); } sub Action_Restore { - my($str, $reply, $i); + my($str, $reply); my $Privileged = CheckPermission($In{host}); if ( !$Privileged ) { - ErrorExit("Only privileged users can restore backup files" - . " for host ${EscapeHTML($In{host})}." ); + ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_restore_backup_files}}")); } - my $host = $In{host}; - my $num = $In{num}; - my(@fileList, $fileListStr, $hiddenStr, $share, $pathHdr, $badFileCnt); + my $host = $In{host}; + my $num = $In{num}; + my $share = $In{share}; + my(@fileList, $fileListStr, $hiddenStr, $pathHdr, $badFileCnt); my @Backups = $bpc->BackupInfoRead($host); - for ( $i = 0 ; $i < @Backups ; $i++ ) { - last if ( $Backups[$i]{num} == $num ); - } - my $mangle = $Backups[$i]{mangle}; - ServerConnect(); + ServerConnect(); if ( !defined($Hosts->{$host}) ) { - ErrorExit("Bad host name ${EscapeHTML($host)}"); + ErrorExit(eval("qq{$Lang->{Bad_host_name}}")); } for ( my $i = 0 ; $i < $In{fcbMax} ; $i++ ) { next if ( !defined($In{"fcb$i"}) ); (my $name = $In{"fcb$i"}) =~ s/%([0-9A-F]{2})/chr(hex($1))/eg; $badFileCnt++ if ( $name =~ m{(^|/)\.\.(/|$)} ); - if ( $name =~ m{^/+(.*?)(/.*)} ) { - $share = $1; - $name = $mangle ? $bpc->fileNameUnmangle($2) : $2; - if ( @fileList == 0 ) { - $pathHdr = $name; - } else { - while ( substr($name, 0, length($pathHdr)) ne $pathHdr ) { - $pathHdr = substr($pathHdr, 0, rindex($pathHdr, "/")); - } - } - } + if ( @fileList == 0 ) { + $pathHdr = $name; + } else { + while ( substr($name, 0, length($pathHdr)) ne $pathHdr ) { + $pathHdr = substr($pathHdr, 0, rindex($pathHdr, "/")); + } + } push(@fileList, $name); - $share = $mangle ? $bpc->fileNameUnmangle($share) : $share; $hiddenStr .= < EOF @@ -1059,14 +805,14 @@ EOF EOF } $hiddenStr .= "\n"; + $hiddenStr .= "\n"; $badFileCnt++ if ( $In{pathHdr} =~ m{(^|/)\.\.(/|$)} ); $badFileCnt++ if ( $In{num} =~ m{(^|/)\.\.(/|$)} ); if ( @fileList == 0 ) { - ErrorExit("You haven't selected any files; please go Back to" - . " select some files."); + ErrorExit($Lang->{You_haven_t_selected_any_files__please_go_Back_to}); } if ( $badFileCnt ) { - ErrorExit("Nice try, but you can't put '..' in any of the file names"); + ErrorExit($Lang->{Nice_try__but_you_can_t_put}); } if ( @fileList == 1 ) { $pathHdr =~ s/(.*)\/.*/$1/; @@ -1083,200 +829,88 @@ EOF # # Tell the user what options they have # - Header("BackupPC: Restore Options for $host"); - print < -You have selected the following files/directories from -share $share, backup number #$num: -
    -$fileListStr -
-

-You have three choices for restoring these files/directories. -Please select one of the following options. -

-${h2("Option 1: Direct Restore")} -

-You can start a restore that will restore these files directly onto -$host. -

-Warning: any existing files that match the ones you have -selected will be overwritten! - -

- - - -$hiddenStr - - - - - - - - - - - - - -
Restore the files to host
Restore the files to share
Restore the files below dir
(relative to share)
-
-EOF + Header(eval("qq{$Lang->{Restore_Options_for__host}}")); + print(eval("qq{$Lang->{Restore_Options_for__host2}}")); # # Verify that Archive::Zip is available before showing the # zip restore option # if ( eval { require Archive::Zip } ) { - print < -You can download a Zip archive containing all the files/directories you have -selected. You can then use a local application, such as WinZip, -to view or extract any of the files. -

-Warning: depending upon which files/directories you have selected, -this archive might be very very large. It might take many minutes to -create and transfer the archive, and you will need enough local disk -space to store it. -

-

- - - -$hiddenStr - - Make archive relative -to ${EscapeHTML($pathHdr eq "" ? "/" : $pathHdr)} -(otherwise archive will contain full paths). -
-Compression (0=off, 1=fast,...,9=best) - -
- -
-EOF + print (eval("qq{$Lang->{Option_2__Download_Zip_archive}}")); } else { - print < -You could download a zip archive, but Archive::Zip is not installed. -Please ask your system adminstrator to install Archive::Zip from -www.cpan.org. -

-EOF + print (eval("qq{$Lang->{Option_2__Download_Zip_archive2}}")); } - print < -You can download a Tar archive containing all the files/directories you -have selected. You can then use a local application, such as tar or -WinZip to view or extract any of the files. -

-Warning: depending upon which files/directories you have selected, -this archive might be very very large. It might take many minutes to -create and transfer the archive, and you will need enough local disk -space to store it. -

-

- - - -$hiddenStr - - Make archive relative -to ${EscapeHTML($pathHdr eq "" ? "/" : $pathHdr)} -(otherwise archive will contain full paths). -
- -
-EOF + print (eval("qq{$Lang->{Option_3__Download_Zip_archive}}")); Trailer(); } elsif ( $In{type} == 1 ) { # # Provide the selected files via a tar archive. + # + # We no longer use fork/exec (as in v1.5.0) since some mod_perls + # do not correctly preserve the stdout connection to the client + # browser, so we execute BackupPC_tarCreate in-line. # - $SIG{CHLD} = 'IGNORE'; - my $pid = fork(); - if ( !defined($pid) ) { - $bpc->ServerMesg("log Can't fork for tar restore request by $User"); - ErrorExit("Can't fork for tar restore"); - } - if ( $pid ) { - # - # This is the parent. - # - my @fileListTrim = @fileList; - if ( @fileListTrim > 10 ) { - @fileListTrim = (@fileListTrim[0..9], '...'); - } - $bpc->ServerMesg("log User $User downloaded tar archive for $host," - . " backup $num; files were: " - . join(", ", @fileListTrim)); - return; - } - # - # This is the child. Print the headers and run BackupPC_tarCreate. - # + my @fileListTrim = @fileList; + if ( @fileListTrim > 10 ) { + @fileListTrim = (@fileListTrim[0..9], '...'); + } + $bpc->ServerMesg(eval("qq{$Lang->{log_User__User_downloaded_tar_archive_for__host}}")); + my @pathOpts; if ( $In{relative} ) { @pathOpts = ("-r", $pathHdr, "-p", ""); } - $bpc->ServerDisconnect(); - print "Content-Type: application/x-gtar\n"; - print "Content-Transfer-Encoding: binary\n"; - print "Content-Disposition: attachment; filename=\"restore.tar\"\n\n"; - exec("$BinDir/BackupPC_tarCreate", + # + # We use syswrite since BackupPC_tarCreate uses syswrite too. + # Need to test this with mod_perl: PaulL says it doesn't work. + # + syswrite(STDOUT, <ServerMesg("log Can't fork for zip restore request by $User"); - ErrorExit("Can't fork for zip restore"); - } - if ( $pid ) { - # - # This is the parent. - # - my @fileListTrim = @fileList; - if ( @fileListTrim > 10 ) { - @fileListTrim = (@fileListTrim[0..9], '...'); - } - $bpc->ServerMesg("log User $User downloaded zip archive for $host," - . " backup $num; files were: " - . join(", ", @fileListTrim)); - return; - } - # - # This is the child. Print the headers and run BackupPC_tarCreate. - # + my @fileListTrim = @fileList; + if ( @fileListTrim > 10 ) { + @fileListTrim = (@fileListTrim[0..9], '...'); + } + $bpc->ServerMesg(eval("qq{$Lang->{log_User__User_downloaded_zip_archive_for__host}}")); + my @pathOpts; if ( $In{relative} ) { @pathOpts = ("-r", $pathHdr, "-p", ""); } - $bpc->ServerDisconnect(); - print "Content-Type: application/zip\n"; - print "Content-Transfer-Encoding: binary\n"; - print "Content-Disposition: attachment; filename=\"restore.zip\"\n\n"; + # + # We use syswrite since BackupPC_tarCreate uses syswrite too. + # Need to test this with mod_perl: PaulL says it doesn't work. + # + syswrite(STDOUT, <{$In{hostDest}}) ) { - ErrorExit("Host ${EscapeHTML($In{hostDest})} doesn't exist"); + ErrorExit(eval("qq{$Lang->{Host__doesn_t_exist}}")); } if ( !CheckPermission($In{hostDest}) ) { - ErrorExit("You don't have permission to restore onto host" - . " ${EscapeHTML($In{hostDest})}"); + ErrorExit(eval("qq{$Lang->{You_don_t_have_permission_to_restore_onto_host}}")); } $fileListStr = ""; foreach my $f ( @fileList ) { @@ -1305,40 +939,15 @@ EOF $host:/$strippedShare$f$In{hostDest}:/$strippedShareDest$targetFile EOF } - Header("BackupPC: Restore Confirm on $host"); - print < -You are about to start a restore directly to the machine $In{hostDest}. -The following files will be restored to share $In{shareDest}, from -backup number $num: -

- - -$fileListStr -
Original file/dirWill be restored to
- -

- - - - - - -$hiddenStr -Do you really want to do this? - - -
-EOF + Header(eval("qq{$Lang->{Restore_Confirm_on__host}}")); + print(eval("qq{$Lang->{Are_you_sure}}")); Trailer(); } elsif ( $In{type} == 4 ) { if ( !defined($Hosts->{$In{hostDest}}) ) { - ErrorExit("Host ${EscapeHTML($In{hostDest})} doesn't exist"); + ErrorExit(eval("qq{$Lang->{Host_doesn_t_exist}}")); } if ( !CheckPermission($In{hostDest}) ) { - ErrorExit("You don't have permission to restore onto host" - . " ${EscapeHTML($In{hostDest})}"); + ErrorExit(eval("qq{$Lang->{You_don_t_have_permission_to_restore_onto_host}}")); } my $hostDest = $1 if ( $In{hostDest} =~ /(.+)/ ); my $ipAddr = ConfirmIPAddress($hostDest); @@ -1380,35 +989,27 @@ EOF print(REQ $dump->Dump); close(REQ); } else { - ErrorExit("Can't open/create " - . ${EscapeHTML("$TopDir/pc/$hostDest/$reqFileName")}); + ErrorExit(eval("qq{$Lang->{Can_t_open_create}}")); } $reply = $bpc->ServerMesg("restore $ipAddr" . " $hostDest $User $reqFileName"); - $str = "Restore requested to host $hostDest, backup #$num," - . " by $User from $ENV{REMOTE_ADDR}"; - Header("BackupPC: Restore Requested on $hostDest"); - print < -Reply from server was: $reply -

-Go back to $hostDest home page. -EOF + $str = eval("qq{$Lang->{Restore_requested_to_host__hostDest__backup___num}}"); + Header(eval("qq{$Lang->{Restore_Requested_on__hostDest}}")); + print (eval("qq{$Lang->{Reply_from_server_was___reply}}")); Trailer(); } } sub Action_RestoreFile { - restoreFile($In{host}, $In{num}, $In{dir}); + restoreFile($In{host}, $In{num}, $In{share}, $In{dir}); } sub restoreFile { - my($host, $num, $dir, $skipHardLink, $origName) = @_; + my($host, $num, $share, $dir, $skipHardLink, $origName) = @_; my($Privileged) = CheckPermission($host); - my($i, $numF, $mangleF, $compressF, $mangle, $compress, $dirUM); + # # Some common content (media) types from www.iana.org (via MIME::Types). # @@ -1494,60 +1095,22 @@ sub restoreFile 'xwd' => 'image/x-xwindowdump', 'z' => 'application/x-compress', 'zip' => 'application/zip', + %{$Conf{CgiExt2ContentType}}, # add site-specific values }; - if ( !$Privileged ) { - ErrorExit("Only privileged users can restore backup files" - . " for host ${EscapeHTML($host)}." ); + ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_restore_backup_files2}}")); } ServerConnect(); - my @Backups = $bpc->BackupInfoRead($host); - if ( $host eq "" ) { - ErrorExit("Empty host name"); - } + ErrorExit($Lang->{Empty_host_name}) if ( $host eq "" ); + $dir = "/" if ( $dir eq "" ); - for ( $i = 0 ; $i < @Backups ; $i++ ) { - if ( !$Backups[$i]{noFill} ) { - $numF = $Backups[$i]{num}; - $mangleF = $Backups[$i]{mangle}; - $compressF = $Backups[$i]{compress}; - } - last if ( $Backups[$i]{num} == $num ); - } - $mangle = $Backups[$i]{mangle}; - $compress = $Backups[$i]{compress}; - if ( !$Backups[$i]{noFill} ) { - # no need to back-fill a filled backup - $numF = $mangleF = $compressF = undef; - } - my $fullPath = "$TopDir/pc/$host/$num/$dir"; - $fullPath =~ s{/+}{/}g; - if ( !-f $fullPath && defined($numF) ) { - my $dirF = $dir; - my $fullPathF; - if ( $mangle && !$mangleF ) { - $fullPathF = "$TopDir/pc/$host/$numF/" - . $bpc->fileNameUnmangle($dir); - } else { - $fullPathF = "$TopDir/pc/$host/$numF/$dir"; - } - if ( -f $fullPathF ) { - $fullPath = $fullPathF; - $compress = $compressF; - } - } - if ( $fullPath =~ m{(^|/)\.\.(/|$)} || !-f $fullPath ) { - ErrorExit("Can't restore bad file ${EscapeHTML($fullPath)}"); + my @Backups = $bpc->BackupInfoRead($host); + my $view = BackupPC::View->new($bpc, $host, \@Backups); + my $a = $view->fileAttrib($num, $share, $dir); + if ( $dir =~ m{(^|/)\.\.(/|$)} || !defined($a) ) { + ErrorExit("Can't restore bad file ${EscapeHTML($dir)}"); } - my $dirUM = $mangle ? $bpc->fileNameUnmangle($dir) : $dir; - my $attr = BackupPC::Attrib->new({compress => $compress}); - my $fullDir = $fullPath; - $fullDir =~ s{(.*)/.*}{$1}; - my $fileName = $1 if ( $dirUM =~ /.*\/(.*)/ ); - $attr->read($fullDir) if ( -f $attr->fileName($fullDir) ); - my $a = $attr->get($fileName); - - my $f = BackupPC::FileZIO->open($fullPath, 0, $compress); + my $f = BackupPC::FileZIO->open($a->{fullPath}, 0, $a->{compress}); my $data; if ( !$skipHardLink && $a->{type} == BPC_FTYPE_HARDLINK ) { # @@ -1560,20 +1123,15 @@ sub restoreFile $f->close; $linkName =~ s/^\.\///; my $share = $1 if ( $dir =~ /^\/?(.*?)\// ); - restoreFile($host, $num, - "$share/" . ($mangle ? $bpc->fileNameMangle($linkName) - : $linkName), 1, $dir); + restoreFile($host, $num, $share, $linkName, 1, $dir); return; } - $dirUM =~ s{//}{/}g; - $fullPath =~ s{//}{/}g; - $bpc->ServerMesg("log User $User recovered file $dirUM ($fullPath)"); + $bpc->ServerMesg("log User $User recovered file $host/$num:$share/$dir ($a->{fullPath})"); $dir = $origName if ( defined($origName) ); - $dirUM = $mangle ? $bpc->fileNameUnmangle($dir) : $dir; - my $ext = $1 if ( $dirUM =~ /\.([^\/\.]+)$/ ); + my $ext = $1 if ( $dir =~ /\.([^\/\.]+)$/ ); my $contentType = $Ext2ContentType->{lc($ext)} || "application/octet-stream"; - $fileName = $1 if ( $dirUM =~ /.*\/(.*)/ ); + my $fileName = $1 if ( $dir =~ /.*\/(.*)/ ); $fileName =~ s/"/\\"/g; print "Content-Type: $contentType\n"; print "Content-Transfer-Encoding: binary\n"; @@ -1607,7 +1165,7 @@ sub Action_HostInfo } } CheckPermission(); - ErrorExit("Unknown host or user ${EscapeHTML($host)}") + ErrorExit(eval("qq{$Lang->{Unknown_host_or_user}}")) if ( !defined($Hosts->{$host}) ); } $In{host} = $host; @@ -1617,8 +1175,7 @@ sub Action_HostInfo %Conf = $bpc->Conf(); my $Privileged = CheckPermission($host); if ( !$Privileged ) { - ErrorExit("Only privileged users can view information about" - . " host ${EscapeHTML($host)}." ); + ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_view_information_about}}")); } ReadUserEmailInfo(); @@ -1648,12 +1205,15 @@ sub Action_HostInfo } my $age = sprintf("%.1f", (time - $Backups[$i]{startTime}) / (24*3600)); my $browseURL = "$MyURL?action=browse&host=$host&num=$Backups[$i]{num}"; - my $filled = $Backups[$i]{noFill} ? "no" : "yes"; + my $filled = $Backups[$i]{noFill} ? $Lang->{No} : $Lang->{Yes}; $filled .= " ($Backups[$i]{fillFromNum}) " if ( $Backups[$i]{fillFromNum} ne "" ); + my $ltype; + if ($Backups[$i]{type} eq "full") { $ltype = $Lang->{full}; } + else { $ltype = $Lang->{incremental}; } $str .= < $Backups[$i]{num} - $Backups[$i]{type} + $ltype $filled $startTime $duration @@ -1662,7 +1222,7 @@ sub Action_HostInfo EOF $sizeStr .= < $Backups[$i]{num} - $Backups[$i]{type} + $ltype $Backups[$i]{nFiles} $MB $MBperSec @@ -1673,13 +1233,17 @@ EOF EOF $Backups[$i]{compress} ||= "off"; + my $is_compress = $Lang->{off}; + if ($Backups[$i]{compress} ne "off") {$is_compress = $Lang->{on}; } + if (! $ExistComp) { $ExistComp = " "; } + if (! $MBExistComp) { $MBExistComp = " "; } $compStr .= < $Backups[$i]{num} - $Backups[$i]{type} - $Backups[$i]{compress} + $ltype + $is_compress $MBExist - $MBExistComp - $ExistComp + $MBExistComp + $ExistComp $MBNew $MBNewComp $NewComp @@ -1687,7 +1251,7 @@ EOF EOF $errStr .= < $Backups[$i]{num} - $Backups[$i]{type} + $ltype XferLOG, Errors $Backups[$i]{xferErrs} @@ -1707,9 +1271,11 @@ EOF my $duration = sprintf("%.1f", $dur / 60); my $MB = sprintf("%.1f", $Restores[$i]{size} / (1024*1024)); my $MBperSec = sprintf("%.2f", $Restores[$i]{size} / (1024*1024*$dur)); + my $Restores_Result = $Lang->{failed}; + if ($Restores[$i]{result} ne "failed") { $Restores_Result = $Lang->{success}; } $restoreStr .= <$Restores[$i]{num} - $Restores[$i]{result} + $Restores_Result $startTime $duration $Restores[$i]{nFiles} @@ -1719,93 +1285,68 @@ EOF EOF } - $restoreStr = < -Click on the restore number for more details. - - - - - - - - - - -$restoreStr -
Restore# Result Start Date Dur/mins #files MB #tar errs #xferErrs
-

-EOF - + if ( $restoreStr ne "" ) { + $restoreStr = eval("qq{$Lang->{Restore_Summary}}"); + } if ( @Backups == 0 ) { - $warnStr = "

This PC has never been backed up!!

\n"; + $warnStr = $Lang->{This_PC_has_never_been_backed_up}; } if ( defined($Hosts->{$host}) ) { my $user = $Hosts->{$host}{user}; + my @moreUsers = sort(keys(%{$Hosts->{$host}{moreUsers}})); + my $moreUserStr; + foreach my $u ( sort(keys(%{$Hosts->{$host}{moreUsers}})) ) { + $moreUserStr .= ", " if ( $moreUserStr ne "" ); + $moreUserStr .= "${UserLink($u)}"; + } + if ( $moreUserStr ne "" ) { + $moreUserStr = " ($Lang->{and} $moreUserStr).\n"; + } else { + $moreUserStr = ".\n"; + } if ( $user ne "" ) { - $statusStr .= <This PC is used by ${UserLink($user)}. -EOF + $statusStr .= eval("qq{$Lang->{This_PC_is_used_by}$moreUserStr}"); } if ( defined($UserEmailInfo{$user}) && $UserEmailInfo{$user}{lastHost} eq $host ) { my $mailTime = timeStamp2($UserEmailInfo{$user}{lastTime}); my $subj = $UserEmailInfo{$user}{lastSubj}; - $statusStr .= <Last email sent to ${UserLink($user)} was at $mailTime, subject "$subj". -EOF + $statusStr .= eval("qq{$Lang->{Last_email_sent_to__was_at___subject}}"); } } if ( defined($Jobs{$host}) ) { my $startTime = timeStamp2($Jobs{$host}{startTime}); (my $cmd = $Jobs{$host}{cmd}) =~ s/$BinDir\///g; - $statusStr .= <The command $cmd is currently running for $host, started $startTime. -EOF + $statusStr .= eval("qq{$Lang->{The_command_cmd_is_currently_running_for_started}}"); } if ( $StatusHost{BgQueueOn} ) { - $statusStr .= <Host $host is queued on the background queue (will be backed up soon). -EOF + $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_background_queue_will_be_backed_up_soon}}"); } if ( $StatusHost{UserQueueOn} ) { - $statusStr .= <Host $host is queued on the user queue (will be backed up soon). -EOF + $statusStr .= eval("qq{$Lang->{Host_host_is_queued_on_the_user_queue__will_be_backed_up_soon}}"); } if ( $StatusHost{CmdQueueOn} ) { - $statusStr .= <A command for $host is on the command queue (will run soon). -EOF + $statusStr .= eval("qq{$Lang->{A_command_for_host_is_on_the_command_queue_will_run_soon}}"); } my $startTime = timeStamp2($StatusHost{endTime} == 0 ? $StatusHost{startTime} : $StatusHost{endTime}); my $reason = ""; if ( $StatusHost{reason} ne "" ) { - $reason = " ($StatusHost{reason})"; + $reason = " ($Lang->{$StatusHost{reason}})"; } - $statusStr .= <Last status is state "$StatusHost{state}"$reason - as of $startTime. -EOF + $statusStr .= eval("qq{$Lang->{Last_status_is_state_StatusHost_state_reason_as_of_startTime}}"); + if ( $StatusHost{error} ne "" ) { - $statusStr .= <Last error is "${EscapeHTML($StatusHost{error})}" -EOF + $statusStr .= eval("qq{$Lang->{Last_error_is____EscapeHTML_StatusHost_error}}"); } my $priorStr = "Pings"; if ( $StatusHost{deadCnt} > 0 ) { - $statusStr .= <Pings to $host have failed $StatusHost{deadCnt} consecutive times. -EOF - $priorStr = "Prior to that, pings"; + $statusStr .= eval("qq{$Lang->{Pings_to_host_have_failed_StatusHost_deadCnt__consecutive_times}}"); + $priorStr = $Lang->{Prior_to_that__pings}; } if ( $StatusHost{aliveCnt} > 0 ) { - $statusStr .= <$priorStr to $host have succeeded $StatusHost{aliveCnt} - consecutive times. -EOF + $statusStr .= eval("qq{$Lang->{priorStr_to_host_have_succeeded_StatusHostaliveCnt_consecutive_times}}"); + if ( $StatusHost{aliveCnt} >= $Conf{BlackoutGoodCnt} && $Conf{BlackoutGoodCnt} >= 0 && $Conf{BlackoutHourBegin} >= 0 && $Conf{BlackoutHourEnd} >= 0 ) { @@ -1817,127 +1358,25 @@ EOF my($t1) = sprintf("%d:%02d", $Conf{BlackoutHourEnd}, 60 * ($Conf{BlackoutHourEnd} - int($Conf{BlackoutHourEnd}))); - $statusStr .= <Because $host has been on the network at least $Conf{BlackoutGoodCnt} -consecutive times, it will not be backed up from $t0 to $t1 on $days. -EOF + $statusStr .= eval("qq{$Lang->{Because__host_has_been_on_the_network_at_least__Conf_BlackoutGoodCnt_consecutive_times___}}"); } } if ( $StatusHost{backoffTime} > time ) { my $hours = sprintf("%.1f", ($StatusHost{backoffTime} - time) / 3600); - $statusStr .= <Backups are deferred for $hours hours - (change this - number). -EOF + $statusStr .= eval("qq{$Lang->{Backups_are_deferred_for_hours_hours_change_this_number}}"); + } if ( @Backups ) { # only allow incremental if there are already some backups $startIncrStr = < + EOF } - Header("BackupPC: Host $host Backup Summary"); - print < -$warnStr -
    -$statusStr -
+ $startIncrStr = eval ("qq{$startIncrStr}"); -${h2("User Actions")} -

-

- -$startIncrStr - - -
- -${h2("Backup Summary")} -

-Click on the backup number to browse and restore backup files. - - - - - - - - - -$str -
Backup# Type Filled Start Date Duration/mins Age/days Server Backup Path
-

- -$restoreStr - -${h2("Xfer Error Summary")} -

- - - - - - - - - -$errStr -
Backup# Type View #Xfer errs #bad files #bad share #tar errs
-

- -${h2("File Size/Count Reuse Summary")} -

-Existing files are those already in the pool; new files are those added -to the pool. -Empty files and SMB errors aren't counted in the reuse and new counts. - - - - - - - - - - - - - - - - - -$sizeStr -
Totals Existing Files New Files
Backup# Type #Files Size/MB MB/sec #Files Size/MB #Files Size/MB
-

- -${h2("Compression Summary")} -

-Compression performance for files already in the pool and newly -compressed files. - - - - - - - - - - - - - - - -$compStr -
Existing Files New Files
Backup# Type Comp Level Size/MB Comp/MB Comp Size/MB Comp/MB Comp
-

-EOF + Header(eval("qq{$Lang->{Host__host_Backup_Summary}}")); + print(eval("qq{$Lang->{Host__host_Backup_Summary2}}")); Trailer(); } @@ -1946,7 +1385,7 @@ sub Action_GeneralInfo GetStatusInfo("info jobs hosts queueLen"); my $Privileged = CheckPermission(); - my($jobStr, $statusStr, $tarPidHdr, $ rivLinks); + my($jobStr, $statusStr, $tarPidHdr); foreach my $host ( sort(keys(%Jobs)) ) { my $startTime = timeStamp2($Jobs{$host}{startTime}); next if ( $host eq $bpc->trashJob @@ -1970,7 +1409,7 @@ EOF $jobStr .= "\n"; } foreach my $host ( sort(keys(%Status)) ) { - next if ( $Status{$host}{reason} ne "backup failed" ); + next if ( $Status{$host}{reason} ne "Reason_backup_failed" ); my $startTime = timeStamp2($Status{$host}{startTime}); my($errorTime, $XferViewStr); if ( $Status{$host}{errorTime} > 0 ) { @@ -2023,59 +1462,15 @@ EOF } elsif ( $Info{cpoolFileCnt} > 0 ) { $poolInfo = $cpoolInfo; } - Header("BackupPC: Server Status"); - print < + Header($Lang->{H_BackupPC_Server_Status}); + #Header("H_BackupPC_Server_Status"); + print (eval ("qq{$Lang->{BackupPC_Server_Status}}")); -${h2("General Server Information")} + #Header($Lang->{BackupPC_Server_Status}); -

    -
  • The server's PID is $Info{pid} on host $Conf{ServerHost}, - version $Info{Version}, started at $serverStartTime. -
  • This status was generated at $now. -
  • PCs will be next queued at $nextWakeupTime. -
  • Other info: -
      -
    • $numBgQueue pending backup requests from last scheduled wakeup, -
    • $numUserQueue pending user backup requests, -
    • $numCmdQueue pending command requests, - $poolInfo -
    • Pool file system was recently at $Info{DUlastValue}% - ($DUlastTime), today's max is $Info{DUDailyMax}% ($DUmaxTime) - and yesterday's max was $Info{DUDailyMaxPrev}%. -
    -
- -${h2("Currently Running Jobs")} -

- - - - - - - - - $tarPidHdr -$jobStr -
Host Type User Start Time Command PID Xfer PID
-

- -${h2("Failures that need attention")} -

- - - - - - - - -$statusStr -
Host Type User Last Try Details Error Time Last error (other than no ping)
-EOF + #my $trans_text = $Lang->{BackupPC_Server_Status}; + #print eval ("qq{$trans_text}"); Trailer(); } @@ -2087,7 +1482,7 @@ sub Action_RestoreInfo my $i; if ( !$Privileged ) { - ErrorExit("Only privileged users can view restore information." ); + ErrorExit($Lang->{Only_privileged_users_can_view_restore_information}); } # # Find the requested restore @@ -2097,8 +1492,7 @@ sub Action_RestoreInfo last if ( $Restores[$i]{num} == $num ); } if ( $i >= @Restores ) { - ErrorExit("Restore number $num for host ${EscapeHTML($host)} does" - . " not exist."); + ErrorExit(eval("qq{$Lang->{Restore_number__num_for_host__does_not_exist}}")); } %RestoreReq = (); @@ -2125,41 +1519,8 @@ sub Action_RestoreInfo EOF } - Header("BackupPC: Restore #$num details for $host"); - print < - - - - - - - - - - - - - - - - - - - -
Number $Restores[$i]{num}
Requested by $RestoreReq{user}
Request time $reqTime
Result $Restores[$i]{result}
Error Message $Restores[$i]{errorMsg}
Source host $RestoreReq{hostSrc}
Source backup num $RestoreReq{num}
Source share $RestoreReq{shareSrc}
Destination host $RestoreReq{hostDest}
Destination share $RestoreReq{shareDest}
Start time $startTime
Duration $duration min
Number of files $Restores[$i]{nFiles}
Total size ${MB} MB
Transfer rate $MBperSec MB/sec
TarCreate errors $Restores[$i]{tarCreateErrs}
Xfer errors $Restores[$i]{xferErrs}
Xfer log file -View, -Errors -
-

-${h1("File/Directory list")} -

- - -$fileListStr -
Original file/dirRestored to
-EOF + Header(eval("qq{$Lang->{Restore___num_details_for__host}}")); + print(eval("qq{$Lang->{Restore___num_details_for__host2 }}")); Trailer(); } @@ -2217,7 +1578,7 @@ sub EscapeHTML $s =~ s/\"/"/g; $s =~ s/>/>/g; $s =~ s/ServerMesg("log User $User (host=$In{host}) got CGI error: $head") if ( defined($bpc) ); - Header("BackupPC: Error"); - print <{Error}) ) { + Header("BackupPC: Error"); + print <$mesg

EOF - Trailer(); + Trailer(); + } else { + Header(eval("qq{$Lang->{Error}}")); + print (eval("qq{$Lang->{Error____head}}")); + Trailer(); + } exit(1); } @@ -2257,14 +1624,7 @@ sub ServerConnect return if ( $bpc->ServerOK() ); $bpc->ServerDisconnect(); if ( my $err = $bpc->ServerConnect($Conf{ServerHost}, $Conf{ServerPort}) ) { - ErrorExit( - "Unable to connect to BackupPC server", - "This CGI script ($MyURL) is unable to connect to the BackupPC" - . " server on $Conf{ServerHost} port $Conf{ServerPort}. The error" - . " was: $err.", - "Perhaps the BackupPC server is not running or there is a " - . " configuration error. Please report this to your Sys Admin." - ); + ErrorExit(eval("qq{$Lang->{Unable_to_connect_to_BackupPC_server}}")); } } @@ -2314,9 +1674,28 @@ sub CheckPermission } $PrivAdmin = $Privileged; $Privileged ||= $User eq $Hosts->{$host}{user}; + $Privileged ||= defined($Hosts->{$host}{operators}{$User}); + return $Privileged; } +# +# Returns the list of hosts that should appear in the navigation bar +# for this user. If $Conf{CgiNavBarAdminAllHosts} is set, the admin +# gets all the hosts. Otherwise, regular users get hosts for which +# they are the user or are listed in the moreUsers column in the +# hosts file. +# +sub GetUserHosts +{ + if ( $Conf{CgiNavBarAdminAllHosts} && CheckPermission() ) { + return sort keys %$Hosts; + } + + return sort grep { $Hosts->{$_}{user} eq $User || + defined($Hosts->{$_}{moreUsers}{$User}) } keys(%$Hosts); +} + # # Given a host name tries to find the IP address. For non-dhcp hosts # we just return the host name. For dhcp hosts we check the address @@ -2338,20 +1717,13 @@ sub ConfirmIPAddress GetStatusInfo("host($host)"); if ( defined($StatusHost{dhcpHostIP}) && $StatusHost{dhcpHostIP} ne $ipAddr ) { - $tryIP = " and $StatusHost{dhcpHostIP}"; + $tryIP = eval("qq{$Lang->{tryIP}}"); ($netBiosHost, $netBiosUser) = $bpc->NetBiosInfoGet($StatusHost{dhcpHostIP}); } if ( $netBiosHost ne $host ) { - ErrorExit("Can't find IP address for ${EscapeHTML($host)}", - < -Until I see $host at a particular DHCP address, you can only -start this request from the client machine itself. -EOF + ErrorExit(eval("qq{$Lang->{Can_t_find_IP_address_for}}"), + eval("qq{$Lang->{host_is_a_DHCP_host}}")); } $ipAddr = $StatusHost{dhcpHostIP}; } @@ -2366,14 +1738,8 @@ sub genPoolInfo my $poolRmSize = sprintf("%.2f", $info->{"${name}KbRm"} / (1000 * 1024)); my $poolTime = timeStamp2($info->{"${name}Time"}); $info->{"${name}FileCntRm"} = $info->{"${name}FileCntRm"} + 0; - return <Pool is ${poolSize}GB comprising $info->{"${name}FileCnt"} files - and $info->{"${name}DirCnt"} directories (as of $poolTime), -
  • Pool hashing gives $info->{"${name}FileCntRep"} repeated - files with longest chain $info->{"${name}FileRepMax"}, -
  • Nightly cleanup removed $info->{"${name}FileCntRm"} files of - size ${poolRmSize}GB (around $poolTime), -EOF + return eval("qq{$Lang->{Pool_Stat}}"); + } ########################################################################### @@ -2384,16 +1750,16 @@ sub Header { my($title) = @_; my @adminLinks = ( - { link => "", name => "Status", + { link => "", name => $Lang->{Status}, priv => 1}, - { link => "?action=summary", name => "PC Summary" }, - { link => "?action=view&type=LOG", name => "LOG file" }, - { link => "?action=LOGlist", name => "Old LOGs" }, - { link => "?action=emailSummary", name => "Email summary" }, - { link => "?action=view&type=config", name => "Config file" }, - { link => "?action=view&type=hosts", name => "Hosts file" }, - { link => "?action=queue", name => "Current queues" }, - { link => "?action=view&type=docs", name => "Documentation", + { link => "?action=summary", name => $Lang->{PC_Summary} }, + { link => "?action=view&type=LOG", name => $Lang->{LOG_file} }, + { link => "?action=LOGlist", name => $Lang->{Old_LOGs} }, + { link => "?action=emailSummary", name => $Lang->{Email_summary} }, + { link => "?action=view&type=config", name => $Lang->{Config_file} }, + { link => "?action=view&type=hosts", name => $Lang->{Hosts_file} }, + { link => "?action=queue", name => $Lang->{Current_queues} }, + { link => "?action=view&type=docs", name => $Lang->{Documentation}, priv => 1}, { link => "http://backuppc.sourceforge.net", name => "SourceForge", priv => 1}, @@ -2404,7 +1770,7 @@ sub Header $title $Conf{CgiHeaders} - + +
    EOF @@ -2412,44 +1778,43 @@ EOF print " \n"; if ( defined($In{host}) && defined($Hosts->{$In{host}}) ) { my $host = $In{host}; - NavSectionTitle("Host $In{host}"); + NavSectionTitle( eval("qq{$Lang->{Host_Inhost}}") ); NavSectionStart(); - NavLink("?host=$host", "Home"); - NavLink("?action=view&type=LOG&host=$host", "LOG file"); - NavLink("?action=LOGlist&host=$host", "Old LOGs"); + NavLink("?host=$host", $Lang->{Home}); + NavLink("?action=view&type=LOG&host=$host", $Lang->{LOG_file}); + NavLink("?action=LOGlist&host=$host", $Lang->{Old_LOGs}); if ( -f "$TopDir/pc/$host/SmbLOG.bad" || -f "$TopDir/pc/$host/SmbLOG.bad.z" || -f "$TopDir/pc/$host/XferLOG.bad" || -f "$TopDir/pc/$host/XferLOG.bad.z" ) { NavLink("?action=view&type=XferLOGbad&host=$host", - "Last bad XferLOG"); + $Lang->{Last_bad_XferLOG}); NavLink("?action=view&type=XferErrbad&host=$host", - "Last bad XferLOG (errors only)"); + $Lang->{Last_bad_XferLOG_errors_only}); } if ( -f "$TopDir/pc/$host/config.pl" ) { - NavLink("?action=view&type=config&host=$host", "Config file"); + NavLink("?action=view&type=config&host=$host", $Lang->{Config_file}); } NavSectionEnd(); } - NavSectionTitle("Hosts"); - if ( %$Hosts > 0 ) { - NavSectionStart(); - foreach my $host ( sort(keys(%$Hosts)) ) { - next if ( $Hosts->{$host}{user} ne $User ); + NavSectionTitle($Lang->{Hosts}); + if ( defined($Hosts) && %$Hosts > 0 ) { + NavSectionStart(0); + foreach my $host ( GetUserHosts() ) { NavLink("?host=$host", $host); } NavSectionEnd(); } print < -
    Host or User name:
    $Lang->{Host_or_User_name}
    - +
    EOF - NavSectionTitle("Server"); + NavSectionTitle($Lang->{NavSectionTitle_}); NavSectionStart(); foreach my $l ( @adminLinks ) { if ( $PrivAdmin || $l->{priv} ) { @@ -2473,31 +1838,6 @@ sub Trailer EOF } -sub h1 -{ - my($str) = @_; - return \< - - $str - - -EOF -} - -sub h2 -{ - my($str) = @_; - return \< - - $str - - -EOF -} sub NavSectionTitle { @@ -2513,8 +1853,11 @@ EOF sub NavSectionStart { + my($padding) = @_; + + $padding = 2 if ( !defined($padding) ); print < + EOF } @@ -2538,3 +1881,29 @@ EOF EOF } } + +sub h1 +{ + my($str) = @_; + return \< + + +
     $str +
    +EOF +} + +sub h2 +{ + my($str) = @_; + return \< + + $str + + +EOF +}