#*****************************************************************************
# 
#  Copyright (c) 2002 Guillaume Cottenceau
#  Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com>
#  Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
#  Copyright (c) 2005, 2007 Mandriva SA
# 
#  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.
# 
#*****************************************************************************
#
# $Id: rpmdrake.pm 267936 2010-04-26 16:40:21Z jvictor $

package rpmdrake;

use lib qw(/usr/lib/libDrakX);
use urpm::download ();
use urpm::prompt;
use urpm::media;

use MDK::Common;
use MDK::Common::System;
use urpm;
use urpm::cfg;
use URPM;
use URPM::Resolve;
use strict;
use c;
use POSIX qw(_exit);
use common;
use Locale::gettext;
use feature 'state';

our @ISA = qw(Exporter);
our $VERSION = '2.27';
our @EXPORT = qw(
    $changelog_first_config
    $compute_updates
    $filter
    $dont_show_selections
    $ignore_debug_media
    $mandrakeupdate_wanted_categories
    $mandrivaupdate_height
    $mandrivaupdate_width
    $max_info_in_descr
    $mode
    $NVR_searches
    $offered_to_add_sources
    $rpmdrake_height
    $rpmdrake_width
    $tree_flat
    $tree_mode
    $use_regexp
    $typical_width
    $clean_cache
    $auto_select
    add_distrib_update_media
    add_medium_and_check
    but
    but_
    check_update_media_version
    choose_mirror
    distro_type
    fatal_msg
    getbanner
    get_icon
    interactive_list
    interactive_list_
    interactive_msg
    interactive_packtable
    myexit
    readconf
    remove_wait_msg
    run_drakbug
    show_urpm_progress
    slow_func
    slow_func_statusbar
    statusbar_msg
    statusbar_msg_remove
    strip_first_underscore
    update_sources
    update_sources_check
    update_sources_interactive
    update_sources_noninteractive
    wait_msg
    warn_for_network_need
    writeconf
);
our $typical_width = 280;

our $dont_show_selections;

# i18n: IMPORTANT: to get correct namespace (rpmdrake instead of libDrakX)
BEGIN { unshift @::textdomains, qw(rpmdrake urpmi rpm-summary-main rpm-summary-contrib rpm-summary-devel rpm-summary-non-free) }

use mygtk2 qw(gtknew);
use ugtk2 qw(:all);
ugtk2::add_icon_path('/usr/share/rpmdrake/icons');

Locale::gettext::bind_textdomain_codeset('rpmdrake', 'UTF8');

our $mandrake_release = cat_(
    -e '/etc/mandrakelinux-release' ? '/etc/mandrakelinux-release' : '/etc/release'
) || '';
chomp $mandrake_release;
our ($distro_version) = $mandrake_release =~ /(\d+\.\d+)/;
our ($branded, %distrib);
$branded = -f '/etc/sysconfig/oem'
    and %distrib = MDK::Common::System::distrib();
our $myname_update = $branded ? N("Software Update") : N("Mageia Update");

@rpmdrake::prompt::ISA = 'urpm::prompt';

