From 7ff32d873460c70bc158a8cc38c04132926e5791 Mon Sep 17 00:00:00 2001 From: Matteo Pasotti Date: Mon, 7 Jan 2013 17:17:05 +0000 Subject: - imported rpmdragora module (still under development) --- AdminPanel/Rpmdragora/.perl_checker | 1 + AdminPanel/Rpmdragora/edit_urpm_sources.pm | 1215 ++++++++++++++++++++++++++++ AdminPanel/Rpmdragora/formatting.pm | 177 ++++ AdminPanel/Rpmdragora/gui.pm | 1105 +++++++++++++++++++++++++ AdminPanel/Rpmdragora/gurpm.pm | 133 +++ AdminPanel/Rpmdragora/icon.pm | 231 ++++++ AdminPanel/Rpmdragora/init.pm | 168 ++++ AdminPanel/Rpmdragora/open_db.pm | 161 ++++ AdminPanel/Rpmdragora/pkg.pm | 990 +++++++++++++++++++++++ AdminPanel/Rpmdragora/rpmnew.pm | 204 +++++ AdminPanel/Rpmdragora/widgets.pm | 51 ++ AdminPanel/Shared.pm | 4 +- AdminPanel/rpmdragora.pm | 989 ++++++++++++++++++++++ 13 files changed, 5427 insertions(+), 2 deletions(-) create mode 100644 AdminPanel/Rpmdragora/.perl_checker create mode 100644 AdminPanel/Rpmdragora/edit_urpm_sources.pm create mode 100644 AdminPanel/Rpmdragora/formatting.pm create mode 100644 AdminPanel/Rpmdragora/gui.pm create mode 100644 AdminPanel/Rpmdragora/gurpm.pm create mode 100644 AdminPanel/Rpmdragora/icon.pm create mode 100644 AdminPanel/Rpmdragora/init.pm create mode 100644 AdminPanel/Rpmdragora/open_db.pm create mode 100644 AdminPanel/Rpmdragora/pkg.pm create mode 100644 AdminPanel/Rpmdragora/rpmnew.pm create mode 100644 AdminPanel/Rpmdragora/widgets.pm create mode 100644 AdminPanel/rpmdragora.pm (limited to 'AdminPanel') diff --git a/AdminPanel/Rpmdragora/.perl_checker b/AdminPanel/Rpmdragora/.perl_checker new file mode 100644 index 0000000..202e053 --- /dev/null +++ b/AdminPanel/Rpmdragora/.perl_checker @@ -0,0 +1 @@ +Basedir .. diff --git a/AdminPanel/Rpmdragora/edit_urpm_sources.pm b/AdminPanel/Rpmdragora/edit_urpm_sources.pm new file mode 100644 index 0000000..7f8a254 --- /dev/null +++ b/AdminPanel/Rpmdragora/edit_urpm_sources.pm @@ -0,0 +1,1215 @@ +package AdminPanel::Rpmdragora::edit_urpm_sources; +#***************************************************************************** +# +# Copyright (c) 2002 Guillaume Cottenceau +# Copyright (c) 2002-2007 Thierry Vignaud +# Copyright (c) 2002-2007 Mandriva Linux +# +# 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: edit_urpm_sources.pm 266598 2010-03-03 12:00:58Z tv $ + + +use strict; +use lib qw(/usr/lib/libDrakX); +use common; +use AdminPanel::rpmdragora; +use AdminPanel::Rpmdragora::open_db; +use AdminPanel::Rpmdragora::formatting; +use URPM::Signature; +use MDK::Common::Math qw(max); +use urpm::media; +use urpm::download; +use urpm::lock; + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(run); + +#use mygtk2 qw(gtknew gtkset); +#use ugtk2 qw(:all); + +my $urpm; +my ($mainw, $list_tv, $something_changed); + +my %col = ( + mainw => { + is_enabled => 0, + is_update => 1, + type => 2, + name => 3, + activatable => 4 + }, +); + + +sub get_medium_type { + my ($medium) = @_; + my %medium_type = ( + cdrom => N("CD-ROM"), + ftp => N("FTP"), + file => N("Local"), + http => N("HTTP"), + https => N("HTTPS"), + nfs => N("NFS"), + removable => N("Removable"), + rsync => N("rsync"), + ssh => N("NFS"), + ); + return N("Mirror list") if $medium->{mirrorlist}; + return $medium_type{$1} if $medium->{url} =~ m!^([^:]*)://!; + return N("Local"); +} + +sub selrow { + my ($o_list_tv) = @_; + defined $o_list_tv or $o_list_tv = $list_tv; + my ($model, $iter) = $o_list_tv->get_selection->get_selected; + $model && $iter or return -1; + my $path = $model->get_path($iter); + my $row = $path->to_string; + return $row; +} + +sub selected_rows { + my ($o_list_tv) = @_; + defined $o_list_tv or $o_list_tv = $list_tv; + my (@rows) = $o_list_tv->get_selection->get_selected_rows; + return -1 if @rows == 0; + map { $_->to_string } @rows; +} + +sub remove_row { + my ($model, $path_str) = @_; + my $iter = $model->get_iter_from_string($path_str); + $iter or return; + $model->remove($iter); +} + +sub remove_from_list { + my ($list, $list_ref, $model) = @_; + my $row = selrow($list); + if ($row != -1) { + splice @$list_ref, $row, 1; + remove_row($model, $row); + } + +} + +sub _want_base_distro() { + distro_type(0) eq 'updates' ? interactive_msg( + N("Choose media type"), +N("In order to keep your system secure and stable, you must at a minimum set up +sources for official security and stability updates. You can also choose to set +up a fuller set of sources which includes the complete official Mageia +repositories, giving you access to more software than can fit on the Mageia +discs. Please choose whether to configure update sources only, or the full set +of sources."), + transient => $::main_window, + yesno => 1, text => { yes => N("Full set of sources"), no => N("Update sources only") }, + ) : 1; +} + +sub easy_add_callback_with_mirror() { + # when called on early init by rpmdragora + $urpm ||= fast_open_urpmi_db(); + + #- cooker and community don't have update sources + my $want_base_distro = _want_base_distro(); + defined $want_base_distro or return; + my $distro = $rpmdragora::mandrake_release; + my ($mirror) = choose_mirror($urpm, message => +N("This will attempt to install all official sources corresponding to your +distribution (%s). + +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?", $distro), + transient => $::main_window, + ) or return 0; + ref $mirror or return; + my $wait = wait_msg(N("Please wait, adding media...")); + add_distrib_update_media($urpm, $mirror, if_(!$want_base_distro, only_updates => 1)); + $offered_to_add_sources->[0] = 1; + remove_wait_msg($wait); + return 1; +} + +sub easy_add_callback() { + # when called on early init by rpmdragora + $urpm ||= fast_open_urpmi_db(); + + #- cooker and community don't have update sources + my $want_base_distro = _want_base_distro(); + defined $want_base_distro or return; + warn_for_network_need(undef, transient => $::main_window) or return; + my $wait = wait_msg(N("Please wait, adding media...")); + add_distrib_update_media($urpm, undef, if_(!$want_base_distro, only_updates => 1)); + $offered_to_add_sources->[0] = 1; + remove_wait_msg($wait); + return 1; +} + +sub add_callback() { + my $w = ugtk2->new(N("Add a medium"), grab => 1, center => 1, transient => $::main_window); + my $prev_main_window = $::main_window; + local $::main_window = $w->{real_window}; + my %radios_infos = ( + local => { name => N("Local files"), url => N("Medium path:"), dirsel => 1 }, + ftp => { name => N("FTP server"), url => N("URL:"), loginpass => 1 }, + rsync => { name => N("RSYNC server"), url => N("URL:") }, + http => { name => N("HTTP server"), url => N("URL:") }, + removable => { name => N("Removable device (CD-ROM, DVD, ...)"), url => N("Path or mount point:"), dirsel => 1 }, + ); + my @radios_names_ordered = qw(local ftp rsync http removable); + # TODO: replace NoteBook by sensitive widgets and Label->set() + my $notebook = gtknew('Notebook'); + $notebook->set_show_tabs(0); $notebook->set_show_border(0); + my ($count_nbs, %pages); + my $size_group = Gtk2::SizeGroup->new('horizontal'); + my ($cb1, $cb2); + foreach (@radios_names_ordered) { + my $info = $radios_infos{$_}; + my $url_entry = sub { + gtkpack_( + gtknew('HBox'), + 1, $info->{url_entry} = gtkentry(), + if_( + $info->{dirsel}, + 0, gtksignal_connect( + gtknew('Button', text => but(N("Browse..."))), + clicked => sub { $info->{url_entry}->set_text(ask_dir()) }, + ) + ), + ); + }; + my $checkbut_entry = sub { + my ($name, $label, $visibility, $callback, $tip) = @_; + my $w = [ gtksignal_connect( + $info->{$name . '_check'} = gtkset(gtknew('CheckButton', text => $label), tip => $tip), + clicked => sub { + $info->{$name . '_entry'}->set_sensitive($_[0]->get_active); + $callback and $callback->(@_); + }, + ), + gtkset_visibility(gtkset_sensitive($info->{$name . '_entry'} = gtkentry(), 0), $visibility) ]; + $size_group->add_widget($info->{$name . '_check'}); + $w; + }; + my $loginpass_entries = sub { + map { + $checkbut_entry->( + @$_, sub { + $info->{pass_check}->set_active($_[0]->get_active); + $info->{login_check}->set_active($_[0]->get_active); + } + ); + } ([ 'login', N("Login:"), 1 ], [ 'pass', N("Password:"), 0 ]); + }; + $pages{$info->{name}} = $count_nbs++; + $notebook->append_page( + gtkshow(create_packtable( + { xpadding => 0, ypadding => 0 }, + [ gtkset_alignment(gtknew('Label', text => N("Medium name:")), 0, 0.5), + $info->{name_entry} = gtkentry('') ], + [ gtkset_alignment(gtknew('Label', text => $info->{url}), 0, 0.5), + $url_entry->() ], + if_($info->{loginpass}, $loginpass_entries->()), + sub { + [ $info->{distrib_check} = $cb1 = gtknew('CheckButton', text => N("Create media for a whole distribution"), + toggled => sub { + return if !$cb2; + my ($w) = @_; + $info->{update_check}->set_sensitive(!$w->get_active); + }) + ]; + }->(), + sub { + [ $info->{update_check} = $cb2 = gtknew('CheckButton', text => N("Tag this medium as an update medium")) ]; + }->(), + )) + ); + } + $size_group->add_widget($_) foreach $cb1, $cb2; + + my $checkok = sub { + my $info = $radios_infos{$radios_names_ordered[$notebook->get_current_page]}; + my ($name, $url) = map { $info->{$_ . '_entry'}->get_text } qw(name url); + $name eq '' || $url eq '' and interactive_msg('rpmdragora', N("You need to fill up at least the two first entries.")), return 0; + if (member($name, map { $_->{name} } @{$urpm->{media}})) { + $info->{name_entry}->select_region(0, -1); + interactive_msg('rpmdragora', +N("There is already a medium by that name, do you +really want to replace it?"), yesno => 1) or return 0; + } + 1; + }; + + my $type = 'local'; + my (%i, %make_url); + gtkadd( + $w->{window}, + gtkpack( + gtknew('VBox', spacing => 5), + gtknew('Title2', label => N("Adding a medium:")), + gtknew('HBox', children_tight => [ + Gtk2::Label->new(but(N("Type of medium:"))), + gtknew('ComboBox', text_ref => \$type, + list => \@radios_names_ordered, + format => sub { $radios_infos{$_[0]}{name} }, + changed => sub { $notebook->set_current_page($pages{$_[0]->get_text}) }) + ]), + $notebook, + gtknew('HSeparator'), + gtkpack( + gtknew('HButtonBox'), + gtknew('Button', text => N("Cancel"), clicked => sub { $w->{retval} = 0; Gtk2->main_quit }), + gtksignal_connect( + gtknew('Button', text => N("Ok")), clicked => sub { + if ($checkok->()) { + $w->{retval} = { nb => $notebook->get_current_page }; + my $info = $radios_infos{$type}; + %i = ( + name => $info->{name_entry}->get_text, + url => $info->{url_entry}->get_text, + distrib => $info->{distrib_check} ? $info->{distrib_check}->get_active : 0, + update => $info->{update_check}->get_active ? 1 : undef, + ); + %make_url = ( + local => "file:/$i{url}", + http => $i{url}, + rsync => $i{url}, + removable => "removable:/$i{url}", + ); + $i{url} =~ s|^ftp://||; + $make_url{ftp} = sprintf "ftp://%s%s", + $info->{login_check}->get_active + ? ($info->{login_entry}->get_text . ':' . $info->{pass_entry}->get_text . '@') + : '', + $i{url}; + Gtk2->main_quit; + } + }, + ), + ), + ), + ); + + if ($w->main) { + $::main_window = $prev_main_window; + if ($i{distrib}) { + add_medium_and_check( + $urpm, + { nolock => 1, distrib => 1 }, + $i{name}, $make_url{$type}, probe_with => 'synthesis', update => $i{update}, + ); + } else { + if (member($i{name}, map { $_->{name} } @{$urpm->{media}})) { + urpm::media::select_media($urpm, $i{name}); + urpm::media::remove_selected_media($urpm); + } + add_medium_and_check( + $urpm, + { nolock => 1 }, + $i{name}, $make_url{$type}, $i{hdlist}, update => $i{update}, + ); + } + return 1; + } + return 0; +} + +sub options_callback() { + my $w = ugtk2->new(N("Global options for package installation"), grab => 1, center => 1, transient => $::main_window); + local $::main_window = $w->{real_window}; + my %verif = (0 => N("never"), 1 => N("always")); + my $verify_rpm = $urpm->{global_config}{'verify-rpm'}; + my @avail_downloaders = urpm::download::available_ftp_http_downloaders(); + my $downloader = $urpm->{global_config}{downloader} || $avail_downloaders[0]; + my %xml_info_policies = ( + 'never' => N("Never"), + 'on-demand' => N("On-demand"), + 'update-only' => N("Update-only"), + 'always' => N("Always"), + ); + my $xml_info_policy = $urpm->{global_config}{'xml-info'}; + + gtkadd( + $w->{window}, + gtkpack( + gtknew('VBox', spacing => 5), + gtknew('HBox', children_loose => [ gtknew('Label', text => N("Verify RPMs to be installed:")), + gtknew('ComboBox', list => [ keys %verif ], text_ref => \$verify_rpm, + format => sub { $verif{$_[0]} || $_[0] }, + ) + ]), + gtknew('HBox', children_loose => [ gtknew('Label', text => N("Download program to use:")), + gtknew('ComboBox', list => \@avail_downloaders, text_ref => \$downloader, + format => sub { $verif{$_[0]} || $_[0] }, + ) + ]), + gtknew('HBox', + children_loose => + [ gtknew('Label', text => N("XML meta-data download policy:")), + gtknew('ComboBox', + list => [ keys %xml_info_policies ], text_ref => \$xml_info_policy, + + format => sub { $xml_info_policies{$_[0]} || $_[0] }, + tip => + join("\n", + N("For remote media, specify when XML meta-data (file lists, changelogs & information) are downloaded."), + '', + N("Never"), + N("For remote media, XML meta-data are never downloaded."), + '', + N("On-demand"), + N("(This is the default)"), + N("The specific XML info file is downloaded when clicking on package."), + '', + N("Update-only"), + N("Updating media implies updating XML info files already required at least once."), + '', + N("Always"), + N("All XML info files are downloaded when adding or updating media."), + ), + ), + ]), + + gtkpack( + gtknew('HButtonBox'), + gtknew('Button', text => N("Cancel"), clicked => sub { Gtk2->main_quit }), + gtksignal_connect( + gtknew('Button', text => N("Ok")), clicked => sub { + $urpm->{global_config}{'verify-rpm'} = $verify_rpm; + $urpm->{global_config}{downloader} = $downloader; + $urpm->{global_config}{'xml-info'} = $xml_info_policy; + $something_changed = 1; + urpm::media::write_config($urpm); + $urpm = fast_open_urpmi_db(); + Gtk2->main_quit; + }, + ), + ), + ), + ); + $w->main; +} + +sub remove_callback() { + my @rows = selected_rows(); + @rows == 0 and return; + interactive_msg( + N("Source Removal"), + @rows == 1 ? + N("Are you sure you want to remove source \"%s\"?", $urpm->{media}[$rows[0]]{name}) : + N("Are you sure you want to remove the following sources?") . "\n\n" . + format_list(map { $urpm->{media}[$_]{name} } @rows), + yesno => 1, scroll => 1, + transient => $::main_window, + ) or return; + + my $wait = wait_msg(N("Please wait, removing medium...")); + foreach my $row (reverse(@rows)) { + $something_changed = 1; + urpm::media::remove_media($urpm, [ $urpm->{media}[$row] ]); + urpm::media::write_urpmi_cfg($urpm); + remove_wait_msg($wait); + } + return 1; +} + +sub renum_media ($$$) { + my ($model, @iters) = @_; + my @rows = map { $model->get_path($_)->to_string } @iters; + my @media = map { $urpm->{media}[$_] } @rows; + $urpm->{media}[$rows[$_]] = $media[1 - $_] foreach 0, 1; + $model->swap(@iters); + $something_changed = 1; + urpm::media::write_config($urpm); + $urpm = fast_open_urpmi_db(); +} + +sub upwards_callback() { + my @rows = selected_rows(); + @rows == 0 and return; + my $model = $list_tv->get_model; + my $prev = $model->get_iter_from_string($rows[0] - 1); + defined $prev and renum_media($model, $model->get_iter_from_string($rows[0]), $prev); + $list_tv->get_selection->signal_emit('changed'); +} + +sub downwards_callback() { + my @rows = selected_rows(); + @rows == 0 and return; + my $model = $list_tv->get_model; + my $iter = $model->get_iter_from_string($rows[0]); + my $next = $model->iter_next($iter); + defined $next and renum_media($model, $iter, $next); + $list_tv->get_selection->signal_emit('changed'); +} + +#- returns the name of the media for which edition failed, or undef on success +sub edit_callback() { + my ($row) = selected_rows(); + $row == -1 and return; + my $medium = $urpm->{media}[$row]; + my $config = urpm::cfg::load_config_raw($urpm->{config}, 1); + my ($verbatim_medium) = grep { $medium->{name} eq $_->{name} } @$config; + my $old_main_window = $::main_window; + my $w = ugtk2->new(N("Edit a medium"), grab => 1, center => 1, transient => $::main_window); + local $::main_window = $w->{real_window}; + my ($url_entry, $downloader_entry, $url, $downloader); + gtkadd( + $w->{window}, + gtkpack_( + gtknew('VBox', spacing => 5), + 0, gtknew('Title2', label => N("Editing medium \"%s\":", $medium->{name})), + 0, create_packtable( + {}, + [ gtknew('Label_Left', text => N("URL:")), $url_entry = gtkentry($verbatim_medium->{url} || $verbatim_medium->{mirrorlist}) ], + [ gtknew('Label_Left', text => N("Downloader:")), + my $download_combo = Gtk2::ComboBox->new_with_strings([ urpm::download::available_ftp_http_downloaders() ], + $verbatim_medium->{downloader} || '') ], + ), + 0, gtknew('HSeparator'), + 0, gtkpack( + gtknew('HButtonBox'), + gtksignal_connect( + gtknew('Button', text => N("Cancel")), + clicked => sub { $w->{retval} = 0; Gtk2->main_quit }, + ), + gtksignal_connect( + gtknew('Button', text => N("Save changes")), + clicked => sub { + $w->{retval} = 1; + $url = $url_entry->get_text; + $downloader = $downloader_entry->get_text; + Gtk2->main_quit; + }, + ), + gtksignal_connect( + gtknew('Button', text => N("Proxy...")), + clicked => sub { proxy_callback($medium) }, + ), + ) + ) + ); + $downloader_entry = $download_combo->entry; + $w->{rwindow}->set_size_request(600, -1); + if ($w->main) { + my ($name, $update) = map { $medium->{$_} } qw(name update); + $url =~ m|^removable://| and ( + interactive_msg( + N("You need to insert the medium to continue"), + N("In order to save the changes, you need to insert the medium in the drive."), + yesno => 1, text => { yes => N("Ok"), no => N("Cancel") } + ) or return 0 + ); + my $saved_proxy = urpm::download::get_proxy($name); + undef $saved_proxy if !defined $saved_proxy->{http_proxy} && !defined $saved_proxy->{ftp_proxy}; + urpm::media::select_media($urpm, $name); + if (my ($media) = grep { $_->{name} eq $name } @{$urpm->{media}}) { + put_in_hash($media, { + ($verbatim_medium->{mirrorlist} ? 'mirrorlist' : 'url') => $url, + name => $name, + if_($update ne $media->{update} || $update, update => $update), + if_($saved_proxy ne $media->{proxy} || $saved_proxy, proxy => $saved_proxy), + if_($downloader ne $media->{downloader} || $downloader, downloader => $downloader), + modified => 1, + }); + urpm::media::write_config($urpm); + local $::main_window = $old_main_window; + update_sources_noninteractive($urpm, [ $name ], transient => $::main_window, nolock => 1); + } else { + urpm::media::remove_selected_media($urpm); + add_medium_and_check($urpm, { nolock => 1, proxy => $saved_proxy }, $name, $url, undef, update => $update, if_($downloader, downloader => $downloader)); + } + return $name; + } + return undef; +} + +sub update_callback() { + update_sources_interactive($urpm, transient => $::main_window, nolock => 1); +} + +sub proxy_callback { + my ($medium) = @_; + my $medium_name = $medium ? $medium->{name} : ''; + my $w = ugtk2->new(N("Configure proxies"), grab => 1, center => 1, transient => $::main_window); + local $::main_window = $w->{real_window}; + require curl_download; + my ($proxy, $proxy_user) = curl_download::readproxy($medium_name); + my ($user, $pass) = $proxy_user =~ /^([^:]*):(.*)$/; + my ($proxybutton, $proxyentry, $proxyuserbutton, $proxyuserentry, $proxypasswordentry); + my $sg = Gtk2::SizeGroup->new('horizontal'); + gtkadd( + $w->{window}, + gtkpack__( + gtknew('VBox', spacing => 5), + gtknew('Title2', label => + $medium_name + ? N("Proxy settings for media \"%s\"", $medium_name) + : N("Global proxy settings") + ), + gtknew('Label_Left', text => N("If you need a proxy, enter the hostname and an optional port (syntax: ):")), + gtkpack_( + gtknew('HBox', spacing => 10), + 1, gtkset_active($proxybutton = gtknew('CheckButton', text => N("Proxy hostname:")), to_bool($proxy)), + 0, gtkadd_widget($sg, gtkset_sensitive($proxyentry = gtkentry($proxy), to_bool($proxy))), + ), + gtkset_active($proxyuserbutton = gtknew('CheckButton', text => N("You may specify a username/password for the proxy authentication:")), to_bool($proxy_user)), + gtkpack_( + my $hb_user = gtknew('HBox', spacing => 10, sensitive => to_bool($proxy_user)), + 1, gtknew('Label_Left', text => N("User:")), + 0, gtkadd_widget($sg, $proxyuserentry = gtkentry($user)), + ), + gtkpack_( + my $hb_pswd = gtknew('HBox', spacing => 10, sensitive => to_bool($proxy_user)), + 1, gtknew('Label_Left', text => N("Password:")), + 0, gtkadd_widget($sg, gtkset_visibility($proxypasswordentry = gtkentry($pass), 0)), + ), + gtknew('HSeparator'), + gtkpack( + gtknew('HButtonBox'), + gtksignal_connect( + gtknew('Button', text => N("Ok")), + clicked => sub { + $w->{retval} = 1; + $proxy = $proxybutton->get_active ? $proxyentry->get_text : ''; + $proxy_user = $proxyuserbutton->get_active + ? ($proxyuserentry->get_text . ':' . $proxypasswordentry->get_text) : ''; + Gtk2->main_quit; + }, + ), + gtksignal_connect( + gtknew('Button', text => N("Cancel")), + clicked => sub { $w->{retval} = 0; Gtk2->main_quit }, + ) + ) + ) + ); + $sg->add_widget($_) foreach $proxyentry, $proxyuserentry, $proxypasswordentry; + $proxybutton->signal_connect( + clicked => sub { + $proxyentry->set_sensitive($_[0]->get_active); + $_[0]->get_active and return; + $proxyuserbutton->set_active(0); + $hb_user->set_sensitive(0); + $hb_pswd->set_sensitive(0); + } + ); + $proxyuserbutton->signal_connect(clicked => sub { $_->set_sensitive($_[0]->get_active) foreach $hb_user, $hb_pswd; + $proxypasswordentry->set_sensitive($_[0]->get_active) }); + + $w->main and do { + $something_changed = 1; + curl_download::writeproxy($proxy, $proxy_user, $medium_name); + }; +} + +sub parallel_read_sysconf() { + my @conf; + foreach (cat_('/etc/urpmi/parallel.cfg')) { + my ($name, $protocol, $command) = /([^:]+):([^:]+):(.*)/ or print STDERR "Warning, unrecognized line in /etc/urpmi/parallel.cfg:\n$_"; + my $medias = $protocol =~ s/\(([^\)]+)\)$// ? [ split /,/, $1 ] : []; + push @conf, { name => $name, protocol => $protocol, medias => $medias, command => $command }; + } + \@conf; +} + +sub parallel_write_sysconf { + my ($conf) = @_; + output '/etc/urpmi/parallel.cfg', + map { my $m = @{$_->{medias}} ? '(' . join(',', @{$_->{medias}}) . ')' : ''; + "$_->{name}:$_->{protocol}$m:$_->{command}\n" } @$conf; +} + +sub remove_parallel { + my ($num, $conf) = @_; + if ($num != -1) { + splice @$conf, $num, 1; + parallel_write_sysconf($conf); + } +} + +sub add_callback_ { + my ($title, $label, $mainw, $widget, $get_value, $check) = @_; + my $w = ugtk2->new($title, grab => 1, transient => $mainw->{real_window}); + local $::main_window = $w->{real_window}; + gtkadd( + $w->{window}, + gtkpack__( + gtknew('VBox', spacing => 5), + gtknew('Label', text => $label), + $widget, + gtknew('HSeparator'), + gtkpack( + gtknew('HButtonBox'), + gtknew('Button', text => N("Ok"), clicked => sub { $w->{retval} = 1; $get_value->(); Gtk2->main_quit }), + gtknew('Button', text => N("Cancel"), clicked => sub { $w->{retval} = 0; Gtk2->main_quit }) + ) + ) + ); + $check->() if $w->main; +} + +sub edit_parallel { + my ($num, $conf) = @_; + my $edited = $num == -1 ? {} : $conf->[$num]; + my $w = ugtk2->new($num == -1 ? N("Add a parallel group") : N("Edit a parallel group"), grab => 1, center => 1, transient => $::main_window); + local $::main_window = $w->{real_window}; + my $name_entry; + + my ($medias_ls, $hosts_ls) = (Gtk2::ListStore->new("Glib::String"), Gtk2::ListStore->new("Glib::String")); + + my ($medias, $hosts) = map { + my $list = Gtk2::TreeView->new_with_model($_); + $list->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); + $list->set_headers_visible(0); + $list->get_selection->set_mode('browse'); + $list; + } $medias_ls, $hosts_ls; + + $medias_ls->append_set([ 0 => $_ ]) foreach @{$edited->{medias}}; + + my $add_media = sub { + my $medias_list_ls = Gtk2::ListStore->new("Glib::String"); + my $medias_list = Gtk2::TreeView->new_with_model($medias_list_ls); + $medias_list->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); + $medias_list->set_headers_visible(0); + $medias_list->get_selection->set_mode('browse'); + $medias_list_ls->append_set([ 0 => $_->{name} ]) foreach @{$urpm->{media}}; + my $sel; + add_callback_(N("Add a medium limit"), N("Choose a medium to add to the media limit:"), + $w, $medias_list, sub { $sel = selrow($medias_list) }, + sub { + return if $sel == -1; + my $media = ${$urpm->{media}}[$sel]{name}; + $medias_ls->append_set([ 0 => $media ]); + push @{$edited->{medias}}, $media; + } + ); + }; + + my $hosts_list; + if ($edited->{protocol} eq 'ssh') { $hosts_list = [ split /:/, $edited->{command} ] } + elsif ($edited->{protocol} eq 'ka-run') { push @$hosts_list, $1 while $edited->{command} =~ /-m (\S+)/g } + $hosts_ls->append_set([ 0 => $_ ]) foreach @$hosts_list; + my $add_host = sub { + my ($entry, $value); + add_callback_(N("Add a host"), N("Type in the hostname or IP address of the host to add:"), + $mainw, $entry = gtkentry(), sub { $value = $entry->get_text }, + sub { $hosts_ls->append_set([ 0 => $value ]); push @$hosts_list, $value } + ); + }; + + my @protocols_names = qw(ka-run ssh); + my @protocols; + gtkadd( + $w->{window}, + gtkpack_( + gtknew('VBox', spacing => 5), + if_( + $num != -1, + 0, gtknew('Label', text => N("Editing parallel group \"%s\":", $edited->{name})) + ), + 1, create_packtable( + {}, + [ N("Group name:"), $name_entry = gtkentry($edited->{name}) ], + [ N("Protocol:"), gtknew('HBox', children_tight => [ + @protocols = gtkradio($edited->{protocol}, @protocols_names) ]) ], + [ N("Media limit:"), + gtknew('HBox', spacing => 5, children => [ + 1, gtknew('Frame', shadow_type => 'in', child => + gtknew('ScrolledWindow', h_policy => 'never', child => $medias)), + 0, gtknew('VBox', children_tight => [ + gtksignal_connect(Gtk2::Button->new(but(N("Add"))), clicked => sub { $add_media->() }), + gtksignal_connect(Gtk2::Button->new(but(N("Remove"))), clicked => sub { + remove_from_list($medias, $edited->{medias}, $medias_ls); + }) ]) ]) ], + [ N("Hosts:"), + gtknew('HBox', spacing => 5, children => [ + 1, gtknew('Frame', shadow_type => 'in', child => + gtknew('ScrolledWindow', h_policy => 'never', child => $hosts)), + 0, gtknew('VBox', children_tight => [ + gtksignal_connect(Gtk2::Button->new(but(N("Add"))), clicked => sub { $add_host->() }), + gtksignal_connect(Gtk2::Button->new(but(N("Remove"))), clicked => sub { + remove_from_list($hosts, $hosts_list, $hosts_ls); + }) ]) ]) ] + ), + 0, gtknew('HSeparator'), + 0, gtkpack( + gtknew('HButtonBox'), + gtksignal_connect( + gtknew('Button', text => N("Ok")), clicked => sub { + $w->{retval} = 1; + $edited->{name} = $name_entry->get_text; + mapn { $_[0]->get_active and $edited->{protocol} = $_[1] } \@protocols, \@protocols_names; + Gtk2->main_quit; + } + ), + gtknew('Button', text => N("Cancel"), clicked => sub { $w->{retval} = 0; Gtk2->main_quit })) + ) + ); + $w->{rwindow}->set_size_request(600, -1); + if ($w->main) { + $num == -1 and push @$conf, $edited; + if ($edited->{protocol} eq 'ssh') { $edited->{command} = join(':', @$hosts_list) } + if ($edited->{protocol} eq 'ka-run') { $edited->{command} = "-c ssh " . join(' ', map { "-m $_" } @$hosts_list) } + parallel_write_sysconf($conf); + return 1; + } + return 0; +} + +sub parallel_callback() { + my $w = ugtk2->new(N("Configure parallel urpmi (distributed execution of urpmi)"), grab => 1, center => 1, transient => $mainw->{real_window}); + local $::main_window = $w->{real_window}; + my $list_ls = Gtk2::ListStore->new("Glib::String", "Glib::String", "Glib::String", "Glib::String"); + my $list = Gtk2::TreeView->new_with_model($list_ls); + each_index { $list->append_column(Gtk2::TreeViewColumn->new_with_attributes($_, Gtk2::CellRendererText->new, 'text' => $::i)) } N("Group"), N("Protocol"), N("Media limit"); + $list->append_column(my $commandcol = Gtk2::TreeViewColumn->new_with_attributes(N("Command"), Gtk2::CellRendererText->new, 'text' => 3)); + $commandcol->set_max_width(200); + + my $conf; + my $reread = sub { + $list_ls->clear; + $conf = parallel_read_sysconf(); + foreach (@$conf) { + $list_ls->append_set([ 0 => $_->{name}, + 1 => $_->{protocol}, + 2 => @{$_->{medias}} ? join(', ', @{$_->{medias}}) : N("(none)"), + 3 => $_->{command} ]); + } + }; + $reread->(); + + gtkadd( + $w->{window}, + gtkpack_( + gtknew('VBox', spacing => 5), + 1, gtkpack_( + gtknew('HBox', spacing => 10), + 1, $list, + 0, gtkpack__( + gtknew('VBox', spacing => 5), + gtksignal_connect( + Gtk2::Button->new(but(N("Remove"))), + clicked => sub { remove_parallel(selrow($list), $conf); $reread->() }, + ), + gtksignal_connect( + Gtk2::Button->new(but(N("Edit..."))), + clicked => sub { + my $row = selrow($list); + $row != -1 and edit_parallel($row, $conf); + $reread->(); + }, + ), + gtksignal_connect( + Gtk2::Button->new(but(N("Add..."))), + clicked => sub { edit_parallel(-1, $conf) and $reread->() }, + ) + ) + ), + 0, gtknew('HSeparator'), + 0, gtkpack( + gtknew('HButtonBox'), + gtknew('Button', text => N("Ok"), clicked => sub { Gtk2->main_quit }) + ) + ) + ); + $w->main; +} + +sub keys_callback() { + my $w = ugtk2->new(N("Manage keys for digital signatures of packages"), grab => 1, center => 1, transient => $mainw->{real_window}); + local $::main_window = $w->{real_window}; + $w->{real_window}->set_size_request(600, 300); + + my $media_list_ls = Gtk2::ListStore->new("Glib::String"); + my $media_list = Gtk2::TreeView->new_with_model($media_list_ls); + $media_list->append_column(Gtk2::TreeViewColumn->new_with_attributes(N("Medium"), Gtk2::CellRendererText->new, 'text' => 0)); + $media_list->get_selection->set_mode('browse'); + + my $key_col_size = 200; + my $keys_list_ls = Gtk2::ListStore->new("Glib::String", "Glib::String"); + my $keys_list = Gtk2::TreeView->new_with_model($keys_list_ls); + $keys_list->set_rules_hint(1); + $keys_list->append_column(my $col = Gtk2::TreeViewColumn->new_with_attributes(N("_:cryptographic keys\nKeys"), my $renderer = Gtk2::CellRendererText->new, 'text' => 0)); + $col->set_sizing('fixed'); + $col->set_fixed_width($key_col_size); + $renderer->set_property('width' => 1); + $renderer->set_property('wrap-width', $key_col_size); + $keys_list->get_selection->set_mode('browse'); + + my ($current_medium, $current_medium_nb, @keys); + + my $read_conf = sub { + $urpm->parse_pubkeys(root => $urpm->{root}); + @keys = map { [ split /[,\s]+/, $_->{'key-ids'} ] } @{$urpm->{media}}; + }; + my $write = sub { + $something_changed = 1; + urpm::media::write_config($urpm); + $urpm = fast_open_urpmi_db(); + $read_conf->(); + $media_list->get_selection->signal_emit('changed'); + }; + $read_conf->(); + my $key_name = sub { + exists $urpm->{keys}{$_[0]} ? $urpm->{keys}{$_[0]}{name} + : N("no name found, key doesn't exist in rpm keyring!"); + }; + $media_list_ls->append_set([ 0 => $_->{name} ]) foreach @{$urpm->{media}}; + $media_list->get_selection->signal_connect(changed => sub { + my ($model, $iter) = $_[0]->get_selected; + $model && $iter or return; + $current_medium = $model->get($iter, 0); + $current_medium_nb = $model->get_path($iter)->to_string; + $keys_list_ls->clear; + $keys_list_ls->append_set([ 0 => sprintf("%s (%s)", $_, $key_name->($_)), 1 => $_ ]) foreach @{$keys[$current_medium_nb]}; + }); + + my $add_key = sub { + my $available_keyz_ls = Gtk2::ListStore->new("Glib::String", "Glib::String"); + my $available_keyz = Gtk2::TreeView->new_with_model($available_keyz_ls); + $available_keyz->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => 0)); + $available_keyz->set_headers_visible(0); + $available_keyz->get_selection->set_mode('browse'); + $available_keyz_ls->append_set([ 0 => sprintf("%s (%s)", $_, $key_name->($_)), 1 => $_ ]) foreach keys %{$urpm->{keys}}; + my $key; + add_callback_(N("Add a key"), N("Choose a key to add to the medium %s", $current_medium), $w, $available_keyz, + sub { + my ($model, $iter) = $available_keyz->get_selection->get_selected; + $model && $iter and $key = $model->get($iter, 1); + }, + sub { + return if !defined $key; + $urpm->{media}[$current_medium_nb]{'key-ids'} = join(',', sort(uniq(@{$keys[$current_medium_nb]}, $key))); + $write->(); + } + ); + + + }; + + my $remove_key = sub { + my ($model, $iter) = $keys_list->get_selection->get_selected; + $model && $iter or return; + my $key = $model->get($iter, 1); + interactive_msg(N("Remove a key"), + N("Are you sure you want to remove the key %s from medium %s?\n(name of the key: %s)", + $key, $current_medium, $key_name->($key)), + yesno => 1, transient => $w->{real_window}) or return; + $urpm->{media}[$current_medium_nb]{'key-ids'} = join(',', difference2(\@{$keys[$current_medium_nb]}, [ $key ])); + $write->(); + }; + + gtkadd( + $w->{window}, + gtkpack_( + gtknew('VBox', spacing => 5), + 1, gtkpack_( + gtknew('HBox', spacing => 10), + 1, create_scrolled_window($media_list), + 1, create_scrolled_window($keys_list), + 0, gtkpack__( + gtknew('VBox', spacing => 5), + gtksignal_connect( + Gtk2::Button->new(but(N("Add"))), + clicked => \&$add_key, + ), + gtksignal_connect( + Gtk2::Button->new(but(N("Remove"))), + clicked => \&$remove_key, + ) + ) + ), + 0, gtknew('HSeparator'), + 0, gtkpack( + gtknew('HButtonBox'), + gtknew('Button', text => N("Ok"), clicked => sub { Gtk2->main_quit }) + ), + ), + ); + $w->main; +} + +sub mainwindow() { + undef $something_changed; + $mainw = ugtk2->new(N("Configure media"), center => 1, transient => $::main_window, modal => 1); + local $::main_window = $mainw->{real_window}; + + my $reread_media; + + my ($menu, $_factory) = create_factory_menu( + $mainw->{real_window}, + [ N("/_File"), undef, undef, undef, '' ], + [ N("/_File") . N("/_Update"), N("U"), sub { update_callback() and $reread_media->() }, undef, '', ], + [ N("/_File") . N("/Add a specific _media mirror"), N("M"), sub { easy_add_callback_with_mirror() and $reread_media->() }, undef, '' ], + [ N("/_File") . N("/_Add a custom medium"), N("A"), sub { add_callback() and $reread_media->() }, undef, '' ], + [ N("/_File") . N("/Close"), N("W"), sub { Gtk2->main_quit }, undef, '', ], + [ N("/_Options"), undef, undef, undef, '' ], + [ N("/_Options") . N("/_Global options"), N("G"), \&options_callback, undef, '' ], + [ N("/_Options") . N("/Manage _keys"), N("K"), \&keys_callback, undef, '' ], + [ N("/_Options") . N("/_Parallel"), N("P"), \¶llel_callback, undef, '' ], + [ N("/_Options") . N("/P_roxy"), N("R"), \&proxy_callback, undef, '' ], + if_($0 =~ /edit-urpm-sources/, + [ N("/_Help"), undef, undef, undef, '' ], + [ N("/_Help") . N("/_Report Bug"), undef, sub { run_drakbug('edit-urpm-sources.pl') }, undef, '' ], + [ N("/_Help") . N("/_Help"), undef, sub { rpmdragora::open_help('sources') }, undef, '' ], + [ N("/_Help") . N("/_About..."), undef, sub { + my $license = formatAlaTeX(translate($::license)); + $license =~ s/\n/\n\n/sg; # nicer formatting + my $w = gtknew('AboutDialog', name => N("Rpmdragora"), + version => $rpmdragora::distro_version, + copyright => N("Copyright (C) %s by Mandriva", '2002-2008'), + license => $license, wrap_license => 1, + comments => N("Rpmdragora is the Mageia package management tool."), + website => 'http://www.mageia.org/', + website_label => N("Mageia"), + authors => 'Thierry Vignaud ', + artists => 'Hélène Durosini ', + translator_credits => + #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith ") + N("_: Translator(s) name(s) & email(s)\n"), + transient_for => $::main_window, modal => 1, position_policy => 'center-on-parent', + ); + $w->show_all; + $w->run; + }, undef, '' + ] + ), + ); + + my $list = Gtk2::ListStore->new("Glib::Boolean", "Glib::Boolean", "Glib::String", "Glib::String", "Glib::Boolean"); + $list_tv = Gtk2::TreeView->new_with_model($list); + $list_tv->get_selection->set_mode('multiple'); + my ($dw_button, $edit_button, $remove_button, $up_button); + $list_tv->get_selection->signal_connect(changed => sub { + my ($selection) = @_; + my @rows = $selection->get_selected_rows; + my $model = $list; + # we can delete several medium at a time: + $remove_button and $remove_button->set_sensitive($#rows != -1); + # we can only edit/move one item at a time: + $_ and $_->set_sensitive(@rows == 1) foreach $up_button, $dw_button, $edit_button; + + # we can only up/down one item if not at begin/end: + return if @rows != 1; + + my $curr_path = $rows[0]; + my $first_path = $model->get_path($model->get_iter_first); + $up_button->set_sensitive($first_path && $first_path->compare($curr_path)); + + $curr_path->next; + my $next_item = $model->get_iter($curr_path); + $dw_button->set_sensitive($next_item); # && !$model->get($next_item, 0) + }); + + $list_tv->set_rules_hint(1); + $list_tv->set_reorderable(1); + + my $reorder_ok = 1; + $list->signal_connect( + row_deleted => sub { + $reorder_ok or return; + my ($model) = @_; + my @media; + $model->foreach( + sub { + my (undef, undef, $iter) = @_; + my $name = $model->get($iter, $col{mainw}{name}); + push @media, urpm::media::name2medium($urpm, $name); + 0; + }, undef); + @{$urpm->{media}} = @media; + }, + ); + + $list_tv->append_column(Gtk2::TreeViewColumn->new_with_attributes(N("Enabled"), + my $tr = Gtk2::CellRendererToggle->new, + 'active' => $col{mainw}{is_enabled})); + $list_tv->append_column(Gtk2::TreeViewColumn->new_with_attributes(N("Updates"), + my $cu = Gtk2::CellRendererToggle->new, + 'active' => $col{mainw}{is_update}, + activatable => $col{mainw}{activatable})); + $list_tv->append_column(Gtk2::TreeViewColumn->new_with_attributes(N("Type"), + Gtk2::CellRendererText->new, + 'text' => $col{mainw}{type})); + $list_tv->append_column(Gtk2::TreeViewColumn->new_with_attributes(N("Medium"), + Gtk2::CellRendererText->new, + 'text' => $col{mainw}{name})); + my $id; + $id = $tr->signal_connect( + toggled => sub { + my (undef, $path) = @_; + $tr->signal_handler_block($id); + my $_guard = before_leaving { $tr->signal_handler_unblock($id) }; + my $iter = $list->get_iter_from_string($path); + $urpm->{media}[$path]{ignore} = !$urpm->{media}[$path]{ignore} || undef; + $list->set($iter, $col{mainw}{is_enabled}, !$urpm->{media}[$path]{ignore}); + urpm::media::write_config($urpm); + my $ignored = $urpm->{media}[$path]{ignore}; + $reread_media->(); + if (!$ignored && $urpm->{media}[$path]{ignore}) { + # reread media failed to un-ignore an ignored medium + # probably because urpm::media::check_existing_medium() complains + # about missing synthesis when the medium never was enabled before; + # thus it restored the ignore bit + $urpm->{media}[$path]{ignore} = !$urpm->{media}[$path]{ignore} || undef; + urpm::media::write_config($urpm); + #- Enabling this media failed, force update + interactive_msg('rpmdragora', + N("This medium needs to be updated to be usable. Update it now?"), + yesno => 1, + ) and $reread_media->($urpm->{media}[$path]{name}); + } + }, + ); + + $cu->signal_connect( + toggled => sub { + my (undef, $path) = @_; + my $iter = $list->get_iter_from_string($path); + $urpm->{media}[$path]{update} = !$urpm->{media}[$path]{update} || undef; + $list->set($iter, $col{mainw}{is_update}, ! !$urpm->{media}[$path]{update}); + $something_changed = 1; + }, + ); + + $reread_media = sub { + my ($name) = @_; + $reorder_ok = 0; + $something_changed = 1; + if (defined $name) { + urpm::media::select_media($urpm, $name); + update_sources_check( + $urpm, + { nolock => 1 }, + N_("Unable to update medium, errors reported:\n\n%s"), + $name, + ); + } + # reread configuration after updating media else ignore bit will be restored + # by urpm::media::check_existing_medium(): + $urpm = fast_open_urpmi_db(); + $list->clear; + foreach (grep { ! $_->{external} } @{$urpm->{media}}) { + my $name = $_->{name}; + $list->append_set($col{mainw}{is_enabled} => !$_->{ignore}, + $col{mainw}{is_update} => ! !$_->{update}, + $col{mainw}{type} => get_medium_type($_), + $col{mainw}{name} => $name, + $col{mainw}{activatable} => to_bool($::expert), + ); + } + $reorder_ok = 1; + }; + $reread_media->(); + $something_changed = 0; + + gtkadd( + $mainw->{window}, + gtkpack_( + gtknew('VBox', spacing => 5), + 0, $menu, + ($0 =~ /rpm-edit-media|edit-urpm-sources/ ? (0, Gtk2::Banner->new($ugtk2::wm_icon, N("Configure media"))) : ()), + 1, gtkpack_( + gtknew('HBox', spacing => 10), + 1, gtknew('ScrolledWindow', child => $list_tv), + 0, gtkpack__( + gtknew('VBox', spacing => 5), + gtksignal_connect( + $remove_button = Gtk2::Button->new(but(N("Remove"))), + clicked => sub { remove_callback() and $reread_media->() }, + ), + gtksignal_connect( + $edit_button = Gtk2::Button->new(but(N("Edit"))), + clicked => sub { + my $name = edit_callback(); defined $name and $reread_media->($name); + } + ), + gtksignal_connect( + Gtk2::Button->new(but(N("Add"))), + clicked => sub { easy_add_callback() and $reread_media->() }, + ), + gtkpack( + gtknew('HBox'), + gtksignal_connect( + $up_button = gtknew('Button', + image => gtknew('Image', stock => 'gtk-go-up')), + clicked => \&upwards_callback), + + gtksignal_connect( + $dw_button = gtknew('Button', + image => gtknew('Image', stock => 'gtk-go-down')), + clicked => \&downwards_callback), + ), + ) + ), + 0, gtknew('HSeparator'), + 0, gtknew('HButtonBox', layout => 'edge', children_loose => [ + gtksignal_connect(Gtk2::Button->new(but(N("Help"))), clicked => sub { rpmdragora::open_help('sources') }), + gtksignal_connect(Gtk2::Button->new(but(N("Ok"))), clicked => sub { Gtk2->main_quit }) + ]) + ) + ); + $_->set_sensitive(0) foreach $dw_button, $edit_button, $remove_button, $up_button; + + $mainw->{rwindow}->set_size_request(600, 400); + $mainw->main; + return $something_changed; +} + + +sub run() { + # ignore rpmdragora's option regarding ignoring debug media: + local $ignore_debug_media = [ 0 ]; + local $ugtk2::wm_icon = get_icon('rpmdragora-mdk', 'title-media'); + my $lock; + { + $urpm = fast_open_urpmi_db(); + my $err_msg = "urpmdb locked\n"; + local $urpm->{fatal} = sub { + interactive_msg('rpmdragora', + N("The Package Database is locked. Please close other applications +working with the Package Database. Do you have another media +manager on another desktop, or are you currently installing +packages as well?")); + die $err_msg; + }; + # lock urpmi DB + eval { $lock = urpm::lock::urpmi_db($urpm, 'exclusive', wait => $urpm->{options}{wait_lock}) }; + if (my $err = $@) { + return if $err eq $err_msg; + die $err; + } + } + + my $res = mainwindow(); + urpm::media::write_config($urpm); + + writeconf(); + + undef $lock; + $res; +} + + +1; diff --git a/AdminPanel/Rpmdragora/formatting.pm b/AdminPanel/Rpmdragora/formatting.pm new file mode 100644 index 0000000..f271ed1 --- /dev/null +++ b/AdminPanel/Rpmdragora/formatting.pm @@ -0,0 +1,177 @@ +package AdminPanel::Rpmdragora::formatting; +#***************************************************************************** +# +# Copyright (c) 2002 Guillaume Cottenceau +# Copyright (c) 2002-2006 Thierry Vignaud +# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +# Copyright (c) 2005, 2006 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: formatting.pm 261189 2009-10-01 14:44:39Z tv $ + +use strict; +use utf8; +use POSIX qw(strftime); +use AdminPanel::rpmdragora; +use lib qw(/usr/lib/libDrakX); +use common; +#use ugtk2 qw(escape_text_for_TextView_markup_format); + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw( + $spacing + ensure_utf8 + format_changelog_changelogs + format_changelog_string + format_field + format_header + format_list + format_name_n_summary + format_size + format_filesize + format_update_field + my_fullname + pkg2medium + rpm_description + split_fullname + urpm_name + ); + + +# from rpmtools, #37482: +sub ensure_utf8 { + if (utf8::is_utf8($_[0])) { + utf8::valid($_[0]) and return; + + utf8::encode($_[0]); #- disable utf8 flag + utf8::upgrade($_[0]); + } else { + utf8::decode($_[0]); #- try to set utf8 flag + utf8::valid($_[0]) and return; + warn "do not know what to with $_[0]\n"; + } +} + +sub rpm_description { + my ($description) = @_; + ensure_utf8($description); + my ($t, $tmp); + foreach (split "\n", $description) { + s/^\s*//; + if (/^$/ || /^\s*(-|\*|\+|o)\s/) { + $t || $tmp and $t .= "$tmp\n"; + $tmp = $_; + } else { + $tmp = ($tmp ? "$tmp " : ($t && "\n") . $tmp) . $_; + } + } + "$t$tmp\n"; +} + +sub split_fullname { $_[0] =~ /^(.*)-([^-]+)-([^-]+)\.([^.-]+)$/ } + +sub my_fullname { + return '?-?-?' unless ref $_[0]; + my ($name, $version, $release) = $_[0]->fullname; + "$name-$version-$release"; +} + +sub urpm_name { + return '?-?-?.?' unless ref $_[0]; + scalar $_[0]->fullname; +} + +sub pkg2medium { + my ($p, $urpm) = @_; + return if !ref $p; + return { name => N("None (installed)") } if !defined($p->id); # if installed + URPM::pkg2media($urpm->{media}, $p) || { name => N("Unknown"), fake => 1 }; +} + +# [ duplicate urpmi's urpm::msg::localtime2changelog() ] +#- strftime returns a string in the locale charset encoding; +#- but gtk2 requires UTF-8, so we use to_utf8() to ensure the +#- output of localtime2changelog() is always in UTF-8 +#- as to_utf8() uses LC_CTYPE for locale encoding and strftime() uses LC_TIME, +#- it doesn't work if those two variables have values with different +#- encodings; but if a user has a so broken setup we can't do much anyway +sub localtime2changelog { to_utf8(POSIX::strftime("%c", localtime($_[0]))) } + +our $spacing = " "; +sub format_changelog_string { + my ($installed_version, $string) = @_; + #- preprocess changelog for faster TextView insert reaction + require Gtk2::Pango; + my %date_attr = ('weight' => Gtk2::Pango->PANGO_WEIGHT_BOLD); + my %update_attr = ('style' => 'italic'); + my $version; + my $highlight; + [ map { + my %attrs; + if (/^\*/) { + add2hash(\%attrs, \%date_attr); + ($version) = /(\S*-\S*)\s*$/; + $highlight = $installed_version ne N("(none)") && 0 < URPM::rpmvercmp($version, $installed_version); + } + add2hash(\%attrs, \%update_attr) if $highlight; + [ "$spacing$_\n", if_(%attrs, \%attrs) ]; + } split("\n", $string) ]; +} + +sub format_changelog_changelogs { + my ($installed_version, @changelogs) = @_; + format_changelog_string($installed_version, join("\n", map { + "* " . localtime2changelog($_->{time}) . " $_->{name}\n\n$_->{text}\n"; + } @changelogs)); +} + +sub format_update_field { + my ($name) = @_; + '' . eval { escape_text_for_TextView_markup_format($name) } . ''; +} + +sub format_name_n_summary { + my ($name, $summary) = @_; + join("\n", '' . $name . '', escape_text_for_TextView_markup_format($summary)); +} + +sub format_header { + my ($str) = @_; + '' . escape_text_for_TextView_markup_format($str) . ''; +} + +sub format_field { + my ($str) = @_; + '' . escape_text_for_TextView_markup_format($str) . ''; +} + +sub format_size { + my ($size) = @_; + $size >= 0 ? + N("%s of additional disk space will be used.", formatXiB($size)) : + N("%s of disk space will be freed.", formatXiB(-$size)); +} + +sub format_filesize { + my ($filesize) = @_; + $filesize ? N("%s of packages will be retrieved.", formatXiB($filesize)) : (); +} + +sub format_list { join("\n", map { s/^(\s)/ $1/mg; "- $_" } sort { uc($a) cmp uc($b) } @_) } + +1; diff --git a/AdminPanel/Rpmdragora/gui.pm b/AdminPanel/Rpmdragora/gui.pm new file mode 100644 index 0000000..9c0b7a0 --- /dev/null +++ b/AdminPanel/Rpmdragora/gui.pm @@ -0,0 +1,1105 @@ +package AdminPanel::Rpmdragora::gui; +#***************************************************************************** +# +# Copyright (c) 2002 Guillaume Cottenceau +# Copyright (c) 2002-2007 Thierry Vignaud +# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +# Copyright (c) 2005-2007 Mandriva SA +# Copyright (c) 2013 Matteo Pasotti +# +# 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$ + +############################################################ +# WARNING: do not modify before asking to matteo or anaselli +############################################################ + +use strict; +our @ISA = qw(Exporter); +use lib qw(/usr/lib/libDrakX); +use common; +use yui; +use AdminPanel::rpmdragora; +use AdminPanel::Rpmdragora::open_db; +use AdminPanel::Rpmdragora::formatting; +use AdminPanel::Rpmdragora::init; +use AdminPanel::Rpmdragora::icon; +use AdminPanel::Rpmdragora::pkg; +use feature 'state'; + +our @EXPORT = qw( + $descriptions + $find_entry + $force_displaying_group + $force_rebuild + $pkgs + $results_ok + $results_none + $size_free + $size_selected + $urpm + %grp_columns + %pkg_columns + @filtered_pkgs + @initial_selection + ask_browse_tree_given_widgets_for_rpmdragora + build_tree + callback_choices + compute_main_window_size + do_action + get_info + get_summary + is_locale_available + node_state + pkgs_provider + real_quit + reset_search + set_node_state + sort_callback + switch_pkg_list_mode + toggle_all + toggle_nodes + ); + +our ($descriptions, %filters, @filtered_pkgs, %filter_methods, $force_displaying_group, $force_rebuild, @initial_selection, $pkgs, $size_free, $size_selected, $urpm); +our ($results_ok, $results_none) = (N("Search results"), N("Search results (none)")); + +our %grp_columns = ( + label => 0, + icon => 2, +); + +our %pkg_columns = ( + text => 0, + state_icon => 1, + state => 2, + selected => 3, + short_name => 4, + version => 5, + release => 6, + 'arch' => 7, + selectable => 8, +); + +sub compute_main_window_size { + my ($w) = @_; + ($typical_width) = string_size($w->{real_window}, translate("Graphical Environment") . "xmms-more-vis-plugins"); + $typical_width > 600 and $typical_width = 600; #- try to not being crazy with a too large value + $typical_width < 150 and $typical_width = 150; +} + +sub get_summary { + my ($key) = @_; + my $summary = translate($pkgs->{$key}{pkg}->summary); + require utf8; + utf8::valid($summary) ? $summary : @{[]}; +} + +sub build_expander { + my ($pkg, $label, $type, $get_data, $o_installed_version) = @_; + my $textview; + gtkadd( + gtkshow(my $exp = gtksignal_connect( + Gtk2::Expander->new(format_field($label)), + activate => sub { + state $first; + return if $first; + $first = 1; + slow_func($::main_window->window, sub { + extract_header($pkg, $urpm, $type, $o_installed_version); + gtktext_insert($textview, $get_data->() || [ [ N("(Not available)") ] ]); + }); + })), + $textview = gtknew('TextView') + ); + $exp->set_use_markup(1); + $exp; +} + + +sub get_advisory_link { + my ($update_descr) = @_; + my $link = gtkshow(Gtk2::LinkButton->new($update_descr->{URL}, N("Security advisory"))); + $link->set_uri_hook(\&run_help_callback); + [ $link ]; +} + +sub get_description { + my ($pkg, $update_descr) = @_; + @{ ugtk2::markup_to_TextView_format(join("\n", + (eval { + escape_text_for_TextView_markup_format( + $pkg->{description} + || $update_descr->{description}); + } || '' . N("No description") . '') + )) }; +} + +sub get_string_from_keywords { + my ($medium, $name) = @_; + my @media_types; + if ($medium->{mediacfg}) { + my ($distribconf, $medium_path) = @{$medium->{mediacfg}}; + @media_types = split(':', $distribconf->getvalue($medium_path, 'media_type')) if $distribconf; + } + + my $unsupported = N("It is not supported by Mageia."); + my $dangerous = N("It may break your system."); + my $s; + $s .= N("This package is not free software") . "\n" if member('non-free', @media_types); + if ($pkgs->{$name}{is_backport} || member('backport', @media_types)) { + return join("\n", + N("This package contains a new version that was backported."), + $unsupported, $dangerous, $s); + } elsif (member('testing', @media_types)) { + return join("\n", + N("This package is a potential candidate for an update."), + $unsupported, $dangerous, $s); + } elsif (member('updates', @media_types)) { + return join("\n", + (member('official', @media_types) ? + N("This is an official update which is supported by Mageia.") + : (N("This is an unofficial update."), $unsupported)) + , + $s); + } else { + $s .= N("This is an official package supported by Mageia") . "\n" if member('official', @media_types); + return $s; + } +} + +sub get_main_text { + my ($medium, $fullname, $name, $summary, $is_update, $update_descr) = @_; + + my $txt = get_string_from_keywords($medium, $fullname); + + ugtk2::markup_to_TextView_format( + # force align "name - summary" to the right with RTL languages (#33603): + if_(lang::text_direction_rtl(), "\x{200f}") . + join("\n", + format_header(join(' - ', $name, $summary)) . + # workaround gtk+ bug where GtkTextView wronly limit embedded widget size to bigger line's width (#25533): + "\x{200b} \x{feff}" . ' ' x 120, + if_($txt, format_field(N("Notice: ")) . $txt), + if_($is_update, # is it an update? + format_field(N("Importance: ")) . format_update_field($update_descr->{importance}), + format_field(N("Reason for update: ")) . format_update_field(rpm_description($update_descr->{pre})), + ), + '' # extra empty line + )); +} + +sub get_details { + my ($pkg, $upkg, $installed_version, $raw_medium) = @_; + my $a = ugtk2::markup_to_TextView_format( + $spacing . join("\n$spacing", + format_field(N("Version: ")) . $upkg->EVR, + ($upkg->flag_installed ? + format_field(N("Currently installed version: ")) . $installed_version : () + ), + format_field(N("Group: ")) . translate_group($upkg->group), + format_field(N("Architecture: ")) . $upkg->arch, + format_field(N("Size: ")) . N("%s KB", int($upkg->size/1024)), + eval { format_field(N("Medium: ")) . $raw_medium->{name} }, + ), + ); + my @link = get_url_link($upkg, $pkg); + push @$a, @link if @link; + $a; +} + +sub get_new_deps { + my ($urpm, $upkg) = @_; + my $deps_textview; + my @a = [ gtkadd( + gtksignal_connect( + gtkshow(my $dependencies = Gtk2::Expander->new(format_field(N("New dependencies:")))), + activate => sub { + slow_func($::main_window->window, sub { + my $state = {}; + my $db = open_rpm_db(); + my @requested = $urpm->resolve_requested__no_suggests_( + $db, $state, + { $upkg->id => 1 }, + ); + @requested = $urpm->resolve_requested_suggests($db, $state, \@requested); + undef $db; + my @nodes_with_deps = map { urpm_name($_) } @requested; + my @deps = sort { $a cmp $b } difference2(\@nodes_with_deps, [ urpm_name($upkg) ]); + @deps = N("All dependencies installed.") if !@deps; + gtktext_insert($deps_textview, join("\n", @deps)); + }); + } + ), + $deps_textview = gtknew('TextView') + ) ]; + $dependencies->set_use_markup(1); + @a; +} + +sub get_url_link { + my ($upkg, $pkg) = @_; + + my $url = $upkg->url || $pkg->{url}; + + if (!$url) { + open_rpm_db()->traverse_tag_find('name', $upkg->name, sub { $url = $_[0]->url }); + } + + return if !$url; + + my @a = + (@{ ugtk2::markup_to_TextView_format(format_field("\n$spacing" . N("URL: "))) }, + [ my $link = gtkshow(Gtk2::LinkButton->new($url, $url)) ]); + $link->set_uri_hook(\&run_help_callback); + @a; +} + +sub files_format { + my ($files) = @_; + ugtk2::markup_to_TextView_format( + '' . $spacing #- to highlight information + . join("\n$spacing", map { "\x{200e}$_" } @$files) + . ''); +} + +sub format_pkg_simplifiedinfo { + my ($pkgs, $key, $urpm, $descriptions) = @_; + my ($name) = split_fullname($key); + my $pkg = $pkgs->{$key}; + my $upkg = $pkg->{pkg}; + return if !$upkg; + my $raw_medium = pkg2medium($upkg, $urpm); + my $medium = !$raw_medium->{fake} ? $raw_medium->{name} : undef; + my $update_descr = $descriptions->{$medium}{$name}; + # discard update fields if not matching: + my $is_update = ($upkg->flag_upgrade && $update_descr && $update_descr->{pre}); + my $summary = get_summary($key); + my $s = get_main_text($raw_medium, $key, $name, $summary, $is_update, $update_descr); + push @$s, get_advisory_link($update_descr) if $is_update; + + push @$s, get_description($pkg, $update_descr); + push @$s, [ "\n" ]; + my $installed_version = eval { find_installed_version($upkg) }; + + push @$s, [ gtkadd(gtkshow(my $details_exp = Gtk2::Expander->new(format_field(N("Details:")))), + gtknew('TextView', text => get_details($pkg, $upkg, $installed_version, $raw_medium))) ]; + $details_exp->set_use_markup(1); + push @$s, [ "\n\n" ]; + push @$s, [ build_expander($pkg, N("Files:"), 'files', sub { files_format($pkg->{files}) }) ]; + push @$s, [ "\n\n" ]; + push @$s, [ build_expander($pkg, N("Changelog:"), 'changelog', sub { $pkg->{changelog} }, $installed_version) ]; + + push @$s, [ "\n\n" ]; + if ($upkg->id) { # If not installed + push @$s, get_new_deps($urpm, $upkg); + } + $s; +} + +sub format_pkg_info { + my ($pkgs, $key, $urpm, $descriptions) = @_; + my $pkg = $pkgs->{$key}; + my $upkg = $pkg->{pkg}; + my ($name, $version) = split_fullname($key); + my @files = ( + format_field(N("Files:\n")), + exists $pkg->{files} + ? '' . join("\n", map { "\x{200e}$_" } @{$pkg->{files}}) . '' #- to highlight information + : N("(Not available)"), + ); + my @chglo = (format_field(N("Changelog:\n")), ($pkg->{changelog} ? @{$pkg->{changelog}} : N("(Not available)"))); + my @source_info = ( + $MODE eq 'remove' || !@$max_info_in_descr + ? () + : ( + format_field(N("Medium: ")) . pkg2medium($upkg, $urpm)->{name}, + format_field(N("Currently installed version: ")) . find_installed_version($upkg), + ) + ); + my @max_info = @$max_info_in_descr && $changelog_first ? (@chglo, @files) : (@files, '', @chglo); + ugtk2::markup_to_TextView_format(join("\n", format_field(N("Name: ")) . $name, + format_field(N("Version: ")) . $version, + format_field(N("Architecture: ")) . $upkg->arch, + format_field(N("Size: ")) . N("%s KB", int($upkg->size/1024)), + if_( + $MODE eq 'update', + format_field(N("Importance: ")) . $descriptions->{$name}{importance} + ), + @source_info, + '', # extra empty line + format_field(N("Summary: ")) . $upkg->summary, + '', # extra empty line + if_( + $MODE eq 'update', + format_field(N("Reason for update: ")) . rpm_description($descriptions->{$name}{pre}), + ), + format_field(N("Description: ")), ($pkg->{description} || $descriptions->{$name}{description} || N("No description")), + @max_info, + )); +} + +sub warn_if_no_pkg { + my ($name) = @_; + my ($short_name) = split_fullname($name); + state $warned; + if (!$warned) { + $warned = 1; + interactive_msg(N("Warning"), + join("\n", + N("The package \"%s\" was found.", $name), + N("However this package is not in the package list."), + N("You may want to update your urpmi database."), + '', + N("Matching packages:"), + '', + join("\n", sort map { + #-PO: this is list fomatting: "- (medium: )" + #-PO: eg: "- rpmdragora (medium: "Main Release" + N("- %s (medium: %s)", $_, pkg2medium($pkgs->{$_}{pkg}, $urpm)->{name}); + } grep { /^$short_name/ } keys %$pkgs), + ), + scroll => 1, + ); + } + return 'XXX'; +} + +sub node_state { + my ($name) = @_; + #- checks $_[0] -> hack for partial tree displaying + return 'XXX' if !$name; + my $pkg = $pkgs->{$name}; + my $urpm_obj = $pkg->{pkg}; + return warn_if_no_pkg($name) if !$urpm_obj; + $pkg->{selected} ? + ($urpm_obj->flag_installed ? + ($urpm_obj->flag_upgrade ? 'to_install' : 'to_remove') + : 'to_install') + : ($urpm_obj->flag_installed ? + ($pkgs->{$name}{is_backport} ? 'backport' : + ($urpm_obj->flag_upgrade ? 'to_update' + : ($urpm_obj->flag_base ? 'base' : 'installed'))) + : 'uninstalled'); +} + +my ($common, $w, %wtree, %ptree, %pix); + +sub set_node_state { + my ($iter, $state, $model) = @_; + return if $state eq 'XXX' || !$state; + $pix{$state} ||= gtkcreate_pixbuf('state_' . $state); + $model->set($iter, $pkg_columns{state_icon} => $pix{$state}); + $model->set($iter, $pkg_columns{state} => $state); + $model->set($iter, $pkg_columns{selected} => to_bool(member($state, qw(base installed to_install)))); #$pkg->{selected})); + $model->set($iter, $pkg_columns{selectable} => to_bool($state ne 'base')); +} + +sub set_leaf_state { + my ($leaf, $state, $model) = @_; + set_node_state($_, $state, $model) foreach @{$ptree{$leaf}}; +} + +sub grep_unselected { grep { exists $pkgs->{$_} && !$pkgs->{$_}{selected} } @_ } + +my %groups_tree; + +sub add_parent { + my ($tree, $root, $state) = @_; + $tree or return undef; + $root or return undef; + my $parent = 0; + my $parentItem = 0; + my @items = split('\|', $root); + my $i = 0; + for my $item (@items) { + my $treeItem = new yui::YTreeItem($item, 0); + if($i == 0){ + $parent = $item; + $parentItem = $treeItem; + if(!defined($groups_tree{$item})) { + $groups_tree{$parent} = (); + $tree->addItem($treeItem); + } + }else{ + if(!grep {$_ eq $item} @{$groups_tree{$parent}}){ + push @{$groups_tree{$parent}}, $item; + } + #$treeItem = new yui::YTreeItem($parentItem, $item, 0); + $parentItem->addChild($treeItem); + } + $i++; + } + $tree->rebuildTree(); + #use Data::Dumper; + #my @chiavi = keys %groups_tree; + #for(keys %groups_tree){ + # print $_."\n"; + # print Dumper($groups_tree{$_}); + #} + # ORIGINAL + #if (my $w = $wtree{$root}) { return $w } + #my $s; + #foreach (split '\|', $root) { + #my $s2 = $s ? "$s|$_" : $_; + #$wtree{$s2} ||=do { + ##my $pixbuf = get_icon($s2, $s); + ##my $iter = $w->{tree_model}->append_set($s ? add_parent($s, $state) : undef, + ## [ $grp_columns{label} => $_, if_($pixbuf, $grp_columns{icon} => $pixbuf) ]); + ##my $iter = $w->{tree}->addItem($s ? add_parent($s, $state) : undef); + ##my $item = new yui::YTreeItem($s ? add_parent($s, $state) : undef, 0); + #print "S: $root :: $s :: $2 \n"; + #my $item = new yui::YTreeItem($s, 0); + #my $iter = $w->{tree}->currentItem()->addChild($item); + #$w->{tree}->rebuildTree(); + #$iter; + #}; + #$s = $s2; + #} + ##set_node_state($wtree{$s}, $state, $w->{tree_model}); #- use this state by default as tree is building. # + #$wtree{$s}; +} + +sub add_node { + my ($leaf, $root, $o_options) = @_; + my $state = node_state($leaf) or return; + if ($leaf) { + my $iter; + if (is_a_package($leaf)) { + my ($name, $version, $release, $arch) = split_fullname($leaf); + $iter = $w->{detail_list_model}->append_set([ $pkg_columns{text} => $leaf, + $pkg_columns{short_name} => format_name_n_summary($name, get_summary($leaf)), + $pkg_columns{version} => $version, + $pkg_columns{release} => $release, + $pkg_columns{arch} => $arch, + ]); + set_node_state($iter, $state, $w->{detail_list_model}); + $ptree{$leaf} = [ $iter ]; + } else { + $iter = $w->{tree_model}->append_set(add_parent($w->{tree},$root, $state), [ $grp_columns{label} => $leaf ]); + push @{$wtree{$leaf}}, $iter; + } + } else { + my $parent = add_parent($w->{tree}, $root, $state); + #- hackery for partial displaying of trees, used in rpmdragora: + #- if leaf is void, we may create the parent and one child (to have the [+] in front of the parent in the ctree) + #- though we use '' as the label of the child; then rpmdragora will connect on tree_expand, and whenever + #- the first child has '' as the label, it will remove the child and add all the "right" children + $o_options->{nochild} or $w->{tree_model}->append_set($parent, [ $grp_columns{label} => '' ]); # test $leaf? + } +} + +my ($prev_label); +sub update_size { + my ($common) = shift @_; + if ($w->{status}) { + my $new_label = $common->{get_status}(); + $prev_label ne $new_label and $w->{status}->setText($prev_label = $new_label); + } +} + +sub children { + my ($w) = @_; + map { $w->{detail_list_model}->get($_, $pkg_columns{text}) } gtktreeview_children($w->{detail_list_model}); +} + +sub toggle_all { + my ($common, $_val) = @_; + my $w = $common->{widgets}; + my @l = children($w) or return; + + my @unsel = grep_unselected(@l); + my @p = @unsel ? + #- not all is selected, select all if no option to potentially override + (exists $common->{partialsel_unsel} && $common->{partialsel_unsel}->(\@unsel, \@l) ? difference2(\@l, \@unsel) : @unsel) + : @l; + toggle_nodes($w->{detail_list}->window, $w->{detail_list_model}, \&set_leaf_state, node_state($p[0]), @p); + update_size($common); +} + +# ask_browse_tree_given_widgets_for_rpmdragora will run gtk+ loop. its main parameter "common" is a hash containing: +# - a "widgets" subhash which holds: +# o a "w" reference on a ugtk2 object +# o "tree" & "info" references a TreeView +# o "info" is a TextView +# o "tree_model" is the associated model of "tree" +# o "status" references a Label +# - some methods: get_info, node_state, build_tree, partialsel_unsel, grep_unselected, rebuild_tree, toggle_nodes, get_status +# - "tree_submode": the default mode (by group, ...), ... +# - "state": a hash of misc flags: => { flat => '0' }, +# o "flat": is the tree flat or not +# - "tree_mode": mode of the tree ("gui_pkgs", "by_group", ...) (mainly used by rpmdragora) + +sub ask_browse_tree_given_widgets_for_rpmdragora { + ($common) = @_; + $w = $common->{widgets}; + + $w->{detail_list} ||= $w->{tree}; + #$w->{detail_list_model} ||= $w->{tree_model}; + + $common->{add_parent} = \&add_parent; + my $clear_all_caches = sub { + %ptree = %wtree = (); + }; + $common->{clear_all_caches} = $clear_all_caches; + $common->{delete_all} = sub { + $clear_all_caches->(); + #$w->{detail_list_model}->clear; + #$w->{tree_model}->clear; + }; + $common->{rebuild_tree} = sub { + $common->{delete_all}->(); + $common->{build_tree}($common->{state}{flat}, $common->{tree_mode}); + update_size($common); + }; + $common->{delete_category} = sub { + my ($cat) = @_; + exists $wtree{$cat} or return; + %ptree = (); + + if (exists $wtree{$cat}) { + my $_iter_str = $w->{tree_model}->get_path_str($wtree{$cat}); + $w->{tree_model}->remove($wtree{$cat}); + delete $wtree{$cat}; + } + update_size($common); + }; + $common->{add_nodes} = sub { + my (@nodes) = @_; + print "@nodes\n"; + #$w->{detail_list}->clear; + #$w->{detail_list}->scroll_to_point(0, 0); + add_node($_->[0], $_->[1], $_->[2]) foreach @nodes; + update_size($common); + }; + + $common->{display_info} = sub { + gtktext_insert($w->{info}, get_info($_[0], $w->{tree}->window)); + $w->{info}->scroll_to_iter($w->{info}->get_buffer->get_start_iter, 0, 0, 0, 0); + 0; + }; + + my $fast_toggle = sub { + my ($iter) = @_; + gtkset_mousecursor_wait($w->{w}{rwindow}->window); + my $_cleaner = before_leaving { gtkset_mousecursor_normal($w->{w}{rwindow}->window) }; + my $name = $w->{detail_list_model}->get($iter, $pkg_columns{text}); + my $urpm_obj = $pkgs->{$name}{pkg}; + + if ($urpm_obj->flag_base) { + interactive_msg(N("Warning"), + N("Removing package %s would break your system", $name)); + return ''; + } + + if ($urpm_obj->flag_skip) { + interactive_msg(N("Warning"), N("The \"%s\" package is in urpmi skip list.\nDo you want to select it anyway?", $name), yesno => 1) or return ''; + $urpm_obj->set_flag_skip(0); + } + + if ($Rpmdragora::pkg::need_restart && !$priority_up_alread_warned) { + $priority_up_alread_warned = 1; + interactive_msg(N("Warning"), '' . N("Rpmdragora or one of its priority dependencies needs to be updated first. Rpmdragora will then restart.") . '' . "\n\n"); + } + + toggle_nodes($w->{tree}->window, $w->{detail_list_model}, \&set_leaf_state, $w->{detail_list_model}->get($iter, $pkg_columns{state}), + $w->{detail_list_model}->get($iter, $pkg_columns{text})); + update_size($common); + }; + #$w->{detail_list}->get_selection->signal_connect(changed => sub { + #my ($model, $iter) = $_[0]->get_selected; + #$model && $iter or return; + # $common->{display_info}($model->get($iter, $pkg_columns{text})); + #}); + #($w->{detail_list}->get_column(0)->get_cell_renderers)[0]->signal_connect(toggled => sub { + # my ($_cell, $path) = @_; #text_ + # my $iter = $w->{detail_list_model}->get_iter_from_string($path); + # $fast_toggle->($iter) if $iter; + # 1; + #}); + $common->{rebuild_tree}->(); + update_size($common); + #$common->{initial_selection} and toggle_nodes($w->{tree}->window, $w->{detail_list_model}, \&set_leaf_state, undef, @{$common->{initial_selection}}); + #my $_b = before_leaving { $clear_all_caches->() }; + $common->{init_callback}->() if $common->{init_callback}; + #OLD $w->{w}->main; + $w->{w}; +} + +our $find_entry; + +sub reset_search() { + return if !$common; + $common->{delete_category}->($_) foreach $results_ok, $results_none; + # clear package list: + $common->{add_nodes}->(); +} + +sub is_a_package { + my ($pkg) = @_; + return exists $pkgs->{$pkg}; +} + +sub switch_pkg_list_mode { + my ($mode) = @_; + return if !$mode; + return if !$filter_methods{$mode}; + $force_displaying_group = 1; + $filter_methods{$mode}->(); +} + +sub is_updatable { + my $p = $pkgs->{$_[0]}; + $p->{pkg} && !$p->{selected} && $p->{pkg}->flag_installed && $p->{pkg}->flag_upgrade; +} + +sub pkgs_provider { + my ($mode, %options) = @_; + return if !$mode; + my $h = &get_pkgs(%options); + ($urpm, $descriptions) = @$h{qw(urpm update_descr)}; + $pkgs = $h->{all_pkgs}; + %filters = ( + non_installed => $h->{installable}, + installed => $h->{installed}, + all => [ keys %$pkgs ], + ); + my %tmp_filter_methods = ( + all => sub { + [ difference2([ keys %$pkgs ], $h->{inactive_backports}) ]; + }, + all_updates => sub { + # potential "updates" from media not tagged as updates: + if (!$options{pure_updates} && !$Rpmdragora::pkg::need_restart) { + [ @{$h->{updates}}, + difference2([ grep { is_updatable($_) } @{$h->{installable}} ], $h->{backports}) ]; + } else { + [ difference2($h->{updates}, $h->{inactive_backports}) ]; + } + }, + backports => sub { $h->{backports} }, + meta_pkgs => sub { + [ difference2($h->{meta_pkgs}, $h->{inactive_backports}) ]; + }, + gui_pkgs => sub { + [ difference2($h->{gui_pkgs}, $h->{inactive_backports}) ]; + }, + ); + foreach my $importance (qw(bugfix security normal)) { + $tmp_filter_methods{$importance} = sub { + my @media = keys %$descriptions; + [ grep { + my ($name) = split_fullname($_); + my $medium = find { $descriptions->{$_}{$name} } @media; + $medium && $descriptions->{$medium}{$name}{importance} eq $importance } @{$h->{updates}} ]; + }; + } + + undef %filter_methods; + foreach my $type (keys %tmp_filter_methods) { + $filter_methods{$type} = sub { + $force_rebuild = 1; # force rebuilding tree since we changed filter (FIXME: switch to SortModel) + @filtered_pkgs = intersection($filters{$filter->[0]}, $tmp_filter_methods{$type}->()); + }; + } + + switch_pkg_list_mode($mode); +} + +sub closure_removal { + local $urpm->{state} = {}; + urpm::select::find_packages_to_remove($urpm, $urpm->{state}, \@_); +} + +sub is_locale_available { + my ($name) = @_; + any { $urpm->{depslist}[$_]->flag_selected } keys %{$urpm->{provides}{$name} || {}} and return 1; + my $found; + open_rpm_db()->traverse_tag_find('name', $name, sub { $found = 1 }); + return $found; +} + +sub callback_choices { + my (undef, undef, undef, $choices) = @_; + return $choices->[0] if $::rpmdragora_options{auto}; + foreach my $pkg (@$choices) { + foreach ($pkg->requires_nosense) { + /locales-/ or next; + is_locale_available($_) and return $pkg; + } + } + my $callback = sub { interactive_msg(N("More information on package..."), get_info($_[0]), scroll => 1) }; + $choices = [ sort { $a->name cmp $b->name } @$choices ]; + my @choices = interactive_list_(N("Please choose"), (scalar(@$choices) == 1 ? + N("The following package is needed:") : N("One of the following packages is needed:")), + [ map { urpm_name($_) } @$choices ], $callback, nocancel => 1); + defined $choices[0] ? $choices->[$choices[0]] : undef; +} + +sub deps_msg { + return 1 if $dont_show_selections->[0]; + my ($title, $msg, $nodes, $nodes_with_deps) = @_; + my @deps = sort { $a cmp $b } difference2($nodes_with_deps, $nodes); + @deps > 0 or return 1; + deps_msg_again: + my $results = interactive_msg( + $title, $msg . + format_list(map { scalar(urpm::select::translate_why_removed_one($urpm, $urpm->{state}, $_)) } @deps) + . "\n\n" . format_size($urpm->selected_size($urpm->{state})), + yesno => [ N("Cancel"), N("More info"), N("Ok") ], + scroll => 1, + ); + if ($results eq + #-PO: Keep it short, this is gonna be on a button + N("More info")) { + interactive_packtable( + N("Information on packages"), + $::main_window, + undef, + [ map { my $pkg = $_; + [ gtknew('HBox', children_tight => [ gtkset_selectable(gtknew('Label', text => $pkg), 1) ]), + gtknew('Button', text => N("More information on package..."), + clicked => sub { + interactive_msg(N("More information on package..."), get_info($pkg), scroll => 1); + }) ] } @deps ], + [ gtknew('Button', text => N("Ok"), + clicked => sub { Gtk2->main_quit }) ] + ); + goto deps_msg_again; + } else { + return $results eq N("Ok"); + } +} + +sub toggle_nodes { + my ($widget, $model, $set_state, $old_state, @nodes) = @_; + @nodes = grep { exists $pkgs->{$_} } @nodes + or return; + #- avoid selecting too many packages at once + return if !$dont_show_selections->[0] && @nodes > 2000; + my $new_state = !$pkgs->{$nodes[0]}{selected}; + + my @nodes_with_deps; + + my $bar_id = statusbar_msg(N("Checking dependencies of package..."), 0); + + my $warn_about_additional_packages_to_remove = sub { + my ($msg) = @_; + statusbar_msg_remove($bar_id); + deps_msg(N("Some additional packages need to be removed"), + formatAlaTeX($msg) . "\n\n", + \@nodes, \@nodes_with_deps) or @nodes_with_deps = (); + }; + + if (member($old_state, qw(to_remove installed))) { # remove pacckages + if ($new_state) { + my @remove; + slow_func($widget, sub { @remove = closure_removal(@nodes) }); + @nodes_with_deps = grep { !$pkgs->{$_}{selected} && !/^basesystem/ } @remove; + $warn_about_additional_packages_to_remove->( + N("Because of their dependencies, the following package(s) also need to be removed:")); + my @impossible_to_remove; + foreach (grep { exists $pkgs->{$_}{base} } @remove) { + ${$pkgs->{$_}{base}} == 1 ? push @impossible_to_remove, $_ : ${$pkgs->{$_}{base}}--; + } + @impossible_to_remove and interactive_msg(N("Some packages cannot be removed"), + N("Removing these packages would break your system, sorry:\n\n") . + format_list(@impossible_to_remove)); + @nodes_with_deps = difference2(\@nodes_with_deps, \@impossible_to_remove); + } else { + slow_func($widget, + sub { @nodes_with_deps = grep { intersection(\@nodes, [ closure_removal($_) ]) } + grep { $pkgs->{$_}{selected} && !member($_, @nodes) } keys %$pkgs }); + push @nodes_with_deps, @nodes; + $warn_about_additional_packages_to_remove->( + N("Because of their dependencies, the following package(s) must be unselected now:\n\n")); + $pkgs->{$_}{base} && ${$pkgs->{$_}{base}}++ foreach @nodes_with_deps; + } + } else { + if ($new_state) { + if (@nodes > 1) { + #- unselect i18n packages of which locales is not already present (happens when user clicks on KDE group) + my @bad_i18n_pkgs; + foreach my $sel (@nodes) { + foreach ($pkgs->{$sel}{pkg}->requires_nosense) { + /locales-([^-]+)/ or next; + $sel =~ /-$1[-_]/ && !is_locale_available($_) and push @bad_i18n_pkgs, $sel; + } + } + @nodes = difference2(\@nodes, \@bad_i18n_pkgs); + } + my @requested; + slow_func( + $widget, + sub { + @requested = $urpm->resolve_requested( + open_rpm_db(), $urpm->{state}, + { map { $pkgs->{$_}{pkg}->id => 1 } @nodes }, + callback_choices => \&callback_choices, + ); + }, + ); + @nodes_with_deps = map { urpm_name($_) } @requested; + statusbar_msg_remove($bar_id); + if (!deps_msg(N("Additional packages needed"), + formatAlaTeX(N("To satisfy dependencies, the following package(s) also need to be installed:\n\n")) . "\n\n", + \@nodes, \@nodes_with_deps)) { + @nodes_with_deps = (); + $urpm->disable_selected(open_rpm_db(), $urpm->{state}, @requested); + goto packages_selection_ok; + } + + if (my $conflicting_msg = urpm::select::conflicting_packages_msg($urpm, $urpm->{state})) { + if (!interactive_msg(N("Conflicting Packages"), $conflicting_msg, yesno => 1, scroll => 1)) { + @nodes_with_deps = (); + $urpm->disable_selected(open_rpm_db(), $urpm->{state}, @requested); + goto packages_selection_ok; + } + } + + if (my @cant = sort(difference2(\@nodes, \@nodes_with_deps))) { + my @ask_unselect = urpm::select::unselected_packages($urpm->{state}); + my @reasons = map { + my $cant = $_; + my $unsel = find { $_ eq $cant } @ask_unselect; + $unsel + ? join("\n", urpm::select::translate_why_unselected($urpm, $urpm->{state}, $unsel)) + : ($pkgs->{$_}{pkg}->flag_skip ? N("%s (belongs to the skip list)", $cant) : $cant); + } @cant; + my $count = @reasons; + interactive_msg( + ($count == 1 ? N("One package cannot be installed") : N("Some packages cannot be installed")), + ($count == 1 ? + N("Sorry, the following package cannot be selected:\n\n%s", format_list(@reasons)) + : N("Sorry, the following packages cannot be selected:\n\n%s", format_list(@reasons))), + scroll => 1, + ); + foreach (@cant) { + next unless $pkgs->{$_}{pkg}; + $pkgs->{$_}{pkg}->set_flag_requested(0); + $pkgs->{$_}{pkg}->set_flag_required(0); + } + } + packages_selection_ok: + } else { + my @unrequested; + slow_func($widget, + sub { @unrequested = $urpm->disable_selected(open_rpm_db(), $urpm->{state}, + map { $pkgs->{$_}{pkg} } @nodes) }); + @nodes_with_deps = map { urpm_name($_) } @unrequested; + statusbar_msg_remove($bar_id); + if (!deps_msg(N("Some packages need to be removed"), + N("Because of their dependencies, the following package(s) must be unselected now:\n\n"), + \@nodes, \@nodes_with_deps)) { + @nodes_with_deps = (); + $urpm->resolve_requested(open_rpm_db(), $urpm->{state}, { map { $_->id => 1 } @unrequested }); + goto packages_unselection_ok; + } + packages_unselection_ok: + } + } + + foreach (@nodes_with_deps) { + #- some deps may exist on some packages which aren't listed because + #- not upgradable (older than what currently installed) + exists $pkgs->{$_} or next; + if (!$pkgs->{$_}{pkg}) { #- can't be removed # FIXME; what about next packages in the loop? + undef $pkgs->{$_}{selected}; + log::explanations("can't be removed: $_"); + } else { + $pkgs->{$_}{selected} = $new_state; + } + $set_state->($_, node_state($_), $model); + if (my $pkg = $pkgs->{$_}{pkg}) { + # FIXME: shouldn't we threat all of them as POSITIVE (as selected size) + $size_selected += $pkg->size * ($pkg->flag_installed && !$pkg->flag_upgrade ? ($new_state ? -1 : 1) : ($new_state ? 1 : -1)); + } + } +} + +sub is_there_selected_packages() { + int(grep { $pkgs->{$_}{selected} } keys %$pkgs); +} + +sub real_quit() { + if (is_there_selected_packages()) { + interactive_msg(N("Some packages are selected."), N("Some packages are selected.") . "\n" . N("Do you really want to quit?"), yesno => 1) or return; + } + Gtk2->main_quit; +} + +sub do_action__real { + my ($options, $callback_action, $o_info) = @_; + require urpm::sys; + if (!urpm::sys::check_fs_writable()) { + $urpm->{fatal}(1, N("Error: %s appears to be mounted read-only.", $urpm::sys::mountpoint)); + return 1; + } + if (!$Rpmdragora::pkg::need_restart && !is_there_selected_packages()) { + interactive_msg(N("You need to select some packages first."), N("You need to select some packages first.")); + return 1; + } + my $size_added = sum(map { if_($_->flag_selected && !$_->flag_installed, $_->size) } @{$urpm->{depslist}}); + if ($MODE eq 'install' && $size_free - $size_added/1024 < 50*1024) { + interactive_msg(N("Too many packages are selected"), + N("Warning: it seems that you are attempting to add so many +packages that your filesystem may run out of free diskspace, +during or after package installation ; this is particularly +dangerous and should be considered with care. + +Do you really want to install all the selected packages?"), yesno => 1) + or return 1; + } + my $res = $callback_action->($urpm, $pkgs); + if (!$res) { + $force_rebuild = 1; + pkgs_provider($options->{tree_mode}, if_($Rpmdragora::pkg::probe_only_for_updates, pure_updates => 1), skip_updating_mu => 1); + reset_search(); + $size_selected = 0; + (undef, $size_free) = MDK::Common::System::df('/usr'); + $options->{rebuild_tree}->() if $options->{rebuild_tree}; + gtktext_insert($o_info, '') if $o_info; + } + $res; +} + +sub do_action { + my ($options, $callback_action, $o_info) = @_; + my $res = eval { do_action__real($options, $callback_action, $o_info) }; + my $err = $@; + # FIXME: offer to report the problem into bugzilla: + if ($err && $err !~ /cancel_perform/) { + interactive_msg(N("Fatal error"), + N("A fatal error occurred: %s.", $err)); + } + $res; +} + +sub translate_group { + join('/', map { translate($_) } split m|/|, $_[0]); +} + +sub ctreefy { + join('|', map { translate($_) } split m|/|, $_[0]); +} + +sub _build_tree { + my ($tree, $elems, @elems) = @_; + #- we populate all the groups tree at first + %$elems = (); + # better loop on packages, create groups tree and push packages in the proper place: + foreach my $pkg (@elems) { + my $grp = $pkg->[1]; + # no state for groups (they're not packages and thus have no state) + add_parent($tree, $grp, undef); + $elems->{$grp} ||= []; + push @{$elems->{$grp}}, $pkg; + } +} + + +sub build_tree { + my ($tree, $tree_model, $elems, $options, $force_rebuild, $flat, $mode) = @_; + state $old_mode; + $mode = $options->{rmodes}{$mode} || $mode; + return if $old_mode eq $mode && !$force_rebuild; + $old_mode = $mode; + undef $force_rebuild; + my @elems; + my $wait; $wait = statusbar_msg(N("Please wait, listing packages...")) if $MODE ne 'update'; + #gtkflush(); + { + my @keys = @filtered_pkgs; + if (member($mode, qw(all_updates security bugfix normal))) { + @keys = grep { + my ($name) = split_fullname($_); + member($descriptions->{$name}{importance}, @$mandrakeupdate_wanted_categories) + || ! $descriptions->{$name}{importance}; + } @keys; + if (@keys == 0) { + add_node($tree, '', N("(none)"), { nochild => 1 }); + state $explanation_only_once; + $explanation_only_once or interactive_msg(N("No update"), + N("The list of updates is empty. This means that either there is +no available update for the packages installed on your computer, +or you already installed all of them.")); + $explanation_only_once = 1; + } + } + # FIXME: better do this on first group access for faster startup... + @elems = map { [ $_, !$flat && ctreefy($pkgs->{$_}{pkg}->group) ] } sort_packages(@keys); + } + my %sortmethods = ( + by_size => sub { sort { $pkgs->{$b->[0]}{pkg}->size <=> $pkgs->{$a->[0]}{pkg}->size } @_ }, + by_selection => sub { sort { $pkgs->{$b->[0]}{selected} <=> $pkgs->{$a->[0]}{selected} + || uc($a->[0]) cmp uc($b->[0]) } @_ }, + by_leaves => sub { + # inlining part of MDK::Common::Data::difference2(): + my %l; @l{map { $_->[0] } @_} = (); + my @pkgs_times = ('rpm', '-q', '--qf', '%{name}-%{version}-%{release}.%{arch} %{installtime}\n', + map { chomp_($_) } run_program::get_stdout('urpmi_rpm-find-leaves')); + sort { $b->[1] <=> $a->[1] } grep { exists $l{$_->[0]} } map { chomp; [ split ] } run_rpm(@pkgs_times); + }, + flat => sub { no locale; sort { uc($a->[0]) cmp uc($b->[0]) } @_ }, + by_medium => sub { sort { $a->[2] <=> $b->[2] || uc($a->[0]) cmp uc($b->[0]) } @_ }, + ); + if ($flat) { + add_node($_->[0], '') foreach $sortmethods{$::mode->[0] || 'flat'}->(@elems); + } else { + if (0 && $MODE eq 'update') { + add_node($tree, $_->[0], N("All")) foreach $sortmethods{flat}->(@elems); + $tree->expand_row($tree_model->get_path($tree_model->get_iter_first), 0); + } elsif ($::mode->[0] eq 'by_source') { + _build_tree($tree, $elems, $sortmethods{by_medium}->(map { + my $m = pkg2medium($pkgs->{$_->[0]}{pkg}, $urpm); [ $_->[0], $m->{name}, $m->{priority} ]; + } @elems)); + } elsif ($::mode->[0] eq 'by_presence') { + _build_tree($tree, $elems, map { + my $pkg = $pkgs->{$_->[0]}{pkg}; + [ $_->[0], $pkg->flag_installed ? + (!$pkg->flag_skip && $pkg->flag_upgrade ? N("Upgradable") : N("Installed")) + : N("Addable") ]; + } $sortmethods{flat}->(@elems)); + } else { + _build_tree($tree, $elems, @elems); + } + } + statusbar_msg_remove($wait) if defined $wait; +} + +sub get_info { + my ($key, $widget) = @_; + #- the package information hasn't been loaded. Instead of rescanning the media, just give up. + exists $pkgs->{$key} or return [ [ N("Description not available for this package\n") ] ]; + #- get the description if needed: + exists $pkgs->{$key}{description} or slow_func($widget, sub { extract_header($pkgs->{$key}, $urpm, 'info', find_installed_version($pkgs->{$key}{pkg})) }); + format_pkg_simplifiedinfo($pkgs, $key, $urpm, $descriptions); +} + +sub sort_callback { + my ($store, $treeiter1, $treeiter2) = @_; + URPM::rpmvercmp(map { $store->get_value($_, $pkg_columns{version}) } $treeiter1, $treeiter2); +} + +sub run_help_callback { + my (undef, $url) = @_; + my ($user) = grep { $_->[2] eq $ENV{USERHELPER_UID} } list_passwd(); + local $ENV{HOME} = $user->[7] if $user && $ENV{USERHELPER_UID}; + run_program::raw({ detach => 1, as_user => 1 }, 'www-browser', $url); +} + +1; diff --git a/AdminPanel/Rpmdragora/gurpm.pm b/AdminPanel/Rpmdragora/gurpm.pm new file mode 100644 index 0000000..412d589 --- /dev/null +++ b/AdminPanel/Rpmdragora/gurpm.pm @@ -0,0 +1,133 @@ +package AdminPanel::Rpmdragora::gurpm; +#***************************************************************************** +# +# Copyright (c) 2002 Guillaume Cottenceau +# Copyright (c) 2002-2007 Thierry Vignaud +# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +# Copyright (c) 2005-2007 Mandriva SA +# Copyright (c) 2013 Matteo Pasotti +# +# 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: gurpm.pm 255450 2009-04-03 16:00:16Z tv $ + +package AdminPanel::Rpmdragora::gurpm; + +use strict; +use lib qw(/usr/lib/libDrakX); +use yui; +use Time::HiRes; +use feature 'state'; + +sub new { + my ($class, $title, $initializing, %options) = @_; + my $self = { + my $label = 0, + my $factory = 0, + my $mainw = 0, + my $vbox = 0, + my $progressbar = 0, + my $cancel = 0 + }; + bless $self, 'AdminPanel::Rpmdragora::gurpm'; + #my $mainw = bless(ugtk2->new($title, %options, default_width => 600, width => 600), $self); + $self->{factory} = yui::YUI::widgetFactory; + $self->{mainw} = $self->{factory}->createPopupDialog(); + $::main_window = $self->{mainw}; + $self->{vbox} = $self->{factory}->createVBox($self->{mainw}); + #OLD $mainw->{label} = gtknew('Label', text => $initializing, alignment => [ 0.5, 0 ]); + $self->{label} = $self->{factory}->createLabel($self->{vbox}, $initializing); + # size label's heigh to 2 lines in order to prevent dummy vertical resizing: + #my $context = $mainw->{label}->get_layout->get_context; + #my $metrics = $context->get_metrics($mainw->{label}->style->font_desc, $context->get_language); + #$mainw->{label}->set_size_request(-1, 2 * Gtk2::Pango->PANGO_PIXELS($metrics->get_ascent + $metrics->get_descent)); + + #OLD $mainw->{progressbar} = gtknew('ProgressBar'); + $self->{progressbar} = $self->{factory}->createProgressBar($self->{vbox}, ""); + #gtkadd($mainw->{window}, $mainw->{vbox} = gtknew('VBox', spacing => 5, border_width => 6, children_tight => [ + # $mainw->{label}, + # $mainw->{progressbar} + #])); + #$mainw->{rwindow}->set_position('center-on-parent'); + #$mainw->{real_window}->show_all; + #select(undef, undef, undef, 0.1); #- hackish :-( + #$mainw->SUPER::sync; + $self->{mainw}->recalcLayout(); + $self->{mainw}->doneMultipleChanges(); + $self; +} + +sub label { + my ($self, $label) = @_; + $self->{label} = $self->{factory}->createLabel($self->{vbox},$label); + #select(undef, undef, undef, 0.1); #- hackish :-( + #$self->flush; +} + +sub progress { + my ($self, $value) = @_; + state $time; + $value = 0 if $value < 0; + $value = 100 if 1 < $value; + $self->{progressbar}->setValue($value); + return if Time::HiRes::clock_gettime() - $time < 0.333; + $time = Time::HiRes::clock_gettime(); + #$self->flush; +} + +sub DESTROY { + my ($self) = @_; + #mygtk2::may_destroy($self); + $self and $self->{mainw}->destroy; + #$self = undef; + $self->{cancel} = undef; #- in case we'll do another one later +} + +sub validate_cancel { + my ($self, $cancel_msg, $cancel_cb) = @_; + if (!$self->{cancel}) { + $self->{cancel} = $self->{factory}->createIconButton($self->{vbox},"",$cancel_msg); + #gtkpack__( + #$self->{vbox}, + #$self->{hbox_cancel} = gtkpack__( + #gtknew('HButtonBox'), + #$self->{cancel} = gtknew('Button', text => $cancel_msg, clicked => \&$cancel_cb), + #), + #); + } + #$self->{cancel}->set_sensitive(1); + #$self->{cancel}->show; + $self->{mainw}->recalcLayout(); + $self->{mainw}->doneMultipleChanges(); +} + +sub invalidate_cancel { + my ($self) = @_; + $self->{cancel} and $self->{cancel}->setEnabled(0); +} + +sub invalidate_cancel_forever { + my ($self) = @_; + #$self->{hbox_cancel} or return; + #$self->{hbox_cancel}->destroy; + # FIXME: temporary workaround that prevents + # Gtk2::Label::set_text() set_text_internal() -> queue_resize() -> + # size_allocate() call chain to mess up when ->shrink_topwindow() + # has been called (#32613): + #$self->shrink_topwindow; +} + +1; diff --git a/AdminPanel/Rpmdragora/icon.pm b/AdminPanel/Rpmdragora/icon.pm new file mode 100644 index 0000000..2616aad --- /dev/null +++ b/AdminPanel/Rpmdragora/icon.pm @@ -0,0 +1,231 @@ +package AdminPanel::Rpmdragora::icon; +#***************************************************************************** +# +# Copyright (c) 2002 Guillaume Cottenceau +# Copyright (c) 2002-2007 Thierry Vignaud +# 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: icon.pm 237459 2008-02-26 14:20:47Z tv $ + +use strict; +our @ISA = qw(Exporter); +use lib qw(/usr/lib/libDrakX); +use common; + + +our @EXPORT = qw(get_icon); +#- /usr/share/rpmlint/config (duplicates are normal, so that we are not too far away from .py) +my %group_icons = ( + N_("All") => 'system_section', + N_("Accessibility") => 'accessibility_section', + N_("Archiving") => 'archiving_section', + join('|', N_("Archiving"), N_("Backup")) => 'backup_section', + join('|', N_("Archiving"), N_("Cd burning")) => 'cd_burning_section', + join('|', N_("Archiving"), N_("Compression")) => 'compression_section', + join('|', N_("Archiving"), N_("Other")) => 'other_archiving', + N_("Communications") => 'communications_section', + join('|', N_("Communications"), N_("Bluetooth")) => 'communications_section', + join('|', N_("Communications"), N_("Bluetooth")) => 'communications_section', + join('|', N_("Communications"), N_("Dial-Up")) => 'communications_section', + join('|', N_("Communications"), N_("Fax")) => 'communications_section', + join('|', N_("Communications"), N_("Mobile")) => 'communications_section', + join('|', N_("Communications"), N_("Radio")) => 'communications_section', + join('|', N_("Communications"), N_("Serial")) => 'communications_section', + join('|', N_("Communications"), N_("Telephony")) => 'communications_section', + N_("Databases") => 'databases_section', + N_("Development") => 'development_section', + join('|', N_("Development"), N_("Basic")) => '', + join('|', N_("Development"), N_("C")) => '', + join('|', N_("Development"), N_("C++")) => '', + join('|', N_("Development"), N_("C#")) => '', + join('|', N_("Development"), N_("Databases")) => 'databases_section', + join('|', N_("Development"), N_("Erlang")) => '', + join('|', N_("Development"), N_("GNOME and GTK+")) => 'gnome_section', + join('|', N_("Development"), N_("Java")) => '', + join('|', N_("Development"), N_("KDE and Qt")) => 'kde_section', + join('|', N_("Development"), N_("Kernel")) => 'hardware_configuration_section', + join('|', N_("Development"), N_("OCaml")) => '', + join('|', N_("Development"), N_("Other")) => 'development_tools_section', + join('|', N_("Development"), N_("Perl")) => '', + join('|', N_("Development"), N_("PHP")) => '', + join('|', N_("Development"), N_("Python")) => '', + join('|', N_("Development"), N_("Tools")) => '', + join('|', N_("Development"), N_("X11")) => 'office_section', + N_("Documentation") => 'documentation_section', + N_("Editors") => 'emulators_section', + N_("Education") => 'education_section', + N_("Emulators") => 'emulators_section', + N_("File tools") => 'file_tools_section', + N_("Games") => 'amusement_section', + join('|', N_("Games"), N_("Adventure")) => 'adventure_section', + join('|', N_("Games"), N_("Arcade")) => 'arcade_section', + join('|', N_("Games"), N_("Boards")) => 'boards_section', + join('|', N_("Games"), N_("Cards")) => 'cards_section', + join('|', N_("Games"), N_("Other")) => 'other_amusement', + join('|', N_("Games"), N_("Puzzles")) => 'puzzle_section', + join('|', N_("Games"), N_("Shooter")) => 'other_amusement', + join('|', N_("Games"), N_("Sports")) => 'sport_section', + join('|', N_("Games"), N_("Strategy")) => 'strategy_section', + N_("Geography") => 'geosciences_section', + N_("Graphical desktop") => 'office_section', + join('|', N_("Graphical desktop"), + #-PO: This is a package/product name. Only translate it if needed: + N_("Enlightenment")) => '', + join('|', N_("Graphical desktop"), N_("FVWM based")) => '', + join('|', N_("Graphical desktop"), + #-PO: This is a package/product name. Only translate it if needed: + N_("GNOME")) => 'gnome_section', + join('|', N_("Graphical desktop"), + #-PO: This is a package/product name. Only translate it if needed: + N_("Icewm")) => '', + join('|', N_("Graphical desktop"), + #-PO: This is a package/product name. Only translate it if needed: + N_("KDE")) => 'kde_section', + join('|', N_("Graphical desktop"), N_("Other")) => 'more_applications_other_section', + join('|', N_("Graphical desktop"), + #-PO: This is a package/product name. Only translate it if needed: + N_("Sawfish")) => '', + join('|', N_("Graphical desktop"), + #-PO: This is a package/product name. Only translate it if needed: + N_("WindowMaker")) => '', + join('|', N_("Graphical desktop"), + #-PO: This is a package/product name. Only translate it if needed: + N_("Xfce")) => '', + N_("Graphics") => 'graphics_section', + join('|', N_("Graphics"), N_("3D")) => '', + join('|', N_("Graphics"), N_("Editors")) => '', + join('|', N_("Graphics"), N_("Other")) => '', + join('|', N_("Graphics"), N_("Photography")) => '', + join('|', N_("Graphics"), N_("Scanning")) => '', + join('|', N_("Graphics"), N_("Viewers")) => '', + N_("Monitoring") => 'monitoring_section', + N_("Multimedia") => 'multimedia_section', + join('|', N_("Multimedia"), N_("Video")) => 'video_section', + N_("Networking") => 'networking_section', + join('|', N_("Networking"), N_("Chat")) => 'chat_section', + join('|', N_("Networking"), N_("File transfer")) => 'file_transfer_section', + join('|', N_("Networking"), N_("IRC")) => 'irc_section', + join('|', N_("Networking"), N_("Instant messaging")) => 'instant_messaging_section', + join('|', N_("Networking"), N_("Mail")) => 'mail_section', + join('|', N_("Networking"), N_("News")) => 'news_section', + join('|', N_("Networking"), N_("Other")) => 'other_networking', + join('|', N_("Networking"), N_("Remote access")) => 'remote_access_section', + join('|', N_("Networking"), N_("WWW")) => 'networking_www_section', + N_("Office") => 'office_section', + join('|', N_("Office"), N_("Dictionary")) => '', + join('|', N_("Office"), N_("Finance")) => '', + join('|', N_("Office"), N_("Management")) => '', + join('|', N_("Office"), N_("Organizer")) => '', + join('|', N_("Office"), N_("Other")) => '', + join('|', N_("Office"), N_("Spreadsheet")) => '', + join('|', N_("Office"), N_("Suite")) => '', + join('|', N_("Office"), N_("Word processor")) => '', + N_("Public Keys") => 'packaging_section', + N_("Publishing") => 'publishing_section', + N_("Security") => 'packaging_section', + N_("Sciences") => 'sciences_section', + join('|', N_("Sciences"), N_("Astronomy")) => 'astronomy_section', + join('|', N_("Sciences"), N_("Biology")) => 'biology_section', + join('|', N_("Sciences"), N_("Chemistry")) => 'chemistry_section', + join('|', N_("Sciences"), N_("Computer science")) => 'computer_science_section', + join('|', N_("Sciences"), N_("Geosciences")) => 'geosciences_section', + join('|', N_("Sciences"), N_("Mathematics")) => 'mathematics_section', + join('|', N_("Sciences"), N_("Other")) => 'other_sciences', + join('|', N_("Sciences"), N_("Physics")) => 'physics_section', + N_("Shells") => 'shells_section', + N_("Sound") => 'sound_section', + join('|', N_("Sound"), N_("Editors and Converters")) => '', + join('|', N_("Sound"), N_("Midi")) => '', + join('|', N_("Sound"), N_("Mixers")) => '', + join('|', N_("Sound"), N_("Players")) => '', + join('|', N_("Sound"), N_("Utilities")) => '', + join('|', N_("Sound"), N_("Visualization")) => '', + N_("System") => 'system_section', + join('|', N_("System"), N_("Base")) => 'system_section', + join('|', N_("System"), N_("Cluster")) => 'parallel_computing_section', + join('|', N_("System"), N_("Configuration")) => 'configuration_section', + join('|', N_("System"), N_("Configuration"), N_("Boot and Init")) => 'boot_init_section', + join('|', N_("System"), N_("Configuration"), N_("Hardware")) => 'hardware_configuration_section', + join('|', N_("System"), N_("Configuration"), N_("Networking")) => 'networking_configuration_section', + join('|', N_("System"), N_("Configuration"), N_("Other")) => 'system_other_section', + join('|', N_("System"), N_("Configuration"), N_("Packaging")) => 'packaging_section', + join('|', N_("System"), N_("Configuration"), N_("Printing")) => 'printing_section', + join('|', N_("System"), N_("Fonts")) => 'chinese_section', + join('|', N_("System"), N_("Fonts"), N_("Console")) => 'interpreters_section', + join('|', N_("System"), N_("Fonts"), N_("True type")) => '', + join('|', N_("System"), N_("Fonts"), N_("Type1")) => '', + join('|', N_("System"), N_("Fonts"), N_("X11 bitmap")) => '', + join('|', N_("System"), N_("Internationalization")) => 'chinese_section', + join('|', N_("System"), N_("Kernel and hardware")) => 'hardware_configuration_section', + join('|', N_("System"), N_("Libraries")) => '', + join('|', N_("System"), N_("Printing")) => 'printing_section', + join('|', N_("System"), N_("Servers")) => '', + join('|', N_("System"), + #-PO: This is a package/product name. Only translate it if needed: + N_("X11")) => 'office_section', + N_("Terminals") => 'terminals_section', + N_("Text tools") => 'text_tools_section', + N_("Toys") => 'toys_section', + N_("Video") => 'video_section', + join('|', N_("Video"), N_("Editors and Converters")) => '', + join('|', N_("Video"), N_("Players")) => '', + join('|', N_("Video"), N_("Utilities")) => '', + + # for Mageia Choice: + N_("Workstation") => 'office_section', + join('|', N_("Workstation"), N_("Configuration")) => 'configuration_section', + join('|', N_("Workstation"), N_("Console Tools")) => 'interpreters_section', + join('|', N_("Workstation"), N_("Documentation")) => 'documentation_section', + join('|', N_("Workstation"), N_("Game station")) => 'amusement_section', + join('|', N_("Workstation"), N_("Internet station")) => 'networking_section', + join('|', N_("Workstation"), N_("Multimedia station")) => 'multimedia_section', + join('|', N_("Workstation"), N_("Network Computer (client)")) => 'other_networking', + join('|', N_("Workstation"), N_("Office Workstation")) => 'office_section', + join('|', N_("Workstation"), N_("Scientific Workstation")) => 'sciences_section', + N_("Graphical Environment") => 'office_section', + + join('|', N_("Graphical Environment"), N_("GNOME Workstation")) => 'gnome_section', + join('|', N_("Graphical Environment"), N_("IceWm Desktop")) => 'icewm', + join('|', N_("Graphical Environment"), N_("KDE Workstation")) => 'kde_section', + join('|', N_("Graphical Environment"), N_("Other Graphical Desktops")) => 'more_applications_other_section', + N_("Development") => 'development_section', + join('|', N_("Development"), N_("Development")) => 'development_section', + join('|', N_("Development"), N_("Documentation")) => 'documentation_section', + N_("Server") => 'archiving_section', + join('|', N_("Server"), N_("DNS/NIS")) => 'networking_section', + join('|', N_("Server"), N_("Database")) => 'databases_section', + join('|', N_("Server"), N_("Firewall/Router")) => 'networking_section', + join('|', N_("Server"), N_("Mail")) => 'mail_section', + join('|', N_("Server"), N_("Mail/Groupware/News")) => 'mail_section', + join('|', N_("Server"), N_("Network Computer server")) => 'networking_section', + join('|', N_("Server"), N_("Web/FTP")) => 'networking_www_section', + + ); + +sub get_icon { + my ($group, $o_parent) = @_; + my $pixbuf; + my $path = $group =~ /\|/ ? '/usr/share/icons/mini/' : '/usr/share/icons/'; + my $create_pixbuf = sub { eval { gtknew('Pixbuf', file => join('', $path, $_[0], '.png')) } }; + $pixbuf = $create_pixbuf->($group_icons{$group}); + $pixbuf ||= $create_pixbuf->($group_icons{$o_parent}) if $o_parent; + $pixbuf ||= $create_pixbuf->('applications_section'); +} + +1; diff --git a/AdminPanel/Rpmdragora/init.pm b/AdminPanel/Rpmdragora/init.pm new file mode 100644 index 0000000..d178653 --- /dev/null +++ b/AdminPanel/Rpmdragora/init.pm @@ -0,0 +1,168 @@ +package AdminPanel::Rpmdragora::init; +#***************************************************************************** +# +# Copyright (c) 2002 Guillaume Cottenceau +# Copyright (c) 2002-2007 Thierry Vignaud +# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +# Copyright (c) 2005-2007 Mandriva SA +# Copyright (c) 2013 Matteo Pasotti +# +# 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: init.pm 263915 2009-12-03 17:41:04Z tv $ + +use strict; +use MDK::Common::Func 'any'; +use lib qw(/usr/lib/libDrakX); +use common; +BEGIN { $::no_global_argv_parsing = 1 } +require urpm::args; + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(init + warn_about_user_mode + $MODE + $changelog_first + $default_list_mode + %rpmdragora_options + @ARGV_copy + ); + +our @ARGV_copy = @ARGV; + +BEGIN { #- we want to run this code before the Gtk->init of the use-my_gtk + my $basename = sub { local $_ = shift; s|/*\s*$||; s|.*/||; $_ }; + any { /^--?h/ } @ARGV and do { + printf join("\n", N("Usage: %s [OPTION]...", $basename->($0)), +N(" --auto assume default answers to questions"), +N(" --changelog-first display changelog before filelist in the description window"), +N(" --media=medium1,.. limit to given media"), +N(" --merge-all-rpmnew propose to merge all .rpmnew/.rpmsave files found"), +N(" --mode=MODE set mode (install (default), remove, update)"), +N(" --justdb update the database, but do not modify the filesystem"), +N(" --no-confirmation don't ask first confirmation question in update mode"), +N(" --no-media-update don't update media at startup"), +N(" --no-verify-rpm don't verify package signatures"), +if_($0 !~ /MageiaUpdate/, N(" --parallel=alias,host be in parallel mode, use \"alias\" group, use \"host\" machine to show needed deps")), +N(" --rpm-root=path use another root for rpm installation"), +N(" --urpmi-root use another root for urpmi db & rpm installation"), +N(" --run-as-root force to run as root"), +N(" --search=pkg run search for \"pkg\""), +N(" --test only verify if the installation can be achieved correctly"), +chomp_(N(" --version print this tool's version number +")), +"" +); + exit 0; + }; +} + +BEGIN { #- for mcc + if ("@ARGV" =~ /--embedded (\w+)/) { + $::XID = $1; + $::isEmbedded = 1; + } +} + + +#- This is needed because text printed by Gtk2 will always be encoded +#- in UTF-8; we first check if LC_ALL is defined, because if it is, +#- changing only LC_COLLATE will have no effect. +use POSIX qw(setlocale LC_ALL LC_COLLATE strftime); +use locale; +my $collation_locale = $ENV{LC_ALL}; +if ($collation_locale) { + $collation_locale =~ /UTF-8/ or setlocale(LC_ALL, "$collation_locale.UTF-8"); +} else { + $collation_locale = setlocale(LC_COLLATE); + $collation_locale =~ /UTF-8/ or setlocale(LC_COLLATE, "$collation_locale.UTF-8"); +} + +our $version = 1; +our %rpmdragora_options; + +my $i; +foreach (@ARGV) { + $i++; + /^-?-(\S+)$/ or next; + my $val = $1; + if ($val =~ /=/) { + my ($name, $values) = split /=/, $val; + my @values = split /,/, $values; + $rpmdragora_options{$name} = \@values if @values; + } else { + if ($val eq 'version') { + print "$0 $version\n"; + exit(0); + } elsif ($val =~ /^(test|expert)$/) { + eval "\$::$1 = 1"; + } elsif ($val =~ /^(q|quiet)$/) { + urpm::args::set_verbose(-1); + } elsif ($val =~ /^(v|verbose)$/) { + urpm::args::set_verbose(1); + } else { + $rpmdragora_options{$val} = 1; + } + } +} + +foreach my $option (qw(media mode parallel rpm-root search)) { + if (defined $rpmdragora_options{$option} && !ref($rpmdragora_options{$option})) { + warn qq(wrong usage of "$option" option!\n); + exit(-1); # too early for my_exit() + } +} + +$urpm::args::options{basename} = 1; + +our $MODE = ref $rpmdragora_options{mode} ? $rpmdragora_options{mode}[0] : undef; +our $overriding_config = defined $MODE; +unless ($MODE) { + $MODE = 'install'; + $0 =~ m|remove$| and $MODE = 'remove'; + $0 =~ m|update$|i and $MODE = 'update'; +} + +our $default_list_mode; +$default_list_mode = 'gui_pkgs' if $MODE eq 'install'; +if ($MODE eq 'remove') { + $default_list_mode = 'installed'; +} elsif ($MODE eq 'update') { + $default_list_mode = 'all_updates'; +} + +$MODE eq 'update' || $rpmdragora_options{'run-as-root'} and require_root_capability(); +$::noborderWhenEmbedded = 1; + +require AdminPanel::rpmdragora; + +our $changelog_first = $rpmdragora::changelog_first_config->[0]; +$changelog_first = 1 if $rpmdragora_options{'changelog-first'}; + +sub warn_about_user_mode() { + $> and (AdminPanel::rpmdragora::interactive_msg(N("Running in user mode"), + N("You are launching this program as a normal user. +You will not be able to perform modifications on the system, +but you may still browse the existing database."), yesno => 1, text => { no => N("Cancel"), yes => N("Ok") }) + or AdminPanel::rpmdragora::myexit(0)); +} + +sub init() { + URPM::bind_rpm_textdomain_codeset(); +} + +1; diff --git a/AdminPanel/Rpmdragora/open_db.pm b/AdminPanel/Rpmdragora/open_db.pm new file mode 100644 index 0000000..4eec7cd --- /dev/null +++ b/AdminPanel/Rpmdragora/open_db.pm @@ -0,0 +1,161 @@ +package AdminPanel::Rpmdragora::open_db; +#***************************************************************************** +# +# Copyright (c) 2002 Guillaume Cottenceau +# Copyright (c) 2002-2007 Thierry Vignaud +# 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: open_db.pm 268344 2010-05-06 13:06:08Z jvictor $ + +use strict; +use common; +use AdminPanel::rpmdragora; +use URPM; +use urpm; +use urpm::args; +use urpm::select; +use urpm::media; +use feature 'state'; + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(fast_open_urpmi_db + get_backport_media + get_inactive_backport_media + get_update_medias + is_it_a_devel_distro + open_rpm_db + open_urpmi_db + ); + + +# because rpm blocks some signals when rpm DB is opened, we don't keep open around: +sub open_rpm_db { + my ($o_force) = @_; + my $host; + log::explanations("opening the RPM database"); + if ($::rpmdragora_options{parallel} && ((undef, $host) = @{$::rpmdragora_options{parallel}})) { + state $done; + my $dblocation = "/var/cache/urpmi/distantdb/$host"; + if (!$done || $o_force) { + print "syncing db from $host to $dblocation..."; + mkdir_p "$dblocation/var/lib/rpm"; + system "rsync -Sauz -e ssh $host:/var/lib/rpm/ $dblocation/var/lib/rpm"; + $? == 0 or die "Couldn't sync db from $host to $dblocation"; + $done = 1; + print "done.\n"; + } + URPM::DB::open($dblocation) or die "Couldn't open RPM DB"; + } else { + my $db; + if ($::env) { + #- URPM has same methods as URPM::DB and empty URPM will be seen as empty URPM::DB. + $db = new URPM; + $db->parse_synthesis("$::env/rpmdb.cz"); + } else { + $db = URPM::DB::open($::rpmdragora_options{'rpm-root'}[0]); + } + $db or die "Couldn't open RPM DB (" . ($::env ? "$::env/rpmdb.cz" : $::rpmdragora_options{'rpm-root'}[0]) . ")"; + } +} + +# do not pay the urpm::media::configure() heavy cost: +sub fast_open_urpmi_db() { + my $urpm = urpm->new; + my $error_happened; + $urpm->{fatal} = sub { + $error_happened = 1; + interactive_msg(N("Fatal error"), + N("A fatal error occurred: %s.", $_[1])); + }; + + urpm::set_files($urpm, $::rpmdragora_options{'urpmi-root'}[0]) if $::rpmdragora_options{'urpmi-root'}[0]; + $::rpmdragora_options{'rpm-root'}[0] ||= $::rpmdragora_options{'urpmi-root'}[0]; + urpm::args::set_root($urpm, $::rpmdragora_options{'rpm-root'}[0]) if $::rpmdragora_options{'rpm-root'}[0]; + urpm::args::set_debug($urpm) if $::rpmdragora_options{debug}; + $urpm->get_global_options; + $urpm->{options}{wait_lock} = $::rpmdragora_options{'wait-lock'}; + $urpm->{options}{'verify-rpm'} = !$::rpmdragora_options{'no-verify-rpm'} if defined $::rpmdragora_options{'no-verify-rpm'}; + $urpm->{options}{auto} = $::rpmdragora_options{auto} if defined $::rpmdragora_options{auto}; + urpm::args::set_verbosity(); + if ($::rpmdragora_options{env} && $::rpmdragora_options{env}[0]) { + $::env = $::rpmdragora_options{env}[0]; + # prevent crashing in URPM.pm prevent when using --env: + $::env = "$ENV{PWD}/$::env" if $::env !~ m!^/!; + urpm::set_env($urpm, $::env); + } + + $urpm::args::options{justdb} = $::rpmdragora_options{justdb}; + + urpm::media::read_config($urpm, 0); + foreach (@{$urpm->{media}}) { + next if $_->{ignore}; + urpm::media::_tempignore($_, 1) if $ignore_debug_media->[0] && $_->{name} =~ /debug/i; + } + # FIXME: seems uneeded with newer urpmi: + if ($error_happened) { + touch('/etc/urpmi/urpmi.cfg'); + exec('edit-urpm-sources.pl'); + } + $urpm; +} + +sub is_it_a_devel_distro() { + state $res; + return $res if defined $res; + + my $path = $::rpmdragora_options{'urpmi-root'}[0] . '/etc/product.id'; + $res = common::parse_LDAP_namespace_structure(cat_($path))->{branch} eq 'Devel'; + return $res; +} + +sub get_backport_media { + my ($urpm) = @_; + grep { $_->{name} =~ /backport/i && + $_->{name} !~ /debug|sources/i } @{$urpm->{media}}; +} + +sub get_inactive_backport_media { + my ($urpm) = @_; + map { $_->{name} } grep { $_->{ignore} } get_backport_media($urpm); +} + +sub get_update_medias { + my ($urpm) = @_; + if (is_it_a_devel_distro()) { + grep { !$_->{ignore} } @{$urpm->{media}}; + } else { + grep { !$_->{ignore} && $_->{update} } @{$urpm->{media}}; + } +} + +sub open_urpmi_db { + my (%urpmi_options) = @_; + my $urpm = fast_open_urpmi_db(); + my $media = ref $::rpmdragora_options{media} ? join(',', @{$::rpmdragora_options{media}}) : ''; + + my $searchmedia = $urpmi_options{update} ? undef : join(',', get_inactive_backport_media($urpm)); + $urpm->{lock} = urpm::lock::urpmi_db($urpm, undef, wait => $urpm->{options}{wait_lock}) if !$::env; + my $previous = $::rpmdragora_options{'previous-priority-upgrade'}; + urpm::select::set_priority_upgrade_option($urpm, (ref $previous ? join(',', @$previous) : ())); + urpm::media::configure($urpm, media => $media, if_($searchmedia, searchmedia => $searchmedia), %urpmi_options); + $urpm; +} + +1; diff --git a/AdminPanel/Rpmdragora/pkg.pm b/AdminPanel/Rpmdragora/pkg.pm new file mode 100644 index 0000000..bf64004 --- /dev/null +++ b/AdminPanel/Rpmdragora/pkg.pm @@ -0,0 +1,990 @@ +package AdminPanel::Rpmdragora::pkg; +#***************************************************************************** +# +# Copyright (c) 2002 Guillaume Cottenceau +# Copyright (c) 2002-2007 Thierry Vignaud +# 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: pkg.pm 270160 2010-06-22 19:55:40Z jvictor $ + +use strict; +use MDK::Common::Func 'any'; +use lib qw(/usr/lib/libDrakX); +use common; +use POSIX qw(_exit); +use URPM; +use utf8; +use AdminPanel::Rpmdragora::open_db; +use AdminPanel::Rpmdragora::gurpm; +use AdminPanel::Rpmdragora::formatting; +use AdminPanel::Rpmdragora::rpmnew; + +use AdminPanel::rpmdragora; +use urpm; +use urpm::lock; +use urpm::install; +use urpm::signature; +use urpm::get_pkgs; +use urpm::select; +use urpm::main_loop; +use urpm::args qw(); + + +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw( + $priority_up_alread_warned + download_callback + extract_header + find_installed_version + get_pkgs + perform_installation + perform_removal + run_rpm + sort_packages + ); + +#use mygtk2 qw(gtknew); +#use ugtk2 qw(:all); + +our $priority_up_alread_warned; + +sub sort_packages_biarch { + sort { + my ($na, $aa) = $a =~ /^(.*-[^-]+-[^-]+)\.([^.-]+)$/; + my ($nb, $ab) = $b =~ /^(.*-[^-]+-[^-]+)\.([^.-]+)$/; + $na cmp $nb || ($ab =~ /64$/) <=> ($aa =~ /64$/); + } @_; +} + +sub sort_packages_monoarch { + sort { uc($a) cmp uc($b) } @_; +} + +*sort_packages = arch() =~ /x86_64/ ? \&sort_packages_biarch : \&sort_packages_monoarch; + +sub run_rpm { + foreach (qw(LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION LC_ALL)) { + local $ENV{$_} = $ENV{$_} . '.UTF-8' if $ENV{$_} && $ENV{$_} !~ /UTF-8/; + } + my @l = map { ensure_utf8($_); $_ } run_program::get_stdout(@_); + wantarray() ? @l : join('', @l); +} + + +sub extract_header { + my ($pkg, $urpm, $xml_info, $o_installed_version) = @_; + my %fields = ( + info => 'description', + files => 'files', + changelog => 'changelog', + ); + # already extracted: + return if $pkg->{$fields{$xml_info}}; + + my $p = $pkg->{pkg}; + + if (!$p) { + warn ">> ghost package '$pkg' has no URPM object!!!\n"; + return; + } + + my $name = $p->fullname; + # fix extracting info for SRPMS and RPM GPG keys: + $name =~ s!\.src!!; + + if ($p->flag_installed && !$p->flag_upgrade) { + my @files = map { chomp_($_) } run_rpm("rpm -ql $name"); + add2hash($pkg, { files => [ @files ? @files : N("(none)") ], + description => rpm_description(scalar(run_rpm("rpm -q --qf '%{description}' $name"))), + changelog => format_changelog_string($o_installed_version, scalar(run_rpm("rpm -q --changelog $name"))) }); + } else { + my $medium = pkg2medium($p, $urpm); + my ($local_source, %xml_info_pkgs, $bar_id); + my $_statusbar_clean_guard = before_leaving { $bar_id and statusbar_msg_remove($bar_id) }; + my $dir = urpm::file_from_local_url($medium->{url}); + $local_source = "$dir/" . $p->filename if $dir; + + if (-e $local_source) { + $bar_id = statusbar_msg(N("Getting information from %s...", $dir), 0); + $urpm->{log}("getting information from rpms from $dir"); + } else { + my $gurpm; + $bar_id = statusbar_msg(N("Getting '%s' from XML meta-data...", $xml_info), 0); + my $_gurpm_clean_guard = before_leaving { undef $gurpm }; + if (my $xml_info_file = eval { urpm::media::any_xml_info($urpm, $medium, $xml_info, undef, sub { + $gurpm ||= Rpmdragora::gurpm->new(N("Please wait"), + '', # FIXME: add a real string after cooker + transient => $::main_window); + download_callback($gurpm, @_) + or goto header_non_available; + }) }) { + require urpm::xml_info; + require urpm::xml_info_pkg; + $urpm->{log}("getting information from $xml_info_file"); + my %nodes = eval { urpm::xml_info::get_nodes($xml_info, $xml_info_file, [ $name ]) }; + goto header_non_available if $@; + put_in_hash($xml_info_pkgs{$name} ||= {}, $nodes{$name}); + } else { + if ($xml_info eq 'info') { + $urpm->{info}(N("No xml info for medium \"%s\", only partial result for package %s", $medium->{name}, $name)); + } else { + $urpm->{error}(N("No xml info for medium \"%s\", unable to return any result for package %s", $medium->{name}, $name)); + } + } + } + + #- even if non-root, search for a header in the global cachedir + if (-s $local_source) { + $p->update_header($local_source) or do { + warn "Warning, could not extract header for $name from $medium!"; + goto header_non_available; + }; + my @files = $p->files; + @files = N("(none)") if !@files; + add2hash($pkg, { description => rpm_description($p->description), + files => \@files, + url => $p->url, + changelog => format_changelog_changelogs($o_installed_version, $p->changelogs) }); + $p->pack_header; # needed in order to call methods on objects outside ->traverse + } elsif ($xml_info_pkgs{$name}) { + if ($xml_info eq 'info') { + add2hash($pkg, { description => rpm_description($xml_info_pkgs{$name}{description}), + url => $xml_info_pkgs{$name}{url} + }); + } elsif ($xml_info eq 'files') { + my @files = map { chomp_(to_utf8($_)) } split("\n", $xml_info_pkgs{$name}{files}); + add2hash($pkg, { files => [ @files ? @files : N("(none)") ] }); + } elsif ($xml_info eq 'changelog') { + add2hash($pkg, { + changelog => format_changelog_changelogs($o_installed_version, + @{$xml_info_pkgs{$name}{changelogs}}) + }); + } + } else { + goto header_non_available; + } + return; + header_non_available: + add2hash($pkg, { summary => $p->summary || N("(Not available)"), description => undef }); + } +} + +sub find_installed_version { + my ($p) = @_; + my $version; + open_rpm_db()->traverse_tag_find('name', $p->name, sub { $version = $_[0]->EVR }); + $version || N("(none)"); +} + +my $canceled; +sub download_callback { + my ($gurpm, $mode, $file, $percent, $total, $eta, $speed) = @_; + $canceled = 0; + if ($mode eq 'start') { + $gurpm->label(N("Downloading package `%s'...", basename($file))); + $gurpm->validate_cancel(but(N("Cancel")), sub { $canceled = 1 }); + } elsif ($mode eq 'progress') { + $gurpm->label( + join("\n", + N("Downloading package `%s'...", basename($file)), + (defined $total && defined $eta ? + N(" %s%% of %s completed, ETA = %s, speed = %s", $percent, $total, $eta, $speed) + : N(" %s%% completed, speed = %s", $percent, $speed) + ) =~ /^\s*(.*)/ + ), + ); + #$gurpm->progress($percenti/100); + $gurpm->progress($percent); + } elsif ($mode eq 'end') { + $gurpm->progress(100); + $gurpm->invalidate_cancel; + } + !$canceled; +} + + +# -=-=-=---=-=-=---=-=-=-- install packages -=-=-=---=-=-=---=-=-=- + +my (@update_medias, $is_update_media_already_asked); + +sub warn_about_media { + my ($w, %options) = @_; + + return if $::MODE ne 'update'; + return if $::rpmdragora_options{'no-media-update'}; + + # we use our own instance of the urpmi db in order not to mess up with skip-list managment (#31092): + # and no need to fully configure urpmi since we may have to do it again anyway because of new media: + my $urpm = fast_open_urpmi_db(); + + my $_lock = urpm::lock::urpmi_db($urpm, undef, wait => $urpm->{options}{wait_lock}); + + # build media list: + @update_medias = get_update_medias($urpm); + + # do not update again media after installing/removing some packages: + $::rpmdragora_options{'no-media-update'} ||= 1; + + if (@update_medias > 0) { + if (!$options{skip_updating_mu} && !$is_update_media_already_asked) { + $is_update_media_already_asked = 1; + $::rpmdragora_options{'no-confirmation'} or interactive_msg(N("Confirmation"), +N("I need to contact the mirror to get latest update packages. +Please check that your network is currently running. + +Is it ok to continue?"), yesno => 1, + widget => gtknew('CheckButton', text => N("Do not ask me next time"), + active_ref => \$::rpmdragora_options{'no-confirmation'} + )) or myexit(-1); + writeconf(); + urpm::media::select_media($urpm, map { $_->{name} } @update_medias); + update_sources($urpm, noclean => 1, medialist => [ map { $_->{name} } @update_medias ]); + } + } else { + if (any { $_->{update} } @{$urpm->{media}}) { + interactive_msg(N("Already existing update media"), +N("You already have at least one update medium configured, but +all of them are currently disabled. You should run the Software +Media Manager to enable at least one (check it in the \"%s\" +column). + +Then, restart \"%s\".", N("Enabled"), $rpmdragora::myname_update)); + myexit(-1); + } + my ($mirror) = choose_mirror($urpm, transient => $w->{real_window} || $::main_window, + message => join("\n\n", + N("You have no configured update media. MageiaUpdate cannot operate without any update media."), + 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?"), + ), + ); + my $m = ref($mirror) ? $mirror->{url} : ''; + $m or interactive_msg(N("How to choose manually your mirror"), +N("You may also choose your desired mirror manually: to do so, +launch the Software Media Manager, and then add a `Security +updates' medium. + +Then, restart %s.", $rpmdragora::myname_update)), myexit(-1); + add_distrib_update_media($urpm, $mirror, only_updates => 1); + } +} + + +sub get_parallel_group() { + $::rpmdragora_options{parallel} ? $::rpmdragora_options{parallel}[0] : undef; +} + +my ($count, $level, $limit, $new_stage, $prev_stage, $total); + +sub init_progress_bar { + my ($urpm) = @_; + undef $_ foreach $count, $prev_stage, $new_stage, $limit; + $level = 0.05; + $total = @{$urpm->{depslist}}; +} + +sub reset_pbar_count { + undef $prev_stage; + $count = 0; + $limit = $_[0]; +} + +sub update_pbar { + my ($gurpm) = @_; + return if !$total; # don't die if there's no source + $count++; + $new_stage = $level+($limit-$level)*$count/$total; + if ($prev_stage + 0.01*100 < $new_stage) { + $prev_stage = $new_stage; + $gurpm->progress($new_stage); + } +} + + +sub get_installed_packages { + my ($urpm, $db, $all_pkgs, $gurpm) = @_; + + my @base = ("basesystem", split /,\s*/, $urpm->{global_config}{'prohibit-remove'}); + my (%base, %basepackages, @installed_pkgs, @processed_base); + reset_pbar_count(0.33); + while (defined(local $_ = shift @base)) { + exists $basepackages{$_} and next; + $db->traverse_tag(m|^/| ? 'path' : 'whatprovides', [ $_ ], sub { + update_pbar($gurpm); + my $name = $_[0]->fullname; + # workaround looping in URPM: + return if member($name, @processed_base); + push @processed_base, $name; + push @{$basepackages{$_}}, $name; + push @base, $_[0]->requires_nosense; + }); + } + foreach (values %basepackages) { + my $n = @$_; #- count number of times it's provided + foreach (@$_) { + $base{$_} = \$n; + } + } + # costly: + $db->traverse(sub { + my ($pkg) = @_; + update_pbar($gurpm); + my $fullname = urpm_name($pkg); + return if $fullname =~ /@/; + $all_pkgs->{$fullname} = { + pkg => $pkg, urpm_name => $fullname, + } if !($all_pkgs->{$fullname} && $all_pkgs->{$fullname}{description}); + if (my $name = $base{$fullname}) { + $all_pkgs->{$fullname}{base} = \$name; + $pkg->set_flag_base(1) if $$name == 1; + } + push @installed_pkgs, $fullname; + $pkg->set_flag_installed; + $pkg->pack_header; # needed in order to call methods on objects outside ->traverse + }); + @installed_pkgs; +} + +urpm::select::add_packages_to_priority_upgrade_list('rpmdragora', 'perl-Glib', 'perl-Gtk2'); + +my ($priority_state, $priority_requested); +our $need_restart; + +our $probe_only_for_updates; + +sub get_updates_list { + my ($urpm, $db, $state, $requested, $requested_list, $requested_strict, $all_pkgs, %limit_preselect) = @_; + + $urpm->request_packages_to_upgrade( + $db, + $state, + $requested, + %limit_preselect + ); + + my %common_opts = ( + callback_choices => \&Rpmdragora::gui::callback_choices, + priority_upgrade => $urpm->{options}{'priority-upgrade'}, + ); + + if ($urpm->{options}{'priority-upgrade'}) { + $need_restart = + urpm::select::resolve_priority_upgrades_after_auto_select($urpm, $db, $state, + $requested, %common_opts); + } + + # list of updates (including those matching /etc/urpmi/skip.list): + @$requested_list = sort map { + my $name = urpm_name($_); + $all_pkgs->{$name} = { pkg => $_ }; + $name; + } @{$urpm->{depslist}}[keys %$requested]; + + # list of pure updates (w/o those matching /etc/urpmi/skip.list but with their deps): + if ($probe_only_for_updates && !$need_restart) { + @$requested_strict = sort map { + urpm_name($_); + } $urpm->resolve_requested($db, $state, $requested, callback_choices => \&Rpmdragora::gui::callback_choices); + + if (my @l = grep { $state->{selected}{$_->id} } + urpm::select::_priority_upgrade_pkgs($urpm, $urpm->{options}{'priority-upgrade'})) { + if (!$need_restart) { + $need_restart = + urpm::select::_resolve_priority_upgrades($urpm, $db, $state, $state->{selected}, + \@l, %common_opts); + } + } + } + + if ($need_restart) { + $requested_strict = [ map { scalar $_->fullname } @{$urpm->{depslist}}[keys %{$state->{selected}}] ]; + # drop non priority updates: + @$requested_list = (); + } + + # list updates including skiped ones + their deps in MageiaUpdate: + @$requested_list = uniq(@$requested_list, @$requested_strict); + + # do not pre select updates in rpmdragora: + @$requested_strict = () if !$probe_only_for_updates; +} + +sub get_pkgs { + my (%options) = @_; + my $w = $::main_window; + + my $gurpm = AdminPanel::Rpmdragora::gurpm->new(1 ? N("Please wait") : N("Package installation..."), N("Initializing..."), transient => $::main_window); + my $_gurpm_clean_guard = before_leaving { undef $gurpm }; + #my $_flush_guard = Gtk2::GUI_Update_Guard->new; + + warn_about_media($w, %options); + + my $urpm = open_urpmi_db(update => $probe_only_for_updates && !is_it_a_devel_distro()); + + my $_drop_lock = before_leaving { undef $urpm->{lock} }; + + $priority_up_alread_warned = 0; + + # update media list in case warn_about_media() added some: + @update_medias = get_update_medias($urpm); + + $gurpm->label(N("Reading updates description")); + $gurpm->progress(100); + + #- parse the description file + my $update_descr = urpm::get_updates_description($urpm, @update_medias); + + my $_unused = N("Please wait, finding available packages..."); + + # find out installed packages: + + init_progress_bar($urpm); + + $gurpm->label(N("Please wait, listing base packages...")); + $gurpm->progress($level*100); + + my $db = eval { open_rpm_db() }; + if (my $err = $@) { + interactive_msg(N("Error"), N("A fatal error occurred: %s.", $err)); + return; + } + + my $sig_handler = sub { undef $db; exit 3 }; + local $SIG{INT} = $sig_handler; + local $SIG{QUIT} = $sig_handler; + + $gurpm->label(N("Please wait, finding installed packages...")); + $gurpm->progress($level = 0.33*100); + reset_pbar_count(0.66*100); + my (@installed_pkgs, %all_pkgs); + if (!$probe_only_for_updates) { + @installed_pkgs = get_installed_packages($urpm, $db, \%all_pkgs, $gurpm); + } + + if (my $group = get_parallel_group()) { + urpm::media::configure($urpm, parallel => $group); + } + + # find out availlable packages: + + $urpm->{state} = {}; + + $gurpm->label(N("Please wait, finding available packages...")); + $gurpm->progress($level = 0.66*100); + + check_update_media_version($urpm, @update_medias); + + my $requested = {}; + my $state = {}; + my (@requested, @requested_strict); + + if ($compute_updates->[0] || $::MODE eq 'update') { + my %filter; + if ($options{pure_updates}) { + # limit to packages from update-media (dependencies can still come from other media) + %filter = (idlist => [ map { $_->{start} .. $_->{end} } @update_medias ]); + } + get_updates_list($urpm, $db, $state, $requested, \@requested, \@requested_strict, \%all_pkgs, %filter); + } + + if ($need_restart) { + $priority_state = $state; + $priority_requested = $requested; + } else { + ($priority_state, $priority_requested) = (); + } + + if (!$probe_only_for_updates) { + $urpm->compute_installed_flags($db); # TODO/FIXME: not for updates + $urpm->{depslist}[$_]->set_flag_installed foreach keys %$requested; #- pretend it's installed + } + $urpm->{rpmdragora_state} = $state; #- Don't forget it + $gurpm->progress($level = 0.7*100); + + my %l; + reset_pbar_count(1); + foreach my $pkg (@{$urpm->{depslist}}) { + update_pbar($gurpm); + $pkg->flag_upgrade or next; + my $key = $pkg->name . $pkg->arch; + $l{$key} = $pkg if !$l{$key} || $l{$key}->compare($pkg); + } + my @installable_pkgs = map { my $n = $_->fullname; $all_pkgs{$n} = { pkg => $_ }; $n } values %l; + undef %l; + + my @inactive_backports; + my @active_backports; + my @backport_medias = get_backport_media($urpm); + + foreach my $medium (@backport_medias) { + update_pbar($gurpm); + + # The 'searchmedia' flag differentiates inactive backport medias + # (because that option was passed to urpm::media::configure to + # temporarily enable them) + + my $backports = + $medium->{searchmedia} ? \@inactive_backports : \@active_backports; + + foreach my $pkg_id ($medium->{start} .. $medium->{end}) { + next if !$pkg_id; + my $pkg = $urpm->{depslist}[$pkg_id]; + $pkg->flag_upgrade or next; + my $name = $pkg->fullname; + push @$backports, $name; + $all_pkgs{$name} = { pkg => $pkg, is_backport => 1 }; + } + } + my @updates = @requested; + # selecting updates by default but skipped ones (MageiaUpdate only): + foreach (@requested_strict) { + $all_pkgs{$_}{selected} = 1; + } + + # urpmi only care about the first medium where it found the package, + # so there's no need to list the same package several time: + @installable_pkgs = uniq(difference2(\@installable_pkgs, \@updates)); + + my @meta_pkgs = grep { /^task-|^basesystem/ } keys %all_pkgs; + + my @gui_pkgs = map { chomp; $_ } cat_('/usr/share/rpmdragora/gui.lst'); + # add meta packages to GUI packages list (which expect basic names not fullnames): + push @gui_pkgs, map { (split_fullname($_))[0] } @meta_pkgs; + + +{ urpm => $urpm, + all_pkgs => \%all_pkgs, + installed => \@installed_pkgs, + installable => \@installable_pkgs, + updates => \@updates, + meta_pkgs => \@meta_pkgs, + gui_pkgs => [ grep { my $p = $all_pkgs{$_}{pkg}; $p && member(($p->fullname)[0], @gui_pkgs) } keys %all_pkgs ], + update_descr => $update_descr, + backports => [ @inactive_backports, @active_backports ], + inactive_backports => \@inactive_backports + }; +} + +sub display_READMEs_if_needed { + my ($urpm, $w) = @_; + return if !$urpm->{readmes}; + my %Readmes = %{$urpm->{readmes}}; + if (keys %Readmes) { #- display the README*.urpmi files + interactive_packtable( + N("Upgrade information"), + $w, + N("These packages come with upgrade information"), + [ map { + my $fullname = $_; + [ gtkpack__( + gtknew('HBox'), + gtkset_selectable(gtknew('Label', text => $Readmes{$fullname}),1), + ), + gtksignal_connect( + gtknew('Button', text => N("Upgrade information about this package")), + clicked => sub { + interactive_msg( + N("Upgrade information about package %s", $Readmes{$fullname}), + (join '' => map { s/$/\n/smg; $_ } formatAlaTeX(scalar cat_($fullname))), + scroll => 1, + ); + }, + ), + ] } keys %Readmes ], + [ gtknew('Button', text => N("Ok"), clicked => sub { Gtk2->main_quit }) ] + ); + } +} + +sub perform_parallel_install { + my ($urpm, $group, $w, $statusbar_msg_id) = @_; + my @pkgs = map { if_($_->flag_requested, urpm_name($_)) } @{$urpm->{depslist}}; + + my @error_msgs; + my $res = !run_program::run('urpmi', '2>', \@error_msgs, '-v', '--X', '--parallel', $group, @pkgs); + + if ($res) { + $$statusbar_msg_id = statusbar_msg( + #N("Everything installed successfully"), + N("All requested packages were installed successfully."), + ); + } else { + interactive_msg( + N("Problem during installation"), + N("There was a problem during the installation:\n\n%s", join("\n", @error_msgs)), + scroll => 1, + ); + } + open_rpm_db('force_sync'); + $w->set_sensitive(1); + return 0; +} + +sub perform_installation { #- (partially) duplicated from /usr/sbin/urpmi :-( + my ($urpm, $pkgs) = @_; + + my @error_msgs; + my $statusbar_msg_id; + my $gurpm; + local $urpm->{fatal} = sub { + my $fatal_msg = $_[1]; + printf STDERR "Fatal: %s\n", $fatal_msg; + undef $gurpm; + interactive_msg(N("Installation failed"), + N("There was a problem during the installation:\n\n%s", $fatal_msg)); + goto return_with_exit_code; + }; + local $urpm->{error} = sub { printf STDERR "Error: %s\n", $_[0]; push @error_msgs, $_[0] }; + + my $w = $::main_window; + $w->set_sensitive(0); + my $_restore_sensitive = before_leaving { $w->set_sensitive(1) }; + + my $_flush_guard = Gtk2::GUI_Update_Guard->new; + + if (my $group = get_parallel_group()) { + return perform_parallel_install($urpm, $group, $w, \$statusbar_msg_id); + } + + my ($lock, $rpm_lock); + if (!$::env) { + $lock = urpm::lock::urpmi_db($urpm, undef, wait => $urpm->{options}{wait_lock}); + $rpm_lock = urpm::lock::rpm_db($urpm, 'exclusive'); + } + my $state = $priority_state || $probe_only_for_updates ? { } : $urpm->{rpmdragora_state}; + + my $bar_id = statusbar_msg(N("Checking validity of requested packages..."), 0); + + # FIXME: THIS SET flag_requested on all packages!!!! + # select packages to install / enssure selected pkg set is consistant: + my %saved_flags; + my $requested = { map { + $saved_flags{$_->id} = $_->flag_requested; + $_->id => undef; + } grep { $_->flag_selected } @{$urpm->{depslist}} }; + urpm::select::resolve_dependencies( + $urpm, $state, $requested, + rpmdb => $::env && "$::env/rpmdb.cz", + callback_choices => \&Rpmdragora::gui::callback_choices, + ); + statusbar_msg_remove($bar_id); + + my ($local_sources, $blist) = urpm::get_pkgs::selected2local_and_blists($urpm, $state->{selected}); + if (!$local_sources && (!$blist || !@$blist)) { + interactive_msg( + N("Unable to get source packages."), + N("Unable to get source packages, sorry. %s", + @error_msgs ? N("\n\nError(s) reported:\n%s", join("\n", @error_msgs)) : ''), + scroll => 1, + ); + goto return_with_exit_code; + } + + my @to_install = @{$urpm->{depslist}}[keys %{$state->{selected}}]; + my @pkgs = map { scalar($_->fullname) } sort(grep { $_->flag_selected } @to_install); + + @{$urpm->{ask_remove}} = sort(urpm::select::removed_packages($urpm->{state})); + my @to_remove = map { if_($pkgs->{$_}{selected} && !$pkgs->{$_}{pkg}->flag_upgrade, $pkgs->{$_}{urpm_name}) } keys %$pkgs; + + my $r = format_list(map { scalar(urpm::select::translate_why_removed_one($urpm, $urpm->{state}, $_)) } @to_remove); + + my ($size, $filesize) = $urpm->selected_size_filesize($state); + my $install_count = int(@pkgs); + my $to_install = $install_count == 0 ? '' : + ($priority_state ? '' . N("Rpmdragora or one of its priority dependencies needs to be updated first. Rpmdragora will then restart.") . '' . "\n\n" : '') . + (P("The following package is going to be installed:", "The following %d packages are going to be installed:", $install_count, $install_count) + . "\n\n" . format_list(map { s!.*/!!; $_ } @pkgs)); + my $remove_count = scalar(@to_remove); + interactive_msg(($to_install ? N("Confirmation") : N("Some packages need to be removed")), + join("\n\n", + ($r ? + (!$to_install ? (P("Remove one package?", "Remove %d packages?", $remove_count, $remove_count), $r) : + (($remove_count == 1 ? + N("The following package has to be removed for others to be upgraded:") + : N("The following packages have to be removed for others to be upgraded:")), $r), if_($to_install, $to_install)) + : $to_install), + format_size($size), + format_filesize($filesize), + N("Is it ok to continue?")), + scroll => 1, + yesno => 1) or return 1; + + my $_umount_guard = before_leaving { urpm::removable::try_umounting_removables($urpm) }; + + # select packages to uninstall for !update mode: + perform_removal($urpm, { map { $_ => $pkgs->{$_} } @to_remove }) if !$probe_only_for_updates; + + $gurpm = Rpmdragora::gurpm->new(1 ? N("Please wait") : N("Package installation..."), N("Initializing..."), transient => $::main_window); + my $_gurpm_clean_guard = before_leaving { undef $gurpm }; + my $something_installed; + + if (@to_install && $::rpmdragora_options{auto_orphans}) { + urpm::orphans::compute_future_unrequested_orphans($urpm, $state); + if (my @orphans = map { scalar $_->fullname } @{$state->{orphans_to_remove}}) { + interactive_msg(N("Orphan packages"), P("The following orphan package will be removed.", + "The following orphan packages will be removed.", scalar(@orphans)) + . "\n" . urpm::orphans::add_leading_spaces(join("\n", @orphans) . "\n"), scroll => 1); + } + } + + urpm::orphans::mark_as_requested($urpm, $state, 0); + + my ($progress, $total, @rpms_upgrade); + my $transaction; + my ($progress_nb, $transaction_progress_nb, $remaining, $done); + my $callback_inst = sub { + my ($urpm, $type, $id, $subtype, $amount, $total) = @_; + my $pkg = defined $id ? $urpm->{depslist}[$id] : undef; + if ($subtype eq 'start') { + if ($type eq 'trans') { + $gurpm->label(1 ? N("Preparing package installation...") : N("Preparing package installation transaction...")); + } elsif (defined $pkg) { + $something_installed = 1; + $gurpm->label(N("Installing package `%s' (%s/%s)...", $pkg->name, ++$transaction_progress_nb, scalar(@{$transaction->{upgrade}})) + . "\n" . N("Total: %s/%s", ++$progress_nb, $install_count)); + } + } elsif ($subtype eq 'progress') { + $gurpm->progress($total ? ($amount/$total)*100 : 100); + } + }; + + # FIXME: sometimes state is lost: + my @ask_unselect = urpm::select::unselected_packages($state); + + # fix flags for orphan computing: + foreach (keys %{$state->{selected}}) { + my $pkg = $urpm->{depslist}[$_]; + $pkg->set_flag_requested($saved_flags{$pkg->id}); + } + my $exit_code = + urpm::main_loop::run($urpm, $state, 1, \@ask_unselect, + { + completed => sub { + # explicitly destroy the progress window when it's over; we may + # have sg to display before returning (errors, rpmnew/rpmsave, ...): + undef $gurpm; + + undef $lock; + undef $rpm_lock; + }, + inst => $callback_inst, + trans => $callback_inst, + ask_yes_or_no => sub { + # handle 'allow-force' and 'allow-nodeps' options: + my ($title, $msg) = @_; + local $::main_window = $gurpm->{real_window}; + interactive_msg($title, $msg, yesno => 1, scroll => 1, + ); + }, + message => sub { + my ($title, $message) = @_; + interactive_msg($title, $message, scroll => 1); + }, + # cancel installation when 'cancel' button is pressed: + trans_log => sub { download_callback($gurpm, @_) or goto return_with_exit_code }, + post_extract => sub { + my ($set, $transaction_sources, $transaction_sources_install) = @_; + $transaction = $set; + $transaction_progress_nb = 0; + $done += grep { !/\.src\.rpm$/ } values %$transaction_sources; #updates + $total = keys(%$transaction_sources_install) + keys %$transaction_sources; + push @rpms_upgrade, keys %$transaction_sources; + $done += grep { !/\.src\.rpm$/ } values %$transaction_sources_install; # installs + }, + pre_removable => sub { + # Gtk2::GUI_Update_Guard->new use of alarm() kill us when + # running system(), thus making DVD being ejected and printing + # wrong error messages (#30463) + + local $SIG{ALRM} = sub { die "ALARM" }; + $remaining = alarm(0); + }, + + post_removable => sub { alarm $remaining }, + copy_removable => sub { + my ($medium) = @_; + interactive_msg( + N("Change medium"), + N("Please insert the medium named \"%s\"", $medium), + yesno => 1, text => { no => N("Cancel"), yes => N("Ok") }, + ); + }, + pre_check_sig => sub { $gurpm->label(N("Verifying package signatures...")) }, + check_sig => sub { $gurpm->progress((++$progress/$total)*100) }, + bad_signature => sub { + my ($msg, $msg2) = @_; + local $::main_window = $gurpm->{real_window}; + $msg =~ s/:$/\n\n/m; # FIXME: to be fixed in urpmi after 2008.0 + interactive_msg( + N("Warning"), "$msg\n\n$msg2", yesno => 1, if_(10 < ($msg =~ tr/\n/\n/), scroll => 1), + ); + }, + post_download => sub { + $canceled and goto return_with_exit_code; + $gurpm->invalidate_cancel_forever; + }, + need_restart => sub { + my ($need_restart_formatted) = @_; + # FIXME: offer to restart the system + interactive_msg(N("Warning"), join("\n", values %$need_restart_formatted), scroll => 1); + }, + trans_error_summary => sub { + my ($nok, $errors) = @_; + interactive_msg( + N("Problem during installation"), + if_($nok, N("%d installation transactions failed", $nok) . "\n\n") . + N("There was a problem during the installation:\n\n%s", + join("\n\n", @$errors, @error_msgs)), + scroll => 1, + ); + }, + need_restart => sub { + my ($need_restart_formatted) = @_; + interactive_msg(N("Warning"), + join("\n\n", values %$need_restart_formatted)); + }, + success_summary => sub { + if (!($done || @to_remove)) { + interactive_msg(N("Error"), + N("Unrecoverable error: no package found for installation, sorry.")); + return; + } + my $id = statusbar_msg(N("Inspecting configuration files..."), 0); + my %pkg2rpmnew; + foreach my $id (@rpms_upgrade) { + my $pkg = $urpm->{depslist}[$id]; + next if $pkg->arch eq 'src'; + $pkg2rpmnew{$pkg->fullname} = [ grep { -r "$_.rpmnew" || -r "$_.rpmsave" } $pkg->conf_files ]; + } + statusbar_msg_remove($id); + dialog_rpmnew(N("The installation is finished; everything was installed correctly. + +Some configuration files were created as `.rpmnew' or `.rpmsave', +you may now inspect some in order to take actions:"), + %pkg2rpmnew) + and statusbar_msg(N("All requested packages were installed successfully."), 1); + statusbar_msg(N("Looking for \"README\" files..."), 1); + display_READMEs_if_needed($urpm, $w); + }, + already_installed_or_not_installable => sub { + my ($msg1, $msg2) = @_; + my $msg = join("\n", @$msg1, @$msg2); + return if !$msg; # workaround missing state + interactive_msg(N("Error"), $msg); + }, + }, + ); + + #- restart rpmdragora if needed, keep command line for that. + if ($need_restart && !$exit_code && $something_installed) { + log::explanations("restarting rpmdragora"); + #- it seems to work correctly with exec instead of system, provided we stop timers + #- added --previous-priority-upgrade to allow checking if yet if + #- priority-upgrade list has changed. and make sure we don't uselessly restart + my @argv = ('--previous-priority-upgrade=' . $urpm->{options}{'priority-upgrade'}, + grep { !/^--no-priority-upgrade$|--previous-priority-upgrade=/ } @Rpmdragora::init::ARGV_copy); + # remove "--emmbedded " from argv: + my $i = 0; + foreach (@argv) { + splice @argv, $i, 2 if /^--embedded$/; + $i++; + } + alarm(0); + # remember not to ask again questions and the like: + writeconf(); + exec($0, @argv); + exit(0); + } + + my $_s1 = N("RPM transaction %d/%d", 0, 0); + my $_s2 = N("Unselect all"); + my $_s3 = N("Details"); + + statusbar_msg_remove($statusbar_msg_id); #- XXX maybe remove this + + if ($exit_code == 0 && !$::rpmdragora_options{auto_orphans}) { + if (urpm::orphans::check_unrequested_orphans_after_auto_select($urpm)) { + if (my $msg = urpm::orphans::get_now_orphans_gui_msg($urpm)) { + interactive_msg(N("Orphan packages"), $msg, scroll => 1); + } + } + } + + return_with_exit_code: + return !($something_installed || scalar(@to_remove)); +} + + +# -=-=-=---=-=-=---=-=-=-- remove packages -=-=-=---=-=-=---=-=-=- + +sub perform_removal { + my ($urpm, $pkgs) = @_; + my @toremove = map { if_($pkgs->{$_}{selected}, $pkgs->{$_}{urpm_name}) } keys %$pkgs; + return if !@toremove; + my $gurpm = Rpmdragora::gurpm->new(1 ? N("Please wait") : N("Please wait, removing packages..."), N("Initializing..."), transient => $::main_window); + my $_gurpm_clean_guard = before_leaving { undef $gurpm }; + + my $may_be_orphans = 1; + urpm::orphans::unrequested_orphans_after_remove($urpm, \@toremove) + or $may_be_orphans = 0; + + my $progress = -1; + local $urpm->{log} = sub { + my $str = $_[0]; + print $str; + $progress++; + return if $progress <= 0; # skip first "creating transaction..." message + $gurpm->label($str); # display "removing package %s" + $gurpm->progress(min(0.99*100, scalar($progress/@toremove)*100)); + #gtkflush(); + }; + + my @results; + slow_func_statusbar( + N("Please wait, removing packages..."), + $::main_window, + sub { + @results = $::rpmdragora_options{parallel} + ? urpm::parallel::remove($urpm, \@toremove) + : urpm::install::install($urpm, \@toremove, {}, {}, + callback_report_uninst => sub { $gurpm->label($_[0]) }, + ); + open_rpm_db('force_sync'); + }, + ); + if (@results) { + interactive_msg( + N("Problem during removal"), + N("There was a problem during the removal of packages:\n\n%s", join("\n", @results)), + if_(@results > 1, scroll => 1), + ); + return 1; + } else { + if ($may_be_orphans && !$::rpmdragora_options{auto_orphans}) { + if (my $msg = urpm::orphans::get_now_orphans_gui_msg($urpm)) { + interactive_msg(N("Information"), $msg, scroll => 1); + } + } + return 0; + } +} + +1; diff --git a/AdminPanel/Rpmdragora/rpmnew.pm b/AdminPanel/Rpmdragora/rpmnew.pm new file mode 100644 index 0000000..4df63c6 --- /dev/null +++ b/AdminPanel/Rpmdragora/rpmnew.pm @@ -0,0 +1,204 @@ +package AdminPanel::Rpmdragora::rpmnew; +#***************************************************************************** +# +# Copyright (c) 2002 Guillaume Cottenceau +# Copyright (c) 2002-2007 Thierry Vignaud +# 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: rpmnew.pm 263914 2009-12-03 17:41:02Z tv $ + +use strict; +use lib qw(/usr/lib/libDrakX); +use common; +use AdminPanel::rpmdragora; +use AdminPanel::Rpmdragora::init; +use AdminPanel::Rpmdragora::pkg; +use AdminPanel::Rpmdragora::open_db; +use AdminPanel::Rpmdragora::formatting; +use Exporter; +our @ISA = qw(Exporter); +our @EXPORT = qw(dialog_rpmnew do_merge_if_needed); + +# /var/lib/nfs/etab /var/lib/nfs/rmtab /var/lib/nfs/xtab /var/cache/man/whatis +my %ignores_rpmnew = map { $_ => 1 } qw( + /etc/adjtime + /etc/fstab + /etc/group + /etc/ld.so.conf + /etc/localtime + /etc/modules + /etc/passwd + /etc/security/fileshare.conf + /etc/shells + /etc/sudoers + /etc/sysconfig/alsa + /etc/sysconfig/autofsck + /etc/sysconfig/harddisks + /etc/sysconfig/harddrake2/previous_hw + /etc/sysconfig/init + /etc/sysconfig/installkernel + /etc/sysconfig/msec + /etc/sysconfig/nfs + /etc/sysconfig/pcmcia + /etc/sysconfig/rawdevices + /etc/sysconfig/saslauthd + /etc/sysconfig/syslog + /etc/sysconfig/usb + /etc/sysconfig/xinetd +); + +sub inspect { + my ($file) = @_; + my ($rpmnew, $rpmsave) = ("$file.rpmnew", "$file.rpmsave"); + my @inspect_wsize = ($typical_width*2.5, 500); + my $rpmfile = 'rpmnew'; + -r $rpmnew or $rpmfile = 'rpmsave'; + -r $rpmnew && -r $rpmsave && (stat $rpmsave)[9] > (stat $rpmnew)[9] and $rpmfile = 'rpmsave'; + $rpmfile eq 'rpmsave' and $rpmnew = $rpmsave; + + foreach (qw(LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION LC_ALL)) { + local $ENV{$_} = $ENV{$_} . '.UTF-8' if $ENV{$_} && $ENV{$_} !~ /UTF-8/; + } + my @diff = map { ensure_utf8($_); $_ } `/usr/bin/diff -u '$file' '$rpmnew'`; + @diff = N("(none)") if !@diff; + my $d = ugtk2->new(N("Inspecting %s", $file), grab => 1, transient => $::main_window); + my $save_wsize = sub { @inspect_wsize = $d->{rwindow}->get_size }; + my %texts; + require Gtk2::SourceView2; + my $lang_manager = Gtk2::SourceView2::LanguageManager->get_default; + gtkadd( + $d->{window}, + gtkpack_( + gtknew('VBox', spacing => 5), + 1, create_vpaned( + create_vpaned( + gtkpack_( + gtknew('VBox'), + 0, gtknew('Label', text_markup => qq($file:)), + 1, gtknew('ScrolledWindow', child => $texts{file} = Gtk2::SourceView2::View->new), + ), + gtkpack_( + gtknew('VBox'), + 0, gtknew('Label', text_markup => qq($rpmnew:)), + 1, gtknew('ScrolledWindow', child => $texts{rpmnew} = Gtk2::SourceView2::View->new), + ), + resize1 => 1, + ), + gtkpack_( + gtknew('VBox'), + 0, gtknew('Label', text => N("Changes:")), + 1, gtknew('ScrolledWindow', child => $texts{diff} = Gtk2::SourceView2::View->new), + ), + resize1 => 1, + ), + 0, Gtk2::HSeparator->new, + 0, gtknew('WrappedLabel', + # prevent bad sizing of Gtk2::WrappedLabel: + width => $inspect_wsize[0], + text => N("You can either remove the .%s file, use it as main file or do nothing. If unsure, keep the current file (\"%s\").", + $rpmfile, N("Remove .%s", $rpmfile)), + ), + 0, gtkpack__( + gtknew('HButtonBox'), + gtksignal_connect( + gtknew('Button', text => N("Remove .%s", $rpmfile)), + clicked => sub { $save_wsize->(); unlink $rpmnew; Gtk2->main_quit }, + ), + gtksignal_connect( + gtknew('Button', text => N("Use .%s as main file", $rpmfile)), + clicked => sub { $save_wsize->(); renamef($rpmnew, $file); Gtk2->main_quit }, + ), + gtksignal_connect( + gtknew('Button', text => N("Do nothing")), + clicked => sub { $save_wsize->(); Gtk2->main_quit }, + ), + ) + ) + ); + my %files = (file => $file, rpmnew => $rpmnew); + foreach (keys %files) { + gtktext_insert($texts{$_}, [ [ scalar(cat_($files{$_})), { 'font' => 'monospace' } ] ]); + my $lang = $lang_manager->guess_language($files{$_}); + $lang ||= $lang_manager->get_language('sh'); + my $buffer = $texts{$_}->get_buffer; + $buffer->set_language($lang) if $lang; + } + gtktext_insert($texts{diff}, [ [ join('', @diff), { 'font' => 'monospace' } ] ]); + my $buffer = $texts{diff}->get_buffer; + my $lang = $lang_manager->get_language('diff'); + $buffer->set_language($lang) if $lang; + $d->{rwindow}->set_default_size(@inspect_wsize); + $d->main; +} + +sub dialog_rpmnew { + my ($msg, %p2r) = @_; + @{$p2r{$_}} = grep { !$ignores_rpmnew{$_} } @{$p2r{$_}} foreach keys %p2r; + my $sum_rpmnew = sum(map { int @{$p2r{$_}} } keys %p2r); + $sum_rpmnew == 0 and return 1; + interactive_packtable( + N("Installation finished"), + $::main_window, + $msg, + [ map { my $pkg = $_; + map { + my $f = $_; + my $b; + [ gtkpack__( + gtknew('HBox'), + gtkset_markup( + gtkset_selectable(gtknew('Label'), 1), + qq($pkg:$f), + ) + ), + gtksignal_connect( + $b = gtknew('Button', text => N("Inspect...")), + clicked => sub { + inspect($f); + -r "$f.rpmnew" || -r "$f.rpmsave" or $b->set_sensitive(0); + }, + ) ]; + } @{$p2r{$pkg}}; + } keys %p2r ], + [ gtknew('Button', text => N("Ok"), + clicked => sub { Gtk2->main_quit }) ] + ); + return 0; +} + + +sub do_merge_if_needed() { + if ($rpmdragora_options{'merge-all-rpmnew'}) { + my %pkg2rpmnew; + my $wait = wait_msg(N("Please wait, searching...")); + print "Searching .rpmnew and .rpmsave files...\n"; + # costly: + open_rpm_db()->traverse(sub { + my $n = my_fullname($_[0]); + $pkg2rpmnew{$n} = [ grep { m|^/etc| && (-r "$_.rpmnew" || -r "$_.rpmsave") } map { chomp_($_) } $_[0]->conf_files ]; + }); + print "done.\n"; + undef $wait; + $typical_width = 330; + dialog_rpmnew('', %pkg2rpmnew) and print "Nothing to do.\n"; + myexit(0); + } +} + +1; diff --git a/AdminPanel/Rpmdragora/widgets.pm b/AdminPanel/Rpmdragora/widgets.pm new file mode 100644 index 0000000..2b42468 --- /dev/null +++ b/AdminPanel/Rpmdragora/widgets.pm @@ -0,0 +1,51 @@ +package Gtk2::Mdv::TextView; +#***************************************************************************** +# +# Copyright (c) 2002 Guillaume Cottenceau +# Copyright (c) 2002-2007 Thierry Vignaud +# 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: widgets.pm 233986 2008-02-06 14:14:06Z tv $ + +use strict; +use MDK::Common::Func 'any'; +use lib qw(/usr/lib/libDrakX); + +use Time::HiRes; +use feature 'state'; + + +sub new { + my ($_class) = @_; + my $w = gtknew('TextView', editable => 0); + state $time; + $w->signal_connect(size_allocate => sub { + my ($w, $requisition) = @_; + return if !ref($w->{anchors}); + return if Time::HiRes::clock_gettime() - $time < 0.200; + $time = Time::HiRes::clock_gettime(); + foreach my $anchor (@{$w->{anchors}}) { + $_->set_size_request($requisition->width-30, -1) foreach $anchor->get_widgets; + } + 1; + }); + $w; +} + +1; diff --git a/AdminPanel/Shared.pm b/AdminPanel/Shared.pm index 0779d0e..503a078 100644 --- a/AdminPanel/Shared.pm +++ b/AdminPanel/Shared.pm @@ -123,8 +123,8 @@ sub ask_YesOrNo { $align = $factory->createRight($layout); my $hbox = $factory->createHBox($align); - my $yesButton = $factory->createPushButton($hbox, N("Yes")); - my $noButton = $factory->createPushButton($hbox, N("No")); + my $yesButton = $factory->createPushButton($hbox, "Yes"); + my $noButton = $factory->createPushButton($hbox, "No"); my $event = $msg_box->waitForEvent(); diff --git a/AdminPanel/rpmdragora.pm b/AdminPanel/rpmdragora.pm new file mode 100644 index 0000000..c72f77b --- /dev/null +++ b/AdminPanel/rpmdragora.pm @@ -0,0 +1,989 @@ +#***************************************************************************** +# +# Copyright (c) 2002 Guillaume Cottenceau +# Copyright (c) 2002-2007 Thierry Vignaud +# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA +# Copyright (c) 2005, 2007 Mandriva SA +# Copyright (c) 2013 Matteo Pasotti +# +# 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: rpmdragora.pm 267936 2010-04-26 16:40:21Z jvictor $ + +package AdminPanel::rpmdragora; + +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'; + +use AdminPanel::Shared; + +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 + $rpmdragora_height + $rpmdragora_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 (rpmdragora instead of libDrakX) +BEGIN { unshift @::textdomains, qw(rpmdragora urpmi rpm-summary-main rpm-summary-contrib rpm-summary-devel rpm-summary-non-free) } + +use yui; +#ugtk2::add_icon_path('/usr/share/rpmdragora/icons'); + +Locale::gettext::bind_textdomain_codeset('rpmdragora', 'UTF8'); + +our $mageia_release = cat_( + -e '/etc/mageia-release' ? '/etc/mageia-release' : '/etc/release' +) || ''; +chomp $mageia_release; +our ($distro_version) = $mageia_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"); + +@rpmdragora::prompt::ISA = 'urpm::prompt'; + +sub rpmdragora::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 rpmdragora::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 = $Rpmdragora::init::rpmdragora_options{env}[0]; + +our $configfile = "$ENV{HOME}/.rpmdragora"; + +# +# 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 ($rpmdragora_height, $rpmdragora_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 ] }, + rpmdragora_height => { var => \$rpmdragora_height, default => [ 0 ] }, + rpmdragora_width => { var => \$rpmdragora_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: + $::rpmdragora_options{'no-confirmation'} = $no_confirmation->[0] if !defined $::rpmdragora_options{'no-confirmation'}; + $Rpmdragora::init::default_list_mode = $tree_mode->[0] if ref $tree_mode && !$Rpmdragora::init::overriding_config; +} + +sub writeconf() { + return if $::env; + unlink $configfile; + + # special case: + $no_confirmation->[0] = $::rpmdragora_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) = @_; + my $retVal = ask_YesOrNo($title, $contents); + return 1 if($retVal eq "Yes"); + return 0; +=comment + $options{transient} ||= $::main_window if $::main_window; + local $::isEmbedded; + my $factory = yui::YUI::widgetFactory; + my $d = $factory->createPopupDialog(); + + 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 $button_yes; + my $vbox = $factory->createVBox($d); + my $text_w = $factory->createMultiLineEdit($vbox, ""); + my $hbox = $factory->createHBox($vbox); + + ref($options{yesno}) eq 'ARRAY' ? map {ss + my $label = $_; + my $button_yes = $factory->createIconButton($hbox,"",$label); + } @{$options{yesno}} + : ( + $options{yesno} ? ( + my $button_no = $factory->createIconButton($hbox, "", $options{text}{no} || N("No")); + $button_yes = $factory->createIconButton($hbox,"", $options{text}{yes} || N("Yes")); + ) + : $button_yes = $factory->createIconButton($hbox,"",N("Ok")); + ) + + #$d->{window}->set_focus($button_yes); + #$text_w->set_size_request($typical_width*2, $options{scroll} ? 300 : -1); + #$d->main; + return $d->{retval}; +=cut +} + +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) = @_; + #return msgBox("Please wait"); + #OLD my $mainw = ugtk2->new(N("Please wait"), grab => 1, if_(exists $options{transient}, transient => $options{transient})); + my $factory = yui::YUI::widgetFactory; + my $mainw = $factory->createPopupDialog(); + #$mainw->{real_window}->set_position($options{transient} ? 'center_on_parent' : 'center_always'); + my $vbox = $factory->createVBox($mainw); + my $title = $factory->createLabel($vbox, N("Please wait")); + #my $label = $factory->createLabel($vbox, $msg); + #OLD 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->recalcLayout(); + $mainw->doneMultipleChanges(); + $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) = @_; + $::statusbar->setText($msg); + #- 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; + Glib::Timeout->add(5000, sub { statusbar_msg_remove(); 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); + $::statusbar->setValue(""); +} + +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 'cauldron' if $mageia_release =~ /cauldron/i; + #- we can't use updates for community while official is not out (release ends in ".0") + if ($want_base_distro || $mageia_release =~ /community/i && etc_version() =~ /\.0$/) { + return 'official' if $mageia_release =~ /official|limited/i; + return 'community' if $mageia_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 Rpmdragora::gurpm; + require Rpmdragora::pkg; + + my $res = urpm::download::sync_url($urpm, $url, + dir => $cachedir, + callback => sub { + $gurpm ||= + Rpmdragora::gurpm->new(N("Please wait"), + transient => $::main_window); + $canceled ||= + !Rpmdragora::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 = $rpmdragora::mageia_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; -- cgit v1.2.1