###
###  Copyright 2000-2007 University of Illinois Board of Trustees
###  All rights reserved. 
###
###  PSGConf::Action::PackageManager::Encap - Encap pkg action type for psgconf
###
###
###  Campus Information Technologies and Educational Services
###  University of Illinois at Urbana-Champaign
###


package PSGConf::Action::PackageManager::Encap;

use strict;

use PSGConf::Action::PackageManager::FTPArchive;
use PSGConf::Data::String;
use PSGConf::Data::Integer;
use PSGConf::Util;

use File::Path;
use File::Basename;
use Cwd;

our @ISA = qw(PSGConf::Action::PackageManager::FTPArchive);


###############################################################################
###  configuration constructor
###############################################################################

sub conf
{
     my ($class, $psgconf) = @_;

     my ($p) = '-share|-' . $psgconf->data_obj('platform')->get();

	### FIXME: An ugly hack to covert back the platform name to
	### the ambigous sparc-solarisX tag
	$p =~ s/-sun4[^-]-/-sparc-/
		if ( $p =~ /-sun4/ );

     my ($a) = join ('|', @{$psgconf->{platform_suffixes}});

     PSGConf::Action::PackageManager::FTPArchive->conf($psgconf);

     $psgconf->register_data(
          pkg_encap_dir     => PSGConf::Data::String->new(
                              value => "/usr/local/encap"
                         ),
          pkg_epkg_umask   => PSGConf::Data::Integer->new(
						value => 022
                         ),
          pkg_epkg_cmd     => PSGConf::Data::String->new(
                              value => "/usr/local/bin/epkg"
                         ),
          pkg_tar_cmd     => PSGConf::Data::String->new(
                              value => "/usr/local/bin/tar"
                         ),
          pkg_gzip_cmd     => PSGConf::Data::String->new(
                              value => "/usr/local/bin/gzip"
                         ),
          pkg_regex     		=> PSGConf::Data::String->new(
                              value => "(\-encap($p(\-$a)?))?\.tar\.(Z|gz)"
                         ),
	);
}

###############################################################################
###  pkg_get_installed() method
###############################################################################

sub pkg_get_installed
{
	my ($self, $psgconf) = @_;
	my (%pkg_installed_list);
	my (@pkgname, $dir, $pkg, $ver);
	local *DIR;

	if (!opendir(DIR, $psgconf->data_obj('pkg_encap_dir')->get())) {
		warn "\t!!! cannot open Encap source directory (" . $psgconf->data_obj('pkg_encap_dir')->get() . "): $!\n";
		return undef;
	}

	map {
		my ($pkg) = $self->Parse($_);
		my ($ver) = $_; $ver =~ s/^\Q${pkg}\E$self->{seperator}->[0]//;
		push (@{$pkg_installed_list{$pkg}}, $ver);

	} grep { -d $psgconf->data_obj('pkg_encap_dir')->get() . '/' . $_ && !/^\.\.?$/; } readdir(DIR);

	closedir(DIR);

	return \%pkg_installed_list;
}


###############################################################################
###  pkg_get_latest_version() method
###############################################################################

sub pkg_get_latest_version
{
     my ($self, $psgconf, $pkg) = @_;
	my ($regex) = "(\Q${pkg}\E\-[^-]+)" .  $psgconf->data_obj('pkg_regex')->get();
	my ($path, $platform);

     ###
     ### Deal with the %p in the path.
     ###
	$platform = $psgconf->data_obj('platform')->get();

	### FIXME: An ugly hack to covert back the platform name to
	### the ambigous sparc-solarisX tag
	$platform =~ s/sun4[^-]-/sparc-/
		if ( $platform =~ /^sun4/ );

	$path = $psgconf->data_obj('env_vars')->find('ENCAP_UPDATE_PATH');
     $path =~ s/%p/$platform/g
          if ( $path =~ /%p/ );

     return $self->get_latest_version ( $psgconf, $pkg, $path, qr{$regex} );
}

###############################################################################
###  pkg_get_dependencies() method
###############################################################################