sub rpmdrake::prompt::prompt {
    my ($self) = @_;
    my @answers;
    my $d = ugtk2->new("", grab => 1, if_($::main_window, transient => $::main_window));
    $d->{rwindow}->set_position('center_on_parent');
    gtkadd(
	$d->{window},
	gtkpack(
	    Gtk2::VBox->new(0, 5),
	    Gtk2::WrappedLabel->new($self->{title}),
	    (map { gtkpack(
		Gtk2::HBox->new(0, 5),
		Gtk2::Label->new($self->{prompts}[$_]),
		$answers[$_] = gtkset_visibility(gtkentry(), !$self->{hidden}[$_]),
	    ) } 0 .. $#{$self->{prompts}}),
	    gtksignal_connect(Gtk2::Button->new(N("Ok")), clicked => sub { Gtk2->main_quit }),
	),
    );
    $d->main;
    map { $_->get_text } @answers;
}

$urpm::download::PROMPT_PROXY = new rpmdrake::prompt(
    N("Please enter your credentials for accessing proxy\n"),
    [ N("User name:"), N("Password:") ],
    undef,
    [ 0, 1 ],
);

sub myexit {
    writeconf();
    ugtk2::exit(undef, @_);
}

my ($root) = grep { $_->[2] == 0 } list_passwd();
$ENV{HOME} = $> == 0 ? $root->[7] : $ENV{HOME} || '/root';
$ENV{HOME} = $::env if $::env = $Rpmdrake::init::rpmdrake_options{env}[0];

our $configfile = "$ENV{HOME}/.rpmdrake";

#
# Configuration File Options
#

# clear download cache after successfull installation of packages
our $clean_cache;

# automatic select dependencies without user intervention
our $auto_select;

our ($changelog_first_config, $compute_updates, $filter, $max_info_in_descr, $mode, $NVR_searches, $tree_flat, $tree_mode, $use_regexp);
our ($mandrakeupdate_wanted_categories, $ignore_debug_media, $offered_to_add_sources, $no_confirmation);
our ($rpmdrake_height, $rpmdrake_width, $mandrivaupdate_height, $mandrivaupdate_width);

our %config = (
    clean_cache => { 
	var => \$clean_cache, 
	default => [ 0 ] 
    },
    auto_select => { 
	var => \$auto_select, 
	default => [ 0 ] 
    },
    changelog_first_config => { var => \$changelog_first_config, default => [ 0 ] },
    compute_updates => { var => \$compute_updates, default => [ 1 ] },
    dont_show_selections => { var => \$dont_show_selections, default => [ $> ? 1 : 0 ] },
    filter => { var => \$filter, default => [ 'all' ] },
    ignore_debug_media => { var => \$ignore_debug_media, default => [ 0 ] },
    mandrakeupdate_wanted_categories => { var => \$mandrakeupdate_wanted_categories, default => [ qw(security) ] },
    mandrivaupdate_height => { var => \$mandrivaupdate_height, default => [ 0 ] },
    mandrivaupdate_width => { var => \$mandrivaupdate_width, default => [ 0 ] },
    max_info_in_descr => { var => \$max_info_in_descr, default => [] },
    mode => { var => \$mode, default => [ 'by_group' ] },
    NVR_searches => { var => \$NVR_searches, default => [ 0 ] },
    'no-confirmation' => { var => \$no_confirmation, default => [ 0 ] },
    offered_to_add_sources => { var => \$offered_to_add_sources, default => [ 0 ] },
    rpmdrake_height => { var => \$rpmdrake_height, default => [ 0 ] },
    rpmdrake_width => { var => \$rpmdrake_width, default => [ 0 ] },
    tree_flat => { var => \$tree_flat, default => [ 0 ] },
    tree_mode => { var => \$tree_mode, default => [ qw(gui_pkgs) ] },
    use_regexp => { var => \$use_regexp, default => [ 0 ] },
);

sub readconf() {
    ${$config{$_}{var}} = $config{$_}{default} foreach keys %config;
    foreach my $l (cat_($configfile)) {
	foreach (keys %config) {
	    ${$config{$_}{var}} = [ split ' ', $1 ] if $l =~ /^\Q$_\E(.*)/;
	}
    }
    # special cases:
    $::rpmdrake_options{'no-confirmation'} = $no_confirmation->[0] if !defined $::rpmdrake_options{'no-confirmation'};
    $Rpmdrake::init::default_list_mode = $tree_mode->[0] if ref $tree_mode && !$Rpmdrake::init::overriding_config;
}

sub writeconf() {
    return if $::env;
    unlink $configfile;

    # special case:
    $no_confirmation->[0] = $::rpmdrake_options{'no-confirmation'};

    output($configfile, map { "$_ " . (ref ${$config{$_}{var}} ? join(' ', @${$config{$_}{var}}) : undef) . "\n" } keys %config);
}

sub getbanner() {
    $::MODE or return undef;
    if (0) {
	+{
	remove  => N("Software Packages Removal"),
	update  => N("Software Packages Update"),
	install => N("Software Packages Installation"),
	};
    }
    Gtk2::Banner->new($ugtk2::wm_icon, $::MODE eq 'update' ? N("Software Packages Update") : N("Software Management"));
}

# return value:
# - undef if if closed (aka really canceled)
# - 0 if if No/Cancel
# - 1 if if Yes/Ok
sub interactive_msg {
    my ($title, $contents, %options) = @_;
    $options{transient} ||= $::main_window if $::main_window;
    local $::isEmbedded;
    my $d = ugtk2->new($title, grab => 1, if_(exists $options{transient}, transient => $options{transient}));
    $d->{rwindow}->set_position($options{transient} ? 'center_on_parent' : 'center_always');
    if ($options{scroll}) {
        $contents = ugtk2::markup_to_TextView_format($contents) if !ref $contents;
    } else { #- because we'll use a WrappedLabel
        $contents = formatAlaTeX($contents) if !ref $contents;
    }
    my $text_w;
    my $button_yes;
    gtkadd(
	$d->{window},
	gtkpack_(
	    Gtk2::VBox->new(0, 5),
	    1,
	    (
		$options{scroll} ?
            ($text_w = create_scrolled_window(gtktext_insert(Gtk2::TextView->new, $contents)))
              : ($text_w = gtknew('WrappedLabel', text_markup => $contents))
	    ),
         if_($options{widget}, 0, $options{widget}),
	    0,
	    gtkpack(
		create_hbox(),
		(
		    ref($options{yesno}) eq 'ARRAY' ? map {
			my $label = $_;
			gtksignal_connect(
			    $button_yes = Gtk2::Button->new($label),
			    clicked => sub { $d->{retval} = $label; Gtk2->main_quit }
			);
		    } @{$options{yesno}}
		    : (
			$options{yesno} ? (
			    gtksignal_connect( 
				Gtk2::Button->new($options{text}{no} || N("No")), 
				clicked => sub { $d->{retval} = 0; Gtk2->main_quit }
			    ),
			    gtksignal_connect(
				$button_yes = Gtk2::Button->new($options{text}{yes} || N("Yes")),
				clicked => sub { $d->{retval} = 1; Gtk2->main_quit }
			    ),
			)
			: gtksignal_connect(
			    $button_yes = Gtk2::Button->new(N("Ok")),
			    clicked => sub { Gtk2->main_quit }
			)
		    )
		)
	    )
	)
    );
    $d->{window}->set_focus($button_yes);
    $text_w->set_size_request($typical_width*2, $options{scroll} ? 300 : -1);
    $d->main;
    return $d->{retval};
}

sub interactive_packtable {
    my ($title, $parent_window, $top_label, $lines, $action_buttons) = @_;
    
    my $w = ugtk2->new($title, grab => 1, transient => $parent_window);
    local $::main_window = $w->{real_window};
    $w->{rwindow}->set_position($parent_window ? 'center_on_parent' : 'center');
    my $packtable = create_packtable({}, @$lines);

    gtkadd($w->{window},
	   gtkpack_(Gtk2::VBox->new(0, 5),
		    if_($top_label, 0, Gtk2::Label->new($top_label)),
		    1, create_scrolled_window($packtable),
		    0, gtkpack__(create_hbox(), @$action_buttons)));
    my $preq = $packtable->size_request;
    my ($xpreq, $ypreq) = ($preq->width, $preq->height);
    my $wreq = $w->{rwindow}->size_request;
    my ($xwreq, $ywreq) = ($wreq->width, $wreq->height);
    $w->{rwindow}->set_default_size(max($typical_width, min($typical_width*2.5, $xpreq+$xwreq)),
 				    max(200, min(450, $ypreq+$ywreq)));
    $w->main;
}

sub interactive_list {
    my ($title, $contents, $list, $callback, %options) = @_;
    my $d = ugtk2->new($title, grab => 1, if_(exists $options{transient}, transient => $options{transient}));
    $d->{rwindow}->set_position($options{transient} ? 'center_on_parent' : 'center_always');
    my @radios = gtkradio('', @$list);
    my $vbradios = $callback ? create_packtable(
	{},
	mapn {
	    my $n = $_[1];
	    [ $_[0],
	    gtksignal_connect(
		Gtk2::Button->new(but(N("Info..."))),
		clicked => sub { $callback->($n) },
	    ) ];
	} \@radios, $list,
    ) : gtkpack__(Gtk2::VBox->new(0, 0), @radios);
    my $choice;
    my $button_ok;
    gtkadd(
	$d->{window},
	gtkpack__(
	    Gtk2::VBox->new(0,5),
	    Gtk2::Label->new($contents),
	    int(@$list) > 8 ? gtkset_size_request(create_scrolled_window($vbradios), 250, 320) : $vbradios,
	    gtkpack__(
		create_hbox(),
          if_(!$options{nocancel},
          gtksignal_connect(
		    Gtk2::Button->new(N("Cancel")), clicked => sub { Gtk2->main_quit }),
          ),
          gtksignal_connect(
		    $button_ok=Gtk2::Button->new(N("Ok")), clicked => sub {
			each_index { $_->get_active and $choice = $::i } @radios;
			Gtk2->main_quit;
		    }
		)
	    )
	)
    );
    $d->{window}->set_focus($button_ok);
    $d->main;
    $choice;
}

sub interactive_list_ { interactive_list(@_, if_($::main_window, transient => $::main_window)) }

sub fatal_msg {
    interactive_msg @_;
    myexit -1;
}

sub wait_msg {
    my ($msg, %options) = @_;
    gtkflush();
    $options{transient} ||= $::main_window if $::main_window;
    local $::isEmbedded;
    my $mainw = ugtk2->new(N("Please wait"), grab => 1, if_(exists $options{transient}, transient => $options{transient}));
    $mainw->{real_window}->set_position($options{transient} ? 'center_on_parent' : 'center_always');
    my $label = ref($msg) =~ /^Gtk/ ? $msg : Gtk2::WrappedLabel->new($msg);
    gtkadd(
	$mainw->{window},
	gtkpack__(
	    gtkset_border_width(Gtk2::VBox->new(0, 5), 6),
	    $label,
	    if_(exists $options{widgets}, @{$options{widgets}}),
	)
    );
    $mainw->sync;
    gtkset_mousecursor_wait($mainw->{rwindow}->window) unless $options{no_wait_cursor};
    $mainw->flush;
    $mainw;
}

sub remove_wait_msg {
    my $w = shift;
    gtkset_mousecursor_normal($w->{rwindow}->window);
    $w->destroy;
}

sub but { "    $_[0]    " }
sub but_ { "        $_[0]        " }

sub slow_func ($&) {
    my ($param, $func) = @_;
    if (ref($param) =~ /^Gtk/) {
	gtkset_mousecursor_wait($param);
	ugtk2::flush();
	$func->();
	gtkset_mousecursor_normal($param);
    } else {
	my $w = wait_msg($param);
	$func->();
	remove_wait_msg($w);
    }
}

sub statusbar_msg {
    unless ($::statusbar) { #- fallback if no status bar
	if (defined &::wait_msg_) { goto &::wait_msg_ } else { goto &wait_msg }
    }
    my ($msg, $o_timeout) = @_;
    #- always use the same context description for now
    my $cx = $::statusbar->get_context_id("foo");
    $::w and $::w->{rwindow} and gtkset_mousecursor_wait($::w->{rwindow}->window);
    #- returns a msg_id to be passed optionnally to statusbar_msg_remove
    my $id = $::statusbar->push($cx, $msg);
    gtkflush();
    Glib::Timeout->add(5000, sub { statusbar_msg_remove($id); 0 }) if $o_timeout;
    $id;
}

sub statusbar_msg_remove {
    my ($msg_id) = @_;
    if (!$::statusbar || ref $msg_id) { #- fallback if no status bar
	goto &remove_wait_msg;
    }
    my $cx = $::statusbar->get_context_id("foo");
    if (defined $msg_id) {
	$::statusbar->remove($cx, $msg_id);
    } else {
	$::statusbar->pop($cx);
    }
    $::w and $::w->{rwindow} and gtkset_mousecursor_normal($::w->{rwindow}->window);
}

sub slow_func_statusbar ($$&) {
    my ($msg, $w, $func) = @_;
    gtkset_mousecursor_wait($w->window);
    my $msg_id = statusbar_msg($msg);
    gtkflush();
    $func->();
    statusbar_msg_remove($msg_id);
    gtkset_mousecursor_normal($w->window);
}

my %u2l = (
	   at => N_("Austria"),
	   au => N_("Australia"),
	   be => N_("Belgium"),
	   br => N_("Brazil"),
	   ca => N_("Canada"),
	   ch => N_("Switzerland"),
	   cr => N_("Costa Rica"),
	   cz => N_("Czech Republic"),
	   de => N_("Germany"),
	   dk => N_("Danmark"),
	   el => N_("Greece"),
	   es => N_("Spain"),
	   fi => N_("Finland"),
	   fr => N_("France"),
	   gr => N_("Greece"),
	   hu => N_("Hungary"),
	   il => N_("Israel"),
	   it => N_("Italy"),
	   jp => N_("Japan"),
	   ko => N_("Korea"),
	   nl => N_("Netherlands"),
	   no => N_("Norway"),
	   pl => N_("Poland"),
	   pt => N_("Portugal"),
	   ru => N_("Russia"),
	   se => N_("Sweden"),
	   sg => N_("Singapore"),
	   sk => N_("Slovakia"),
	   tw => N_("Taiwan"),
	   uk => N_("United Kingdom"),
	   cn => N_("China"),
	   com => N_("United States"),
	   org => N_("United States"),
	   net => N_("United States"),
	   edu => N_("United States"),
	  );
my $us = [ qw(com org net edu) ];
my %t2l = (
	   'America/\w+' =>       $us,
	   'Asia/Tel_Aviv' =>     [ qw(il ru it cz at de fr se) ],
	   'Asia/Tokyo' =>        [ qw(jp ko tw), @$us ],
	   'Asia/Seoul' =>        [ qw(ko jp tw), @$us ],
	   'Asia/Taipei' =>       [ qw(tw jp), @$us ],
	   'Asia/(Shanghai|Beijing)' => [ qw(cn tw sg), @$us ],
	   'Asia/Singapore' =>    [ qw(cn sg), @$us ],
	   'Atlantic/Reykjavik' => [ qw(uk no se fi dk), @$us, qw(nl de fr at cz it) ],
	   'Australia/\w+' =>     [ qw(au jp ko tw), @$us ],
	   'Brazil/\w+' =>        [ 'br', @$us ],
	   'Canada/\w+' =>        [ 'ca', @$us ],
	   'Europe/Amsterdam' =>  [ qw(nl be de at cz fr se dk it) ],
	   'Europe/Athens' =>     [ qw(gr pl cz de it nl at fr) ],
	   'Europe/Berlin' =>     [ qw(de be at nl cz it fr se) ],
	   'Europe/Brussels' =>   [ qw(be de nl fr cz at it se) ],
	   'Europe/Budapest' =>   [ qw(cz it at de fr nl se) ],
	   'Europe/Copenhagen' => [ qw(dk nl de be se at cz it) ],
	   'Europe/Dublin' =>     [ qw(uk fr be nl dk se cz it) ],
	   'Europe/Helsinki' =>   [ qw(fi se no nl be de fr at it) ],
	   'Europe/Istanbul' =>   [ qw(il ru it cz it at de fr nl se) ],
	   'Europe/Lisbon' =>     [ qw(pt es fr it cz at de se) ],
	   'Europe/London' =>     [ qw(uk fr be nl de at cz se it) ],
	   'Europe/Madrid' =>     [ qw(es fr pt it cz at de se) ],
	   'Europe/Moscow' =>     [ qw(ru de pl cz at se be fr it) ],
	   'Europe/Oslo' =>       [ qw(no se fi dk de be at cz it) ],
	   'Europe/Paris' =>      [ qw(fr be de at cz nl it se) ],
	   'Europe/Prague' =>     [ qw(cz it at de fr nl se) ],
	   'Europe/Rome' =>       [ qw(it fr cz de at nl se) ],
	   'Europe/Stockholm' =>  [ qw(se no dk fi nl de at cz fr it) ],
	   'Europe/Vienna' =>     [ qw(at de cz it fr nl se) ],
	  );

#- get distrib release number (2006.0, etc)
sub etc_version() {
    (my $v) = split / /, cat_('/etc/version');
    return $v;
}

#- returns the keyword describing the type of the distribution.
#- the parameter indicates whether we want base or update sources
sub distro_type {
    my ($want_base_distro) = @_;
    return 'cooker' if $mandrake_release =~ /cooker/i;
    #- we can't use updates for community while official is not out (release ends in ".0")
    if ($want_base_distro || $mandrake_release =~ /community/i && etc_version() =~ /\.0$/) {
	return 'official' if $mandrake_release =~ /official|limited/i;
	return 'community' if $mandrake_release =~ /community/i;
	#- unknown: fallback to updates
    }
    return 'updates';
}

sub compat_arch_for_updates($) {
    # FIXME: We prefer 64-bit packages to update on biarch platforms,
    # since the system is populated with 64-bit packages anyway.
    my ($arch) = @_;
    return $arch =~ /x86_64|amd64/ if arch() eq 'x86_64';
    MDK::Common::System::compat_arch($arch);
}

sub mirrors {
    my ($urpm, $want_base_distro) = @_;
    my $cachedir = $urpm->{cachedir} || '/root';
    require mirror;
    mirror::register_downloader(
        sub {
            my ($url) = @_;
            my $file = $url;
            $file =~ s!.*/!$cachedir/!;
            unlink $file;       # prevent "partial file" errors
            before_leaving(sub { unlink $file });

            my ($gurpm, $id, $canceled);
            # display a message in statusbar (if availlable):
            $::statusbar and $id = statusbar_msg(
                $branded
                  ? N("Please wait, downloading mirror addresses.")
                    : N("Please wait, downloading mirror addresses from the Mageia website."),
                0);
            my $_clean_guard = before_leaving {
                undef $gurpm;
                $id and statusbar_msg_remove($id);
            };

            require Rpmdrake::gurpm;
            require Rpmdrake::pkg;

            my $res = urpm::download::sync_url($urpm, $url,
                                           dir => $cachedir,
                                           callback => sub {
                                               $gurpm ||= 
                                                 Rpmdrake::gurpm->new(N("Please wait"),
                                                                      transient => $::main_window);
                                               $canceled ||=
                                                 !Rpmdrake::pkg::download_callback($gurpm, @_);
                                               gtkflush();
                                           },
                                       );
            $res or die N("retrieval of [%s] failed", $file) . "\n";
            return $canceled ? () : cat_($file);
        });
    my @mirrors = @{ mirror::list(common::parse_LDAP_namespace_structure(cat_('/etc/product.id')), 'distrib') || [] };
    require timezone;
    my $tz = ${timezone::read()}{timezone};
    foreach my $mirror (@mirrors) {
	    my $goodness;
	    each_index { $_ = $u2l{$_} || $_; $_ eq $mirror->{country} and $goodness ||= 100-$::i } (map { if_($tz =~ /^$_$/, @{$t2l{$_}}) } keys %t2l), @$us;
         $mirror->{goodness} = $goodness + rand();
         $mirror->{country} = translate($mirror->{country});
    }
    unless (-x '/usr/bin/rsync') {
	@mirrors = grep { $_->{url} !~ /^rsync:/ } @mirrors;
    }
    return sort { $b->{goodness} <=> $a->{goodness} } @mirrors;
}

sub warn_for_network_need {
    my ($message, %options) = @_;
    $message ||= 
$branded
? N("I need to access internet to get the mirror list.
Please check that your network is currently running.

Is it ok to continue?")
: N("I need to contact the Mageia website to get the mirror list.
Please check that your network is currently running.

Is it ok to continue?");
    interactive_msg(N("Mirror choice"), $message, yesno => 1, %options) or return '';
}

sub choose_mirror {
    my ($urpm, %options) = @_;
    delete $options{message};
    my @transient_options = exists $options{transient} ? (transient => $options{transient}) : ();
    warn_for_network_need($options{message}, %options) or return;
    my @mirrors = eval { mirrors($urpm, $options{want_base_distro}) };
    my $error = $@;
    if ($error) {
        $error = "\n$error\n";
	interactive_msg(N("Error during download"),
($branded
? N("There was an error downloading the mirror list:

%s
The network, or the website, may be unavailable.
Please try again later.", $error)
: N("There was an error downloading the mirror list:

%s
The network, or the Mageia website, may be unavailable.
Please try again later.", $error)), %options

	);
	return '';
    }

    !@mirrors and interactive_msg(N("No mirror"),
($branded
? N("I can't find any suitable mirror.")
: N("I can't find any suitable mirror.

There can be many reasons for this problem; the most frequent is
the case when the architecture of your processor is not supported
by Mageia Official Updates.")), %options
    ), return '';

    my $w = ugtk2->new(N("Mirror choice"), grab => 1, @transient_options);
    $w->{rwindow}->set_position($options{transient} ? 'center_on_parent' : 'center_always');
    my $tree_model = Gtk2::TreeStore->new("Glib::String");
    my $tree = Gtk2::TreeView->new_with_model($tree_model);
    $tree->get_selection->set_mode('browse');
    $tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, text => 0));
    $tree->set_headers_visible(0);

    gtkadd(
	$w->{window}, 
	gtkpack_(
	    Gtk2::VBox->new(0,5),
	    0, N("Please choose the desired mirror."),
	    1, create_scrolled_window($tree),
	    0, gtkpack(
		create_hbox('edge'),
		map {
		    my $retv = $_->[1];
		    gtksignal_connect(
			Gtk2::Button->new(but($_->[0])),
			clicked => sub {
			    if ($retv) {
				my ($model, $iter) = $tree->get_selection->get_selected;
				$model and $w->{retval} = { sel => $model->get($iter, 0) };
			    }
			    Gtk2->main_quit;
			},
		    );
		} [ N("Cancel"), 0 ], [ N("Ok"), 1 ]
	    ),
	)
    );
    my %roots;
    $tree_model->append_set($roots{$_->{country}} ||= $tree_model->append_set(undef, [ 0 => $_->{country} ]),
			    [ 0 => $_->{url} ]) foreach @mirrors;

    $w->{window}->set_size_request(500, 400);
    $w->{rwindow}->show_all;

    my $path = Gtk2::TreePath->new_first;
    $tree->expand_row($path, 0);
    $path->down;
    $tree->get_selection->select_path($path);

    $w->main && return grep { $w->{retval}{sel} eq $_->{url} } @mirrors;
}

sub show_urpm_progress {
    my ($label, $pb, $mode, $file, $percent, $total, $eta, $speed) = @_;
    $file =~ s|([^:]*://[^/:\@]*:)[^/:\@]*(\@.*)|$1xxxx$2|; #- if needed...
    state $medium;
    if ($mode eq 'copy') {
	$pb->set_fraction(0);
	$label->set_label(N("Copying file for medium `%s'...", $file));
    } elsif ($mode eq 'parse') {
	$pb->set_fraction(0);
	$label->set_label(N("Examining file of medium `%s'...", $file));
    } elsif ($mode eq 'retrieve') {
	$pb->set_fraction(0);
	$label->set_label(N("Examining remote file of medium `%s'...", $file));
        $medium = $file;
    } elsif ($mode eq 'done') {
	$pb->set_fraction(1.0);
	$label->set_label($label->get_label . N(" done."));
        $medium = undef;
    } elsif ($mode eq 'failed') {
	$pb->set_fraction(1.0);
	$label->set_label($label->get_label . N(" failed!"));
        $medium = undef;
    } else {
        # FIXME: we're displaying misplaced quotes such as "downloading `foobar from 'medium Main Updates'ยด"
        $file = $medium && length($file) < 40 ? #-PO: We're downloading the said file from the said medium
                                                 N("%s from medium %s", basename($file), $medium)
                                               : basename($file);
        if ($mode eq 'start') {
            $pb->set_fraction(0);
            $label->set_label(N("Starting download of `%s'...", $file));
        } elsif ($mode eq 'progress') {
            if (defined $total && defined $eta) {
                $pb->set_fraction($percent/100);
                $label->set_label(N("Download of `%s'\ntime to go:%s, speed:%s", $file, $eta, $speed));
            } else {
                $pb->set_fraction($percent/100);
                $label->set_label(N("Download of `%s'\nspeed:%s", $file, $speed));
            }
        }
    }
    Gtk2->main_iteration while Gtk2->events_pending;
}

sub update_sources {
    my ($urpm, %options) = @_;
    my $cancel = 0;
    my $w; my $label; $w = wait_msg(
	$label = Gtk2::Label->new(N("Please wait, updating media...")),
	no_wait_cursor => 1,
	widgets => [
	    my $pb = gtkset_size_request(Gtk2::ProgressBar->new, 300, -1),
	    gtkpack(
		create_hbox(),
		gtksignal_connect(
		    Gtk2::Button->new(N("Cancel")),
		    clicked => sub {
			$cancel = 1;
                        $urpm->{error}->(N("Canceled"));
			$w and $w->destroy;
		    },
		),
	    ),
	],
    );
    my @media; @media = @{$options{medialist}} if ref $options{medialist};
    my $outerfatal = $urpm->{fatal};
    local $urpm->{fatal} = sub { $w->destroy; $outerfatal->(@_) };
    urpm::media::update_those_media($urpm, [ urpm::media::select_media_by_name($urpm, \@media) ],
	%options, allow_failures => 1,
	callback => sub {
	    $cancel and goto cancel_update;
	    my ($type, $media) = @_;
	    return if $type !~ /^(?:start|progress|end)$/ && @media && !member($media, @media);
	    if ($type eq 'failed') {
		$urpm->{fatal}->(N("Error retrieving packages"),
N("It's impossible to retrieve the list of new packages from the media
`%s'. Either this update media is misconfigured, and in this case
you should use the Software Media Manager to remove it and re-add it in order
to reconfigure it, either it is currently unreachable and you should retry
later.",
    $media));
	    } else {
		show_urpm_progress($label, $pb, @_);
	    }
	},
    );
    $w->destroy;
  cancel_update:
}

sub update_sources_check {
    my ($urpm, $options, $error_msg, @media) = @_;
    my @error_msgs;
    local $urpm->{fatal} = sub { push @error_msgs, $_[1]; goto fatal_error };
    local $urpm->{error} = sub { push @error_msgs, $_[0] };
    update_sources($urpm, %$options, noclean => 1, medialist => \@media);
  fatal_error:
    if (@error_msgs) {
        interactive_msg(N("Error"), sprintf(translate($error_msg), join("\n", map { formatAlaTeX($_) } @error_msgs)), scroll => 1);
        return 0;
    }
    return 1;
}

sub update_sources_interactive {
    my ($urpm, %options) = @_;
    my $w = ugtk2->new(N("Update media"), grab => 1, center => 1, %options);
    $w->{rwindow}->set_position($options{transient} ? 'center_on_parent' : 'center_always');
    my @buttons;
    my @media = grep { ! $_->{ignore} } @{$urpm->{media}};
    unless (@media) {
        interactive_msg(N("Warning"), N("No active medium found. You must enable some media to be able to update them."));
	return 0;
    }
    gtkadd(
	$w->{window},
	gtkpack_(
	    0, Gtk2::VBox->new(0,5),
	    0, Gtk2::Label->new(N("Select the media you wish to update:")),
            1, gtknew('ScrolledWindow', height => 300, child =>
                     # FIXME: using a listview would be just better:
                     gtknew('VBox', spacing => 5, children_tight => [
                         @buttons = map {
                             Gtk2::CheckButton->new_with_label($_->{name});
                         } @media
                     ])
	    ),
	    0, Gtk2::HSeparator->new,
	    0, gtkpack(
		create_hbox(),
		gtksignal_connect(
		    Gtk2::Button->new(N("Cancel")),
		    clicked => sub { $w->{retval} = 0; Gtk2->main_quit },
		),
		gtksignal_connect(
		    Gtk2::Button->new(N("Select all")),
		    clicked => sub { $_->set_active(1) foreach @buttons },
		),
		gtksignal_connect(
		    Gtk2::Button->new(N("Update")),
		    clicked => sub {
			$w->{retval} = any { $_->get_active } @buttons;
			# list of media listed in the checkbox panel
			my @buttonmedia = grep { !$_->{ignore} } @{$urpm->{media}};
			@media = map_index { if_($_->get_active, $buttonmedia[$::i]{name}) } @buttons;
			Gtk2->main_quit;
		    },
		),
	    )
	)
    );
    if ($w->main) {
        return update_sources_noninteractive($urpm, \@media, %options);
    }
    return 0;
}

sub update_sources_noninteractive {
    my ($urpm, $media, %options) = @_;

        urpm::media::select_media($urpm, @$media);
        update_sources_check(
	    $urpm,
	    {},
	    N_("Unable to update medium; it will be automatically disabled.\n\nErrors:\n%s"),
	    @$media,
	);
	return 1;
}

sub add_medium_and_check {
    my ($urpm, $options) = splice @_, 0, 2;
    my @newnames = ($_[0]); #- names of added media
    my $fatal_msg;
    my @error_msgs;
    local $urpm->{fatal} = sub { printf STDERR "Fatal: %s\n", $_[1]; $fatal_msg = $_[1]; goto fatal_error };
    local $urpm->{error} = sub { printf STDERR "Error: %s\n", $_[0]; push @error_msgs, $_[0] };
    if ($options->{distrib}) {
	@newnames = urpm::media::add_distrib_media($urpm, @_);
    } else {
	urpm::media::add_medium($urpm, @_);
    }
    if (@error_msgs) {
        interactive_msg(
	    N("Error"),
	    N("Unable to add medium, errors reported:\n\n%s",
	    join("\n", map { formatAlaTeX($_) } @error_msgs)) . "\n\n" . N("Medium: ") . "$_[0] ($_[1])",
	    scroll => 1,
	);
        return 0;
    }

    foreach my $name (@newnames) {
	urpm::download::set_proxy_config($_, $options->{proxy}{$_}, $name) foreach keys %{$options->{proxy} || {}};
    }

    if (update_sources_check($urpm, $options, N_("Unable to add medium, errors reported:\n\n%s"), @newnames)) {
        urpm::media::write_config($urpm);
	$options->{proxy} and urpm::download::dump_proxy_config();
    } else {
	urpm::media::read_config($urpm, 0);
        return 0;
    }

    my %newnames; @newnames{@newnames} = ();
    if (any { exists $newnames{$_->{name}} } @{$urpm->{media}}) {
        return 1;
    } else {
        interactive_msg(N("Error"), N("Unable to create medium."));
        return 0;
    }

  fatal_error:
    interactive_msg(N("Failure when adding medium"),
                    N("There was a problem adding medium:\n\n%s", $fatal_msg));
    return 0;
}

#- Check whether the default update media (added by installation)
#- matches the current mdk version
sub check_update_media_version {
    my $urpm = shift;
    foreach (@_) {
	if ($_->{name} =~ /(\d+\.\d+).*\bftp\du\b/ && $1 ne $distro_version) {
	    interactive_msg(
		N("Warning"),
		$branded
		? N("Your medium `%s', used for updates, does not match the version of %s you're running (%s).
It will be disabled.",
		    $_->{name}, $distrib{system}, $distrib{product})
		: N("Your medium `%s', used for updates, does not match the version of Mageia you're running (%s).
It will be disabled.",
		    $_->{name}, $distro_version)
	    );
	    $_->{ignore} = 1;
	    urpm::media::write_config($urpm) if -w $urpm->{config};
	    return 0;
	}
    }
    1;
}

sub add_distrib_update_media {
    my ($urpm, $mirror, %options) = @_;
    #- ensure a unique medium name
    my $medium_name = $rpmdrake::mandrake_release =~ /(\d+\.\d+) \((\w+)\)/ ? $2 . $1 . '-' : 'distrib';
    my $initial_number = 1 + max map { $_->{name} =~ /\(\Q$medium_name\E(\d+)\b/ ? $1 : 0 } @{$urpm->{media}};
    add_medium_and_check(
        $urpm,
        { nolock => 1, distrib => 1 },
        $medium_name,
        ($mirror ? $mirror->{url} : (undef, mirrorlist => '$MIRRORLIST')),
        probe_with => 'synthesis', initial_number => $initial_number, %options, 
        usedistrib => 1,
    );
}

sub open_help {
    my ($mode) = @_;
    use run_program;
    run_program::raw({ detach => 1, as_user => 1 },  'drakhelp', '--id', $mode ?  "software-management-$mode" : 'software-management');
    my $_s = N("Help launched in background");
    statusbar_msg(N("The help window has been started, it should appear shortly on your desktop."), 1);
}

sub run_drakbug {
    my ($id) = @_;
    run_program::raw({ detach => 1, as_user => 1 }, 'drakbug', '--report', $id);
}

mygtk2::add_icon_path('/usr/share/mcc/themes/default/');
sub get_icon {
    my ($mcc_icon, $fallback_icon) = @_;
    my $icon = eval { mygtk2::_find_imgfile($mcc_icon) };
    $icon ||= eval { mygtk2::_find_imgfile($fallback_icon) };
    $icon;
}

sub strip_first_underscore { join '', map { s/_//; $_ } @_ }

1;