#!@PERL_PATH@

our $PREFIX="@PREFIX@";
our $INSTALL_USER="@INSTALL_USER@";
our $INSTALL_GROUP="@INSTALL_GROUP@";

use strict;
use Cwd;
use Digest::MD5;
use Data::Dumper;
use File::Copy;
use File::Path;
use Getopt::Long;

my %opts;

GetOptions(\%opts,
	"dist",
	"uninstall",
);

#
# Set the umask so nothing goes odd on systems which
# change it.
#
umask( 0022 );

### Trim prefix if needed 

$PREFIX =~ s{/+$}{};

our $DESTDIR;

# for convenience we support a --dist mode which will copy the installed files
# into $DESTDIR without $PREFIX
if ($opts{dist})
{
	$DESTDIR = "$ENV{DESTDIR}";
}
else
{
	$DESTDIR = "$ENV{DESTDIR}$PREFIX";
}

$DESTDIR =~ s{/+$}{};

our $SIGNATURE_PATH="$DESTDIR/SIGNATURES";

my(undef,undef,$uid) = getpwnam($INSTALL_USER);
my(undef,undef,$gid) = getgrnam($INSTALL_GROUP);

# Warn if we can't install as the requested user/group

unless( $uid )
{
	warn("Warning! User $INSTALL_USER doesn't exist. You probably need to do: useradd $INSTALL_USER\n");
}

unless( $gid )
{
	warn("Warning! Group $INSTALL_GROUP doesn't exist. You probably need to do: groupadd $INSTALL_GROUP\n");
}

unless( $opts{dist} or $< == 0 or (defined $uid and $uid == $<) )
{
	warn("Warning! It looks like you're trying to install as user $INSTALL_USER without root. You probably need to do: sudo $0\n");
}

