#!/usr/bin/perl
################################################################################
# Mdkupdate                                                                    # 
#                                                                              #
# Copyright (C) 2002-2004 Mandriva                                             #
#                                                                              #
# Daouda Lo <daouda at mandriva dot com>                                              #
#                                                                              #
# This program is free software; you can redistribute it and/or modify         #
# it under the terms of the GNU General Public License Version 2 as            #
# published by the Free Software Foundation.                                   #
#                                                                              #
# 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;
use POSIX;
use lib qw(/usr/lib/libDrakX /usr/lib/libDrakX/drakfirsttime);
use common;
use interactive;

use mdkonline;
use Data::Dumper;
use urpm;

use Getopt::Long;

BEGIN { unshift @::textdomains, 'mdkupdate' }

require_root_capability();

my $confdir = '/root/.MdkOnline';
my $conffile = "$confdir/mdkupdate";
my $difflog = '/var/tmp/diff.log';

my $currentrpm = "$confdir/rpm_qa_installed_current";
my $afterrpm =  "$confdir/rpm_qa_installed_after";

my $logfile = '/var/tmp/mdkupdate.log';

my $onlineUrl = "https://www.mandrivaonline.com/";

my $url;
foreach (qw(online_dif online_update online3_RemoteAction wizard)) {
    $url->{$_} = $onlineUrl . $_ . '.php';
}
my $VERSION = "2.0";
my $YEARS = "2002-2005";

#for compatibilities
mkdir_p($confdir) if !-d $confdir;
-e '/root/.mdkupdate' and system "/bin/mv", "/root/.mdkupdate", $conffile;

my ($scheduled, $noscheduled);

