#!@PERL_PATH@ -w

######################################################################
#
# EPrints 2 installer : Handles upgrading, simple command detection,
# and the creation of the SystemSettings.pm file. This doesn't do the
# mySQL install, etc. - you'll need the SuperInstaller for that.
#
######################################################################
# __LICENSE__
######################################################################

my %from_configure;
$from_configure{"dist_path"}="@DIST_PATH@";
$from_configure{"perl"}="@PERL_PATH@";
$from_configure{"prefix"}="@PREFIX@";
$from_configure{"user"}="@INSTALL_USER@";
$from_configure{"group"}="@INSTALL_GROUP@";
$from_configure{"disable_df"}=@DISABLE_DF@;
$from_configure{"virtualhost"}="@VIRTUALHOST@";

$from_configure{"unzip"}="@UNZIP_PATH@";
$from_configure{"tar"}="@TAR_PATH@";
$from_configure{"gunzip"}="@GUNZIP_PATH@";
$from_configure{"wget"}="@WGET_PATH@";
$from_configure{"sendmail"}="@SENDMAIL_PATH@";
$from_configure{"smtp_server"}="@SMTP_SERVER@";
$from_configure{"latex"}="@LATEX_PATH@";
$from_configure{"dvips"}="@DVIPS_PATH@";
$from_configure{"convert"}="@CONVERT_PATH@";

$from_configure{"enable_gdome"}="@ENABLE_GDOME@";
$from_configure{"apache"}="@APACHE@";

$from_configure{"platform"}="@PLATFORM@";

# hard wired for now.
$from_configure{"cp"}="/bin/cp";
$from_configure{"rm"}="/bin/rm";

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

if( 
	$from_configure{"apache"} ne "" && 
	$from_configure{"apache"} ne "1" && 
	$from_configure{"apache"} ne "2" )
{
	print "Apache version must be '1' or '2'.\n";
	print "re-run configure with new options.\n";
	exit 1;
}

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

if( defined $ARGV[0] ) { print "Destination directory overridden to: $ARGV[0]\n"; }

use strict;
use Cwd;
use Digest::MD5;
use Data::Dumper;

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

#
# Grab version id number from VERSION file.
#

my( $version_id, $version ) = read_version( $from_configure{"dist_path"}."/VERSION" );

### Trim perfix if needed 

