#============================================================= -*-perl-*-
#
# BackupPC::CGI::DirHistory package
#
# DESCRIPTION
#
#   This module implements the DirHistory action for the CGI interface.
#
# AUTHOR
#   Craig Barratt  <cbarratt@users.sourceforge.net>
#
# COPYRIGHT
#   Copyright (C) 2003-2020  Craig Barratt
#
#   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 3 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, see <http://www.gnu.org/licenses/>.
#
#========================================================================
#
# Version 4.3.2, released 17 Feb 2020.
#
# See http://backuppc.sourceforge.net.
#
#========================================================================

package BackupPC::CGI::DirHistory;

use strict;
use BackupPC::CGI::Lib qw(:all);
use BackupPC::View;
use BackupPC::XS qw(:all);
use Encode;

sub action
{
    my $Privileged = CheckPermission($In{host});
    my($i, $dirStr, $fileStr, $attr);
    my $checkBoxCnt = 0;

    if ( !$Privileged ) {
        ErrorExit(eval("qq{$Lang->{Only_privileged_users_can_browse_backup_files}}"));
    }
    my $host   = $In{host};
    my $share  = $In{share};
    my $dir    = $In{dir};
    my $dirURI = $dir;
    my $shareURI = $share;
    $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 "" );

    my @Backups = $bpc->BackupInfoRead($host);
    my $view = BackupPC::View->new($bpc, $host, \@Backups, {inode => 1});
    my $hist = $view->dirHistory($share, $dir);
    my($backupNumStr, $backupTimeStr, $fileStr);

    $dir = "/$dir" if ( $dir !~ /^\// );

    if ( "/$host/$share/$dir/" =~ m{/\.\./} ) {
        ErrorExit($Lang->{Nice_try__but_you_can_t_put});
    }

    my @backupList = $view->backupList($share, $dir);
    foreach $i ( @backupList ) {
	my $backupTime  = timeStamp2($Backups[$i]{startTime});
	my $num = $Backups[$i]{num};
	$backupNumStr  .= "<td align=center><a href=\"$MyURL?action=browse"
			. "&host=${EscURI($host)}&num=$num&share=$shareURI"
			. "&dir=$dirURI\">$num</a></td>";
	$backupTimeStr .= "<td align=center>$backupTime</td>";
    }

    #
    # For V3 we rely on inodes to identify identical files.
    #
    # For V4 the (fake) inodes might be the same between different versions of
    # a file, so we rely on matching the digests instead.
    #
    # So, if digests are defined, we use those.  Otherwise we used inodes.
    #
    foreach my $f ( sort {uc($a) cmp uc($b)} keys(%$hist) ) {
	my %inode2name;
	my %digest2name;
	my $nameCnt = 0;
	(my $fDisp  = "${EscHTML($f)}") =~ s/ /&nbsp;/g;
        $fDisp      = decode_utf8($fDisp);
	$fileStr   .= "<tr><td align=\"left\"  class=\"histView\">$fDisp</td>";
	my($colSpan, $url, $inode, $type, $digest);
	my $tdClass = ' class="histView"';
	foreach $i ( @backupList ) {
	    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)
		# or if the inodes and digests agree and the types are the same.
		#
		if ( defined($hist->{$f}[$i])
		    && $hist->{$f}[$i]{type} == $type
		    && (($hist->{$f}[$i]{size} == 0 && $inode == -1)
		        || ($hist->{$f}[$i]{type} == BPC_FTYPE_DIR && $inode == -2)
		        || (length($hist->{$f}[$i]{digest}) ? $hist->{$f}[$i]{digest} eq $digest
                                                            : $hist->{$f}[$i]{inode} == $inode)) ) {
		    $colSpan++;
		    next;
		}
		#
		# Also handle the case of a sequence of missing files
		#
		if ( !defined($hist->{$f}[$i]) && $inode == -3 ) {
		    $colSpan++;
		    next;
		}
		$fileStr .= "<td align=center colspan=$colSpan$tdClass>"
			  . "$url</td>";
		$colSpan = 0;
		$tdClass = ' class="histView"';
	    }
	    if ( !defined($hist->{$f}[$i]) ) {
		$colSpan = 1;
		$url     = "&nbsp;";
		$inode   = -3;			# special value for missing
                $digest  = "";
		$tdClass = ' class="histViewMis"';
		next;
	    }
            if ( $dir eq "" ) {
                $path = "/$f";
            } else {
                ($path = "$dir/$f") =~ s{//+}{/}g;
            }
	    $path =~ s{^/+}{/};
	    $path =~ s/([^\w.\/-])/uc sprintf("%%%02X", ord($1))/eg;
	    my $num = $hist->{$f}[$i]{backupNum};
	    if ( $hist->{$f}[$i]{type} == BPC_FTYPE_DIR ) {
		$inode = -2;			# special value for dir
		$type  = $hist->{$f}[$i]{type};
		$url   = <<EOF;
<a href="$MyURL?action=dirHistory&host=${EscURI($host)}&share=$shareURI&dir=$path">$Lang->{DirHistory_dirLink}</a>
EOF
	    } else {
                my $thisName;
		$inode  = $hist->{$f}[$i]{inode};
		$digest = $hist->{$f}[$i]{digest};
		$type   = $hist->{$f}[$i]{type};
		#
		# special value for empty file
		#
		$inode = -1 if ( $hist->{$f}[$i]{size} == 0 );
		if ( length($digest) ) {
                    if ( !defined($digest2name{$digest}) ) {
                        $digest2name{$digest} = "$Lang->{DirHistory_fileLink}$nameCnt";
                        $nameCnt++;
                    }
                    $thisName = $digest2name{$digest};
                } else {
                    if ( !defined($inode2name{$inode}) ) {
                        $inode2name{$inode} = "$Lang->{DirHistory_fileLink}$nameCnt";
                        $nameCnt++;
                    }
                    $thisName = $inode2name{$inode}
		}
		$url = <<EOF;
<a href="$MyURL?action=RestoreFile&host=${EscURI($host)}&num=$num&share=$shareURI&dir=$path">$thisName</a>
EOF
	    }
	    $colSpan = 1;
	}
	if ( $colSpan > 0 ) {
	    $fileStr .= "<td align=center colspan=$colSpan$tdClass>$url</td>";
	    $colSpan = 0;
	}
	$fileStr .= "</tr>\n";
    }

    #
    # allow each level of the directory path to be navigated to
    #
    my($thisPath, $dirDisplay);
    my $dirClean = $dir;
    $dirClean =~ s{//+}{/}g;
    $dirClean =~ s{/+$}{};
    my @dirElts = split(/\//, $dirClean);
    @dirElts = ("/") if ( !@dirElts );
    foreach my $d ( @dirElts ) {
        my($thisDir);

        if ( $thisPath eq "" ) {
            $thisDir  = decode_utf8($share);
            $thisPath = "/";
        } else {
            $thisPath .= "/" if ( $thisPath ne "/" );
            $thisPath .= "$d";
            $thisDir = decode_utf8($d);
        }
        my $thisPathURI = $thisPath;
        $thisPathURI =~ s/([^\w.\/-])/uc sprintf("%%%02x", ord($1))/eg;
        $dirDisplay .= "/" if ( $dirDisplay ne "" );
        $dirDisplay .= "<a href=\"$MyURL?action=dirHistory&host=${EscURI($host)}&share=$shareURI&dir=$thisPathURI\">${EscHTML($thisDir)}</a>";
    }
    my $content = eval("qq{$Lang->{DirHistory_for__host}}");
    Header(eval("qq{$Lang->{DirHistory_backup_for__host}}"), $content);
    Trailer();
}

1;
