#!/usr/bin/perl

#- Copyright (C) 1999,2001 MandrakeSoft (pixel@linux-mandrake.com)
#-
#- 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 2, 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, write to the Free Software
#- Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

#use strict qw(subs vars refs);
use urpm;

#- get I18N translation method.
import urpm _;

#- default options.
my $update = 0;
my $auto = 0;
my $allow_medium_change = 0;
my $auto_select = 0;
my $force = 0;
my $sync = undef;
my $X = 0;
my $WID = 0;
my $all = 0;
my $rpm_opt = "vh";
my $use_provides = 0;
my $verbose = 0;

my $uid;
my @files;
my @names;

#$ENV{PATH} = "/sbin:/usr/sbin:/bin:/usr/bin:/usr/X11R6/bin";
#delete @ENV{qw(ENV BASH_ENV IFS CDPATH)};
#($<, $uid) = ($>, $<);
    
sub usage {
    print STDERR _("urpmi version %s
Copyright (C) 1999, 2000, 2001 MandrakeSoft.
This is free software and may be redistributed under the terms of the GNU GPL.

usage:
") . _("  --help         - print this help message.
") . _("  --update       - use only update media.
") . _("  --auto         - automatically select a good package in choices.
") . _("  --auto-select  - automatically select packages for upgrading the system.
") . _("  --force        - force invocation even if some packages do not exist.
") . _("  --wget         - use wget to retrieve distant files.
") . _("  --curl         - use curl to retrieve distant files.
") . _("  --X            - use X interface.
") . _("  --best-output  - choose best interface according to the environment:
                   X or text mode.
") . _("  -a             - select all matches on command line.
") . _("  -p             - allow search in provides to find package.
") . _("  -q             - quiet mode.
") . _("  -v             - verbose mode.
") . "\n" . _("  names or rpm files (only for root) given on command line are installed.
", $urpm::VERSION);
    exit(0);
}

#- parse arguments list.
my @nextargv;
for (@ARGV) {
    /^--help$/ and do { usage; next };
    /^--update$/ and do { $update = 1; next };
    /^--auto$/ and do { $auto = 1; next };
    /^--allow-medium-change$/ and do { $allow_medium_change = 1; next };
    /^--auto-select$/ and do { $auto_select = 1; next };
    /^--force$/ and do { $force = 1; next };
    /^--wget$/ and do { $sync = \&urpm::sync_wget; next };
    /^--curl$/ and do { $sync = \&urpm::sync_curl; next };
    /^--X$/ and do { $X = 1; next };
    /^--WID=(.*)$/ and do { $WID = $1; next };
    /^--WID$/ and do { push @nextargv, \$WID; next };
    /^--best-output$/ and do { $X ||= $ENV{DISPLAY} && -x "/usr/X11R6/bin/grpmi" && system('/usr/X11R6/bin/xtest', '') == 0;
			       next };
    /^--comment$/ and do { push @nextargv, undef; next };
    /^-(.*)$/ and do { foreach (split //, $1) {
	/[\?h]/ and do { usage; next };
	/a/ and do { $all = 1; next };
	/c/ and do { next };
	/m/ and do { next };
	/M/ and do { next }; #- nop
	/q/ and do { $rpm_opt = ""; next };
	/p/ and do { $use_provides = 1; next };
	/v/ and do { $verbose = 1; next };
	die _("urpmi: unknown option \"-%s\", check usage with --help\n", $1); } next };
    @nextargv and do { my $r = shift @nextargv; $r and $$r = $_; next };
    #- only superuser by default can install packages,
    #- TODO check for source package, to download but not to install ?
    $uid == 0 or $urpm->fatal(1, _("Only superuser is allowed to install packages"));
    /\.rpm$/ and do { push @files, untaint($_); next };
    push @names, $_;
}

#- log only at this point in case of query usage.
log_it(scalar localtime, " urpmi called with @ARGV\n");

my ($pid_out, $pid_err);

open SAVEOUT, ">&STDOUT"; select SAVEOUT; $| = 1;
open SAVEERR, ">&STDERR"; select SAVEERR; $| = 1;
unless ($pid_out = open STDOUT, "|-") {
    open F, ">>/var/log/urpmi.log"; select F; $| = 1;
    select SAVEOUT; $| = 1;
    $/ = \1;
    while (<STDIN>) {
	print SAVEOUT $_;
	print F $_;
    }
    close F;
    exit 0;
}
unless ($pid_err = open STDERR, "|-") {
    open F, ">>/var/log/urpmi.log"; select F; $| = 1;
    select SAVEERR; $| = 1;
    $/ = \1;
    while (<STDIN>) {
	print SAVEERR $_;
	print F $_;
    }
    close F;
    exit 0;
}
select STDERR; $| = 1;     # make unbuffered
select STDOUT; $| = 1;     # make unbuffered

#- params contains informations to parse installed system.
my $urpm = new urpm;

#- use specific sync routine.
$sync and $urpm->{sync} = $sync;

#- remove verbose if not asked.
$verbose or $urpm->{log} = sub {};

$urpm->read_config;
$urpm->parse_synthesis($_) foreach grep { !$_->{ignore} && (!$update || $_->{active}) } @{$urpm->{media} || []};

if (@files) {
    $uid == 0 or $urpm->fatal(1, _("Only superuser is allowed to install local packages"));

    #- sanity check of pathname.
    m|^/| or $_ = "./$_" foreach @files;

    #- build closure with local package and return list of names.
    push @names, $urpm->register_local_packages(@files);
}

#- relocate depslist.
$urpm->relocate_depslist_provides();


#- search the packages according the selection given by the user,
#- basesystem is added to the list so if it need to be upgraded,
#- all its dependency will be updated too.
#- make sure basesystem exists before.
my %packages;
$urpm->search_packages(\%packages, [ @names],
		       all => $all,
		       use_provides => $use_provides) or $force or exit 1;

#- filter to add in packages selected required packages.
my $ask_choice = sub {
    my ($urpm, $from_id, @choices_id) = @_;
    my $n = 1; #- default value.
    my ($from, @l) = map { my $info = $urpm->{params}{depslist}[$_];
			   "$info->{name}-$info->{version}-$info->{release}" } ($from_id, @choices_id);

    if (@l > 1 && !$auto) {
	my $msg = (defined $from_id ?
		   _("One of the following packages is needed to install %s:", $from) :
		   _("One of the following packages is needed:"));
	if ($X) {
	    `gchooser "$msg" @l`;
	    $n = $? >> 8 || die;
	} else {
	    print SAVEOUT "$msg\n";
	    my $i = 0; foreach (@l) { print SAVEOUT " ", ++$i, "- $_\n"; }
	    while (1) {
		printf SAVEOUT _("What is your choice? (1-%d) ", $i);
		$n = <STDIN>;
		1 <= $n && $n <= $i and last;
		print SAVEOUT _("Sorry, bad choice, try again\n");
	    }
	}
    }

    $choices_id[$n - 1];
};

#- auto select package for upgrading the distribution.
if ($auto_select) {
    my (%to_remove, %keep_files);

    $urpm->select_packages_to_upgrade('', \%packages, \%to_remove, \%keep_files);

    if (keys(%to_remove) > 0) {
	print STDERR "some packages have to be removed for being upgraded, this is not supported yet\n";
    }
}

$urpm->filter_packages_to_upgrade(\%packages, $ask_choice);

#- get out of package that should not be upgraded.
$urpm->deselect_unwanted_packages(\%packages);

#- package to install as a array of strings.
my @to_install;

#- check if there is at least one package to install that
#- has not been given by the user.
my $ask_user = $auto_select && scalar(keys %packages);
my $sum = 0;
foreach (keys %packages) {
    defined $packages{$_} and $ask_user = 1;
    my $info  = $urpm->{params}{depslist}[$_];
    $sum += $info->{size};
    push @to_install, "$info->{name}-$info->{version}-$info->{release}";
}
if (!$auto) {
    if ($ask_user) {
	my $msg = _("To satisfy dependencies, the following packages are going to be installed (%d MB)", toMb($sum));
	my $msg2 = _("Is it OK?");
	if ($X) {
	    my $p = join "\n", @to_install;
	    my $ok = _("Ok");
	    my $cancel = _("Cancel");
	    `gmessage -default $ok -buttons "$ok:0,$cancel:2" "$msg:\n$p\n\n$msg2"`;
	    $? and exit 0;
	} else {
	    $noexpr = _("Nn");
	    $yesexpr = _("Yy");
	    print SAVEOUT "$msg:\n@to_install\n$msg2" . _(" (Y/n) ");
	    <STDIN> =~ /[$noexpr]/ and exit 0;
	}
    }
}

my ($local_sources, $list, $local_to_removes) = $urpm->get_source_packages(\%packages);
unless ($local_sources || $list) {
    $urpm->{fatal}(3, _("unable to get source packages, aborting"));
}
#- clean cache with file that are not necessary with this transaction.
#- TODO check not another urpmi is doing the same...
foreach (@$local_to_removes) {
    unlink $_;
}

my %sources = $urpm->upload_source_packages($local_sources, $list, ($X ? '' : 'force_local'),
					    (!$auto || $allow_medium_change) && sub {
						my $msg = _("Please insert the medium named \"%s\" on device [%s]", @_);
						my $msg2 = _("Press Enter when it's done...");
						if ($X) {
						    my $ok = _("Ok");
						    my $cancel = _("Cancel");
						    $msg =~ s/"/\\"/g;
						    `gmessage -default $ok -buttons "$ok:0,$cancel:2" "$msg"`;
						    !$?;
						} else {
						    print SAVEOUT "$msg\n$msg2 ";
						    <STDIN>; 1;
						}
					    });

#- install package.
my @rpms_install = grep { $_ !~ /\.src.\.rpm/ } values %{$urpm->extract_packages_to_install(\%sources) || {}};
my @rpms_upgrade = grep { $_ !~ /\.src.\.rpm/ } values %sources;
if (@rpms_install || @rpms_upgrade) {
    printf SAVEOUT _("installing %s\n", join(' ', @rpms_install, @rpms_upgrade));
    log_it(scalar localtime, " @_\n");
    #- check for local files.
    foreach (@rpms_install, @rpms_upgrade) {
	m|^/| && ! -e $_ or next;
	message(_("Installation failed, some files are missing.\nYou may want to update your urpmi database"));
	exit 2;
    }
    $urpm->{log}("starting installing packages");
    if ($X) {
	system("grpmi", $WID ? ("--WID=$WID") : (),
	       (map { ("-noupgrade", $_) } @rpms_install), @rpms_upgrade);
	if ($?) {
	    message(_("Installation failed")); #- grpmi handles --nodeps and --force by itself,
	    exit(($? >> 8) + 32); #- forward grpmi error + 32
	}
    } else {
	if (@rpms_install) {
	    system("rpm", "-i$rpm_opt", @rpms_install);
	    if (@$_) {
		message(_("Installation failed"));

		m|^/| && !-e $_ and exit 2 foreach @rpms_install; #- missing local file

		$noexpr = _("Nn");
		$yesexpr = _("Yy");
		print SAVEOUT _("Try installation without checking dependencies? (y/N) ");
		$auto and exit 1; #- if auto has been set, avoid asking user.
		$force or <STDIN> =~ /[$yesexpr]/ or exit 1;
		$urpm->{log}("starting installing packages without deps");
		system("rpm", "-i$rpm_opt", "--nodeps", @rpms_install);

		if ($?) {
		    message(_("Installation failed"));
		    print SAVEOUT _("Try installation even more strongly (--force)? (y/N) ");
		    $force or <STDIN> =~ /[$yesexpr]/ or exit 1;
		    $urpm->{log}("starting force installing packages without deps");
		    system("rpm", "-i$rpm_opt", "--nodeps", "--force", @rpms_install);	
		}
	    }
	}
	if (@rpms_upgrade) {
	    system("rpm", "-U$rpm_opt", @rpms_upgrade);
	    if ($?) {
		message(_("Installation failed"));

		m|^/| && !-e $_ and exit 2 foreach @rpms_upgrade; #- missing local file

		$noexpr = _("Nn");
		$yesexpr = _("Yy");
		print SAVEOUT _("Try installation without checking dependencies? (y/N) ");
		$auto and exit 1; #- if auto has been set, avoid asking user.
		$force or <STDIN> =~ /[$yesexpr]/ or exit 1;
		$urpm->{log}("starting installing packages without deps");
		system("rpm", "-U$rpm_opt", "--nodeps", @rpms_upgrade);

		if ($?) {
		    message(_("Installation failed"));
		    print SAVEOUT _("Try installation even more strongly (--force)? (y/N) ");
		    $force or <STDIN> =~ /[$yesexpr]/ or exit 1;
		    $urpm->{log}("starting force installing packages without deps");
		    system("rpm", "-U$rpm_opt", "--nodeps", "--force", @rpms_upgrade);	
		}
	    }
	}
    }
} else {
    message_auto(_("everything already installed"));
}

#- this help flushing correctly by closing this file before (piped on tee).
#- but killing them is generally better.
fork() or do { sleep 1; kill 15, $pid_err, $pid_out };
close STDERR;
close STDOUT;

sub toMb {
    my $nb = $_[0] / 1024 / 1024;
    int $nb + 0.5;
}

sub message { $X ? `gmessage -default Ok -buttons Ok "$_[0]"` : print SAVEOUT "$_[0]\n"; }

sub message_auto { $auto ? print SAVEOUT "$_[0]\n" : message($_[0]); }

sub log_it {
    local *LOG;
    open LOG, ">>/var/log/urpmi.log" or die "can't output to log file\n";
    print LOG @_;
}

sub untaint {
    my @r = ();
    foreach (@_) {
        /(.*)/;
	push @r, $1;
    }
    @r == 1 ? $r[0] : @r
}