sub pkg_get_dependencies
{
	my ($self, $psgconf, $pkg) = @_;
	my (@pkgs, @files, $file, $ftp, $path, $platform);
	my ($regex) = "\Q${pkg}\E" .  $psgconf->data_obj('pkg_regex')->get();

	# First see if the package is already locally installed
	if ( -d $psgconf->data_obj('pkg_encap_dir')->get() . '/' . $pkg ) {
		@pkgs = &_extract_deps($psgconf, $psgconf->data_obj('pkg_encap_dir')->get(), $pkg);
	} else {

		###
		### Deal with the %p in the path.
		###
		$path = $psgconf->data_obj('env_vars')->find('ENCAP_UPDATE_PATH');

		### FIXME: An ugly hack to covert back the platform name to
		### the ambigous sparc-solarisX tag
		$platform = $psgconf->data_obj('platform')->get();
		$platform =~ s/sun4[^-]-/sparc-/
			if ( $platform =~ /^sun4/ );

		$path =~ s/%p/$platform/g
			if ( $path =~ /%p/ );

		# Else find the package on the server
		# Need to escape the + and . in the package name, hence \Q and \E
		@files = $self->list_files_from_url($psgconf, $path, qr{$regex});

		# we should have only found one entry
		if ( scalar @files == 1 ) {

			# See if we have the encap tarball 'cached'.
			$file = &basename ($files[0]);
			$file = $self->get_file_from_url($psgconf, $files[0])
				if ( ! -f "$psgconf->{tmpdir}/$file" );

			if ( -f "$psgconf->{tmpdir}/$file" ) {

				&_unpack_encap ($psgconf, $pkg, $file);

				@pkgs = &_extract_deps($psgconf, $psgconf->{tmpdir}, $pkg);

				# Remove the encapinfo and directory.
				my ($dir) = $psgconf->{tmpdir} . '/' . $pkg;
				if ( -d $dir ) {
					warn "\t!!! unlink('$dir'): $!\n"
						if ( $psgconf->{rm_tmpfiles} && !rmtree ($dir) );
				}

			}
		} elsif ( ! scalar @files ) {
			warn "\tCould not find URL for ($pkg)\n"; 
		} else {
			warn "\tFound too many URLs for ($pkg)\n"; 
		}
	}

	return @pkgs;
}

sub _unpack_encap {
	my ($psgconf, $pkg, $file) = @_;
	my ($cmd, $dir, $err);

	$dir = getcwd;
	chdir($psgconf->{tmpdir});

	$cmd = $psgconf->data_obj('pkg_tar_cmd')->get() . ' --use-compress-program=' . $psgconf->data_obj('pkg_gzip_cmd')->get() . ' -xf ';
	$cmd .= $psgconf->{tmpdir} . '/' . $file;
	$cmd .= ' ' . $pkg . '/encapinfo';

	$err=&PSGConf::Util::RunCommand($cmd);

	warn "\tCould not unpack encapinfo file\n"
		if ( $err );

	chdir($dir);
}

sub _extract_deps {
	my ($psgconf, $dir, $pkg) = @_;
	my (@deps);
	local *FP;

	if (open(FP, "<$dir/$pkg/encapinfo")) {
		grep (/prereq pkgspec [^\s]+ ([^\s]*)$/o 
			&& (push @deps, $1),
			<FP>);
		close FP;
	} else {
          warn "\t!!! encapinfo file for ($pkg) could not be found\n";
	}

	return @deps;
}


###############################################################################
###  pkg_install() method
###############################################################################

sub pkg_install
{
	my ($self, $psgconf, $pkg) = @_;
	my ($err, $umask, $cmd, $tarball);

	$tarball = glob ($psgconf->{tmpdir} . '/'
		. "\Q${pkg}\E" .  $psgconf->data_obj('pkg_regex')->get());

	$cmd  = 'ENCAP_UPDATE_PATH="';
	$cmd .= $psgconf->data_obj('env_vars')->find('ENCAP_UPDATE_PATH');
	$cmd .= '" ' . $psgconf->data_obj('pkg_epkg_cmd')->get() . ' -S';

	### If the package is already on disk,
	### then just make it the linked in version.
	$cmd .= 'u'
		if ( ! -d $psgconf->data_obj('pkg_encap_dir')->get() . '/' . $pkg 
			&& ! -f $tarball );

	$cmd .= 'q'
		if (! $psgconf->{verbose});

	map { $cmd .= " -A $_"; } @{$psgconf->{platform_suffixes}};

	$cmd .= ' ' . ((-f $tarball) ? $tarball: $pkg);

	### Force the umask to be something that everyone will be
	### able to read and execute the files that are being installed
	### and not rely on the default enviroment to set. 
	$umask = umask $psgconf->data_obj('pkg_epkg_umask')->get();

	$err=&PSGConf::Util::RunCommand($cmd);

	### Be nice and reset the umask back to what the Administrator had
	umask $umask;

     print "\n"
          if ($psgconf->{verbose});

	return $err;
}

###############################################################################
###  pkg_remove() method
###############################################################################

