# ex:ts=8 sw=4:
# $OpenBSD: Replace.pm,v 1.69 2010/01/18 10:04:53 espie Exp $
#
# Copyright (c) 2004-2010 Marc Espie <espie@openbsd.org>
#
# Permission to use, copy, modify, and distribute this software for any
# purpose with or without fee is hereby granted, provided that the above
# copyright notice and this permission notice appear in all copies.
#
# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF

use strict;
use warnings;

use OpenBSD::Delete;

package OpenBSD::PackingElement;
sub can_update
{
	my ($self, $installing, $state) = @_;

	my $issue = $self->update_issue($installing);
	
	if (defined $issue) {
	    	push(@{$state->{journal}}, $issue);
	}
}

sub update_issue { undef }

sub extract
{
	my ($self, $state) = @_;
	$state->{partial}->{$self} = 1;
}

sub extract_and_progress
{
	my ($self, $state, $donesize, $totsize) = @_;
	$self->extract($state);
	if ($state->{interrupted}) {
		die "Interrupted";
	}
	$self->mark_progress($state->progress, $donesize, $totsize);
}

package OpenBSD::PackingElement::FileBase;
use OpenBSD::Temp;

sub extract
{
	my ($self, $state) = @_;

	my $file = $self->prepare_to_extract($state);

	if (defined $self->{link} || defined $self->{symlink}) {
		$state->{archive}->skip;
		return;
	}
	
	$self->SUPER::extract($state);

	# figure out a safe directory where to put the temp file
	my $d = dirname($file->{destdir}.$file->name);
	# we go back up until we find an existing directory.
	# hopefully this will be on the same file system.
	while (!-d $d && -e _ || defined $state->{noshadow}->{$d}) {
		$d = dirname($d);
	}
	if ($state->{not}) {
		$state->say("extracting tempfile under $d") 
		    if $state->verbose >= 3;
		$state->{archive}->skip;
	} else {
		if (!-e _) {
			File::Path::mkpath($d);
		}
		my ($fh, $tempname) = OpenBSD::Temp::permanent_file($d, "pkg");

		$state->say("extracting $tempname") if $state->verbose >= 3;
		$self->{tempname} = $tempname;

		# XXX don't apply destdir twice
		$file->{destdir} = '';
		$file->set_name($tempname);
		$file->create;
		$self->may_check_digest($file, $state);
	}
}

package OpenBSD::PackingElement::Dir;
sub extract
{
	my ($self, $state) = @_;
	my $fullname = $self->fullname;
	my $destdir = $state->{destdir};

	return if -e $destdir.$fullname;
	$self->SUPER::extract($state);
	$state->say("new directory ", $destdir, $fullname) 
	    if $state->verbose >= 3;
	return if $state->{not};
	File::Path::mkpath($destdir.$fullname);
}


package OpenBSD::PackingElement::Sample;
sub extract
{
}

package OpenBSD::PackingElement::Sampledir;
sub extract
{
}

package OpenBSD::PackingElement::ScriptFile;
sub update_issue
{ 
	my ($self, $installing) = @_;
	return $self->name." script";
}

package OpenBSD::PackingElement::FINSTALL;
sub update_issue
{ 
	my ($self, $installing) = @_;
	return if !$installing;
	return $self->SUPER::update_issue($installing);
}

package OpenBSD::PackingElement::FDEINSTALL;
sub update_issue
{ 
	my ($self, $installing) = @_;
	return if $installing;
	return $self->SUPER::update_issue($installing);
}

package OpenBSD::PackingElement::Exec;
sub update_issue
{ 
	my ($self, $installing) = @_;
	return if !$installing;
	return '@'.$self->keyword.' '.$self->{expanded};
}

package OpenBSD::PackingElement::ExecAdd;
sub update_issue { undef }

package OpenBSD::PackingElement::Unexec;
sub update_issue
{ 
	my ($self, $installing) = @_;

	return if $installing;

	return '@'.$self->keyword.' '.$self->{expanded};
}

package OpenBSD::PackingElement::UnexecDelete;
sub update_issue { undef }

package OpenBSD::Replace;

sub perform_extraction
{
	my ($handle, $state) = @_;

	$handle->{partial} = {};
	$state->{partial} = $handle->{partial};
	my $totsize = $handle->{totsize};
	$state->{archive} = $handle->{location};
	my $donesize = 0;
	$state->{donesize} = 0;
	$handle->{plist}->extract_and_progress($state, \$donesize, $totsize);
}

sub check_plist_exec
{
	my ($plist, $state, $new) = @_;

	$state->{journal} = [];
	$plist->can_update($new, $state);
	return 1 if @{$state->{journal}} == 0;

	$state->errsay($new ? "New": "Old", " package ", $plist->pkgname, 
	    " contains potentially unsafe operations");
	for my $i (@{$state->{journal}}) {
		$state->errsay("| ", $i);
	}
	return 0;
}

sub can_old_package_be_replaced
{
	my ($plist, $state) = @_;
	return check_plist_exec($plist, $state, 0);
}

sub is_new_package_safe
{
	my ($plist, $state) = @_;
	return check_plist_exec($plist, $state, 1);
}

sub is_set_safe
{
	my ($set, $state) = @_;

	if ($state->{defines}->{update} && !$state->verbose) {
		return 1;
	}

	my $ok = 1;

	for my $pkg ($set->older) {
		$ok = 0 unless can_old_package_be_replaced($pkg->plist, $state);
	}
	for my $pkg ($set->newer) {
		$ok = 0 unless is_new_package_safe($pkg->plist, $state);
	}
	return 1 if $ok;

	if ($state->{defines}->{update}) {
		$state->errsay("Forcing update");
		return 1;
	} elsif ($state->{interactive}) {

		if ($state->confirm("proceed with update anyways", 0)) {
			return 1;
		} else {
			return 0;
		}
	} else {
		$state->errsay("Cannot install ", $set->print, 
		    " (use -D update)");
		return 0;
    	}
}


1;