sub usage() {
    print STDERR N("mdkupdate version %s
Copyright (C) %s Mandriva.
This is free software and may be redistributed under the terms of the GNU GPL.

usage:
", $VERSION, $YEARS) . N("  --help		- print this help message.
") . N("  --auto		- Mandriva Update launched automatically.
") . N("  --applet		- launch Mandriva Update.
") . N("  --mnf			- launch mnf specific scripts.
") . N("  --noX			- text mode version of Mandriva Update.
") . N("  --bundle file.bundle	- parse and install package from .bundle metainfo file.
");
    exit(0);
}

my ($auto, $applet, $mnf, $noX, $bundle);
my %options = (
	       'auto'	    => \$auto,
	       'a|applet'   => \$applet,
	       'mnf'	    => \$mnf,
	       'noX'	    => \$noX,
	       'b|bundle:s' => sub { $bundle = $_[1] || 'webbundle' } 
	      );

GetOptions(%options);

-s $conffile || $bundle or die N("No %s file found. Run mdkonline wizard first", $conffile);

$bundle eq "webbundle" and mdkonline::get_site("http://online3.mandriva.com/one/web/bundle"), exit(0);

my %o = getVarsFromSh($conffile);

if (!$bundle) {
    if ($o{LOGIN} && $o{PASS} && $o{LOGIN} !~ /\s+/ && $o{PASS} !~ /\s+/) {
	my $u;
	my $MandrakeUpdateURL = $url->{online3_RemoteAction} . '?action=ScheduledRPM' . '&log=' . $o{LOGIN} . '&pass=' . $o{PASS} . '&host=' . $o{MACHINE} . '&key=' . $o{CURRENTKEY};
	my $resp = mdkonline::get_from_URL($MandrakeUpdateURL, 'MdkUpdateAgent');
	my $contents = $resp->content;
	# print "CONTENTS = $contents \n";
	-e $logfile and system "/bin/rm", $logfile;
	if ($resp->is_success) {
	    if ($contents =~ /TRUE/) { $u = 10 } elsif ($contents =~ m/(\d+)/) { $u = sprintf("%d", $1) } else { $u = 83 }
	    my $action = {
			  10 => sub {
			      my $c2h = split_contents($contents);
			      #printf("\nCONTENTS = $contents \n torf = %s\n OLDKEY = %s = %s \n NEWKEY = %s \n FTP = %s\n", $c2h->{torf}, $c2h->{OLDKEY}, $o{CURRENTKEY}, $c2h->{NEWKEY}, $c2h->{FTP} );
			      if ($c2h->{torf} eq "TRUE" && $c2h->{OLDKEY} && $c2h->{NEWKEY}) { update_conf($c2h->{OLDKEY}, $c2h->{NEWKEY}) }
			      if ($c2h->{FTP}) { 
				  # for debugging purpose
				  printf("FTP = %s\n", $c2h->{FTP});
				  #$c2h->{FTP} = 'ftp://ftp.lip6.fr/pub/linux/distributions/mandrake/updates';
				  add_media($c2h->{FTP});
			      }
			      $scheduled = join(',', @{$c2h->{sched}});
			      $noscheduled = join(',', @{$c2h->{nosched}}); $noscheduled =~ s/\.rpm//g;
			      #printf "SCHEDULED = $scheduled\nNOSCHEDULED = $noscheduled\n";
			      -f $currentrpm or rpm_qa($currentrpm);
			      my ($release) = mdkonline::get_release();
			      if ($applet) {
				  my $rpm_exec_name = $release >= 2006.0 ? "/usr/bin/MandrivaUpdate" : "/usr/bin/MandrakeUpdate";
				  my $is_no_media_update =  $release <= 10.1 ? '' : "--no-media-update";
				  system $rpm_exec_name, "--no-confirmation", $is_no_media_update, "--media=update_source";
			      } elsif ($noX) {
				  my $in = interactive->vnew;
				  my $pkgs = ask_pkgs($in);
				  install_pkgs($in, $pkgs) if $pkgs;
				  $in->exit(0);
			      } elsif ($auto) {
				  $o{AUTO} eq 'TRUE' and auto_install_rpms($c2h->{sched});
			      }
			      rpm_qa($afterrpm);
			      my %new = getVarsFromSh($conffile);
			      my $need_upload = get_rpm_diff();
			      if ($need_upload || $auto || $mnf) {
				  #- send configuration and get back key to use...
				  my $r;
				  ($r, $new{CURRENTKEY}) = send_conf_update($new{LOGIN}, $new{PASS}, $new{MACHINE});
				  $new{FTP} = $mnf ? $c2h->{FTP} : '';
				  if ($r eq 'TRUE') { delete $new{OLDKEY}; setVarsInSh($conffile, \%new) }
			      }
			      clean_dir();
			  },
			  80 => sub { 
			      output_p($logfile, "[mdkupdate] Error 80: Client password does not match in database");
			  },
			  81 => sub {
			      output_p($logfile, "[mdkupdate] Error 81: Bad Authentification key. Please rerun mdkonline wizard");
			  },
			  82 => sub {
			      output_p($logfile, "[mdkupdate] Error 82: No active Online service found for this host.");
			  },
			  83 => sub {
			      output_p($logfile, "[mdkupdate] Error 83: Unknown problem, better relaunch mdkonline wizard or check connexion");
			  }
			 };
	    $action->{$u}->();
	} else {
	    output_p($logfile, N("Connection problem") . "\n" . N("Mandriva Update could not contact the site, we will try again."));
	}
    } else {
	my $ret = -1; 
    }
} else {
    my $data;
    my $namespace    = 'http://online3.mandriva.com/o/soap/';
    my $serviceProxy = $namespace;
    my %bundle_vars = getVarsFromSh($bundle);
    
    $o{LOGIN} or system("/usr/sbin/mdkonline");
    $o{LOGIN} or die("Configuration not uploaded to Mandriva Online");
    
    my $s = SOAP::Lite
      ->proxy($serviceProxy)
	->uri($namespace);
    $data = mdkonline::soap_authenticate_user($o{LOGIN}, $o{PASS});
    $data = mdkonline::soap_register_host($o{LOGIN}, $o{PASS}, $o{MACHINE}, "Testing online");
    my $id  = $data->{data}->{host_id}; my $key = $data->{data}->{host_key};
    $data = $s->query( $id, $key, 'Software::get_bundle', $bundle_vars{BUNDLE} )->result();
}

sub ask_pkgs {
    my ($in) = @_;
    my $pkgs = get_updatable_pkgs();
    $in->ask_browse_tree_info('Mdkupdate', N("Choose which packages should be installed and Press Ok"),
			      {
			       node_state => sub { $pkgs->{$_[0]}{selected} ? 'selected' : 'unselected' },
			       build_tree => sub {
				   my ($add_node, $flat) = @_;
				   $add_node->($_, undef) foreach sort keys %$pkgs;
			       },
			       grep_unselected => sub { grep { !$pkgs->{$_}{selected} } @_ },
			       toggle_nodes => sub {
				   my ($set_state, @nodes) = @_;
				   my $new_state = !$pkgs->{$nodes[0]}{selected};
				   foreach (@nodes) {
				       $set_state->($_, $new_state ? 'selected' : 'unselected');
				       $pkgs->{$_}{selected} = $new_state;
				   }
			       },
			       get_info => sub {},
			      }) or return keys %$pkgs; #- no change on cancel.
    [ grep { $pkgs->{$_}{selected} } keys %$pkgs ];
}
sub install_pkgs {
    my ($in, $choosed) = @_;
    my $w = $in->wait_message(N("Please wait"), N("Installing packages ...\n"));
    eval {
	system "/usr/sbin/urpmi", "--auto", "--media", "update_source", @$choosed;
	$? == 0 or die N("Unable to update packages from update_source medium.\n");
    };
    undef $w;
}
sub get_updatable_pkgs() {
    my $urpm = new urpm;
    $urpm->read_config;
    my %installable_pkgs; my @update_medias;
    my ($medium) = grep { $_->{name} eq "update_source" } @{$urpm->{media}};
    
    if ($medium) {
	$urpm->configure(media => $medium->{name});
	@update_medias = grep { !$_->{ignore} && $_->{update} } @{$urpm->{media}};
	$urpm->compute_installed_flags(URPM::DB::open);
	foreach my $pkg (@{$urpm->{depslist}}) {
	    $pkg->flag_upgrade or next;
	    my $selected = 0;
	    $pkg->flag_installed or next;
	    any { $pkg->id >= $_->{start} && $pkg->id <= $_->{end} } @update_medias or next;
	    $selected = member($pkg->name, qw(perl-URPM, urpmi, mdkonline, drakxtools)) ? 1 : 0;
	    $installable_pkgs{my_fullname($pkg)} = { selected => $selected, pkg => $pkg };
	}
    }
    \%installable_pkgs;
}
sub my_fullname {
    return '?-?-?' unless ref $_[0];
    my ($name, $version, $release) = $_[0]->fullname;
    "$name-$version-$release";
}
sub split_contents {
    my $cont = shift;
    my ($elem, $s);
    $s = [ split /\n/, $cont ];
    $elem->{torf} = $s->[0];
    if ($elem->{torf} eq 'TRUE') {
	($elem->{torf}, $elem->{NEWKEY}, $elem->{OLDKEY}, $elem->{FTP}) = splice(@$s, 0, 4);
	($elem->{sched}, $elem->{nosched}) = partition { /(i586|ppc|ia64|noarch|x86_64|amd64|ppc64)$/ } @$s;
    }
    $elem;
}
sub auto_install_rpms {
    my ($pkgs) = shift;
    my @pkg;
    push(@pkg, $_ . '.rpm') foreach @$pkgs;
    my $ret = update_pkgs(@pkg);
    $ret == 1 or output_p($logfile, "[mdkupdate] Error 100: Packages failed to upgrade");
}
sub update_conf {
    my ($oldkey, $newkey) = @_;
    my %l = getVarsFromSh $conffile;
    setVarsInSh($conffile, {
		OLDKEY => $oldkey,
		CURRENTKEY => $newkey,
		VER => $l{VER},
       	        MACHINE => $l{MACHINE},
		PASS => $l{PASS},
		LOGIN => $l{LOGIN},
		AUTO => $l{AUTO}
	      });
}
sub add_media {
    my $mirror = shift;
    my ($r, $da, $is_x8664, $dist_name);
    ($r) = mdkonline::get_release();
    # retrieve dist and arch from /etc/mandrakelinux file
    $da = mdkonline::get_distro_type();
    my $media_varfile = "/var/lib/urpmi/list.mdkupdate";
    my ($path2new_arch, $path2new_synthesis) = $r <= 10.0 ? ('/RPMS/', '../base/synthesis.hdlist.cz') : ('/main_updates/', 'media_info/synthesis.hdlist.cz');
    #sometimes server returns the full link http:// or ftp://
    ($is_x8664) = $da->{arch} =~ /(x86_64)/;
    $dist_name = $da->{name};
    my $fullpath2mir = if_($mirror !~ m!^(?:http|ftp)://! , "ftp://") . $mirror . if_($is_x8664, "/$is_x8664") . if_($dist_name, "/$dist_name") . "/$r" . $path2new_arch;
    eval {
	#Remove historical mdkupdate source 
	-f $media_varfile and system "/usr/sbin/urpmi.removemedia", "mdkupdate";
	system "/usr/sbin/urpmi.removemedia", "update_source";
	system "/usr/sbin/urpmi.addmedia", "--update", "update_source", $fullpath2mir, "with", $path2new_synthesis;
    };
    $@ and die "Problem adding Update Media with urpmi";
}
sub update_pkgs {
    @_ or return;
    eval {
	system "/usr/sbin/urpmi", "--auto", "--media", "update_source", map { /^(.*)\.rpm$/ && $1 } @_;
	$? == 0 or die N("Unable to update packages from update_source medium.\n");
    };
    $@ and output_p($logfile, "[mdkupdate] Error 99: $@"), return 0;
    return 1;
}
sub send_conf_update {
    my ($login, $password, $boxname) = @_;
    mdkonline::report_config("$confdir/$login.$password.$boxname.online.log");
    my $tag = { submit => 'upload_wizard', wizard => [ "$confdir/$login.$password.$boxname.online.log.bz2.uue" ] };
    my ($res, $key) = mdkonline::send_config($url->{wizard}, $tag);
    ($res, $key);
}
sub get_rpm_diff {
    my $isdif = `sdiff -s $afterrpm $currentrpm`;
    $isdif and output_p($difflog, $isdif), return 1;
    return 0;
}
sub new_rpm_base {
    -f $afterrpm and system('/bin/mv', $afterrpm, $currentrpm);
}
sub clean_dir() {
    new_rpm_base();
    mdkonline::clean_confdir();
    output_p($logfile, 'OK');
}
sub rpm_qa {
    my ($file) = @_;
    output($file, chomp_(join('', sort(`rpm -qa`))));
}