if( $from_configure{"prefix"} =~ s#/$## )
{
	print "Removing trailing slash from prefix.\n";
}

### Print out some key information, which is bound to
### cause problems if incorrect.

# shorter variables for clarity later
my $from_path = $from_configure{dist_path};
my $to_path = $from_configure{prefix};
# Implement DESTDIR support for GNU compatibility.
# If an argument is specified install there instead.
if( defined $ARGV[0] ) { $to_path = $ARGV[0]; }

print "Installing from: $from_path\n";

my $system_settings_file = "$to_path/perl_lib/EPrints/SystemSettings.pm";
my $upgrade = 0;
if( -e $system_settings_file )
{
	$upgrade = 1;
	print "Previous install detected at install location.\n";
	print "Upgrading to: $to_path\n";
}
else
{
	print "Installing to: $to_path\n";
	print "Installing as user: ".$from_configure{"user"}."\n";
	print "Installing as group: ".$from_configure{"group"}."\n";
}

my $systemsettings = {};

$systemsettings->{"platform"} = $from_configure{"platform"};

$systemsettings->{"version_history"} = [ $version_id ];

#
# Set up some default settings.
#
$systemsettings->{"invocation"} = {
	zip     => '$(unzip) 1>/dev/null 2>&1 -qq -o -d $(DIR) $(ARC)',
	targz   => '$(gunzip) -c < $(ARC) 2>/dev/null | $(tar) xf - -C $(DIR) >/dev/null 2>&1',
	wget    => '$(wget)  -r -L -q -m -nH -np --execute="robots=off" --cut-dirs=$(CUTDIRS) $(URL)',
	sendmail => '$(sendmail) -oi -t -odb --',
	latex => '$(latex) \'$(SOURCE)\'',
	dvips => '$(dvips) \'$(SOURCE)\' -o \'$(TARGET)\'',
	convert_crop_white => '$(convert) -crop 0x0 -bordercolor white -border 4x4 \'$(SOURCE)\' \'$(TARGET)\'',
	cpall => '$(cp) -pR \'$(SOURCE)\'/* \'$(TARGET)\'',
	rmall => '$(rm) -rf \'$(TARGET)\'/*',
};

$systemsettings->{"archive_extensions"} = {};
$systemsettings->{"archive_formats"} = [];
if( $from_configure{"unzip"} ne "" )
{
	$systemsettings->{"archive_extensions"}->{"zip"} = ".zip" ;
	push @{$systemsettings->{"archive_formats"}},"zip";
}
if( $from_configure{"tar"} ne "" && $from_configure{"gunzip"} ne "" )
{
	$systemsettings->{"archive_extensions"}->{"targz"} = ".tar.gz" ;
	push @{$systemsettings->{"archive_formats"}},"targz";
}

$systemsettings->{"executables"} = {};
foreach( 
	"perl", "unzip", "tar", "gunzip", "wget", 
	"sendmail", "latex", "dvips", "convert",
	"cp", "rm", 
)
{
	next unless( defined $from_configure{$_} );
	$systemsettings->{"executables"}->{$_} = $from_configure{$_};
}

$systemsettings->{"file_perms"} = 0664 unless defined $systemsettings->{"file_perms"};
$systemsettings->{"dir_perms"} = 02775 unless defined $systemsettings->{"dir_perms"};

$systemsettings->{"disable_df"} = $from_configure{"disable_df"};
$systemsettings->{"virtualhost"} = $from_configure{"virtualhost"};



if( $upgrade )
{
	unless( require $system_settings_file )
	{
		print <<END;
Failed to read previous values from system settings file:
$system_settings_file - Aborting.
END
		exit 1;
	}

	print "Previous version of eprints is: ";
	print $EPrints::SystemSettings::conf->{"version_id"}."\n";

	# create a minimal version history if there was not one already
	if( !defined $EPrints::SystemSettings::conf->{"version_history"} )
	{
		$EPrints::SystemSettings::conf->{"version_history"} =
			[ $EPrints::SystemSettings::conf->{"version_id"} ];
	}

	# Current values take precedence.
	foreach( keys %$EPrints::SystemSettings::conf )
	{
		if( defined $systemsettings->{$_} )
		{
			$systemsettings->{$_} = merge_fields(
				$EPrints::SystemSettings::conf->{$_}, 
				$systemsettings->{$_} );
		}
		else
		{
			$systemsettings->{$_} = 
				$EPrints::SystemSettings::conf->{$_};
		}
	}
	$systemsettings->{"user"} = $EPrints::SystemSettings::conf->{"user"};
	$systemsettings->{"group"} = $EPrints::SystemSettings::conf->{"group"};

	print "Upgrading as user: ".$systemsettings->{"user"}."\n";
	print "Upgrading as group: ".$systemsettings->{"group"}."\n";
	$upgrade = 1;
}
else
{	
	$systemsettings->{"user"} = $from_configure{"user"};
	$systemsettings->{"group"} = $from_configure{"group"};
	$systemsettings->{"base_path"} = $from_configure{"prefix"};
}

$systemsettings->{"version_id"} = $version_id;
$systemsettings->{"version"} = $version;

set( 'smtp_server', \%from_configure, $systemsettings, '' );
set( 'enable_gdome', \%from_configure, $systemsettings, 0 );
set( 'show_ids_in_log', \%from_configure, $systemsettings, 0 );
set( 'apache', \%from_configure, $systemsettings, "2" );


# Check the user exists

unless( getpwnam($systemsettings->{"user"}) )
{
	print "User ".$from_configure{"user"}." does not exist. Aborting.\n";
	exit 1;
}


# Check the group exists

unless( getgrnam($systemsettings->{"group"}) )
{
	print "Group ".$from_configure{"group"}." does not exist. Aborting.\n";
	exit 1;
}

my(undef,undef,$uid) = getpwnam($systemsettings->{"user"});
my(undef,undef,$gid) = getgrnam($systemsettings->{"group"});

my %oldhashes = ();

# 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.

if( $upgrade )
{
	my $sigfile = "$to_path/SIGNATURES";
	if (-e $sigfile )
	{
		print "Loading SIGNATURES...\n";
		open(SIGS, $sigfile ) || die "Can't open $sigfile";
		binmode(SIGS);
		foreach(<SIGS>)
		{
			chomp;
			my( $file, $digest ) = split( " ", $_);
			$oldhashes{$file} = $digest;
		}
		close(SIGS);
	}
	else
	{
		print "Upgrading, but could not find an old SIGNATURES file.\n";
	}
}






#
# Actual install starts here.
#
ensure_dir( $to_path, $uid, $gid, 02775 );
ensure_dir( "$to_path/testdata", $uid, $gid, 02775 );

my $newsighash = {};

my @normal_dirs = ( "defaultcfg", "cfg", "perl_lib", "lib", "testdata/data", "var" );

my @executable_dirs = ( "bin", "cgi", "testdata/bin" );

my @base_files = ( "VERSION", "CHANGELOG", "COPYING", "NEWS", "AUTHORS", "README" ); 

foreach my $dir (@normal_dirs)
{
	install_dir($from_path, $dir, 0664, $uid, $gid, $to_path, \%oldhashes, $newsighash);
}
foreach my $dir (@executable_dirs)
{
	install_dir($from_path, $dir, 0775, $uid, $gid, $to_path, \%oldhashes, $newsighash);
}
foreach my $file (@base_files)
{
	install_file($from_path, $file, undef, 0664, $uid, $gid, $to_path, \%oldhashes, $newsighash );
}

ensure_dir( "$to_path/archives", $uid, $gid, 02775 );

foreach my $oldfile ( keys %oldhashes )
{
	next if defined $newsighash->{$oldfile};

	my $filepath = "$to_path/$oldfile";

	next if( !-e $filepath );

	my $repl_digest = get_digest( $filepath );

	if( $repl_digest ne $oldhashes{$oldfile} )
	{
		move_out_of_harms_way( $filepath );
	}
	else
	{
		print "Removing $filepath\n";
		unlink( $filepath );
	}
}


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


save_settings( 
	$system_settings_file, 
	$systemsettings, 
	$systemsettings->{user}, 
	$systemsettings->{group} );

# Warnings!
if( $upgrade )
{
	print "Upgraded eprints at: $to_path\n";
}
else
{
	print "Installed EPrints to: $to_path\n";
}
my $soapdir = "$to_path/cgi/soap";
if( -d $soapdir )
{
	print <<END;

WARNING: 3.0-beta-1 soap directory detected!
The files in this directory represent a security hole and therefore it
is recommended that you remove this directory. The installer will not 
do this automatically.
Suggested action:
 rm -rf $soapdir
END
}

if( $systemsettings->{smtp_server} eq "" && !$upgrade )
{
	print <<END;

WARNING: You have not set a value for smtp_server. Edit the
SystemSettings file and set the smtp_server option to be your local 
SMTP server. If you don't then outgoing email won't work.
END
}



print "\n";

exit;











######################################################################
# 
# set( $param, $hash_from_configure, $systemsettings, $default )
# 
# Set the $param param in systemsettings to the $param passed
# in from configure.
# If nothing was passed in by configure and a value is not already
# set, then set it to the $default.
#
######################################################################

sub set
{
	my( $param, $from_configure, $systemsettings, $default ) = @_;

	if( defined $from_configure->{$param} && $from_configure->{$param} ne '' )
	{
		# a setting has actually been configured by configure
		$systemsettings->{$param} = $from_configure->{$param};
		return;
	}

	if( !defined $systemsettings->{$param} )
	{
		# no setting is defined at all, not even ""
		# so use the default
		$systemsettings->{$param} = $default;
		return;
	}

	return;
}




sub save_settings
{
	my( $sysfile, $syshash, $user, $group ) = @_;

	my(undef,undef,$uid,$gid) = getpwnam($user);

	my $dumper = Data::Dumper->new( 
		[ $syshash ] , 
		[ 'EPrints::SystemSettings::conf' ] );

	print "Writing $sysfile\n";
	unless( open(FILEOUT, ">".$sysfile) )
	{
		print "Failed to write $sysfile\n";
		exit 1;
	}
	binmode(FILEOUT);
	print FILEOUT <<SPIEL;
######################################################################
#
# These are your system settings (as autogenerated by the installer).
# We suggest that you do not alter these, as future installers will
# probably override them.
# 
######################################################################
#
# This file is part of EPrints 2.
#
# EPrints 2 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.
#
# EPrints 2 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 EPrints 2; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
######################################################################

# This file should only be use'd by EPrints::Config
package EPrints::SystemSettings;

SPIEL

	print FILEOUT $dumper->Dump();
	print FILEOUT "\n1;";
	close(FILEOUT);

 	if( $from_configure{"platform"} eq "unix" )
	{
		chmod( 0664, $sysfile );
		chown($uid, $gid, $sysfile );
	}
}

sub merge_fields
{
	my($ref1, $ref2) = @_;
	my %outref;
	my $type = ref $ref1;
	if($type eq "HASH")
	{
		# $ref1 takes precedence
		foreach(keys %$ref1)
		{
			$outref{$_} = $ref1->{$_};
		}
		foreach(keys %$ref2)
		{
			$outref{$_} = $ref2->{$_} unless defined $outref{$_};
		}
		return \%outref;
	}
	elsif($type eq "ARRAY")
	{
		my $out = [];
		my $done = {};
		if (defined $ref1)
		{
			foreach(@$ref1)
			{
				$done->{$_} = 1;
				push @{$out}, $_;
			}
		}
		if (defined $ref2)
		{
			foreach(@$ref2)
			{
				next if( $done->{$_} );
				push @{$out}, $_;
			}
		}
		return $out;
	}
	else
	{
		return $ref1;
	}
	
}



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

sub detect_df 
{

	my $dir = "/";
	my ($fmt, $res);

	# try with statvfs..
	eval 
	{  
		{
			package main;
			require "sys/syscall.ph";
		}
		$fmt = "\0" x 512;
		$res = syscall (&main::SYS_statvfs, $dir, $fmt) ;
		$res == 0;
	}
	# try with statfs..
	|| eval 
	{ 
		{
			package main;
			require "sys/syscall.ph";
		}	
		$fmt = "\0" x 512;
		$res = syscall (&main::SYS_statfs, $dir, $fmt);
		$res == 0;
	}
}



# Post installation bits

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);
 	if( $from_configure{"platform"} eq "unix" )
	{
		chown($uid, $gid, $sigfile );
		chmod( 0664, $sigfile );
	}
}