$uid = $< || 0 unless defined $uid;
$gid = $( || 0 unless defined $gid;

# If we are upgrading and have a digest file, read the digests into a hash
# to check them against the files in place to see if they've changed.

uninstall( $DESTDIR, $SIGNATURE_PATH );

exit if $opts{uninstall};

#
# Actual install starts here.
#
ensure_dir( $DESTDIR, $uid, $gid, 0755 );
ensure_dir( "$DESTDIR/archives", $uid, $gid, 02770 );
ensure_dir( "$DESTDIR/testdata", $uid, $gid, 0750 );

my $newsighash = {};

my @dirs = (
	bin => [0750, $uid, $gid, 0750],
	cgi => [0640, $uid, $gid, 0750],
	cfg => [0640, $uid, $gid, 0750],
	lib => [0660, $uid, $gid, 02770], # epms installed as 'apache'
	licenses => [0660, $uid, $gid, 0750],
	perl_lib => [0640, $uid, $gid, 0750],
	flavours => [0640, $uid, $gid, 0750],
	ingredients => [0640, $uid, $gid, 0750],
	"testdata/bin" => [0750, $uid, $gid, 0750],
	"testdata/data" => [0640, $uid, $gid, 0750],
	tests => [0640, $uid, $gid, 0750],
	tmp => [0660, $uid, $gid, 02770], # temp files/dirs created as 'apache'
	tools => [0750, $uid, $gid, 0750],
	var => [0660, $uid, $gid, 02770], # indexer may run as 'apache'
);

foreach my $i (grep { $_ % 2 == 0 } 0 .. $#dirs)
{
	install_dir($dirs[$i], @{$dirs[$i+1]}, $DESTDIR, $newsighash);
}

write_sigs( $DESTDIR, $newsighash, $uid, $gid );

print "Installed EPrints to: $DESTDIR\n";

# Post installation bits

sub uninstall
{
	my( $destdir, $sig_path ) = @_;
	
	return if !-e $sig_path;

	print "Uninstalling ...\n";

	open(my $fh, "<", $sig_path) or die "Error opening $sig_path: $!";
	while(<$fh>)
	{
		chomp;
		my( $filepath, $digest ) = split /\s/, $_;
		$filepath = "$destdir/$filepath";
		next if !-e $filepath;
		if( get_digest($filepath) eq $digest )
		{
			unlink($filepath);
		}
		else
		{
			move_out_of_harms_way($filepath);
		}
	}
}

sub write_sigs
{
	my( $dir, $sigs, $uid, $gid ) = @_;

	my $sigfile = $dir.'/SIGNATURES';
	# Dump out signature file
	unless( open (SIGOUT, ">".$sigfile ) )
	{
		print "Unable to write $sigfile: $!\n";
		exit 1;
	}
	binmode(SIGOUT);
		
	foreach( sort keys %{$sigs} )
	{
		print SIGOUT $_." ".$sigs->{$_}."\n";
	}
	close(SIGOUT);
	chown($uid, $gid, $sigfile );
	chmod( 0660, $sigfile );
}

# Calculate the MD5 digest of a file.

sub get_digest
{
	my($file) = @_;
	my $ctx = Digest::MD5->new;
	open(my $fh, "<", $file) or die "Error opening $file: $!";
	$ctx->addfile( $fh );
	return $ctx->hexdigest;
}

# Install $file from directory $dir into $dest. The permissions
# from $perms are set, and the group and user are set from $group
# and $user. The %hash is used for MD5 comparisons - currently
# not doing anything active, but checking is enabled.

sub install_file
{
	my($file, $dir, $perms, $uid, $gid, $dest, $newsighash) = @_;

	my $relpath = $file;
	if( defined $dir ) { $relpath = "$dir/$file"; }
	if( !defined $dir ) { $dir = ""; }

	return unless (-e "$relpath");

	$newsighash->{$relpath} = get_digest($relpath);
	
	if( -e "$dest/$relpath" )
	{
		move_out_of_harms_way( "$dest/$relpath" );
	}

	copy($relpath, "$dest/$relpath");

	chmod($perms, "$dest/$relpath");
	chown($uid, $gid, "$dest/$relpath");
}

sub install_dir
{
	my( $dir, $fmode, $uid, $gid, $dmode, $dest, $newsighash) = @_;

	ensure_dir("$dest/$dir", $uid, $gid, $dmode);

	opendir(INDIR, $dir) or return;

	my @dirs = ();
	my @files = ();

	while(my $item = readdir(INDIR))
	{
		next if $item =~ /^\./;
		next if $item =~ /\.in$/; # source files are copied by make dist
		if( -d "$dir/$item" ) 
		{ 
			push(@dirs, $item); 
		}
		else 
		{ 
			push(@files, $item); 
		}
	}
	closedir(INDIR);

	foreach(@files)
	{
		install_file($_, $dir, $fmode, $uid, $gid, $dest, $newsighash );
	}
	foreach(@dirs)
	{
		install_dir("$dir/".$_, $fmode, $uid, $gid, $dmode, $dest, $newsighash );
	}
}

##############################################################

sub move_out_of_harms_way
{
	my( $file ) = @_;

	my @date = localtime;
	my $safename = sprintf( 
		"%s.backup.%04d-%02d-%02d", 
		$file, 
		$date[5]+1900, 
		$date[4]+1, 
		$date[3] );

	my $n = 0;
	while( -e $safename )
	{
		++$n;
		$safename = sprintf( 
			"%s.backup.%04d-%02d-%02d.%d", 
			$file, 
			$date[5]+1900, 
			$date[4]+1, 
			$date[3],
			$n );
	}

	rename $file, $safename;

	print "\n$file has been modified.\nSaving old version as $safename\n";
}


sub ensure_dir
{
	my( $dir, $uid, $gid, $mode ) = @_;

	File::Path::make_path($dir, {
		# we're not installing into the dist
		($opts{dist} ? () : (
			user => $uid,
			group => $gid,
		)),
		mode => $mode,
	});

	if( !-d $dir ) 
	{
		die "Error creating directory $dir: $!\n";
	}		

	return;
}

