DirHistory.pm (BackupPC-4.3.2) | : | DirHistory.pm (BackupPC-4.4.0) | ||
---|---|---|---|---|
skipping to change at line 30 | skipping to change at line 30 | |||
# This program is distributed in the hope that it will be useful, | # This program is distributed in the hope that it will be useful, | |||
# but WITHOUT ANY WARRANTY; without even the implied warranty of | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
# GNU General Public License for more details. | # GNU General Public License for more details. | |||
# | # | |||
# You should have received a copy of the GNU General Public License | # You should have received a copy of the GNU General Public License | |||
# along with this program. If not, see <http://www.gnu.org/licenses/>. | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |||
# | # | |||
#======================================================================== | #======================================================================== | |||
# | # | |||
# Version 4.3.2, released 17 Feb 2020. | # Version 4.4.0, released 20 Jun 2020. | |||
# | # | |||
# See http://backuppc.sourceforge.net. | # See http://backuppc.sourceforge.net. | |||
# | # | |||
#======================================================================== | #======================================================================== | |||
package BackupPC::CGI::DirHistory; | package BackupPC::CGI::DirHistory; | |||
use strict; | use strict; | |||
use BackupPC::CGI::Lib qw(:all); | use BackupPC::CGI::Lib qw(:all); | |||
use BackupPC::View; | use BackupPC::View; | |||
skipping to change at line 53 | skipping to change at line 53 | |||
sub action | sub action | |||
{ | { | |||
my $Privileged = CheckPermission($In{host}); | my $Privileged = CheckPermission($In{host}); | |||
my($i, $dirStr, $fileStr, $attr); | my($i, $dirStr, $fileStr, $attr); | |||
my $checkBoxCnt = 0; | my $checkBoxCnt = 0; | |||
if ( !$Privileged ) { | if ( !$Privileged ) { | |||
ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_browse_backup_files }}")); | ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_browse_backup_files }}")); | |||
} | } | |||
my $host = $In{host}; | ||||
my $share = $In{share}; | my $host = $In{host}; | |||
my $dir = $In{dir}; | my $share = $In{share}; | |||
my $dirURI = $dir; | my $dir = $In{dir}; | |||
my $dirURI = $dir; | ||||
my $shareURI = $share; | my $shareURI = $share; | |||
$dirURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; | ||||
$shareURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; | $dirURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; | |||
$shareURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; | ||||
ErrorExit($Lang->{Empty_host_name}) if ( $host eq "" ); | ErrorExit($Lang->{Empty_host_name}) if ( $host eq "" ); | |||
my @Backups = $bpc->BackupInfoRead($host); | my @Backups = $bpc->BackupInfoRead($host); | |||
my $view = BackupPC::View->new($bpc, $host, \@Backups, {inode => 1}); | my $view = BackupPC::View->new($bpc, $host, \@Backups, {inode => 1}); | |||
my $hist = $view->dirHistory($share, $dir); | my $hist = $view->dirHistory($share, $dir); | |||
my($backupNumStr, $backupTimeStr, $fileStr); | my($backupNumStr, $backupTimeStr, $fileStr); | |||
$dir = "/$dir" if ( $dir !~ /^\// ); | $dir = "/$dir" if ( $dir !~ /^\// ); | |||
if ( "/$host/$share/$dir/" =~ m{/\.\./} ) { | if ( "/$host/$share/$dir/" =~ m{/\.\./} ) { | |||
ErrorExit($Lang->{Nice_try__but_you_can_t_put}); | ErrorExit($Lang->{Nice_try__but_you_can_t_put}); | |||
} | } | |||
my @backupList = $view->backupList($share, $dir); | my @backupList = $view->backupList($share, $dir); | |||
foreach $i ( @backupList ) { | foreach $i ( @backupList ) { | |||
my $backupTime = timeStamp2($Backups[$i]{startTime}); | my $backupTime = timeStamp2($Backups[$i]{startTime}); | |||
my $num = $Backups[$i]{num}; | my $num = $Backups[$i]{num}; | |||
$backupNumStr .= "<td align=center><a href=\"$MyURL?action=browse" | $backupNumStr .= | |||
. "&host=${EscURI($host)}&num=$num&share=$shareURI" | "<td align=center><a href=\"$MyURL?action=browse" | |||
. "&dir=$dirURI\">$num</a></td>"; | . "&host=${EscURI($host)}&num=$num&share=$shareURI" | |||
$backupTimeStr .= "<td align=center>$backupTime</td>"; | . "&dir=$dirURI\">$num</a></td>"; | |||
$backupTimeStr .= "<td align=center>$backupTime</td>"; | ||||
} | } | |||
# | # | |||
# For V3 we rely on inodes to identify identical files. | # For V3 we rely on inodes to identify identical files. | |||
# | # | |||
# For V4 the (fake) inodes might be the same between different versions of | # For V4 the (fake) inodes might be the same between different versions of | |||
# a file, so we rely on matching the digests instead. | # a file, so we rely on matching the digests instead. | |||
# | # | |||
# So, if digests are defined, we use those. Otherwise we used inodes. | # So, if digests are defined, we use those. Otherwise we used inodes. | |||
# | # | |||
foreach my $f ( sort {uc($a) cmp uc($b)} keys(%$hist) ) { | foreach my $f ( sort { uc($a) cmp uc($b) } keys(%$hist) ) { | |||
my %inode2name; | my %inode2name; | |||
my %digest2name; | my %digest2name; | |||
my $nameCnt = 0; | my $nameCnt = 0; | |||
(my $fDisp = "${EscHTML($f)}") =~ s/ / /g; | (my $fDisp = "${EscHTML($f)}") =~ s/ / /g; | |||
$fDisp = decode_utf8($fDisp); | $fDisp = decode_utf8($fDisp); | |||
$fileStr .= "<tr><td align=\"left\" class=\"histView\">$fDisp</td>"; | $fileStr .= "<tr><td align=\"left\" class=\"histView\">$fDisp</td>"; | |||
my($colSpan, $url, $inode, $type, $digest); | my($colSpan, $url, $inode, $type, $digest); | |||
my $tdClass = ' class="histView"'; | my $tdClass = ' class="histView"'; | |||
foreach $i ( @backupList ) { | ||||
my($path); | foreach $i ( @backupList ) { | |||
if ( $colSpan > 0 ) { | my($path); | |||
# | if ( $colSpan > 0 ) { | |||
# The file is the same if it also size==0 (inode == -1) | # | |||
# or if it is a directory and the previous one is (inode == -2) | # The file is the same if it also size==0 (inode == -1) | |||
# or if the inodes and digests agree and the types are the same. | # or if it is a directory and the previous one is (inode == -2) | |||
# | # or if the inodes and digests agree and the types are the same. | |||
if ( defined($hist->{$f}[$i]) | # | |||
&& $hist->{$f}[$i]{type} == $type | if ( | |||
&& (($hist->{$f}[$i]{size} == 0 && $inode == -1) | defined($hist->{$f}[$i]) | |||
|| ($hist->{$f}[$i]{type} == BPC_FTYPE_DIR && $inode == - | && $hist->{$f}[$i]{type} == $type | |||
2) | && ( | |||
|| (length($hist->{$f}[$i]{digest}) ? $hist->{$f}[$i]{dig | ($hist->{$f}[$i]{size} == 0 && $inode == -1) | |||
est} eq $digest | || ($hist->{$f}[$i]{type} == BPC_FTYPE_DIR && $inode == | |||
: $hist->{$f}[$i]{in | -2) | |||
ode} == $inode)) ) { | || ( | |||
$colSpan++; | length($hist->{$f}[$i]{digest}) | |||
next; | ? $hist->{$f}[$i]{digest} eq $digest | |||
} | : $hist->{$f}[$i]{inode} == $inode | |||
# | ) | |||
# Also handle the case of a sequence of missing files | ) | |||
# | ) { | |||
if ( !defined($hist->{$f}[$i]) && $inode == -3 ) { | $colSpan++; | |||
$colSpan++; | next; | |||
next; | } | |||
} | # | |||
$fileStr .= "<td align=center colspan=$colSpan$tdClass>" | # Also handle the case of a sequence of missing files | |||
. "$url</td>"; | # | |||
$colSpan = 0; | if ( !defined($hist->{$f}[$i]) && $inode == -3 ) { | |||
$tdClass = ' class="histView"'; | $colSpan++; | |||
} | next; | |||
if ( !defined($hist->{$f}[$i]) ) { | } | |||
$colSpan = 1; | $fileStr .= "<td align=center colspan=$colSpan$tdClass>$url</td> | |||
$url = " "; | "; | |||
$inode = -3; # special value for missing | $colSpan = 0; | |||
$tdClass = ' class="histView"'; | ||||
} | ||||
if ( !defined($hist->{$f}[$i]) ) { | ||||
$colSpan = 1; | ||||
$url = " "; | ||||
$inode = -3; # special value for missing | ||||
$digest = ""; | $digest = ""; | |||
$tdClass = ' class="histViewMis"'; | $tdClass = ' class="histViewMis"'; | |||
next; | next; | |||
} | } | |||
if ( $dir eq "" ) { | if ( $dir eq "" ) { | |||
$path = "/$f"; | $path = "/$f"; | |||
} else { | } else { | |||
($path = "$dir/$f") =~ s{//+}{/}g; | ($path = "$dir/$f") =~ s{//+}{/}g; | |||
} | } | |||
$path =~ s{^/+}{/}; | $path =~ s{^/+}{/}; | |||
$path =~ s/([^\w.\/-])/uc sprintf("%%%02X", ord($1))/eg; | $path =~ s/([^\w.\/-])/uc sprintf("%%%02X", ord($1))/eg; | |||
my $num = $hist->{$f}[$i]{backupNum}; | my $num = $hist->{$f}[$i]{backupNum}; | |||
if ( $hist->{$f}[$i]{type} == BPC_FTYPE_DIR ) { | if ( $hist->{$f}[$i]{type} == BPC_FTYPE_DIR ) { | |||
$inode = -2; # special value for dir | $inode = -2; # special value for dir | |||
$type = $hist->{$f}[$i]{type}; | $type = $hist->{$f}[$i]{type}; | |||
$url = <<EOF; | $url = <<EOF; | |||
<a href="$MyURL?action=dirHistory&host=${EscURI($host)}&share=$shareURI&dir=$pat h">$Lang->{DirHistory_dirLink}</a> | <a href="$MyURL?action=dirHistory&host=${EscURI($host)}&share=$shareURI&dir=$pat h">$Lang->{DirHistory_dirLink}</a> | |||
EOF | EOF | |||
} else { | } else { | |||
my $thisName; | my $thisName; | |||
$inode = $hist->{$f}[$i]{inode}; | $inode = $hist->{$f}[$i]{inode}; | |||
$digest = $hist->{$f}[$i]{digest}; | $digest = $hist->{$f}[$i]{digest}; | |||
$type = $hist->{$f}[$i]{type}; | $type = $hist->{$f}[$i]{type}; | |||
# | # | |||
# special value for empty file | # special value for empty file | |||
# | # | |||
$inode = -1 if ( $hist->{$f}[$i]{size} == 0 ); | $inode = -1 if ( $hist->{$f}[$i]{size} == 0 ); | |||
if ( length($digest) ) { | if ( length($digest) ) { | |||
if ( !defined($digest2name{$digest}) ) { | if ( !defined($digest2name{$digest}) ) { | |||
$digest2name{$digest} = "$Lang->{DirHistory_fileLink}$na meCnt"; | $digest2name{$digest} = "$Lang->{DirHistory_fileLink}$na meCnt"; | |||
$nameCnt++; | $nameCnt++; | |||
} | } | |||
$thisName = $digest2name{$digest}; | $thisName = $digest2name{$digest}; | |||
} else { | } else { | |||
if ( !defined($inode2name{$inode}) ) { | if ( !defined($inode2name{$inode}) ) { | |||
$inode2name{$inode} = "$Lang->{DirHistory_fileLink}$name Cnt"; | $inode2name{$inode} = "$Lang->{DirHistory_fileLink}$name Cnt"; | |||
$nameCnt++; | $nameCnt++; | |||
} | } | |||
$thisName = $inode2name{$inode} | $thisName = $inode2name{$inode}; | |||
} | } | |||
$url = <<EOF; | $url = <<EOF; | |||
<a href="$MyURL?action=RestoreFile&host=${EscURI($host)}&num=$num&share=$shareUR I&dir=$path">$thisName</a> | <a href="$MyURL?action=RestoreFile&host=${EscURI($host)}&num=$num&share=$shareUR I&dir=$path">$thisName</a> | |||
EOF | EOF | |||
} | } | |||
$colSpan = 1; | $colSpan = 1; | |||
} | } | |||
if ( $colSpan > 0 ) { | if ( $colSpan > 0 ) { | |||
$fileStr .= "<td align=center colspan=$colSpan$tdClass>$url</td>"; | $fileStr .= "<td align=center colspan=$colSpan$tdClass>$url</td>"; | |||
$colSpan = 0; | $colSpan = 0; | |||
} | } | |||
$fileStr .= "</tr>\n"; | $fileStr .= "</tr>\n"; | |||
} | } | |||
# | # | |||
# allow each level of the directory path to be navigated to | # allow each level of the directory path to be navigated to | |||
# | # | |||
my($thisPath, $dirDisplay); | my($thisPath, $dirDisplay); | |||
my $dirClean = $dir; | my $dirClean = $dir; | |||
$dirClean =~ s{//+}{/}g; | $dirClean =~ s{//+}{/}g; | |||
$dirClean =~ s{/+$}{}; | $dirClean =~ s{/+$}{}; | |||
my @dirElts = split(/\//, $dirClean); | my @dirElts = split(/\//, $dirClean); | |||
skipping to change at line 210 | skipping to change at line 220 | |||
$thisDir = decode_utf8($share); | $thisDir = decode_utf8($share); | |||
$thisPath = "/"; | $thisPath = "/"; | |||
} else { | } else { | |||
$thisPath .= "/" if ( $thisPath ne "/" ); | $thisPath .= "/" if ( $thisPath ne "/" ); | |||
$thisPath .= "$d"; | $thisPath .= "$d"; | |||
$thisDir = decode_utf8($d); | $thisDir = decode_utf8($d); | |||
} | } | |||
my $thisPathURI = $thisPath; | my $thisPathURI = $thisPath; | |||
$thisPathURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; | $thisPathURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg; | |||
$dirDisplay .= "/" if ( $dirDisplay ne "" ); | $dirDisplay .= "/" if ( $dirDisplay ne "" ); | |||
$dirDisplay .= "<a href=\"$MyURL?action=dirHistory&host=${EscURI($host)} | $dirDisplay .= | |||
&share=$shareURI&dir=$thisPathURI\">${EscHTML($thisDir)}</a>"; | "<a href=\"$MyURL?action=dirHistory&host=${EscURI($host)}&share=$share | |||
URI&dir=$thisPathURI\">${EscHTML($thisDir)}</a>"; | ||||
} | } | |||
my $content = eval("qq{$Lang->{DirHistory_for__host}}"); | my $content = eval("qq{$Lang->{DirHistory_for__host}}"); | |||
Header(eval("qq{$Lang->{DirHistory_backup_for__host}}"), $content); | Header(eval("qq{$Lang->{DirHistory_backup_for__host}}"), $content); | |||
Trailer(); | Trailer(); | |||
} | } | |||
1; | 1; | |||
End of changes. 13 change blocks. | ||||
92 lines changed or deleted | 102 lines changed or added |