# Calculate the MD5 digest of a file.

sub get_digest
{
	my($file) = @_;
	unless( open(FILE, $file) )
	{
		print "Can't open '$file': $!\n";
		exit 1;
	}
	binmode(FILE);
	my $outdigest = new Digest::MD5;
	foreach(<FILE>)
	{
		$outdigest->add($_);
	}
	close(FILE);
	return $outdigest->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 comparisions - currently
# not doing anything active, but checking is enabled.

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

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

	return unless (-e $distdir."/$relpath");

	my @linesout = ();	
	my $linecopy = 0;
	if($dir eq "bin" || $dir eq "testdata/bin")
	{
		$linecopy = 1;
	}

	if( $linecopy )
	{
		my $currline = "";
		my $outdigest = Digest::MD5->new;
		open(INFILE, $distdir."/$relpath");
		binmode(INFILE);
		if ($file eq "startup.pl")
		{
			# NJS 2007-05-22: Changed $dest to $from_configure{"prefix"} so
			# that we don't end up with the wrong invocation paths when building
			# the Debian package with pbuilder.
			$currline = "use lib '".$from_configure{"prefix"}."/perl_lib';\n";
			$outdigest->add($currline);
		}
		# NJS 2007-05-29: Added check for new eprints_maintenance and
		# eprints_alerts scripts, which are shell scripts rather than Perl.
		elsif (($file eq "eprints_maintenance") || ($file eq "eprints_alerts"))
		{
			$currline = "#!/bin/sh\n";
			$outdigest->add($currline);
		}
		else
		{
			# NJS 2007-05-22: Changed $dest to $from_configure{"prefix"} so
			# that we don't end up with the wrong invocation paths when building
			# the Debian package with pbuilder.
			$currline = "#!".$from_configure{perl}." -w -I".$from_configure{"prefix"}."/perl_lib\n";
			$outdigest->add($currline);
		}
		push @linesout, $currline;
		my $skipme = <INFILE>;	
		while(my $line=<INFILE>)
		{
			chomp $line; 
			$currline = "$line\n";
			$outdigest->add($currline);
			push @linesout, $currline;
		}		
		close(INFILE);
		$newsighash->{$relpath} = $outdigest->hexdigest;
	}
	else
	{
		$newsighash->{$relpath} = get_digest("$distdir/$relpath");
	}
	
	if ($sighash->{"$relpath"} && -e "$dest/$relpath")
	{
		my $repl_digest = get_digest("$dest/$relpath");
		if ($repl_digest ne $sighash->{"$relpath"})
		{
			move_out_of_harms_way( "$dest/$relpath" );
		}
	}


	if( $linecopy )	
	{
		unless( open(OUTFILE, ">$dest/$relpath") )
		{
			print "\nCan't write to $dest/$relpath : $!\n";
			exit 1;
		}
		binmode(OUTFILE);
		print OUTFILE join( "", @linesout );
		close(OUTFILE);
	}
	else
	{
		my $cmd = "cp $distdir/$relpath $dest/$relpath ";
		system( $cmd );
	}

 	if( $from_configure{"platform"} eq "unix" )
	{
		unless( chmod($perms, "$dest/$relpath") )
		{
			print "\nUnable to chmod $dest/$relpath : $!\n";
			exit 1;
		}
		unless( chown($uid, $gid, "$dest/$relpath") )
		{
			print "\nUnable to chown $dest/$relpath : $!\n";
			exit 1;
		}
	}
}