sub pkg_remove
{
	my ($self, $psgconf, $pkg, $force) = @_;
	my ($err, $cmd);

     $cmd = $psgconf->data_obj('pkg_epkg_cmd')->get() . ' -rS';
     $cmd .= 'q'
          if (! $psgconf->{verbose});
     $cmd .= 'f'
          if ($force);
	$cmd .= " $pkg";

     if (! ($err=&PSGConf::Util::RunCommand($cmd)))
	{
		### Now clean up the removed encap package
		rmtree ($psgconf->data_obj('pkg_encap_dir')->get() . '/' . $pkg);
	}

     print "\n"
          if ($psgconf->{verbose});


	return $err;
}


###############################################################################
###  documentation
###############################################################################

1;

__END__

=head1 NAME

PSGConf::Action::PackageManager::Encap - install Encap packages

=head1 SYNOPSIS

  use PSGConf::Action::PackageManager::Encap;

  $psgconf->register_actions(
		PSGConf::Action::PackageManager::Encap->new(
			'quiet'		=> 1
		),
		...
	);

=head1 DESCRIPTION

The B<PSGConf::Action::PackageManager::Encap> module provides a B<PSGConf>
action class for updating Encap packages.

The B<PSGConf::Action::PackageManager::Encap> class is derived from the
B<PSGConf::Action::PackageManager::FTPArchive> class, but it 
defines/overrides the following methods:

=over 4

=item I<pkg_get_installed()>

A method that will assign values to the I<pkg_installed_list>, which
will be all the packages currently installed.  The I<pkg_installed_list>
is a reference to a hash of arrays.  The array is sorted to have
versions in ascending order.  Called from the 
B<PSGConf::Action::PackageManager::check()> method.

=item I<pkg_get_latest_version($pkg)>

A method that will return a B<PSGConf::Data::String> that contains
the latest version of $pkg from the package repository.  Called from the 
B<PSGConf::Action::PackageManager::check()> method.  It uses the
environment variable B<ENCAP_UPDATE_PATH> to determine where to look
for these updated packages.

=item I<pkg_get_dependencies($pkg)>

A method that will return a B<PSGConf::Data::List> that contains
all the packages that $pkg will depend upon, regardless
if they have been installed or not.   This is for the package on
the package repository and $pkg will have the version in it as well.
Called from the B<PSGConf::Action::PackageManager::diff()> method.

=item I<pkg_install($pkg)>

A method that will install a $pkg on the system.  Returns 0 for a
successfull install, non-zero for a failure.  Called from 
B<PSGConf::Action::PackageManager::do()> method.  It uses the
environment variable B<ENCAP_UPDATE_PATH> to determine where to get
these packages from.

=item I<pkg_remove($pkg, $force)>

A method that will remove $pkg from the system, and may have to force
the removal, depending on the value of $force, which will be a
B<PSGConf::Data::Boolean>.  Returns 0 for a successfull install,
non-zero for a failure.  Called from 
B<PSGConf::Action::PackageManager::do()> method.

=back


The constructor also registers the following data objects:

=over 4

=item I<pkg_encap_dir>

A B<PSGConf::Data::String> object that determines where Encap packages
are installed.  The default is I</usr/local/encap>.

=item I<pkg_epkg_umask>

A B<PSGConf::Data::Integer> object that determines the default umask
for epkg to use.  The default is I<022>.

=item I<pkg_epkg_cmd>

A B<PSGConf::Data::String> object that determines where epkg is located.
The default is I</usr/local/bin/epkg>.

=item I<pkg_tar_cmd>

A B<PSGConf::Data::String> object that determines where tar is located.
Used to extract dependencies from the encap tarball.
The default is I</usr/local/bin/tar>.

=item I<pkg_gzip_cmd>

A B<PSGConf::Data::String> object that determines where gzip is located.
Used to extract dependencies from the encap tarball.
The default is I</usr/local/bin/gzip>.

=item I<pkg_regex>

A B<PSGConf::Data::String> object that determines what the regex is to
match against the FTPArchive listing.
The default is I<(\-encap($p(\-$a)?))?\.tar\.(Z|gz)>.

=back


=head1 SEE ALSO

C<CE<lt>epkg(1)E<gt>>

L<perl>

L<PSGConf>

L<PSGConf::Action>

L<PSGConf::Action::PackageManager::FTPArchive>

L<PSGConf::Data::String>

L<PSGConf::Data::Integer>

L<PSGConf::Util>

L<File::Path>

L<File::Basename>

L<Cwd>

=cut



syntax highlighted by Code2HTML, v. 0.9.1