aboutsummaryrefslogtreecommitdiffstats
path: root/rpmdrake.pm
diff options
context:
space:
mode:
Diffstat (limited to 'rpmdrake.pm')
-rw-r--r--rpmdrake.pm997
1 files changed, 997 insertions, 0 deletions
diff --git a/rpmdrake.pm b/rpmdrake.pm
new file mode 100644
index 00000000..a6180a39
--- /dev/null
+++ b/rpmdrake.pm
@@ -0,0 +1,997 @@
+#*****************************************************************************
+#
+# 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 ($mdk_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}}) : ()) . "\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);
+ 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 $mdk_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}, $mdk_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');
+ 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;