sub install_dir
{
	my( $distdir, $dir, $perms, $uid, $gid, $dest, $sighash, $newsighash ) = @_;

	unless( opendir(INDIR, $dir) )
	{
		print "Unable to open package directory: $dir. $!\n";
		exit 1;
	}
	my @dirs = ();
	my @files = ();
	ensure_dir("$dest/$dir", $uid, $gid, 02775);
 	if( $from_configure{"platform"} eq "unix" )
	{
		unless( chown($uid, $gid, "$dest/$dir") )
		{
			print "\nUnable to chown $dest/$dir : $!\n"; 
			exit 1;
		}
	}

	while(my $item = readdir(INDIR))
	{
		next if( $item eq "." || $item eq ".." );
		if( -d $distdir."/$dir/$item" ) 
		{ 
			push(@dirs, $item); 
		}
		else 
		{ 
			push(@files, $item); 
		}
	}
	closedir(INDIR);
	foreach(@files)
	{
		install_file($distdir, $_, $dir, $perms, $uid, $gid, $dest, $sighash, $newsighash );
	}
	foreach(@dirs)
	{
		install_dir($distdir, "$dir/".$_, $perms, $uid, $gid, $dest, $sighash, $newsighash );
	}
}

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

sub upgrade
{
	my( $base_path, $dist_path, $user, $group ) = @_;

	
	my(undef,undef,$uid,$gid) = getpwnam($user);
	print "Upgrading files : [";


	print <<HOORAY;
======================================================================

 Upgrade successful. You should now:

 - su to $user
 - make any changes described by the "updating" chapter of
   the documentation.
 - re-run bin/generate_apacheconf (the just-installed version, 
   not the version that came in the tar.gz.
 - restart your web server
 - restart the eprints indexer

 - To find and share ideas and problems try the EPrints technical
    mailing list and wiki at http://eprints.org/software/

======================================================================
HOORAY
}


sub full_install
{
	my( $base_path, $dist_path, $user, $group ) = @_;



	

	print <<HOORAY;

======================================================================
Hooray! Your EPrints2 installation was successful!
======================================================================

 What Now?

 - su to root
 - Open your apache.conf file, and make the
   following alterations:
   o Add the line
       Include $base_path/cfg/apache.conf
   o Replace the 'User <username>' line with
       User $user
   o Replace the 'Group <groupname>' line with
       Group $group
 - su to $user
 - Move into $base_path and run:
     bin/configure_archive

 Good Luck!

 - To find and share ideas and problems try the EPrints technical
    mailing list and wiki at http://eprints.org/software/

======================================================================
HOORAY
}

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 read_version
{
	my( $vfile ) = @_;

	unless( open(VERSIONIN, $from_configure{"dist_path"}."/VERSION") )
	{
		print "No VERSION file in distribution - invalid distribution?\n";
		exit 1;
	}
	binmode(VERSIONIN);
	my $version_id = <VERSIONIN>;
	unless( defined($version_id) ) 
	{
		print "Undefined version number.\n";
		exit 1;
	}
	chomp($version_id);
	my $version = <VERSIONIN>;
	unless( defined($version) ) 
	{
		print "Undefined version description.\n"; 
		exit 1;
	}
	chomp($version);
	close(VERSIONIN);

	return( $version_id, $version );
}

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

	if( !-d $dir ) 
	{
		if( !mkdir($dir) )
		{
			print "Unable to make dir: $dir.\n";
			print "Aborting!\n";
			exit 1;
		}
	}		
	
	unless( chown($uid, $gid, $dir) )
	{
		print "Unable to chown ".$dir." : $!\n"; 
		print "Aborting!\n";
		exit 1;
	}

	unless( chmod($mode, $dir) )
	{
		print "Unable to chmod ".$dir." : $!\n"; 
		print "Aborting!\n";
		exit 1;
	}

	return;
}

