aboutsummaryrefslogtreecommitdiffstats
path: root/lib/ManaTools
diff options
context:
space:
mode:
Diffstat (limited to 'lib/ManaTools')
-rw-r--r--lib/ManaTools/Category.pm262
-rw-r--r--lib/ManaTools/ConfigReader.pm119
-rw-r--r--lib/ManaTools/MainDisplay.pm628
-rw-r--r--lib/ManaTools/Module.pm162
-rw-r--r--lib/ManaTools/Module/AdminMouse.pm282
-rw-r--r--lib/ManaTools/Module/Clock.pm518
-rw-r--r--lib/ManaTools/Module/DisplayManager.pm298
-rw-r--r--lib/ManaTools/Module/Firewall.pm1208
-rw-r--r--lib/ManaTools/Module/Hosts.pm530
-rw-r--r--lib/ManaTools/Module/LogViewer.pm568
-rw-r--r--lib/ManaTools/Module/Proxy.pm396
-rw-r--r--lib/ManaTools/Module/Services.pm588
-rw-r--r--lib/ManaTools/Module/Users.pm2637
-rw-r--r--lib/ManaTools/Privileges.pm61
-rw-r--r--lib/ManaTools/Rpmdragora/.perl_checker1
-rw-r--r--lib/ManaTools/Rpmdragora/edit_urpm_sources.pm2243
-rw-r--r--lib/ManaTools/Rpmdragora/formatting.pm209
-rw-r--r--lib/ManaTools/Rpmdragora/gui.pm1828
-rw-r--r--lib/ManaTools/Rpmdragora/gurpm.pm298
-rw-r--r--lib/ManaTools/Rpmdragora/icon.pm234
-rw-r--r--lib/ManaTools/Rpmdragora/init.pm181
-rw-r--r--lib/ManaTools/Rpmdragora/open_db.pm171
-rw-r--r--lib/ManaTools/Rpmdragora/pkg.pm1085
-rw-r--r--lib/ManaTools/Rpmdragora/rpmnew.pm363
-rw-r--r--lib/ManaTools/SettingsReader.pm101
-rw-r--r--lib/ManaTools/Shared.pm376
-rw-r--r--lib/ManaTools/Shared/Firewall.pm35
-rw-r--r--lib/ManaTools/Shared/GUI.pm1118
-rw-r--r--lib/ManaTools/Shared/Hosts.pm216
-rw-r--r--lib/ManaTools/Shared/JournalCtl.pm141
-rw-r--r--lib/ManaTools/Shared/Locales.pm280
-rw-r--r--lib/ManaTools/Shared/Proxy.pm36
-rw-r--r--lib/ManaTools/Shared/RunProgram.pm352
-rw-r--r--lib/ManaTools/Shared/Services.pm955
-rw-r--r--lib/ManaTools/Shared/Shorewall.pm271
-rw-r--r--lib/ManaTools/Shared/TimeZone.pm799
-rw-r--r--lib/ManaTools/Shared/Users.pm1612
-rw-r--r--lib/ManaTools/rpmdragora.pm1165
38 files changed, 22327 insertions, 0 deletions
diff --git a/lib/ManaTools/Category.pm b/lib/ManaTools/Category.pm
new file mode 100644
index 0000000..4ef7fa7
--- /dev/null
+++ b/lib/ManaTools/Category.pm
@@ -0,0 +1,262 @@
+# vim: set et ts=4 sw=4:
+# Copyright 2012 Steven Tucker
+#
+# This file is part of ManaTools
+#
+# ManaTools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# ManaTools 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 ManaTools. If not, see <http://www.gnu.org/licenses/>.
+
+
+#Class Category
+package ManaTools::Category;
+
+use strict;
+use warnings;
+use diagnostics;
+use yui;
+
+## Can only add the config file data at constructor
+## The Gui elements are added in setupGui inside MainDisplay
+#=============================================================
+
+=head2 new
+
+=head3 INPUT
+
+ $newName: new category name
+ $newIcon: new category icon
+
+=head3 OUTPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ Constructor: creates a new category named Name
+
+=cut
+
+#=============================================================
+
+sub new {
+ my ($class, $newName, $newIcon) = @_;
+ my $self = {
+ name => 0,
+ button => 0,
+ icon => 0,
+ modules => [],
+ };
+ bless $self, 'ManaTools::Category';
+
+ $self->{name} = $newName;
+ $self->{icon} = $newIcon;
+
+ return $self;
+}
+
+## Add a new module to the list
+#=============================================================
+
+=head2 loadModule
+
+=head3 INPUT
+
+ $self: this object
+ $module: module to add
+
+=head3 OUTPUT
+
+ 1: if the module has been added
+ 0: otherwise
+
+=head3 DESCRIPTION
+
+ This method adds a module to the loaded
+ modules if it is not already in.
+
+=cut
+
+#=============================================================
+sub loadModule {
+ my ($self, $module) = @_;
+
+ if (!$self->moduleLoaded($module->{name})) {
+ push ( @{$self->{modules}}, $module );
+
+ return 1;
+ }
+ return 0;
+}
+
+#=============================================================
+
+=head2 moduleLoaded
+
+=head3 INPUT
+
+ $self: this object
+ $module_name or -CLASS => name : module/CLASS name to look for
+
+=head3 OUTPUT
+
+ $present: module present or not
+
+=head3 DESCRIPTION
+
+ This method looks for the given module and if already in
+ returns true.
+=cut
+
+#=============================================================
+sub moduleLoaded {
+ my $self = shift;
+ my ($module_name) = @_;
+ my %params = ();
+ if ($module_name eq '-CLASS') {
+ (%params) = @_;
+ }
+
+ my $present = 0;
+
+ if (!$module_name || ! $self->{modules}) {
+ return $present;
+ }
+
+ foreach my $mod (@{$self->{modules}}) {
+ if (exists $params{-CLASS} && ref($mod) eq $params{-CLASS}) {
+ $present = 1;
+ last;
+ }
+ elsif ($mod->{name} eq $module_name) {
+ $present = 1;
+ last;
+ }
+ }
+
+ return $present;
+}
+
+#=============================================================
+
+=head2 addButtons
+
+=head3 INPUT
+
+ $self: this object
+ $panel: parent panel layout in which to create buttons
+ $factory: yui factory
+
+=head3 DESCRIPTION
+
+ Creates and adds buttons for each module_name
+
+=cut
+
+#=============================================================
+sub addButtons {
+ my($self, $panel, $factory) = @_;
+ my $tmpButton;
+ my $currLayout = 0;
+ my %weights = ();
+ my $curr;
+ my $count = 0;
+ foreach my $mod (@{$self->{modules}}) {
+ if(($count % 2) != 1) {
+ $factory->createVSpacing($panel, 0.5);
+ $currLayout = $factory->createHBox($panel);
+ $factory->createHSpacing($currLayout, 1);
+ $currLayout->setWeight($yui::YD_VERT, 10);
+ }
+
+ $tmpButton = $factory->createPushButton(
+ $currLayout,
+ $mod->name
+ );
+ $count++;
+ if (($count < scalar @{$self->{modules}}) || (($count >= scalar @{$self->{modules}}) && ($count % 2) == 0)) {
+ $tmpButton->setWeight($yui::YD_HORIZ, 20);
+ }
+ $factory->createHSpacing($currLayout, 1);
+ $mod->setButton($tmpButton);
+ $tmpButton->setLabel($mod->name);
+ $tmpButton->setIcon($mod->icon);
+ }
+}
+
+#=============================================================
+
+=head2 removeButtons
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ Delete the module buttons
+
+=cut
+
+#=============================================================
+sub removeButtons {
+ my($self) = @_;
+
+ for(@{$self->{modules}}) {
+ $_->removeButton();
+ }
+}
+
+#=============================================================
+
+=head2 setIcon
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ set the button icon
+
+=cut
+
+#=============================================================
+sub setIcon {
+ my($self) = @_;
+
+ $self->{button}->setIcon($self->{icon});
+}
+
+1;
+__END__
+
+=pod
+
+=head1 NAME
+
+ Category - add new category to window
+
+=head1 SYNOPSIS
+
+ $category = new Category('Category Name');
+
+
+=head1 USAGE
+
+ This class is used by MainDisplay internally and should not
+ be used outside, since MainDisplay::setupGui use it to
+ build GUI layout.
+
+=head1 FUNCTIONS
+
+=cut
diff --git a/lib/ManaTools/ConfigReader.pm b/lib/ManaTools/ConfigReader.pm
new file mode 100644
index 0000000..792dc0e
--- /dev/null
+++ b/lib/ManaTools/ConfigReader.pm
@@ -0,0 +1,119 @@
+# vim: set et ts=4 sw=4:
+# Copyright 2012 Steven Tucker
+#
+# This file is part of ManaTools
+#
+# ManaTools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# ManaTools 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 ManaTools. If not, see <http://www.gnu.org/licenses/>.
+
+
+#Class ConfigReader
+package ManaTools::ConfigReader;
+
+use strict;
+use warnings;
+use diagnostics;
+use XML::Simple;
+use Data::Dumper;
+
+sub new {
+ my ($class, $fileName) = @_;
+
+ my $self = {
+ data => 0,
+ catLen => 0,
+ currCat => 0,
+ modLen => 0,
+ currMod => 0,
+ placeHolder => 0,
+ };
+ bless $self, 'ManaTools::ConfigReader';
+
+ my $xml = new XML::Simple (KeyAttr=>[]);
+ $self->{data} = $xml->XMLin($fileName);
+ if (ref($self->{data}->{category}) eq "HASH") {
+ # one element alone
+ my @categories;
+ push @categories, $self->{data}->{category};
+ $self->{data}->{category} = undef;
+ push @{$self->{data}->{category}}, @categories;
+ }
+ $self->{catLen} = scalar(@{$self->{data}->{category}});
+ $self->{currCat} = -1;
+
+ if(ref(@{$self->{data}->{category}}[0]->{module}) eq "ARRAY") {
+ $self->{modLen} = scalar(@{@{$self->{data}->{category}}[0]->{module}});
+ } else {
+ $self->{modLen} = 1;
+ }
+ $self->{currMod} = -1;
+
+ return $self;
+}
+
+sub hasNextCat {
+ my ($self) = @_;
+
+ if($self->{currCat} + 1 >= $self->{catLen}) {
+ return 0;
+ }
+ return 1;
+}
+
+sub getNextCat {
+ my ($self) = @_;
+
+ $self->{currCat}++;
+ if($self->{currCat} >= $self->{catLen}) {
+ return 0;
+ }
+
+ # Reset the Module Count and Mod length for new Category
+ $self->{currMod} = -1;
+ if(ref(@{$self->{data}->{category}}[$self->{currCat}]->{module}) eq "ARRAY") {
+ $self->{modLen} = scalar(@{@{$self->{data}->{category}}[$self->{currCat}]->{module}});
+ } else {
+ $self->{modLen} = 1;
+ }
+
+ my $tmp = @{$self->{data}->{category}}[$self->{currCat}];
+
+ return $tmp;
+}
+
+sub hasNextMod {
+ my ($self) = @_;
+
+ if($self->{currMod} + 1 >= $self->{modLen}) {
+ return 0;
+ }
+ return 1;
+}
+
+sub getNextMod {
+ my ($self) = @_;
+
+ my $ret = 0;
+
+ $self->{currMod}++;
+
+ if($self->{modLen} == 1) {
+ $ret = @{$self->{data}->{category}}[$self->{currCat}]->{module};
+ } else {
+ $ret = @{@{$self->{data}->{category} }[$self->{currCat}]->{module}}[$self->{currMod}];
+ }
+
+ return $ret;
+}
+
+1;
diff --git a/lib/ManaTools/MainDisplay.pm b/lib/ManaTools/MainDisplay.pm
new file mode 100644
index 0000000..19d0a7f
--- /dev/null
+++ b/lib/ManaTools/MainDisplay.pm
@@ -0,0 +1,628 @@
+# vim: set et ts=4 sw=4:
+# Copyright 2012 Steven Tucker
+#
+# This file is part of ManaTools
+#
+# ManaTools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# ManaTools 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 ManaTools. If not, see <http://www.gnu.org/licenses/>.
+
+
+package ManaTools::MainDisplay;
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ManaTools::MainDisplay - class for AdminPaneol main window
+
+=head1 SYNOPSIS
+
+ $mainDisplay = new ManaTools::MainDisplay();
+ $mainDisplay->start();
+ $mainDisplay->destroy();
+
+=head1 METHODS
+
+=head1 DESCRIPTION
+
+Long_description
+
+=head1 EXPORT
+
+exported
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command:
+
+perldoc ManaTools::MainDisplay
+
+=head1 SEE ALSO
+
+SEE_ALSO
+
+=head1 AUTHOR
+
+Steven Tucker
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2012, Steven Tucker
+Copyright (C) 2014, Angelo Naselli.
+
+ManaTools is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+ManaTools 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 ManaTools. If not, see <http://www.gnu.org/licenses/>.
+
+=head1 FUNCTIONS
+
+=cut
+
+
+
+=head1 VERSION
+
+Version 0.01
+
+=cut
+
+our $VERSION = '1.0.0';
+
+use strict;
+use warnings;
+use diagnostics;
+use ManaTools::SettingsReader;
+use ManaTools::ConfigReader;
+use ManaTools::Category;
+use ManaTools::Module;
+use ManaTools::Shared;
+use ManaTools::Shared::GUI;
+use ManaTools::Shared::Locales;
+use File::ShareDir ':ALL';
+
+use yui;
+
+#=============================================================
+
+=head2 new
+
+=head3 DESCRIPTION
+
+This method instanziates the MainWindo object, and setups
+the startup GUI.
+
+=cut
+
+#=============================================================
+sub new {
+
+ my $self = {
+ categories => 0,
+ event => 0,
+ factory => 0,
+ mainWin => 0,
+ mainLayout => 0,
+ menuLayout => 0,
+ menus => {
+ file => 0,
+ help => 0
+ },
+ layout => 0,
+ leftPane => 0,
+ rightPane => 0,
+ currCategory => 0,
+ confDir => 0,
+ title => 0,
+ settings => 0,
+ exitButton => 0,
+ aboutButton => 0,
+ loc => 0,
+ replacePoint => 0,
+ };
+ bless $self, 'ManaTools::MainDisplay';
+
+## Default values
+ $self->{name} = "Mana-tools panel";
+ $self->{categories} = [];
+ $self->{confDir} = "/etc/mpan",
+ $self->{title} = "mpan",
+
+ my $cmdline = new yui::YCommandLine;
+
+ ## TODO add parameter check
+ my $pos = $cmdline->find("--name");
+ if ($pos > 0)
+ {
+ $self->{title} = $cmdline->arg($pos+1);
+ }
+ $pos = $cmdline->find("--conf_dir");
+ if ($pos > 0)
+ {
+ $self->{confDir} = $cmdline->arg($pos+1);
+ }
+ else
+ {
+ $self->{confDir} = "/etc/$self->{title}";
+ }
+
+ # TODO localize
+ $self->{loc} = ManaTools::Shared::Locales->new(domain_name => 'libDrakX-standalone');
+ $self->setupGui();
+
+ return $self;
+}
+
+
+sub _showAboutDialog {
+ my $self = shift;
+
+ my $translators = $self->{loc}->N("_: Translator(s) name(s) & email(s)\n");
+ $translators =~ s/\</\&lt\;/g;
+ $translators =~ s/\>/\&gt\;/g;
+ my $sh_gui = ManaTools::Shared::GUI->new();
+ $sh_gui->AboutDialog({ name => $self->{name},
+ version => $ManaTools::MainDisplay::VERSION,
+ credits => $self->{loc}->N("Copyright (C) %s Mageia community", '2013-2015'),
+ license => $self->{loc}->N("GPLv2"),
+ description => $self->{loc}->N("mpan is the mana-tools panel that collects all the utilities."),
+ authors => $self->{loc}->N("<h3>Developers</h3>
+ <ul><li>%s</li>
+ <li>%s</li>
+ </ul>
+ <h3>Translators</h3>
+ <ul><li>%s</li></ul>",
+ "Angelo Naselli &lt;anaselli\@linux.it&gt;",
+ "Matteo Pasotti &lt;matteo.pasotti\@gmail.com&gt;",
+ $translators
+ ),
+
+ });
+}
+
+
+
+## Begin the program event loop
+sub start {
+ my ($self) = shift;
+ my $reqExit = 0;
+
+ ##Default category selection
+ if (!$self->{currCategory}) {
+ $self->{currCategory} = @{$self->{categories}}[0];
+ }
+ $self->{currCategory}->addButtons($self->{rightPane}, $self->{factory});
+ $self->{rightPaneFrame}->setLabel($self->{currCategory}->{name});
+ $self->{factory}->createSpacing($self->{rightPane}, 1, 1, 1.0 );
+ my $launch = 0;
+ while(!$launch) {
+
+ ## Grab Event
+ $self->{event} = $self->{mainWin}->waitForEvent();
+ my $eventType = $self->{event}->eventType();
+
+ ## Check for window close
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::MenuEvent) {
+ ### MENU ###
+ my $item = $self->{event}->item();
+ my $menuLabel = $item->label();
+ if ($menuLabel eq $self->{menus}->{file}->{ quit }->label()) {
+ ## quit menu item
+ last;
+ }
+ elsif ($menuLabel eq $self->{menus}->{help}->{ about }->label()) {
+ $self->_showAboutDialog();
+ }
+ elsif ($menuLabel eq $self->{menus}->{help}->{ help }->label()) {
+ # TODO Help
+ }
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ my $widget = $self->{event}->widget();
+
+ ## Check for Exit button push or menu
+ if($widget == $self->{exitButton}) {
+ last;
+ }
+ elsif ($widget == $self->{aboutButton}) {
+ $self->_showAboutDialog();
+ }
+ else {
+ # category button selected?
+ my $isCat = $self->_categorySelected($widget);
+ if (!$isCat) {
+ # module button selected?
+ $launch = $self->_moduleSelected($widget);
+ }
+ }
+ }
+ }
+
+ return $launch;
+}
+
+#=============================================================
+
+=head2 destroy
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method destroyes the main window and all the
+ relevanto bojects (category and modules buttons).
+
+=cut
+
+#=============================================================
+sub destroy {
+ my ($self) = shift;
+
+ $self->{mainWin}->destroy();
+ for (my $cat=0; $cat < scalar(@{$self->{categories}}); $cat++ ) {
+ @{$self->{categories}}[$cat]->{button} = 0;
+ @{$self->{categories}}[$cat]->removeButtons();
+ }
+}
+
+#=============================================================
+
+=head2 setupGui
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method load configuration and build the GUI layout.
+
+=cut
+
+#=============================================================
+sub setupGui {
+ my ($self) = shift;
+
+ $self->_loadSettings();
+ yui::YUILog::setLogFileName($self->{settings}->{log}) if defined($self->{settings}->{log});
+ $self->{name} = $self->{settings}->{title};
+ yui::YUI::app()->setApplicationTitle($self->{name});
+ my $icon = defined($self->{settings}->{icon}) ?
+ $self->{settings}->{icon} :
+ File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/mageia.png');
+
+ yui::YUI::app()->setApplicationIcon($icon);
+
+ $self->{factory} = yui::YUI::widgetFactory;
+ $self->{mainWin} = $self->{factory}->createMainDialog;
+
+ $self->{mainLayout} = $self->{factory}->createVBox($self->{mainWin});
+ $self->{menuLayout} = $self->{factory}->createHBox($self->{mainLayout});
+
+ ## Menu File
+ my $align = $self->{factory}->createAlignment($self->{menuLayout}, 1, 0);
+ $self->{menus}->{file} = {
+ widget => $self->{factory}->createMenuButton($align, $self->{loc}->N("File")),
+ quit => new yui::YMenuItem($self->{loc}->N("&Quit")),
+ };
+
+ my @ordered_menu_lines = qw(quit);
+ foreach (@ordered_menu_lines) {
+ $self->{menus}->{file}->{ widget }->addItem($self->{menus}->{file}->{ $_ });
+ }
+ $self->{menus}->{file}->{ widget }->rebuildMenuTree();
+
+ $align = $self->{factory}->createAlignment($self->{menuLayout}, 2, 0);
+ $self->{menus}->{help} = {
+ widget => $self->{factory}->createMenuButton($align, $self->{loc}->N("Help")),
+ help => new yui::YMenuItem($self->{loc}->N("Help")),
+ about => new yui::YMenuItem($self->{loc}->N("&About")),
+ };
+
+ ## Menu Help
+ @ordered_menu_lines = qw(help about);
+ foreach (@ordered_menu_lines) {
+ $self->{menus}->{help}->{ widget }->addItem($self->{menus}->{help}->{ $_ });
+ }
+ $self->{menus}->{help}->{ widget }->rebuildMenuTree();
+
+ $self->{layout} = $self->{factory}->createHBox($self->{mainLayout});
+
+ #create left Panel Frame no need to add a label for title
+ $self->{leftPaneFrame} = $self->{factory}->createFrame($self->{layout}, $self->{settings}->{category_title});
+ #create right Panel Frame no need to add a label for title (use setLabel when module changes)
+ $self->{rightPaneFrame} = $self->{factory}->createFrame($self->{layout}, "");
+ #create replace point for dynamically created widgets
+ $self->{replacePoint} = $self->{factory}->createReplacePoint($self->{rightPaneFrame});
+
+ $self->{rightPane} = $self->{factory}->createVBox($self->{replacePoint});
+ $self->{leftPane} = $self->{factory}->createVBox($self->{leftPaneFrame});
+
+ #logo from settings
+ my $logofile = defined($self->{settings}->{logo}) ?
+ $self->{settings}->{logo} :
+ File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/logo_mageia.png');
+
+ my $logo = $self->{factory}->createImage($self->{leftPane}, $logofile);
+ $logo->setAutoScale(1);
+
+# $self->{leftPaneFrame}->setWeight(0, 1);
+ $self->{rightPaneFrame}->setWeight(0, 2);
+
+ $self->_loadCategories();
+ $self->{factory}->createVStretch($self->{leftPane});
+
+ $self->{aboutButton} = $self->{factory}->createPushButton($self->{leftPane}, "&About");
+ $self->{aboutButton}->setStretchable(0, 1);
+
+ $self->{exitButton} = $self->{factory}->createPushButton($self->{leftPane}, "&Quit");
+ my $quitIcon = File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/quit.png');
+ $self->{exitButton}->setIcon($quitIcon);
+ $self->{exitButton}->setStretchable(0, 1);
+}
+
+
+## internal methods
+
+## Check if event is from current Category View
+## If icon click, returns the module to be launched
+
+sub _moduleSelected {
+ my ($self, $selectedWidget) = @_;
+
+ for(@{$self->{currCategory}->{modules}}) {
+ if( $_->{button} == $selectedWidget ){
+ return $_;
+ }
+ }
+ return 0;
+}
+
+
+## Discover if a category button was selected.
+## If category button is selected, sets right panel to display
+## the selected Category Modules
+## returns 1 if category button is selected
+sub _categorySelected {
+ my ($self, $selectedWidget) = @_;
+ for (@{$self->{categories}}) {
+ if( $_->{button} == $selectedWidget ) {
+
+ #if current is already set then skips
+ if ($self->{currCategory} == $_) {
+ ## returns 1 to skip any other checks on
+ ## the selected widget
+ return 1;
+ }
+ ## Menu item selected, set right pane
+ $self->{mainWin}->startMultipleChanges();
+ ## Remove existing modules
+ $self->{replacePoint}->deleteChildren();
+ $self->{rightPane} = $self->{factory}->createVBox($self->{replacePoint});
+
+ ## Change Current Category to the selected one
+ $self->{currCategory} = $_;
+ ## Add new Module Buttons to Right Pane
+ $self->{currCategory}->addButtons($self->{rightPane}, $self->{factory});
+ $self->{rightPaneFrame}->setLabel($self->{currCategory}->{name});
+ $self->{factory}->createSpacing($self->{rightPane}, 1, 1, 1.0 );
+ $self->{replacePoint}->showChild();
+ $self->{mainWin}->recalcLayout();
+ $self->{mainWin}->doneMultipleChanges();
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+## adpanel settings
+sub _loadSettings {
+ my ($self, $force_load) = @_;
+ # configuration file name
+ my $fileName = "$self->{confDir}/settings.conf";
+ die "Configuration file missing" if (! -e $fileName);
+ if (!$self->{settings} || $force_load) {
+ $self->{settings} = new ManaTools::SettingsReader($fileName);
+ }
+}
+
+#=============================================================
+# _categoryLoaded
+#
+# INPUT
+#
+# $self: this object
+# $category: category to look for
+#
+# OUTPUT
+#
+# $present: category is present or not
+#
+# DESCRIPTION
+#
+# This method looks for the given category and if already in
+# returns true.
+#
+#=============================================================
+sub _categoryLoaded {
+ my ($self, $category) = @_;
+ my $present = 0;
+
+ if (!$category) {
+ return $present;
+ }
+
+ foreach my $cat (@{$self->{categories}}) {
+ if ($cat->{name} eq $category->{name}) {
+ $present = 1;
+ last;
+ }
+ }
+
+ return $present;
+}
+
+#=============================================================
+# _getCategory
+#
+# INPUT
+#
+# $self: this object
+# $name: category name
+#
+# OUTPUT
+#
+# $category: category object if exists
+#
+# DESCRIPTION
+#
+# This method looks for the given category name and returns
+# the realte object.
+#=============================================================
+sub _getCategory {
+ my ($self, $name) = @_;
+ my $category = undef;
+
+ foreach $category (@{$self->{categories}}) {
+ if ($category->{name} eq $name) {
+ return $category;
+ }
+ }
+
+ return $category;
+}
+
+# _loadCategory
+#
+# creates a new button representing a category
+#
+sub _loadCategory {
+ my ($self, $category) = @_;
+
+ if (!$self->_categoryLoaded($category)) {
+ push ( @{$self->{categories}}, $category );
+
+ @{$self->{categories}}[-1]->{button} = $self->{factory}->createPushButton(
+ $self->{leftPane},
+ $self->{categories}[-1]->{name}
+ );
+ @{$self->{categories}}[-1]->setIcon();
+
+ @{$self->{categories}}[-1]->{button}->setStretchable(0, 1);
+ }
+ else {
+ for (my $cat=0; $cat < scalar(@{$self->{categories}}); $cat++ ) {
+ if( @{$self->{categories}}[$cat]->{name} eq $category->{name} &&
+ !@{$self->{categories}}[$cat]->{button}) {
+ @{$self->{categories}}[$cat]->{button} = $self->{factory}->createPushButton(
+ $self->{leftPane},
+ $self->{categories}[$cat]->{name}
+ );
+ @{$self->{categories}}[$cat]->setIcon();
+ @{$self->{categories}}[$cat]->{button}->setStretchable(0, 1);
+ last;
+
+ }
+ }
+ }
+}
+
+sub _loadCategories {
+ my ($self) = @_;
+
+ # category files
+ my @categoryFiles;
+ my $fileName = "$self->{confDir}/categories.conf";
+
+
+ # configuration file dir
+ my $directory = "$self->{confDir}/categories.conf.d";
+
+ push(@categoryFiles, $fileName);
+ push(@categoryFiles, <$directory/*.conf>);
+ my $currCategory;
+
+ foreach $fileName (@categoryFiles) {
+ my $inFile = new ManaTools::ConfigReader($fileName);
+ my $tmpCat;
+ my $tmp;
+ my $hasNextCat = $inFile->hasNextCat();
+ while( $hasNextCat ) {
+ $tmp = $inFile->getNextCat();
+ $tmpCat = $self->_getCategory($tmp->{title});
+ if (!$tmpCat) {
+ $tmpCat = new ManaTools::Category($tmp->{title}, $tmp->{icon});
+ }
+ $self->_loadCategory($tmpCat);
+ $hasNextCat = $inFile->hasNextCat();
+ $currCategory = $tmpCat;
+
+ my $hasNextMod = $inFile->hasNextMod();
+ while( $hasNextMod ) {
+ $tmp = $inFile->getNextMod();
+ my $tmpMod;
+ my $loaded = 0;
+
+ if (exists $tmp->{title}) {
+ if (not $currCategory->moduleLoaded($tmp->{title})) {
+ $tmpMod = ManaTools::Module->create(name => $tmp->{title},
+ icon => $tmp->{icon},
+ launch => $tmp->{launcher}
+ );
+ }
+ }
+ elsif (exists $tmp->{class}) {
+ if (not $currCategory->moduleLoaded(-CLASS => $tmp->{class})) {
+ $tmpMod = ManaTools::Module->create(-CLASS => $tmp->{class});
+ }
+ }
+ if ($tmpMod) {
+ $loaded = $currCategory->loadModule($tmpMod);
+ undef $tmpMod if !$loaded;
+ }
+ $hasNextMod = $inFile->hasNextMod();
+ }
+ }
+ }
+}
+
+1;
+
+=pod
+
+=head2 start
+
+ contains the main loop of the application
+ where we can check for events
+
+=head2 setupGui
+
+ creates a popupDialog using a YUI::WidgetFactory
+ and then populate this dialog with some components
+
+=cut
+
diff --git a/lib/ManaTools/Module.pm b/lib/ManaTools/Module.pm
new file mode 100644
index 0000000..1e13d92
--- /dev/null
+++ b/lib/ManaTools/Module.pm
@@ -0,0 +1,162 @@
+# vim: set et ts=4 sw=4:
+# Copyright 2012 Steven Tucker
+#
+# This file is part of ManaTools
+#
+# ManaTools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# ManaTools 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 ManaTools. If not, see <http://www.gnu.org/licenses/>.
+
+
+#Class Module
+package ManaTools::Module;
+
+use Moose;
+
+=head1 VERSION
+
+Version 0.01
+
+=cut
+
+our $VERSION = '1.0.0';
+
+use yui;
+
+=head1 SUBROUTINES/METHODS
+
+=head2 create - returns a Module object such as a module
+ launcher (this object) or an extension of
+ this class
+
+=cut
+
+sub create {
+ my $class = shift;
+ $class = ref $class || $class;
+ my (%params) = @_;
+
+ my $obj;
+ if ( exists $params{-CLASS} ) {
+ my $driver = $params{-CLASS};
+
+ eval {
+ my $pkg = $driver;
+ $pkg =~ s/::/\//g;
+ $pkg .= '.pm';
+ require $pkg;
+ $obj=$driver->new();
+ };
+ if ( $@ ) {
+ die "Error getting obj for driver $params{-CLASS}: $@";
+ return undef;
+ }
+ }
+ else {
+ $obj = new ManaTools::Module(@_);
+ }
+ return $obj;
+}
+
+has 'icon' => (
+ is => 'rw',
+ isa => 'Str',
+);
+
+has 'name' => (
+ is => 'rw',
+ isa => 'Str',
+);
+
+has 'launch' => (
+ is => 'rw',
+ isa => 'Str',
+);
+
+has 'button' => (
+ is => 'rw',
+ init_arg => undef,
+);
+
+
+#=============================================================
+
+=head2 setButton
+
+=head3 INPUT
+
+$self: this object
+$button: yui push button to be assigned to this module
+
+=head3 DESCRIPTION
+
+This method assignes a button to this module
+
+=cut
+
+#=============================================================
+sub setButton {
+ my ($self, $button) = @_;
+ $self->{button} = $button;
+}
+
+#=============================================================
+
+=head2 removeButton
+
+=head3 INPUT
+
+$self: this object
+
+=head3 DESCRIPTION
+
+This method remove the assigned button from this module
+
+=cut
+
+#=============================================================
+sub removeButton {
+ my($self) = @_;
+
+ undef($self->{button});
+}
+
+# base class launcher
+#=============================================================
+
+=head2 start
+
+=head3 INPUT
+
+$self: this object
+
+=head3 DESCRIPTION
+
+This method is the base class launcher, run external modules.
+
+=cut
+
+#=============================================================
+sub start {
+ my $self = shift;
+
+ if ($self->{launch}) {
+ my $err = yui::YUI::app()->runInTerminal( $self->{launch} . " --ncurses");
+ if ($err == -1) {
+ system($self->{launch});
+ }
+ }
+}
+
+
+no Moose;
+1;
diff --git a/lib/ManaTools/Module/AdminMouse.pm b/lib/ManaTools/Module/AdminMouse.pm
new file mode 100644
index 0000000..279fb76
--- /dev/null
+++ b/lib/ManaTools/Module/AdminMouse.pm
@@ -0,0 +1,282 @@
+# vim: set et ts=4 sw=4:
+#*****************************************************************************
+#
+# Copyright (c) 2013 - 2015 Angelo Naselli <anaselli@linux.it>
+# from drakx services
+#
+# 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.
+#
+#*****************************************************************************
+
+# NOTE this module has not been ported and does not work
+# TODO porting it if it is really needed nowadays
+package ManaTools::Module::AdminMouse;
+
+#leaving them atm
+use lib qw(/usr/lib/libDrakX);
+
+# i18n: IMPORTANT: to get correct namespace (drakx-kbd-mouse-x11 instead of libDrakX)
+BEGIN { unshift @::textdomains, 'drakx-kbd-mouse-x11' }
+
+use common;
+use modules;
+use mouse;
+use c;
+
+use ManaTools::Shared;
+
+use yui;
+use Moose;
+
+extends qw( ManaTools::Module );
+
+has '+icon' => (
+ default => "/usr/share/mcc/themes/default/mousedrake-mdk.png",
+);
+
+has '+name' => (
+ default => N("AdminMouse"),
+);
+
+sub start {
+ my $self = shift;
+
+ $self->_adminMouseDialog();
+}
+
+sub _getUntranslatedName {
+ my ($self, $name, $list) = @_;
+
+ foreach my $n (@{$list}) {
+ my @names = split(/\|/, $n);
+ for (my $lev=0; $lev < scalar(@names); $lev++) {
+ if (translate($names[$lev]) eq $name) {
+ return $names[$lev];
+ }
+ }
+ }
+
+ return undef;
+}
+
+
+sub _adminMouseDialog {
+ my $self = shift;
+
+ my $datavalue = "TEST";
+
+ my $appTitle = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($self->name);
+ ## set icon if not already set by external launcher
+ yui::YUI::app()->setApplicationIcon($self->icon);
+
+ my $factory = yui::YUI::widgetFactory;
+
+ my $dialog = $factory->createMainDialog;
+ my $vbox = $factory->createVBox( $dialog );
+ my $frame = $factory->createFrame ($vbox, N("Please choose your type of mouse."));
+ my $treeWidget = $factory->createTree($frame, "");
+
+ my $modules_conf = modules::any_conf->read;
+
+ my $mouse = mouse::read();
+
+ if (!$::noauto) {
+ my $probed_mouse = mouse::detect($modules_conf);
+ $mouse = $probed_mouse if !$mouse->{Protocol} || !$probed_mouse->{unsafe};
+ }
+
+ if (!$mouse || !$::auto) {
+ $mouse ||= mouse::fullname2mouse('Universal|Any PS/2 & USB mice');
+
+ my $prev = my $fullname = $mouse->{type} . '|' . $mouse->{name};
+ my $selected = $mouse->{name};
+
+ my $fullList = { list => [ mouse::_fullnames() ], items => [], separator => '|', val => \$fullname,
+ format => sub { join('|', map { translate($_) } split('\|', $_[0])) } } ;
+ my $i;
+
+ my $itemColl = new yui::YItemCollection;
+ my @items;
+ for ($i=0; $i<scalar(@{$fullList->{list}}); $i++) {
+ my @names = split(/\|/, $fullList->{list}[$i]);
+ for (my $lev=0; $lev < scalar(@names); $lev++) {
+ $names[$lev] = N($names[$lev]);
+ }
+ if ($i == 0 || $names[0] ne $items[0]->{label}) {
+ if ($i != 0) {
+ $itemColl->push($items[0]->{item});
+ push @{$fullList->{items}}, $items[-1]->{item};;
+ }
+ @items = undef;
+ my $item = new yui::YTreeItem ($names[0]);
+
+ if ($selected eq $self->_getUntranslatedName($item->label(), $fullList->{list})) {
+ $item->setSelected(1) ;
+ $item->setOpen(1);
+ my $parent = $item;
+ while($parent = $parent->parent()) {
+ $parent->setOpen(1);
+ }
+ }
+ $item->DISOWN();
+ @items = ({item => $item, label => $names[0], level => 0});
+ for (my $lev=1; $lev < scalar(@names); $lev++) {
+ $item = new yui::YTreeItem ($items[$lev-1]->{item}, $names[$lev]);
+
+ if ($selected eq $self->_getUntranslatedName($item->label(), $fullList->{list})) {
+ $item->setSelected(1) ;
+ $item->setOpen(1);
+ my $parent = $item;
+ while($parent = $parent->parent()) {
+ $parent->setOpen(1);
+ }
+ }
+ $item->DISOWN();
+ if ($lev < scalar(@names)-1) {
+ push @items, {item => $item, label => $names[$lev], level => $lev};
+ }
+ }
+ }
+ else {
+ my $prevItem = 0;
+ for (my $lev=1; $lev < scalar(@names); $lev++) {
+ my $it;
+ for ($it=1; $it < scalar(@items); $it++){
+ if ($items[$it]->{label} eq $names[$lev] && $items[$it]->{level} == $lev) {
+ $prevItem = $it;
+ last;
+ }
+ }
+ if ($it == scalar(@items)) {
+ my $item = new yui::YTreeItem ($items[$prevItem]->{item}, $names[$lev]);
+
+ if ($selected eq $self->_getUntranslatedName($item->label(), $fullList->{list})) {
+ $item->setSelected(1) ;
+ $item->setOpen(1);
+ my $parent = $item;
+ while($parent = $parent->parent()) {
+ $parent->setOpen(1);
+ }
+ }
+ $item->DISOWN();
+ push @items, {item => $item, label => $names[$lev], level => $lev};
+ }
+ }
+ }
+ }
+ $itemColl->push($items[0]->{item});
+ push @{$fullList->{items}}, $items[-1]->{item};
+
+ $treeWidget->addItems($itemColl);
+ my $align = $factory->createLeft($vbox);
+ my $hbox = $factory->createHBox($align);
+ my $aboutButton = $factory->createPushButton($hbox, N("About") );
+ $align = $factory->createRight($hbox);
+ $hbox = $factory->createHBox($align);
+ my $cancelButton = $factory->createPushButton($hbox, N("Cancel") );
+ my $okButton = $factory->createPushButton($hbox, N("Ok") );
+
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $aboutButton) {
+ my $license = translate($ManaTools::Shared::License);
+ ManaTools::Shared::AboutDialog(
+ { name => N("AdminMouse"),
+ version => $self->VERSION,
+ copyright => N("Copyright (C) %s Mageia community", '2014'),
+ license => $license,
+ comments => N("AdminMouse is the Mageia mouse management tool \n(from the original idea of Mandriva mousedrake)."),
+ website => 'http://www.mageia.org',
+ website_label => N("Mageia"),
+ authors => "Angelo Naselli <anaselli\@linux.it>\nMatteo Pasotti <matteo.pasotti\@gmail.com>",
+ translator_credits =>
+ #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith <jsmith@nowhere.com>")
+ N("_: Translator(s) name(s) & email(s)\n")}
+ );
+ }
+ elsif ($widget == $okButton) {
+ my $continue = 1;
+ my $selectedItem = $treeWidget->selectedItem();
+
+ my $it=$selectedItem;
+ my $fullname = $self->_getUntranslatedName($it->label(), $fullList->{list});
+ while($it = yui::toYTreeItem($it)->parent()) {
+ $fullname = join("|", $self->_getUntranslatedName($it->label(), $fullList->{list}), $fullname);
+ }
+
+ if ($fullname ne $prev) {
+ my $mouse_ = mouse::fullname2mouse($fullname, device => $mouse->{device});
+ if ($fullname =~ /evdev/) {
+ $mouse_->{evdev_mice} = $mouse_->{evdev_mice_all} = $mouse->{evdev_mice_all};
+ }
+ %$mouse = %$mouse_;
+ }
+
+ if ($mouse->{nbuttons} < 3 ) {
+ $mouse->{Emulate3Buttons} = ManaTools::Shared::ask_YesOrNo('', N("Emulate third button?"));
+ }
+ if ($mouse->{type} eq 'serial') {
+ my @list = ();
+ foreach (detect_devices::serialPorts()) {
+ push @list, detect_devices::serialPort2text($_);
+ }
+ my $choice = ManaTools::Shared::ask_fromList(N("Mouse Port"),
+ N("Please choose which serial port your mouse is connected to."),
+ \@list);
+ if ( !$choice ) {
+ $continue = 0;
+ }
+ else {
+ $mouse->{device} = $choice;
+ }
+ }
+
+ if ($continue) {
+ last;
+ }
+ }
+ }
+ }
+
+ }
+
+ # TODO manage write conf without interactive things
+ # mouse::write_conf($in->do_pkgs, $modules_conf, $mouse, 1);
+ system('systemctl', 'try-restart', 'gpm.service') if -e '/usr/lib/systemd/system/gpm.service';
+
+ ManaTools::Shared::infoMsgBox(N("Not implemented yet: configuration is not changed"));
+
+ $dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+}
+
+1;
diff --git a/lib/ManaTools/Module/Clock.pm b/lib/ManaTools/Module/Clock.pm
new file mode 100644
index 0000000..f2bb5c0
--- /dev/null
+++ b/lib/ManaTools/Module/Clock.pm
@@ -0,0 +1,518 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Module::Clock;
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ManaTools::Module::Clock - This module aims to configure system clock and time
+
+=head1 SYNOPSIS
+
+ my $clockSettings = ManaTools::Module::Clock->new();
+ $clockSettings->start();
+
+=head1 DESCRIPTION
+
+Long_description
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command:
+
+perldoc ManaTools::Module::Clock
+
+=head1 SEE ALSO
+
+SEE_ALSO
+
+=head1 AUTHOR
+
+Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2014-2015, Angelo Naselli.
+
+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
+
+=head1 FUNCTIONS
+
+=cut
+
+use Moose;
+
+use diagnostics;
+
+use ManaTools::Shared::GUI;
+use ManaTools::Shared::Locales;
+use ManaTools::Shared::TimeZone;
+
+use Time::Piece;
+
+use yui;
+
+extends qw( ManaTools::Module );
+
+### TODO icon
+has '+icon' => (
+ default => "/usr/share/mcc/themes/default/time-mdk.png",
+);
+
+has 'loc' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_localeInitialize'
+);
+
+sub _localeInitialize {
+ my $self = shift;
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'libDrakX-standalone') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+has 'sh_gui' => (
+ is => 'rw',
+ lazy => 1,
+ init_arg => undef,
+ builder => '_SharedGUIInitialize'
+);
+
+sub _SharedGUIInitialize {
+ my $self = shift;
+
+ $self->sh_gui(ManaTools::Shared::GUI->new() );
+}
+
+has 'sh_tz' => (
+ is => 'rw',
+ lazy => 1,
+ builder => '_SharedTimeZoneInitialize'
+);
+
+sub _SharedTimeZoneInitialize {
+ my $self = shift;
+
+ $self->sh_tz(ManaTools::Shared::TimeZone->new() );
+}
+
+
+=head1 VERSION
+
+Version 1.0.0
+
+=cut
+
+our $VERSION = '1.0.0';
+
+#=============================================================
+
+=head2 BUILD
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ The BUILD method is called after a Moose object is created,
+ in this methods Services loads all the service information.
+
+=cut
+
+#=============================================================
+sub BUILD {
+ my $self = shift;
+
+ if (! $self->name) {
+ $self->name ($self->loc->N("Date, Clock & Time Zone Settings"));
+ }
+}
+
+#=============================================================
+
+=head2 start
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method extends Module::start and is invoked to
+ start admin clock
+
+=cut
+
+#=============================================================
+sub start {
+ my $self = shift;
+
+ $self->_adminClockPanel();
+};
+
+### _get_NTPservers
+## returns ntp servers in the format
+## Zone|Nation: server
+#
+sub _get_NTPservers {
+ my $self = shift;
+
+ my $servs = $self->sh_tz->ntpServers();
+ [ map { "$servs->{$_}|$_" } sort { $servs->{$a} cmp $servs->{$b} || $a cmp $b } keys %$servs ];
+}
+
+### _restoreValues
+## restore NTP server and Time Zone from configuration files
+#
+## input '$datetime_only' restore date and time only
+#
+## returns 'info', a HASH references containing:
+## time_zone => time zone hash reference to be restored
+## ntp_server => ntp server address
+## date => date string
+## time => time string
+## ntp_running => is NTP running?
+#
+sub _restoreValues {
+ my ($self, $datetime_only) = @_;
+
+ my $info;
+ if (!$datetime_only) {
+ $info->{time_zone} = $self->sh_tz->readConfiguration();
+ $info->{ntp_server} = $self->sh_tz->ntpCurrentServer();
+ #- strip digits from \d+.foo.pool.ntp.org
+ $info->{ntp_server} =~ s/^\d+\.// if $info->{ntp_server};
+ $info->{ntp_running} = $self->sh_tz->isNTPRunning();
+ }
+ my $t = localtime;
+ my $day = $t->strftime("%F");
+ my $time = $t->strftime("%H:%M:%S");
+ $info->{date} = $day;
+ $info->{time} = $time;
+
+ return $info;
+}
+
+sub _adminClockPanel {
+ my $self = shift;
+
+ my $appTitle = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($self->name);
+ ## set icon if not already set by external launcher
+ yui::YUI::app()->setApplicationIcon($self->icon);
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optFactory = yui::YUI::optionalWidgetFactory;
+ die "calendar widgets missing" if (!$optFactory->hasDateField() || !$optFactory->hasTimeField());
+
+ # Create Dialog
+ my $dialog = $factory->createMainDialog;
+# my $minSize = $factory->createMinSize($dialog, 40, 15);
+
+ # Start Dialog layout:
+ my $layout = $factory->createVBox($dialog);
+ my $align = $factory->createLeft($layout);
+
+ my $frame = $factory->createFrame($align, $self->loc->N("Setting date and time"));
+ my $hbox = $factory->createHBox($frame);
+
+ my $dateField = $optFactory->createDateField($hbox, "");
+ $factory->createHSpacing($hbox, 3.0);
+ my $timeField = $optFactory->createTimeField($hbox, "");
+ $factory->createHSpacing($hbox, 1.0);
+ $factory->createVSpacing($hbox, 1.0);
+ $factory->createVSpacing($layout, 1.0);
+
+ $align = $factory->createLeft($layout);
+ $hbox = $factory->createHBox($align);
+ my $ntpFrame = $factory->createCheckBoxFrame($hbox, $self->loc->N("Enable Network Time Protocol"), 0);
+
+ my $hbox1 = $factory->createHBox($ntpFrame);
+ my $changeNTPButton = $factory->createPushButton($hbox1, $self->loc->N("Change &NTP server"));
+ $factory->createHSpacing($hbox1, 1.0);
+ $factory->createLabel($hbox1,$self->loc->N("Current:"));
+ $factory->createHSpacing($hbox1, 1.0);
+ my $ntpLabel = $factory->createLabel($hbox1, $self->loc->N("not defined"));
+ $factory->createHSpacing($hbox1, 1.0);
+ $ntpLabel->setWeight($yui::YD_HORIZ, 2);
+ $changeNTPButton->setWeight($yui::YD_HORIZ, 1);
+ $factory->createHSpacing($hbox, 1.0);
+
+ $factory->createVSpacing($layout, 1.0);
+ $align = $factory->createLeft($layout);
+ $hbox = $factory->createHBox($align);
+ $frame = $factory->createFrame ($hbox, $self->loc->N("TimeZone"));
+ $hbox1 = $factory->createHBox( $frame );
+ my $changeTZButton = $factory->createPushButton($hbox1, $self->loc->N("Change &Time Zone"));
+ $factory->createHSpacing($hbox1, 1.0);
+ $factory->createLabel($hbox1,$self->loc->N("Current:"));
+ $factory->createHSpacing($hbox1, 1.0);
+ my $timeZoneLbl = $factory->createLabel($hbox1, $self->loc->N("not defined"));
+ $factory->createHSpacing($hbox1, 1.0);
+ $timeZoneLbl->setWeight($yui::YD_HORIZ, 2);
+ $changeTZButton->setWeight($yui::YD_HORIZ, 1);
+
+ $factory->createHSpacing($hbox, 1.0);
+
+ # buttons on the last line
+ $factory->createVSpacing($layout, 1.0);
+ $hbox = $factory->createHBox($layout);
+
+ $align = $factory->createLeft($hbox);
+ $hbox = $factory->createHBox($align);
+ my $aboutButton = $factory->createPushButton($hbox, $self->loc->N("&About") );
+ my $resetButton = $factory->createPushButton($hbox, $self->loc->N("&Reset") );
+
+ $align = $factory->createRight($hbox);
+ $hbox = $factory->createHBox($align);
+ my $cancelButton = $factory->createPushButton($hbox, $self->loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($hbox, $self->loc->N("&Ok"));
+ $factory->createHSpacing($hbox, 1.0);
+
+ ## no changes by default
+ $dialog->setDefaultButton($cancelButton);
+
+ # End Dialof layout
+
+ ## default value
+ my $info = $self->_restoreValues();
+
+ $dateField->setValue($info->{date});
+ $timeField->setValue($info->{time});
+
+ if (exists $info->{time_zone} && $info->{time_zone}->{ZONE}) {
+ $timeZoneLbl->setValue($info->{time_zone}->{ZONE});
+ }
+
+ if ($info->{ntp_server}) {
+ $ntpLabel->setValue($info->{ntp_server});
+ }
+ $ntpFrame->setValue($info->{ntp_running});
+
+
+ # get only once
+ my $NTPservers = $self->_get_NTPservers();
+
+ while(1) {
+ my $event = $dialog->waitForEvent(1000);
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::TimeoutEvent) {
+ my $t = Time::Piece->strptime($timeField->value(), "%H:%M:%S") + 1;
+ $timeField->setValue($t->strftime("%H:%M:%S"));
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $okButton) {
+ yui::YUI::app()->busyCursor();
+ my $finished = 1;
+ # (1) write new TZ settings
+ # (2) write new NTP settigs if checked
+ # (3) use date time fields if NTP is not checked
+
+ my $old_conf = $self->sh_tz->readConfiguration();
+ if ($info->{time_zone}->{UTC} != $old_conf->{UTC} ||
+ $info->{time_zone}->{ZONE} ne $old_conf->{ZONE}) {
+ # (1)
+ eval { $self->sh_tz->writeConfiguration($info->{time_zone}) };
+ my $errors = $@;
+ if ($errors) {
+ $finished = 0;
+ $self->sh_gui->warningMsgBox({
+ title => $self->loc->N("Write configuration failed"),
+ text => "$errors",
+ richtext => 1,
+ });
+ }
+ }
+ if ($ntpFrame->value()) {
+ # (2)
+ if ($info->{ntp_server}) {
+ eval { $self->sh_tz->setNTPServer($info->{ntp_server}) };
+ my $errors = $@;
+ if ($errors) {
+ $finished = 0;
+ $self->sh_gui->warningMsgBox({
+ title => $self->loc->N("Set NTP failed"),
+ text => "$errors",
+ richtext => 1,
+ });
+ }
+ }
+ else {
+ $self->sh_gui->warningMsgBox({text => $self->loc->N("Please enter a valid NTP server address.")});
+ $finished = 0;
+ }
+ }
+ else {
+ my $t = Time::Piece->strptime($dateField->value()."T".$timeField->value(),
+ "%Y-%m-%dT%H:%M:%S"
+ );
+ eval {
+ $self->sh_tz->disableAndStopNTP();
+ # (3)
+ $self->sh_tz->setTime($t->epoch());
+ };
+ my $errors = $@;
+ if ($errors) {
+ $finished = 0;
+ $self->sh_gui->warningMsgBox({
+ title => $self->loc->N("Set system time failed"),
+ text => "$errors",
+ richtext => 1,
+ });
+ }
+ }
+ yui::YUI::app()->normalCursor();
+
+ last if ($finished);
+ }
+ elsif ($widget == $changeNTPButton) {
+ # get time to calculate elapsed
+ my $t0 = localtime;
+ my $item = $self->sh_gui->ask_fromTreeList({title => $self->loc->N("NTP server - DrakClock"),
+ header => $self->loc->N("Choose your NTP server"),
+ default_button => 1,
+ item_separator => '|',
+ default_item => $info->{ntp_server},
+ skip_path => 1,
+ list => $NTPservers});
+ if ($item) {
+ $ntpLabel->setValue($item);
+ $info->{ntp_server} = $item;
+ }
+ # fixing elapsed time (dialog is modal)
+ my $t1 = localtime;
+ my $elapsed = $t1->epoch - $t0->epoch;
+
+ my $t = Time::Piece->strptime($dateField->value() . "T" . $timeField->value(),
+ '%Y-%m-%dT%H:%M:%S') + $elapsed;
+ $timeField->setValue($t->strftime("%H:%M:%S"));
+ $dateField->setValue($t->strftime("%F"));
+ }
+ elsif ($widget == $changeTZButton) {
+ # get time to calculate elapsed
+ my $t0 = localtime;
+ my $timezones = $self->sh_tz->getTimeZones();
+ if (!$timezones || scalar (@{$timezones}) == 0) {
+ $self->sh_gui->warningMsgBox({title => $self->loc->N("Timezone - DrakClock"),
+ text => $self->loc->N("Failed to retrieve timezone list"),
+ });
+ $changeTZButton->setDisabled();
+ }
+ else {
+ my $item = $self->sh_gui->ask_fromTreeList({title => $self->loc->N("Timezone - DrakClock"),
+ header => $self->loc->N("Which is your timezone?"),
+ default_button => 1,
+ item_separator => '/',
+ default_item => $info->{time_zone}->{ZONE},
+ list => $timezones});
+ if ($item) {
+ my $utc = 0;
+ if ($info->{time_zone}->{UTC} ) {
+ $utc = $info->{time_zone}->{UTC};
+ }
+ $utc = $self->sh_gui->ask_YesOrNo({
+ title => $self->loc->N("GMT - manaclock"),
+ text => $self->loc->N("Is your hardware clock set to GMT?"),
+ default_button => 1,
+ });
+ $info->{time_zone}->{UTC} = $utc;
+ $info->{time_zone}->{ZONE} = $item;
+ $timeZoneLbl->setValue($info->{time_zone}->{ZONE});
+ }
+ }
+ # fixing elapsed time (dialog is modal)
+ my $t1 = localtime;
+ my $elapsed = $t1->epoch - $t0->epoch;
+
+ my $t = Time::Piece->strptime($dateField->value() . "T" . $timeField->value(),
+ '%Y-%m-%dT%H:%M:%S') + $elapsed;
+ $timeField->setValue($t->strftime("%H:%M:%S"));
+ $dateField->setValue($t->strftime("%F"));
+ }
+ elsif ($widget == $resetButton) {
+ my $datetime_only = $self->sh_gui->ask_YesOrNo({
+ title => $self->loc->N("Restore data"),
+ text => $self->loc->N("Restore date and time only?"),
+ default_button => 1, #Yes
+ });
+ my $newInfo = $self->_restoreValues($datetime_only);
+ if ($datetime_only) {
+ $info->{date} = $newInfo->{date};
+ $info->{time} = $newInfo->{time};
+ }
+ else{
+ $info = $newInfo;
+ }
+
+ $dateField->setValue($info->{date});
+ $timeField->setValue($info->{time});
+ if (exists $info->{time_zone} && $info->{time_zone}->{ZONE}) {
+ $timeZoneLbl->setValue($info->{time_zone}->{ZONE});
+ }
+ else {
+ $timeZoneLbl->setValue($self->loc->N("not defined"));
+ }
+ if ($info->{ntp_server}) {
+ $ntpLabel->setValue($info->{ntp_server});
+ }
+ else {
+ $ntpLabel->setValue($self->loc->N("not defined"));
+ }
+ $ntpFrame->setValue($info->{ntp_running});
+ }
+ elsif($widget == $aboutButton) {
+ my $translators = $self->loc->N("_: Translator(s) name(s) & email(s)\n");
+ $translators =~ s/\</\&lt\;/g;
+ $translators =~ s/\>/\&gt\;/g;
+ $self->sh_gui->AboutDialog({ name => $self->name,
+ version => $self->VERSION,
+ credits => $self->loc->N("Copyright (C) %s Mageia community", '2014-2015'),
+ license => $self->loc->N("GPLv2"),
+ description => $self->loc->N("Date, Clock & Time Zone Settings allows to setup time zone and adjust date and time"),
+ authors => $self->loc->N("<h3>Developers</h3>
+ <ul><li>%s</li>
+ <li>%s</li>
+ </ul>
+ <h3>Translators</h3>
+ <ul><li>%s</li></ul>",
+ "Angelo Naselli &lt;anaselli\@linux.it&gt;",
+ $translators
+ ),
+ }
+ );
+ }
+ }
+ }
+ $dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+}
+
+
+
diff --git a/lib/ManaTools/Module/DisplayManager.pm b/lib/ManaTools/Module/DisplayManager.pm
new file mode 100644
index 0000000..39aa617
--- /dev/null
+++ b/lib/ManaTools/Module/DisplayManager.pm
@@ -0,0 +1,298 @@
+# vim: set et ts=4 sw=4:
+#*****************************************************************************
+#
+# Copyright (c) 2013-2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#*****************************************************************************
+
+package ManaTools::Module::DisplayManager;
+
+use Modern::Perl '2011';
+use autodie;
+use Moose;
+use POSIX qw(ceil);
+use English;
+use utf8;
+
+use yui;
+use ManaTools::Shared qw(trim apcat);
+use ManaTools::Shared::GUI;
+# use ManaTools::Shared::DisplayManager;
+
+# TODROP but provides network::network
+use lib qw(/usr/lib/libDrakX);
+use network::network;
+use MDK::Common::System qw(getVarsFromSh addVarsInSh);
+use MDK::Common::Func qw(find);
+
+extends qw( ManaTools::Module );
+
+
+has '+icon' => (
+ default => "/usr/share/mcc/themes/default/drakedm-mdk.png",
+);
+
+has '+name' => (
+ default => "DisplayManager",
+);
+
+=head1 VERSION
+
+Version 1.0.0
+
+=cut
+
+our $VERSION = '1.0.0';
+
+has 'dialog' => (
+ is => 'rw',
+ init_arg => undef
+);
+
+has 'conffile' => (
+ is => 'rw',
+ isa => 'Str',
+ default => '/etc/sysconfig/desktop',
+);
+
+has 'dmlist' => (
+ is => 'rw',
+ isa => 'ArrayRef',
+ builder => '_build_dmlist',
+);
+
+has 'desc_for_i18n' => (
+ is => 'rw',
+ isa => 'ArrayRef',
+);
+
+has 'sh_gui' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_SharedUGUIInitialize'
+);
+
+has 'loc' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_localeInitialize'
+);
+
+sub _build_desc_for_i18n {
+ my $self = shift();
+
+ my @_DESCRIPTIONS_for_i18n = (
+ $self->loc->N("LightDM (The Light Display Manager)"),
+ $self->loc->N("GDM (GNOME Display Manager)"),
+ $self->loc->N("KDM (KDE Display Manager)"),
+ $self->loc->N("XDM (X Display Manager)"),
+ );
+
+ $self->desc_for_i18n(\@_DESCRIPTIONS_for_i18n);
+
+ return 1;
+}
+
+sub _build_dmlist {
+ my $self = shift();
+
+ my @list = map {
+ my %l = map { /(\S+)=(.*)/ } apcat($_);
+ \%l;
+ } sort(glob("/usr/share/X11/dm.d/*.conf"));
+ return \@list;
+}
+
+sub _localeInitialize {
+ my $self = shift();
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'drakdm') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+sub _SharedUGUIInitialize {
+ my $self = shift();
+
+ $self->sh_gui( ManaTools::Shared::GUI->new() );
+}
+
+#=============================================================
+
+=head2 start
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method extends Module::start and is invoked to
+ start proxy manager
+
+=cut
+
+#=============================================================
+sub start {
+ my $self = shift;
+
+ if ($EUID != 0) {
+ $self->sh_gui->warningMsgBox({
+ title => $self->name,
+ text => $self->loc->N("root privileges required"),
+ });
+ return;
+ }
+
+ # initialize dm descriptions for i18n
+ $self->_build_desc_for_i18n();
+
+ $self->_manageProxyDialog();
+};
+
+#=============================================================
+
+=head2 ask_for_X_restart
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method shows a message box warning the user
+ that a X server restart is required
+
+=cut
+
+#=============================================================
+
+sub ask_for_X_restart {
+ my $self = shift;
+
+ $self->sh_gui->warningMsgBox({title=>$self->loc->N("X Restart Required"),text=>$self->loc->N("You need to log out and back in again for changes to take effect"),richtext=>1});
+}
+
+sub _manageProxyDialog {
+ my $self = shift;
+
+ ## TODO fix for manatools
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ my $appIcon = yui::YUI::app()->applicationIcon();
+ ## set new title to get it in dialog
+ my $newTitle = $self->loc->N("Display Manager");
+ yui::YUI::app()->setApplicationTitle($newTitle);
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+ my $label_width = 25;
+ my $inputfield_width = 45;
+
+ my ($dm_NAME) = apcat($self->conffile) =~ /^DISPLAYMANAGER=(.*)/m;
+ my $dm = (MDK::Common::Func::find { uc($_->{NAME}) eq uc($dm_NAME) } @{$self->dmlist});
+
+ $self->dialog($factory->createMainDialog());
+ my $layout = $factory->createVBox($self->dialog);
+
+ my $hbox_header = $factory->createHBox($layout);
+ my $headLeft = $factory->createHBox($factory->createLeft($hbox_header));
+ my $headRight = $factory->createHBox($factory->createRight($hbox_header));
+
+ my $logoImage = $factory->createImage($headLeft, $appIcon);
+ my $labelAppDescription = $factory->createLabel($headRight,$newTitle);
+ $logoImage->setWeight($yui::YD_HORIZ,0);
+ $labelAppDescription->setWeight($yui::YD_HORIZ,3);
+
+ # app description
+ my $hbox_content = $factory->createHBox($layout);
+ $factory->createLabel($hbox_content, $self->loc->N("Choosing a display manager"));
+
+ $hbox_content = $factory->createHBox($layout);
+
+ my $vbox_spacer = $factory->createVBox($hbox_content);
+ $factory->createHSpacing($vbox_spacer,2);
+ my $vbox_labels_flags = $factory->createVBox($hbox_content);
+ my $vbox_inputfields = $factory->createVBox($hbox_content);
+
+ # list of desktop managers
+ my $rb_group = $factory->createRadioButtonGroup($vbox_labels_flags);
+ my $rbbox = $factory->createVBox($rb_group);
+ foreach my $d (@{$self->dmlist()})
+ {
+ my $rowentry = $factory->createHBox($factory->createLeft($rbbox));
+ my $rb = $factory->createRadioButton($rowentry, $d->{NAME});
+ $rb->setWeight($yui::YD_HORIZ, 1);
+ my $desc = $factory->createLabel($rowentry, $self->loc->N($d->{DESCRIPTION}));
+ $desc->setWeight($yui::YD_HORIZ, 2);
+ if($d->{PACKAGE} eq lc($dm_NAME))
+ {
+ $rb->setValue(1);
+ }
+ $rb_group->addRadioButton($rb);
+ $rb->DISOWN();
+ }
+ my $hbox_filler = $factory->createHBox($layout);
+ $factory->createSpacing($hbox_filler,$yui::YD_VERT,2);
+
+ my $hbox_foot = $factory->createHBox($layout);
+ my $vbox_foot_left = $factory->createVBox($factory->createLeft($hbox_foot));
+ my $vbox_foot_right = $factory->createVBox($factory->createRight($hbox_foot));
+ my $aboutButton = $factory->createPushButton($vbox_foot_left,$self->loc->N("&About"));
+ my $cancelButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&OK"));
+
+ # main loop
+ while(1) {
+ my $event = $self->dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+### Buttons and widgets ###
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }elsif ($widget == $aboutButton) {
+ $self->sh_gui->AboutDialog({
+ name => $appTitle,
+ version => $VERSION,
+ credits => "Copyright (c) 2013-2015 by Matteo Pasotti",
+ license => "GPLv2",
+ description => $self->loc->N("Graphical configurator for system Display Manager"),
+ authors => "Matteo Pasotti &lt;matteo.pasotti\@gmail.com&gt;"
+ }
+ );
+ }elsif ($widget == $okButton) {
+ my $current_choice = ManaTools::Shared::trim($rb_group->currentButton()->label());
+ $current_choice =~s/\&//g;
+ addVarsInSh($self->conffile, { DISPLAYMANAGER => lc($current_choice) } );
+ $self->ask_for_X_restart();
+ last;
+ }
+ }
+ }
+
+ $self->dialog->destroy() ;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+}
+
+1;
diff --git a/lib/ManaTools/Module/Firewall.pm b/lib/ManaTools/Module/Firewall.pm
new file mode 100644
index 0000000..2ac56a5
--- /dev/null
+++ b/lib/ManaTools/Module/Firewall.pm
@@ -0,0 +1,1208 @@
+# vim: set et ts=4 sw=4:
+#*****************************************************************************
+#
+# Copyright (c) 2013-2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#*****************************************************************************
+
+package ManaTools::Module::Firewall;
+
+use Modern::Perl '2011';
+use autodie;
+use Moose;
+use Moose::Autobox;
+use utf8;
+
+use yui;
+use ManaTools::Shared qw(trim);
+use ManaTools::Shared::GUI;
+use ManaTools::Shared::Firewall;
+use ManaTools::Shared::Shorewall;
+use ManaTools::Shared::Services;
+
+use MDK::Common::Func qw(if_ partition);
+use MDK::Common::System qw(getVarsFromSh);
+use MDK::Common::Various qw(text2bool to_bool);
+use MDK::Common::DataStructure qw(intersection);
+use MDK::Common::File qw(substInFile output_with_perm);
+
+use List::Util qw(any);
+use List::MoreUtils qw(uniq);
+
+extends qw( ManaTools::Module );
+
+has '+icon' => (
+ default => "/usr/share/icons/manawall.png",
+);
+
+has '+name' => (
+ default => "Firewall Manager",
+);
+
+=head1 VERSION
+
+Version 1.0.0
+
+=cut
+
+our $VERSION = '1.0.0';
+
+has 'dialog' => (
+ is => 'rw',
+ init_arg => undef
+);
+
+has 'sh_gui' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_SharedUGUIInitialize'
+);
+
+has 'loc' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_localeInitialize',
+ required => 1,
+);
+
+has 'all_servers' => (
+ is => 'rw',
+ init_arg => undef,
+ isa => 'ArrayRef',
+);
+
+has 'ifw_rules' => (
+ is => 'rw',
+ init_arg => undef,
+ isa => 'ArrayRef',
+);
+
+has 'wdg_ifw' => (
+ is => 'rw',
+ init_arg => undef,
+ isa => 'ArrayRef',
+ default => sub { [] },
+);
+
+has 'wdg_servers' => (
+ is => 'rw',
+ init_arg => undef,
+ isa => 'ArrayRef',
+ default => sub { [] },
+);
+
+has 'net' => (
+ is => 'rw',
+ init_arg => undef,
+ isa => 'HashRef',
+ builder => '_initNet',
+);
+
+has 'unlisted' => (
+ is => 'rw',
+ init_arg => undef,
+ isa => 'ArrayRef',
+ builder => '_initUnlisted',
+);
+
+has 'log_net_drop' => (
+ is => 'rw',
+ isa => 'Bool',
+ default => sub { return 1; }
+);
+
+has 'aboutDialog' => (
+ is => 'ro',
+ init_arg => undef,
+ isa => 'HashRef',
+ builder => '_setupAboutDialog',
+);
+
+sub _setupAboutDialog {
+ my $self = shift();
+ return {
+ name => "",
+ version => $VERSION,
+ credits => "Copyright (c) 2013-2015 by Matteo Pasotti",
+ license => "GPLv2",
+ description => "",
+ authors => "Matteo Pasotti &lt;matteo.pasotti\@gmail.com&gt;"
+ };
+}
+
+sub _localeInitialize {
+ my $self = shift();
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'drakx-net') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+sub _SharedUGUIInitialize {
+ my $self = shift();
+
+ $self->sh_gui(ManaTools::Shared::GUI->new() );
+}
+
+sub _initAllServers {
+ my $self = shift();
+ my @all_servers = (
+ {
+ id => 'www',
+ name => $self->loc->N("Web Server"),
+ pkg => 'apache apache-mod_perl boa lighttpd thttpd',
+ ports => '80/tcp 443/tcp',
+ },
+ {
+ id => 'dns',
+ name => $self->loc->N("Domain Name Server"),
+ pkg => 'bind dnsmasq mydsn',
+ ports => '53/tcp 53/udp',
+ },
+ {
+ id => 'ssh',
+ name => $self->loc->N("SSH server"),
+ pkg => 'openssh-server',
+ ports => '22/tcp',
+ },
+ {
+ id => 'ftp',
+ name => $self->loc->N("FTP server"),
+ pkg => 'ftp-server-krb5 wu-ftpd proftpd pure-ftpd',
+ ports => '20/tcp 21/tcp',
+ },
+ {
+ id => 'dhcp',
+ name => $self->loc->N("DHCP Server"),
+ pkg => 'dhcp-server udhcpd',
+ ports => '67/udp 68/udp',
+ hide => 1,
+ },
+ {
+ id => 'mail',
+ name => $self->loc->N("Mail Server"),
+ pkg => 'sendmail postfix qmail exim',
+ ports => '25/tcp 465/tcp 587/tcp',
+ },
+ {
+ id => 'popimap',
+ name => $self->loc->N("POP and IMAP Server"),
+ pkg => 'imap courier-imap-pop',
+ ports => '109/tcp 110/tcp 143/tcp 993/tcp 995/tcp',
+ },
+ {
+ id => 'telnet',
+ name => $self->loc->N("Telnet server"),
+ pkg => 'telnet-server-krb5',
+ ports => '23/tcp',
+ hide => 1,
+ },
+ {
+ id => 'nfs',
+ name => $self->loc->N("NFS Server"),
+ pkg => 'nfs-utils nfs-utils-clients',
+ ports => '111/tcp 111/udp 2049/tcp 2049/udp ' . network::nfs::list_nfs_ports(),
+ hide => 1,
+ prepare => sub { network::nfs::write_nfs_ports(network::nfs::read_nfs_ports()) },
+ restart => 'nfs-common nfs-server',
+ },
+ {
+ id => 'smb',
+ name => $self->loc->N("Windows Files Sharing (SMB)"),
+ pkg => 'samba-server',
+ ports => '137/tcp 137/udp 138/tcp 138/udp 139/tcp 139/udp 445/tcp 445/udp 1024:1100/tcp 1024:1100/udp',
+ hide => 1,
+ },
+ {
+ id => 'bacula',
+ name => $self->loc->N("Bacula backup"),
+ pkg => 'bacula-fd bacula-sd bacula-dir-common',
+ ports => '9101:9103/tcp',
+ hide => 1,
+ },
+ {
+ id => 'syslog',
+ name => $self->loc->N("Syslog network logging"),
+ pkg => 'rsyslog syslog-ng',
+ ports => '514/udp',
+ hide => 1,
+ },
+ {
+ id => 'cups',
+ name => $self->loc->N("CUPS server"),
+ pkg => 'cups',
+ ports => '631/tcp 631/udp',
+ hide => 1,
+ },
+ {
+ id => 'mysql',
+ name => $self->loc->N("MySQL server"),
+ pkg => 'mysql',
+ ports => '3306/tcp 3306/udp',
+ hide => 1,
+ },
+ {
+ id => 'postgresql',
+ name => $self->loc->N("PostgreSQL server"),
+ pkg => 'postgresql8.2 postgresql8.3',
+ ports => '5432/tcp 5432/udp',
+ hide => 1,
+ },
+ {
+ id => 'echo',
+ name => $self->loc->N("Echo request (ping)"),
+ ports => '8/icmp',
+ force_default_selection => 0,
+ },
+ {
+ id => 'zeroconf',
+ name => $self->loc->N("Network services autodiscovery (zeroconf and slp)"),
+ ports => '5353/udp 427/udp',
+ pkg => 'avahi cups openslp',
+ },
+ {
+ id => 'bittorrent',
+ name => $self->loc->N("BitTorrent"),
+ ports => '6881:6999/tcp 6881:6999/udp',
+ hide => 1,
+ pkg => 'bittorrent deluge ktorrent transmission vuze rtorrent ctorrent',
+ },
+ {
+ id => 'wmds',
+ name => $self->loc->N("Windows Mobile device synchronization"),
+ pkg => 'synce-hal',
+ ports => '990/tcp 999/tcp 5678/tcp 5679/udp 26675/tcp',
+ hide => 1,
+ },
+ );
+ return \@all_servers;
+}
+
+sub _initIFW {
+ my $self = shift();
+ my @ifw_rules = (
+ {
+ id => 'psd',
+ name => $self->loc->N("Port scan detection"),
+ ifw_rule => 'psd',
+ },
+ );
+ return \@ifw_rules;
+}
+
+sub _initNet {
+ my $self = shift();
+ my $net = {};
+ network::network::read_net_conf($net);
+ return $net;
+}
+
+sub _initUnlisted {
+ my $self = shift();
+ my @unlisted = ();
+ return \@unlisted;
+}
+
+#=============================================================
+
+sub check_ports_syntax {
+ my ($ports) = @_;
+ foreach (split ' ', $ports) {
+ my ($nb, $range, $nb2) = m!^(\d+)(:(\d+))?/(tcp|udp|icmp)$! or return $_;
+ foreach my $port ($nb, if_($range, $nb2)) {
+ 1 <= $port && $port <= 65535 or return $_;
+ }
+ $nb < $nb2 or return $_ if $range;
+ }
+ return '';
+}
+
+#=============================================================
+
+=head2 port2server
+
+=head3 INPUT
+
+ $self: this object
+
+ $ports: port object
+
+=head3 DESCRIPTION
+
+ This method retrieves the server from a given port
+
+=cut
+
+#=============================================================
+
+sub port2server {
+ my $self = shift();
+ my ($port) = @_;
+ for my $service(@{$self->all_servers()})
+ {
+ if(any { $port eq $_ } split(' ', $service->{ports}))
+ {
+ return $service;
+ }
+ }
+ return 0;
+}
+
+#=============================================================
+
+=head2 to_ports
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method converts from server definitions to port definitions
+
+=cut
+
+#=============================================================
+
+sub to_ports {
+ my ($self, $servers) = @_;
+ my $ports = join(' ', (map { $_->{ports} } @$servers), @{$self->unlisted()});
+ return $ports;
+}
+
+#=============================================================
+
+=head2 from_ports
+
+=head3 INPUT
+
+ $self: this object
+
+ $ports: ports object
+
+=head3 DESCRIPTION
+
+ This method does...
+
+=cut
+
+#=============================================================
+
+sub from_ports {
+ my $self = shift();
+ my ($ports) = @_;
+
+ my @l;
+ foreach (split ' ', $ports) {
+ if (my $s = $self->port2server($_)) {
+ push @l, $s;
+ } else {
+ push (@{$self->unlisted()}, $_);
+ }
+ }
+ my @result = [ uniq(@l) ], join(' ', @{$self->unlisted()});
+ return \@result;
+}
+
+#=============================================================
+
+=head2 get_conf
+
+=head3 INPUT
+
+ $self: this object
+
+ $disabled: boolean
+
+ $o_ports: object representing ports
+
+=head3 DESCRIPTION
+
+ This method retrieves the configuration
+
+=cut
+
+#=============================================================
+
+sub get_conf {
+ my $self = shift();
+ my ($disabled, $o_ports) = @_;
+ my $possible_servers = undef;
+ my $conf = ManaTools::Shared::Shorewall::read_();
+ my $shorewall = (ManaTools::Shared::Shorewall::get_config_file('zones', '') && $conf);
+
+ if ($o_ports) {
+ return ($disabled, $self->from_ports($o_ports));
+ } elsif ($shorewall) {
+ # WARNING: this condition fails (the method fails)
+ # if manawall runs as unprivileged user
+ # cause it can't read the interfaces file
+ return ($shorewall->{disabled}, $self->from_ports($shorewall->{ports}), $shorewall->{log_net_drop});
+ } else {
+ $self->sh_gui->ask_OkCancel({
+ title => $self->loc->N("Firewall configuration"),
+ text => $self->loc->N("drakfirewall configurator
+ This configures a personal firewall for this Mageia machine."),
+ richtext => 1
+ }) or return;
+
+ $self->sh_gui->ask_OkCancel({
+ title => $self->loc->N("Firewall configuration"),
+ text => $self->loc->N("drakfirewall configurator
+Make sure you have configured your Network/Internet access with
+drakconnect before going any further."),
+ richtext => 1
+ }) or return;
+
+ return($disabled, $possible_servers, '');
+ }
+}
+
+sub set_ifw {
+ # my ($do_pkgs, $enabled, $rules, $ports) = @_;
+ my $self = shift();
+ my ($enabled, $rules, $ports) = @_;
+ if ($enabled)
+ {
+ my $ports_by_proto = ManaTools::Shared::Shorewall::ports_by_proto($ports);
+ output_with_perm("$::prefix/etc/ifw/rules", 0644,
+ (map { ". /etc/ifw/rules.d/$_\n" } @$rules),
+ map {
+ my $proto = $_;
+ map {
+ my $multiport = /:/ && " -m multiport";
+ "iptables -A Ifw -m conntrack --ctstate NEW -p $proto$multiport --dport $_ -j IFWLOG --log-prefix NEW\n";
+ } @{$ports_by_proto->{$proto}};
+ } intersection([ qw(tcp udp) ], [ keys %$ports_by_proto ]),
+ );
+ }
+
+ substInFile {
+ undef $_ if m!^INCLUDE /etc/ifw/rules|^iptables -I INPUT 2 -j Ifw!;
+ } "$::prefix/etc/shorewall/start";
+ ManaTools::Shared::Shorewall::set_in_file('start', $enabled, "INCLUDE /etc/ifw/start", "INCLUDE /etc/ifw/rules", "iptables -I INPUT 1 -j Ifw");
+ ManaTools::Shared::Shorewall::set_in_file('stop', $enabled, "iptables -D INPUT -j Ifw", "INCLUDE /etc/ifw/stop");
+}
+
+#=============================================================
+
+=head2 choose_watched_services
+
+=head3 INPUT
+
+ $self: this object
+
+ $servers: array of hashes representing servers
+
+=head3 DESCRIPTION
+
+ This method shows the main dialog to let users choose the allowed services
+
+=cut
+
+#=============================================================
+
+sub choose_watched_services {
+ my ($self, $servers) = @_;
+
+ my @l = (@{$self->ifw_rules()}, @$servers, map { { ports => $_ } } @{$self->unlisted()});
+
+ my $enabled = 1;
+ $_->{ifw} = 1 foreach @l;
+
+ my $retval = $self->ask_WatchedServices({
+ title => $self->loc->N("Interactive Firewall"),
+ icon => $self->icon(),
+ # if_(!$::isEmbedded, banner_title => N("Interactive Firewall")),
+ messages =>
+ $self->loc->N("You can be warned when someone accesses to a service or tries to intrude into your computer.
+Please select which network activities should be watched."),
+ },
+ [
+ {
+ id=>'useifw',
+ text => $self->loc->N("Use Interactive Firewall"),
+ val => $enabled,
+ type => 'bool'
+ },
+ map {
+ {
+ text => (exists $_->{name} ? $_->{name} : $_->{ports}),
+ val => $_->{ifw},
+ type => 'bool',
+ id => $_->{id},
+ },
+ } @l,
+ ]);
+
+ return if($retval == 0);
+
+ for my $server(@{$self->wdg_ifw()})
+ {
+ for my $k(keys @l)
+ {
+ if(defined($l[$k]->{id}) && defined($server->{id}))
+ {
+ if($server->{id} eq 'useifw')
+ {
+ $enabled = $server->{value};
+ }
+ else
+ {
+ if($l[$k]->{id} eq $server->{id})
+ {
+ $l[$k]->{ifw} = $server->{value};
+ last;
+ }
+ }
+ }
+ }
+ }
+
+ my ($rules, $ports) = partition { exists $_->{ifw_rule} } grep { $_->{ifw} } @l;
+ $self->set_ifw($enabled, [ map { $_->{ifw_rule} } @$rules ], $self->to_ports($ports));
+
+ # return something to say that we are done ok
+ return ($rules, $ports);
+}
+
+#=============================================================
+
+sub ask_WatchedServices {
+ my $self = shift;
+
+ my ($dlg_data,
+ $items) = @_;
+
+ my $old_title = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($dlg_data->{title});
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+ $self->dialog($factory->createMainDialog());
+ my $layout = $factory->createVBox($self->dialog);
+
+ my $hbox_header = $factory->createHBox($layout);
+ my $headLeft = $factory->createHBox($factory->createLeft($hbox_header));
+ my $headRight = $factory->createHBox($factory->createRight($hbox_header));
+
+ my $logoImage = $factory->createImage($headLeft, $dlg_data->{icon});
+ my $labelAppDescription = $factory->createLabel($headRight,$dlg_data->{messages});
+ $logoImage->setWeight($yui::YD_HORIZ,0);
+ $labelAppDescription->setWeight($yui::YD_HORIZ,3);
+
+ my $hbox_content = $factory->createHBox($layout);
+
+ my $widgetContainer = $factory->createVBox($hbox_content);
+
+
+ foreach my $item(@{$items})
+ {
+ if(defined($item->{label}))
+ {
+ $factory->createLabel($factory->createLeft($factory->createHBox($widgetContainer)), $item->{label});
+ }
+ elsif(defined($item->{text}))
+ {
+ my $ckbox = $factory->createCheckBox(
+ $factory->createLeft($factory->createHBox($widgetContainer)),
+ $item->{text},
+ $item->{val}
+ );
+ $ckbox->setNotify(1);
+ push @{$self->wdg_ifw()}, {
+ id => $item->{id},
+ widget => \$ckbox,
+ value => $item->{val},
+ };
+ $ckbox->DISOWN();
+ }
+ }
+
+ my $hbox_foot = $factory->createHBox($layout);
+ my $vbox_foot_left = $factory->createVBox($factory->createLeft($hbox_foot));
+ my $vbox_foot_right = $factory->createVBox($factory->createRight($hbox_foot));
+ my $aboutButton = $factory->createPushButton($vbox_foot_left,$self->loc->N("&About"));
+ my $cancelButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&OK"));
+
+ my $retval = 0;
+
+ # main loop
+ while(1) {
+ my $event = $self->dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### Buttons and widgets ###
+ my $widget = $event->widget();
+
+ # loop on every checkbox representing servers
+ foreach my $server(@{$self->wdg_ifw()})
+ {
+ if($widget == ${$server->{widget}})
+ {
+ if($server->{id} eq 'useifw')
+ {
+ if(!${$server->{widget}}->value())
+ {
+ yui::YUI::ui()->blockEvents();
+ foreach my $server(@{$self->wdg_ifw()})
+ {
+ if($server->{id} ne 'useifw')
+ {
+ ${$server->{widget}}->setValue(0);
+ $server->{value} = ${$server->{widget}}->value();
+ }
+ }
+ yui::YUI::ui()->unblockEvents();
+ last;
+ }
+ }
+ else
+ {
+ $server->{value} = ${$server->{widget}}->value();
+ }
+ }
+ }
+ if ($widget == $cancelButton) {
+ last;
+ }elsif ($widget == $aboutButton) {
+ my $abtdlg = $self->aboutDialog();
+ $abtdlg->{name} = $dlg_data->{title};
+ $abtdlg->{description} = $self->loc->N("Graphical manager for interactive firewall rules");
+ $self->sh_gui->AboutDialog($abtdlg
+ );
+ }elsif ($widget == $okButton) {
+ $retval = 1;
+ last;
+ }
+ }
+ }
+
+ $self->dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($old_title);
+
+ return $retval;
+}
+
+
+#=============================================================
+
+=head2 choose_allowed_services
+
+=head3 INPUT
+
+ $self: this object
+
+ $disabled: boolean
+
+ $servers: array of hashes representing servers
+
+=head3 DESCRIPTION
+
+ This method shows the main dialog to let users choose the allowed services
+
+=cut
+
+#=============================================================
+
+sub choose_allowed_services {
+ my ($self, $disabled, $servers) = @_;
+
+ $_->{on} = 0 foreach @{$self->all_servers()};
+ $_->{on} = 1 foreach @$servers;
+ my @l = grep { $_->{on} || !$_->{hide} } @{$self->all_servers()};
+
+ my $dialog_data = {
+ title => $self->loc->N("Firewall"),
+ icon => $self->icon(),
+ # if_(!$::isEmbedded, banner_title => $self->loc->N("Firewall")),
+ banner_title => $self->loc->N("Firewall"),
+ };
+
+ my $items = [
+ { label => $self->loc->N("Which services would you like to allow the Internet to connect to?"), title => 1 },
+ if_($self->net()->{PROFILE} && network::network::netprofile_count() > 0, { label => $self->loc->N("Those settings will be saved for the network profile <b>%s</b>", $self->net()->{PROFILE}) }),
+ { text => $self->loc->N("Everything (no firewall)"), val => \$disabled, type => 'bool' },
+ (map { { text => $_->{name}, val => \$_->{on}, type => 'bool', disabled => sub { $disabled }, id => $_->{id} } } @l),
+ ];
+
+ return if(!$self->ask_AllowedServices($dialog_data, $items));
+
+ for my $server(@{$self->wdg_servers()})
+ {
+ for my $k(keys @l)
+ {
+ if(defined($l[$k]->{id}) && defined($server->{id}))
+ {
+ if($l[$k]->{id} eq $server->{id})
+ {
+ $l[$k]->{on} = ${$server->{value}};
+ last;
+ }
+ }
+ else
+ {
+ # fake server, the checkbox allowing the user to disable the firewall
+ # if Everything checkbox is selected, value = 1 then firewall disabled = 1
+ $disabled = ${$server->{value}};
+ last;
+ }
+ }
+ }
+
+ return ($disabled, [ grep { $_->{on} } @l ]);
+}
+
+#=============================================================
+
+sub ask_AllowedServices {
+ my $self = shift;
+
+ my ($dlg_data,
+ $items) = @_;
+
+ my $old_title = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($dlg_data->{title});
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+ $self->dialog($factory->createMainDialog());
+ my $layout = $factory->createVBox($self->dialog);
+
+ my $hbox_header = $factory->createHBox($layout);
+ my $headLeft = $factory->createHBox($factory->createLeft($hbox_header));
+ my $headRight = $factory->createHBox($factory->createRight($hbox_header));
+
+ my $logoImage = $factory->createImage($headLeft, $dlg_data->{icon});
+ my $labelAppDescription = $factory->createLabel($headRight,$dlg_data->{title});
+ $logoImage->setWeight($yui::YD_HORIZ,0);
+ $labelAppDescription->setWeight($yui::YD_HORIZ,3);
+
+ my $hbox_content = $factory->createHBox($layout);
+
+ my $widgetContainer = $factory->createVBox($hbox_content);
+
+ my $evry = undef;
+
+ foreach my $item(@{$items})
+ {
+ if(defined($item->{label}))
+ {
+ $factory->createLabel($factory->createLeft($factory->createHBox($widgetContainer)), $item->{label});
+ }
+ elsif(defined($item->{text}))
+ {
+ my $ckbox = $factory->createCheckBox(
+ $factory->createLeft($factory->createHBox($widgetContainer)),
+ $item->{text},
+ ${$item->{val}}
+ );
+ $ckbox->setNotify(1);
+ if(!defined($item->{id}))
+ {
+ $evry = $ckbox;
+ }
+ if(defined($item->{disabled}))
+ {
+ $ckbox->setEnabled(!$item->{disabled}->());
+ }
+ push @{$self->wdg_servers()}, {
+ id => $item->{id},
+ widget => \$ckbox,
+ value => $item->{val},
+ };
+ $ckbox->DISOWN();
+ }
+ }
+
+ my $hbox_foot = $factory->createHBox($layout);
+ my $vbox_foot_left = $factory->createVBox($factory->createLeft($hbox_foot));
+ my $vbox_foot_right = $factory->createVBox($factory->createRight($hbox_foot));
+ my $advButton = $factory->createPushButton($vbox_foot_left,$self->loc->N("A&dvanced"));
+ my $aboutButton = $factory->createPushButton($vbox_foot_left,$self->loc->N("&About"));
+ my $cancelButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&OK"));
+
+ my $retval = 0;
+
+ # main loop
+ while(1) {
+ my $event = $self->dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### Buttons and widgets ###
+ my $widget = $event->widget();
+
+ # loop on every checkbox representing servers
+ foreach my $server(@{$self->wdg_servers()})
+ {
+ if($widget == ${$server->{widget}})
+ {
+ ${$server->{value}} = !${$server->{value}};
+ }
+ }
+
+ if ($widget == $cancelButton) {
+ last;
+ }elsif ($widget == $aboutButton) {
+ my $abtdlg = $self->aboutDialog();
+ $abtdlg->{name} = $dlg_data->{title};
+ $abtdlg->{description} = $self->loc->N("Graphical manager for firewall rules");
+ $self->sh_gui->AboutDialog($abtdlg);
+ }elsif ($widget == $okButton) {
+ $retval = 1;
+ last;
+ }
+ elsif ($widget == $advButton) {
+ $self->ask_CustomPorts();
+ }
+ elsif ($widget == $evry) {
+ foreach my $wdg_ckbox(@{$self->wdg_servers()})
+ {
+ if(defined($wdg_ckbox->{id}))
+ {
+ ${$wdg_ckbox->{widget}}->setEnabled(!${$wdg_ckbox->{widget}}->isEnabled());
+ }
+ }
+ }
+ }
+ }
+
+ $self->dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($old_title);
+
+ return $retval;
+}
+
+sub ask_CustomPorts {
+ my $self = shift();
+
+ my $adv_msg = $self->loc->N("You can enter miscellaneous ports.
+Valid examples are: 139/tcp 139/udp 600:610/tcp 600:610/udp.
+Have a look at /etc/services for information.");
+
+ my $old_title = yui::YUI::app()->applicationTitle();
+ my $win_title = $self->loc->N("Define miscellaneus ports");
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($win_title);
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+ my $advdlg = $factory->createPopupDialog();
+ my $layout = $factory->createVBox($advdlg);
+
+ my $hbox_header = $factory->createHBox($layout);
+ my $headLeft = $factory->createHBox($factory->createLeft($hbox_header));
+ my $headRight = $factory->createHBox($factory->createRight($hbox_header));
+
+ my $labelAppDescription = $factory->createLabel($headRight,$self->loc->N("Other ports"));
+ $labelAppDescription->setWeight($yui::YD_HORIZ,3);
+
+ my $hbox_content = $factory->createHBox($layout);
+ my $vbox_inputs = $factory->createVBox($hbox_content);
+ my $labelAdvMessage = $factory->createLabel($factory->createHBox($vbox_inputs), $adv_msg);
+ my $txtPortsList = $factory->createInputField($vbox_inputs,'');
+ $txtPortsList->setValue(join(' ',@{$self->unlisted()}));
+ my $ckbLogFWMessages = $factory->createCheckBox($factory->createHBox($vbox_inputs), $self->loc->N("Log firewall messages in system logs"), $self->log_net_drop());
+ my $hbox_foot = $factory->createHBox($layout);
+ my $vbox_foot_left = $factory->createVBox($factory->createLeft($hbox_foot));
+ my $vbox_foot_right = $factory->createVBox($factory->createRight($hbox_foot));
+ my $cancelButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&OK"));
+
+ my $retval = 0;
+
+ # main loop
+ while(1) {
+ my $event = $advdlg->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### Buttons and widgets ###
+ my $widget = $event->widget();
+ if( $widget == $cancelButton )
+ {
+ $retval = 0;
+ last;
+ }
+ elsif( $widget == $okButton )
+ {
+ if(scalar(@{$self->unlisted()}) > 0)
+ {
+ $self->unlisted([]);
+ }
+ my $invalid_ports = check_ports_syntax($txtPortsList->value());
+ if(ManaTools::Shared::trim($invalid_ports) eq '')
+ {
+ if($txtPortsList->value() =~m/\s+/g)
+ {
+ my @unlstd = split(' ', $txtPortsList->value());
+ foreach my $p(@unlstd)
+ {
+ push(@{$self->unlisted()},$p);
+ }
+ }
+ else
+ {
+ if(ManaTools::Shared::trim($txtPortsList->value()) ne '')
+ {
+ push(@{$self->unlisted()}, ManaTools::Shared::trim($txtPortsList->value()));
+ }
+ }
+ $retval = 1;
+ }
+ else
+ {
+ $self->sh_gui->warningMsgBox({
+ title=>$self->loc->N("Invalid port given"),
+ text=> $self->loc->N("Invalid port given: %s.
+The proper format is \"port/tcp\" or \"port/udp\",
+where port is between 1 and 65535.
+
+You can also give a range of ports (eg: 24300:24350/udp)", $invalid_ports)
+ });
+ $retval = 0;
+ }
+ last;
+ }
+ }
+ }
+
+ $advdlg->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($old_title);
+
+ return $retval;
+}
+
+sub get_zones {
+ my $self = shift();
+ my $confref = shift();
+ my $disabled = shift();
+ my $conf = ${$confref};
+ my $interfacesfile = ManaTools::Shared::Shorewall::get_config_file('interfaces', $conf->{version} || '');
+ network::network::read_net_conf($self->net());
+ #- find all interfaces but alias interfaces
+ my @all_intf = grep { !/:/ } uniq(keys(%{$self->net()->{ifcfg}}), detect_devices::get_net_interfaces());
+ my %net_zone = map { $_ => undef } @all_intf;
+ $net_zone{$_} = 1 foreach ManaTools::Shared::Shorewall::get_net_zone_interfaces($interfacesfile, $self->net(), \@all_intf);
+
+ # if firewall/shorewall is not disabled (i.e. everything has been allowed)
+ # then ask for network interfaces to protect
+ if(!$disabled)
+ {
+ my $retvals = $self->sh_gui->ask_multiple_fromList({
+ title => $self->loc->N("Firewall configuration"),
+ header => $self->loc->N("Please select the interfaces that will be protected by the firewall.
+
+All interfaces directly connected to Internet should be selected,
+while interfaces connected to a local network may be unselected.
+
+If you intend to use Mageia Internet Connection sharing,
+unselect interfaces which will be connected to local network.
+
+Which interfaces should be protected?
+"),
+ list => [
+ map {
+ {
+ id => $_,
+ text => network::tools::get_interface_description($self->net(), $_),
+ val => \$net_zone{$_},
+ type => 'bool'
+ };
+ } (sort keys %net_zone) ]
+ });
+
+ if(!defined($retvals))
+ {
+ return 0;
+ }
+ else
+ {
+ # it was: ($conf->{net_zone}, $conf->{loc_zone}) = partition { $net_zone{$_} } keys %net_zone;
+ foreach my $net_int (@{$retvals})
+ {
+ push (@{$conf->{net_zone}}, $net_int);
+ }
+ return $retvals;
+ }
+ }
+
+ foreach my $net_int(keys %net_zone)
+ {
+ push (@{$conf->{net_zone}}, $net_int);
+ }
+ return keys %net_zone;
+}
+
+#=============================================================
+
+=head2 set_ports
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method extends Module::start and is invoked to
+ start host manager
+
+=cut
+
+#=============================================================
+
+sub set_ports {
+ my ($self, $disabled, $ports, $log_net_drop) = @_;
+
+ if (!$disabled || -x "$::prefix/sbin/shorewall") {
+ # $do_pkgs->ensure_files_are_installed([ [ qw(shorewall shorewall) ], [ qw(shorewall-ipv6 shorewall6) ] ], $::isInstall) or return;
+ my $conf = ManaTools::Shared::Shorewall::read_();
+ if(!$self->get_zones(\$conf,$disabled))
+ {
+ # Cancel button has been pressed, aborting
+ return 0;
+ }
+ my $shorewall = (ManaTools::Shared::Shorewall::get_config_file('zones', '') && $conf);
+ if (!$shorewall) {
+ print ("unable to read shorewall configuration, skipping installation");
+ return 0;
+ }
+
+ $shorewall->{disabled} = $disabled;
+ $shorewall->{ports} = $ports;
+ $shorewall->{log_net_drop} = $log_net_drop;
+
+ print ($disabled ? "disabling shorewall" : "configuring shorewall to allow ports: $ports");
+
+ # NOTE: the 2nd param is undef in this case!
+ if(!ManaTools::Shared::Shorewall::write_($shorewall))
+ {
+ # user action request
+ my $action = $self->sh_gui->ask_fromList({
+ title => $self->loc->N("Firewall"),
+ header => $self->loc->N("Your firewall configuration has been manually edited and contains
+ rules that may conflict with the configuration that has just been set up.
+ What do you want to do?"),
+ list => [ "keep", "drop"],
+ default => "keep",
+ });
+ ManaTools::Shared::Shorewall::write_($shorewall,$action);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#=============================================================
+
+=head2 start
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method extends Module::start and is invoked to
+ start host manager
+
+=cut
+
+#=============================================================
+
+sub start {
+ my $self = shift;
+
+ my @server = ();
+ $self->wdg_servers(@server);
+
+ # init servers definitions
+ $self->all_servers($self->_initAllServers());
+
+ # initialize ifw_rules here
+ $self->ifw_rules($self->_initIFW());
+
+ my ($disabled, $servers, $log_net_drop) = $self->get_conf(undef) or return;
+
+ # $log_net_drop: network::shorewall log_net_drop attribute
+ $self->log_net_drop($log_net_drop);
+ undef($log_net_drop);
+ ($disabled, $servers) = $self->choose_allowed_services($disabled, @$servers) or return;
+
+ my $system_file = '/etc/sysconfig/drakx-net';
+ my %global_settings = getVarsFromSh($system_file);
+
+ if (!$disabled && (!defined($global_settings{IFW}) || text2bool($global_settings{IFW}))) {
+ $self->choose_watched_services($servers) or return;
+ }
+
+ # preparing services when required ( look at $self->all_servers() )
+ foreach (@$servers) {
+ exists $_->{prepare} and $_->{prepare}();
+ }
+
+ my $ports = $self->to_ports($servers);
+
+ $self->set_ports($disabled, $ports, $self->log_net_drop()) or return;
+
+ # restart mandi
+ my $services = ManaTools::Shared::Services->new();
+ $services->is_service_running("mandi") and $services->restart("mandi");
+
+ # restarting services if needed
+ foreach my $service (@$servers) {
+ if ($service->{restart}) {
+ $services->is_service_running($_) and $services->restart($_) foreach split(' ', $service->{restart});
+ }
+ }
+
+ # clearing pending ifw notifications in net_applet
+ system('killall -s SIGUSR1 net_applet');
+
+ return ($disabled, $ports);
+};
+
+1;
diff --git a/lib/ManaTools/Module/Hosts.pm b/lib/ManaTools/Module/Hosts.pm
new file mode 100644
index 0000000..faae3f0
--- /dev/null
+++ b/lib/ManaTools/Module/Hosts.pm
@@ -0,0 +1,530 @@
+# vim: set et ts=4 sw=4:
+#*****************************************************************************
+#
+# Copyright (c) 2013-2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#*****************************************************************************
+
+package ManaTools::Module::Hosts;
+
+use Modern::Perl '2011';
+use autodie;
+use Moose;
+use POSIX qw(ceil);
+use utf8;
+
+use Glib;
+use yui;
+use ManaTools::Shared qw(trim);
+use ManaTools::Shared::GUI;
+use ManaTools::Shared::Hosts;
+
+extends qw( ManaTools::Module );
+
+
+has '+icon' => (
+ default => "/usr/lib/libDrakX/icons/IC-Dhost-48.png"
+);
+
+has '+name' => (
+ default => "Hostmanager",
+);
+
+=head1 VERSION
+
+Version 1.0.0
+
+=cut
+
+our $VERSION = '1.0.0';
+
+has 'dialog' => (
+ is => 'rw',
+ init_arg => undef
+);
+
+has 'table' => (
+ is => 'rw',
+ init_arg => undef
+);
+
+has 'cfgHosts' => (
+ is => 'rw',
+ init_arg => undef
+);
+
+has 'sh_gui' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_SharedUGUIInitialize'
+);
+
+has 'loc' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_localeInitialize'
+);
+
+sub _localeInitialize {
+ my $self = shift();
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'drakx-net') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+sub _SharedUGUIInitialize {
+ my $self = shift();
+
+ $self->sh_gui(ManaTools::Shared::GUI->new() );
+}
+
+#=============================================================
+
+=head2 start
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method extends Module::start and is invoked to
+ start host manager
+
+=cut
+
+#=============================================================
+sub start {
+ my $self = shift;
+
+ $self->_manageHostsDialog();
+};
+
+#=============================================================
+
+=head2 _changeHostNameDialog
+
+=head3 INPUT
+
+ $self: this object
+
+ $headerString: a title for the dialog
+
+=head3 DESCRIPTION
+
+ This method display a dialog allowing the user
+ to change the hostname
+
+=cut
+
+#=============================================================
+sub _changeHostNameDialog {
+ my $self = shift;
+
+ my $headerString = shift();
+
+ my $factory = yui::YUI::widgetFactory;
+ my $dlg = $factory->createPopupDialog();
+ my $layout = $factory->createVBox($dlg);
+
+ my $hbox_header = $factory->createHBox($layout);
+ my $vbox_content = $factory->createVBox($layout);
+ my $hbox_footer = $factory->createHBox($layout);
+
+ # header
+ my $labelDescription = $factory->createLabel($hbox_header,$headerString);
+
+ # content
+ my $firstHbox = $factory->createHBox($vbox_content);
+ my $secondHbox = $factory->createHBox($vbox_content);
+ my $thirdHbox = $factory->createHBox($vbox_content);
+ my $fourthHbox = $factory->createHBox($vbox_content);
+ my $fifthHbox = $factory->createHBox($vbox_content);
+ my $sixthHbox = $factory->createHBox($vbox_content);
+
+ my $labelHostName = $factory->createLabel($secondHbox,$self->loc->N("Hostname"));
+ $labelHostName->setWeight($yui::YD_HORIZ, 10);
+ my $textHostName = $factory->createInputField($secondHbox,"");
+ $textHostName->setWeight($yui::YD_HORIZ, 30);
+
+ my $labelPrettyHostName = $factory->createLabel($thirdHbox,$self->loc->N("Pretty Hostname"));
+ $labelPrettyHostName->setWeight($yui::YD_HORIZ, 10);
+ my $textPrettyHostName = $factory->createInputField($thirdHbox,"");
+ $textPrettyHostName->setWeight($yui::YD_HORIZ, 30);
+
+ my $labelStaticHostName = $factory->createLabel($fourthHbox,$self->loc->N("Static Hostname"));
+ $labelStaticHostName->setWeight($yui::YD_HORIZ, 10);
+ my $textStaticHostName = $factory->createInputField($fourthHbox,"");
+ $textStaticHostName->setWeight($yui::YD_HORIZ, 30);
+
+ my $labelChassis = $factory->createLabel($fifthHbox,$self->loc->N("Chassis"));
+ $labelChassis->setWeight($yui::YD_HORIZ, 10);
+ my $textChassis = $factory->createInputField($fifthHbox,"");
+ $textChassis->setWeight($yui::YD_HORIZ, 30);
+
+ my $labelIconName = $factory->createLabel($sixthHbox,$self->loc->N("Icon Name"));
+ $labelIconName->setWeight($yui::YD_HORIZ, 10);
+ my $textIconName = $factory->createInputField($sixthHbox,"");
+ $textIconName->setWeight($yui::YD_HORIZ, 30);
+
+ $textHostName->setValue($self->cfgHosts->_getLocalHostName());
+ $textPrettyHostName->setValue($self->cfgHosts->_getLocalPrettyHostName());
+ $textStaticHostName->setValue($self->cfgHosts->_getLocalStaticHostName());
+ $textChassis->setValue($self->cfgHosts->_getLocalChassis());
+ $textIconName->setValue($self->cfgHosts->_getLocalIconName());
+
+ # footer
+ my $cancelButton = $factory->createPushButton($factory->createLeft($hbox_footer),$self->loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($factory->createRight($hbox_footer),$self->loc->N("&OK"));
+
+ while(1){
+ my $event = $dlg->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif($widget == $okButton) {
+ $self->cfgHosts->_setLocalHostName($textHostName->value());
+ $self->cfgHosts->_setLocalPrettyHostName($textPrettyHostName->value());
+ $self->cfgHosts->_setLocalStaticHostName($textStaticHostName->value());
+ $self->cfgHosts->_setLocalChassis($textChassis->value());
+ $self->cfgHosts->_setLocalIconName($textIconName->value());
+ last;
+ }
+ }
+ }
+
+ destroy $dlg;
+}
+
+sub _manipulateHostDialog {
+ my $self = shift;
+
+ my $headerString = shift();
+ my $boolEdit = shift();
+
+ my $hostIpString = "";
+ my $hostNameString = "";
+ my $hostAliasesString = "";
+
+ if($boolEdit == 1){
+ $hostIpString = shift();
+ $hostNameString = shift();
+ $hostAliasesString = shift();
+ }
+
+ my $factory = yui::YUI::widgetFactory;
+ my $dlg = $factory->createPopupDialog();
+ my $layout = $factory->createVBox($dlg);
+
+ my $hbox_header = $factory->createHBox($layout);
+ my $vbox_content = $factory->createVBox($layout);
+ my $hbox_footer = $factory->createHBox($layout);
+
+ # header
+ my $labelDescription = $factory->createLabel($hbox_header,$headerString);
+
+ # content
+ my $firstHbox = $factory->createHBox($vbox_content);
+ my $secondHbox = $factory->createHBox($vbox_content);
+ my $thirdHbox = $factory->createHBox($vbox_content);
+
+ my $labelIPAddress = $factory->createLabel($firstHbox,$self->loc->N("IP Address"));
+ my $labelHostName = $factory->createLabel($secondHbox,$self->loc->N("Hostname"));
+ my $labelHostAlias = $factory->createLabel($thirdHbox,$self->loc->N("Host aliases"));
+ $labelIPAddress->setWeight($yui::YD_HORIZ, 10);
+ $labelHostName->setWeight($yui::YD_HORIZ, 10);
+ $labelHostAlias->setWeight($yui::YD_HORIZ, 10);
+
+ my $textIPAddress = $factory->createInputField($firstHbox,"");
+ my $textHostName = $factory->createInputField($secondHbox,"");
+ my $textHostAlias = $factory->createInputField($thirdHbox,"");
+ $textIPAddress->setWeight($yui::YD_HORIZ, 30);
+ $textHostName->setWeight($yui::YD_HORIZ, 30);
+ $textHostAlias->setWeight($yui::YD_HORIZ, 30);
+
+ if($boolEdit == 1){
+ $textIPAddress->setValue($hostIpString);
+ $textHostName->setValue($hostNameString);
+ $textHostAlias->setValue($hostAliasesString);
+ }
+
+ # footer
+ my $cancelButton = $factory->createPushButton($factory->createLeft($hbox_footer),$self->loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($factory->createRight($hbox_footer),$self->loc->N("&OK"));
+
+ while(1){
+ my $event = $dlg->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif($widget == $okButton) {
+ my $res = undef;
+ my @hosts_toadd;
+ push @hosts_toadd, $textHostName->value();
+ if(ManaTools::Shared::trim($textHostAlias->value()) ne ""){
+ push @hosts_toadd, $textHostAlias->value();
+ }
+ if($boolEdit == 0){
+ $res = $self->cfgHosts->_insertHost($textIPAddress->value(),[@hosts_toadd]);
+ }else{
+ $res = $self->cfgHosts->_modifyHost($textIPAddress->value(),[@hosts_toadd]);
+ }
+ $res = $self->cfgHosts->_writeHosts();
+ last;
+ }
+ }
+ }
+
+ destroy $dlg;
+}
+
+#=============================================================
+
+=head2 _addHostDialog
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+This subroutine creates the Host dialog to add host definitions
+
+=cut
+
+#=============================================================
+
+sub _addHostDialog {
+ my $self = shift();
+ return $self->_manipulateHostDialog($self->loc->N("Add the information"),0);
+}
+
+#=============================================================
+
+=head2 _edtHostDialog
+
+=head3 INPUT
+
+=over 4
+
+=item $self: this object
+
+=item B<$hostIp> : the ip of the host entry that we want to modify
+
+=item B<$hostName> : the name of the host entry we want to modify
+
+=item B<$hostAliases> : aliases of the host entry we want to modify
+
+=back
+
+=head3 DESCRIPTION
+
+This subroutine creates the Host dialog to modify host definitions
+
+=cut
+
+#=============================================================
+
+sub _edtHostDialog {
+ my $self = shift();
+ my $hostIp = shift();
+ my $hostName = shift();
+ my $hostAliases = shift();
+ return $self->_manipulateHostDialog($self->loc->N("Modify the information"),1,$hostIp,$hostName,$hostAliases);
+}
+
+#=============================================================
+
+=head2 setupTable
+
+=head3 INPUT
+
+ $self: this object
+
+ $data: reference to the array containaing the host data to show into the table
+
+=head3 DESCRIPTION
+
+This subroutine populates a previously created YTable with the hosts data
+retrieved by the Config::Hosts module
+
+=cut
+
+#=============================================================
+sub setupTable {
+ my $self = shift();
+
+ my @hosts = $self->cfgHosts->_getHosts();
+ # clear table
+ $self->table->deleteAllItems();
+ foreach my $host (@hosts){
+ my $tblItem;
+ my $aliases = join(',',@{$host->{'hosts'}});
+ if(scalar(@{$host->{'hosts'}}) > 1){
+ $aliases =~s/^$host->{'hosts'}[0]\,*//g;
+ }elsif(scalar(@{$host->{'hosts'}}) == 1){
+ $aliases = "";
+ }
+ $tblItem = new yui::YTableItem($host->{'ip'},$host->{'hosts'}[0],$aliases);
+ $self->table->addItem($tblItem);
+ }
+}
+
+sub _manageHostsDialog {
+ my $self = shift;
+
+ ## TODO fix for manatools
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ my $appIcon = yui::YUI::app()->applicationIcon();
+ ## set new title to get it in dialog
+ my $newTitle = $self->loc->N("Manage hosts definitions");
+ yui::YUI::app()->setApplicationTitle($newTitle);
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+
+ $self->dialog($factory->createMainDialog());
+ my $layout = $factory->createVBox($self->dialog);
+
+ my $hbox_header = $factory->createHBox($layout);
+ my $headLeft = $factory->createHBox($factory->createLeft($hbox_header));
+ my $headRight = $factory->createHBox($factory->createRight($hbox_header));
+
+ my $logoImage = $factory->createImage($headLeft, $appIcon);
+ my $labelAppDescription = $factory->createLabel($headRight,$newTitle);
+ $logoImage->setWeight($yui::YD_HORIZ,0);
+ $labelAppDescription->setWeight($yui::YD_HORIZ,3);
+
+ my $hbox_content = $factory->createHBox($layout);
+
+ my $tableHeader = new yui::YTableHeader();
+ $tableHeader->addColumn($self->loc->N("IP Address"));
+ $tableHeader->addColumn($self->loc->N("Hostname"));
+ $tableHeader->addColumn($self->loc->N("Host Aliases"));
+ my $leftContent = $factory->createLeft($hbox_content);
+ $leftContent->setWeight($yui::YD_HORIZ,45);
+ $self->table($factory->createTable($leftContent,$tableHeader));
+
+ # initialize Config::Hosts
+ $self->cfgHosts(ManaTools::Shared::Hosts->new());
+ $self->setupTable();
+
+ my $rightContent = $factory->createRight($hbox_content);
+ $rightContent->setWeight($yui::YD_HORIZ,10);
+ my $topContent = $factory->createTop($rightContent);
+ my $vbox_commands = $factory->createVBox($topContent);
+ my $addButton = $factory->createPushButton($factory->createHBox($vbox_commands),$self->loc->N("A&dd"));
+ my $edtButton = $factory->createPushButton($factory->createHBox($vbox_commands),$self->loc->N("&Edit"));
+ my $remButton = $factory->createPushButton($factory->createHBox($vbox_commands),$self->loc->N("&Remove"));
+ my $hnButton = $factory->createPushButton($factory->createHBox($vbox_commands),$self->loc->N("&Hostname"));
+ $addButton->setWeight($yui::YD_HORIZ,1);
+ $edtButton->setWeight($yui::YD_HORIZ,1);
+ $remButton->setWeight($yui::YD_HORIZ,1);
+ $hnButton->setWeight($yui::YD_HORIZ,1);
+
+ my $hbox_foot = $factory->createHBox($layout);
+ my $vbox_foot_left = $factory->createVBox($factory->createLeft($hbox_foot));
+ my $vbox_foot_right = $factory->createVBox($factory->createRight($hbox_foot));
+ my $aboutButton = $factory->createPushButton($vbox_foot_left,$self->loc->N("&About"));
+ my $cancelButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&OK"));
+
+ # main loop
+ while(1) {
+ my $event = $self->dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+### Buttons and widgets ###
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $addButton) {
+ $self->_addHostDialog();
+ $self->setupTable();
+ }
+ elsif ($widget == $edtButton) {
+ my $tblItem = yui::toYTableItem($self->table->selectedItem());
+ if($tblItem->cellCount() >= 3){
+ $self->_edtHostDialog($tblItem->cell(0)->label(),$tblItem->cell(1)->label(),$tblItem->cell(2)->label());
+ }else{
+ $self->_edtHostDialog($tblItem->cell(0)->label(),$tblItem->cell(1)->label(),"");
+ }
+ $self->setupTable();
+ }
+ elsif ($widget == $remButton) {
+ # implement deletion dialog
+ if($self->sh_gui->ask_YesOrNo({title => $self->loc->N("Confirmation"), text => $self->loc->N("Are you sure to drop this host?")}) == 1){
+ my $tblItem = yui::toYTableItem($self->table->selectedItem());
+ # drop the host using the ip
+ $self->cfgHosts->_dropHost($tblItem->cell(0)->label());
+ # write changes
+ $self->cfgHosts->_writeHosts();
+ $self->setupTable();
+ }
+ }elsif ($widget == $hnButton) {
+ $self->_changeHostNameDialog("Change the HostName FQDN");
+ $self->setupTable();
+ }elsif ($widget == $aboutButton) {
+ $self->sh_gui->AboutDialog({
+ name => $appTitle,
+ version => $VERSION,
+ credits => "Copyright (c) 2013-2015 by Matteo Pasotti",
+ license => "GPLv2",
+ description => $self->loc->N("Graphical manager for hosts definitions"),
+ authors => "Matteo Pasotti &lt;matteo.pasotti\@gmail.com&gt;"
+ }
+ );
+ }elsif ($widget == $okButton) {
+ # write changes
+ $self->cfgHosts->_writeHosts();
+ last;
+ }
+ }
+ }
+
+ $self->dialog->destroy() ;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+}
+
+1;
diff --git a/lib/ManaTools/Module/LogViewer.pm b/lib/ManaTools/Module/LogViewer.pm
new file mode 100644
index 0000000..e009c1c
--- /dev/null
+++ b/lib/ManaTools/Module/LogViewer.pm
@@ -0,0 +1,568 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Module::LogViewer;
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ManaTools::Module::LogViewer - Log viewer
+
+=head1 SYNOPSIS
+
+my $logViewer = ManaTools::Module::LogViewer->new();
+$logViewer->start();
+
+=head1 DESCRIPTION
+
+Log viewer is a backend to journalctl, it can also load a custom
+file.
+
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command:
+
+perldoc ManaTools::Module::::LogViewer
+
+=head1 AUTHOR
+
+Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2014-2015, Angelo Naselli.
+
+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
+
+=head1 FUNCTIONS
+
+=cut
+
+use Moose;
+
+use diagnostics;
+use open OUT => ':utf8';
+
+use ManaTools::Shared::GUI;
+use ManaTools::Shared::Locales;
+use ManaTools::Shared::Services;
+use ManaTools::Shared::JournalCtl;
+
+
+use POSIX qw/strftime floor/;
+use English;
+use Date::Simple ();
+use File::HomeDir qw(home);
+
+use yui;
+
+extends qw( ManaTools::Module );
+
+### TODO icon
+has '+icon' => (
+ default => "/usr/share/mcc/themes/default/logdrake-mdk.png",
+);
+
+has 'loc' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_localeInitialize'
+);
+
+sub _localeInitialize {
+ my $self = shift;
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'libDrakX-standalone') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+has 'sh_gui' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_SharedUGUIInitialize'
+);
+
+sub _SharedUGUIInitialize {
+ my $self = shift;
+
+ $self->sh_gui(ManaTools::Shared::GUI->new() );
+}
+
+=head1 VERSION
+
+Version 1.0.0
+
+=cut
+
+our $VERSION = '1.0.0';
+
+
+my %prior = ('emerg' => 0,
+ 'alert' => 1,
+ 'crit' => 2,
+ 'err' => 3,
+ 'warning' => 4,
+ 'notice' => 5,
+ 'info' => 6,
+ 'debug' => 7);
+
+
+#=============================================================
+
+=head2 BUILD
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ The BUILD method is called after a Moose object is created,
+ in this methods Services loads all the service information.
+
+=cut
+
+#=============================================================
+sub BUILD {
+ my $self = shift;
+
+ if (! $self->name) {
+ $self->name ($self->loc->N("Log viewer"));
+ }
+}
+
+
+#=============================================================
+
+=head2 start
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method extends Module::start and is invoked to
+ start the log viewer
+
+=cut
+
+#=============================================================
+sub start {
+ my $self = shift;
+
+ $self->_logViewerPanel();
+};
+
+
+
+
+
+sub _logViewerPanel {
+ my $self = shift;
+
+ if(!$self->_warn_about_user_mode()) {
+ return 0;
+ }
+
+ my $appTitle = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($self->name);
+ ## set icon if not already set by external launcher
+ yui::YUI::app()->setApplicationIcon($self->icon);
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optFactory = yui::YUI::optionalWidgetFactory;
+
+ # Create Dialog
+ my $dialog = $factory->createMainDialog;
+
+ # Start Dialog layout:
+ my $layout = $factory->createVBox( $dialog );
+ my $align = $factory->createAlignment($layout, $yui::YAlignCenter, $yui::YAlignUnchanged);
+ $factory->createLabel( $align, $self->loc->N("A tool to monitor your logs"), 1, 0 );
+
+ #### matching
+ my $hbox = $factory->createHBox($layout);
+ my $matchingInputField = $factory->createInputField($hbox, $self->loc->N("Matching"));
+ $factory->createHSpacing($hbox, 1);
+
+ #### not matching
+ my $notMatchingInputField = $factory->createInputField($hbox, $self->loc->N("but not matching"));
+ $matchingInputField->setWeight($yui::YD_HORIZ, 2);
+ $notMatchingInputField->setWeight($yui::YD_HORIZ, 2);
+
+ my $frame = $factory->createFrame($layout, $self->loc->N("Options"));
+
+ #### lastBoot
+ my $vbox = $factory->createVBox( $frame );
+ $align = $factory->createLeft($vbox);
+ my $lastBoot = $factory->createCheckBox($align, $self->loc->N("Last boot"), 1);
+ $factory->createVSpacing($vbox, 0.5);
+ $lastBoot->setNotify(1);
+
+ my $row1 = $factory->createHBox($vbox);
+ $factory->createVSpacing($vbox, 0.5);
+ my $row2 = $factory->createHBox($vbox);
+ $factory->createVSpacing($vbox, 0.5);
+ my $row3 = $factory->createHBox($vbox);
+
+ #### since and until
+ my $sinceDate;
+ my $sinceTime;
+ my $sinceFrame = $factory->createCheckBoxFrame($row1, $self->loc->N("Since"), 1);
+ $sinceFrame->setNotify(1);
+
+ my $untilDate;
+ my $untilTime;
+ my $untilFrame = $factory->createCheckBoxFrame($row2, $self->loc->N("Until"), 1);
+ $untilFrame->setNotify(1);
+ if ($optFactory->hasDateField()) {
+ my $hbox1 = $factory->createHBox($sinceFrame);
+
+ $sinceDate = $optFactory->createDateField($hbox1, "");
+ $factory->createHSpacing($hbox1, 1.0);
+ $sinceTime = $optFactory->createTimeField($hbox1, "");
+ my $day = strftime "%F", localtime;
+ $sinceDate->setValue($day);
+ $sinceTime->setValue("00:00:00");
+
+ $hbox1 = $factory->createHBox($untilFrame);
+ $untilDate = $optFactory->createDateField($hbox1, "");
+ $factory->createHSpacing($hbox1, 1.0);
+ $untilTime = $optFactory->createTimeField($hbox1, "");
+ $untilDate->setValue($day);
+ $untilTime->setValue("23:59:59");
+ }
+ else {
+ $sinceFrame->enable(0);
+ $untilFrame->enable(0);
+ }
+
+ #### units
+ my $spacing = $factory->createHSpacing($row1, 2.0);
+
+ my $unitsFrame = $factory->createCheckBoxFrame($row1, $self->loc->N("Select a unit"), 1);
+ $unitsFrame->setNotify(1);
+ $align = $factory->createLeft($unitsFrame);
+ my $units = $factory->createComboBox ( $align, "" );
+ my $itemCollection = new yui::YItemCollection;
+
+ yui::YUI::app()->busyCursor();
+ my $serv = ManaTools::Shared::Services->new();
+ my ($l, $active_services) = $serv->services();
+
+ foreach (@{$active_services}) {
+ my $serviceName = $_;
+ my $item = new yui::YItem($serviceName);
+ $itemCollection->push($item);
+ $item->DISOWN();
+ }
+ $units->addItems($itemCollection);
+ yui::YUI::app()->normalCursor();
+
+ #### priority
+ # From
+ $factory->createHSpacing($row2, 2.0);
+ my $priorityFromFrame = $factory->createCheckBoxFrame($row2, $self->loc->N("From priority"), 1);
+ $priorityFromFrame->setNotify(1);
+ $priorityFromFrame->setWeight($yui::YD_HORIZ, 1);
+ my $priorityFrom = $factory->createComboBox ( $priorityFromFrame, "" );
+ $itemCollection->clear();
+
+ my @pr = ('emerg', 'alert', 'crit', 'err',
+ 'warning', 'notice', 'info', 'debug');
+ foreach (@pr) {
+ my $item = new yui::YItem($_);
+ if ( $_ eq 'emerg' ) {
+ $item->setSelected(1);
+ }
+ $itemCollection->push($item);
+ $item->DISOWN();
+ }
+ $priorityFrom->addItems($itemCollection);
+
+ $factory->createHSpacing( $row2, 2.0 );
+ # To
+ my $priorityToFrame = $factory->createCheckBoxFrame($row2, $self->loc->N("To priority"), 1);
+ $priorityToFrame->setNotify(1);
+ $priorityToFrame->setWeight($yui::YD_HORIZ, 1);
+ my $priorityTo = $factory->createComboBox ( $priorityToFrame, "" );
+ $itemCollection->clear();
+
+ foreach (@pr) {
+ my $item = new yui::YItem($_);
+ if ( $_ eq 'debug' ) {
+ $item->setSelected(1);
+ }
+ $itemCollection->push($item);
+ $item->DISOWN();
+ }
+ $priorityTo->addItems($itemCollection);
+
+ #### search
+ $align = $factory->createRight($row3);
+ my $searchButton = $factory->createPushButton($align, $self->loc->N("&Find"));
+
+ #### create log view object
+ my $logView = $factory->createLogView($layout, $self->loc->N("Log content"), 10, 0);
+
+
+ ### NOTE CheckBoxFrame doesn't honoured his costructor checked value for his children
+ $unitsFrame->setValue(0);
+ $sinceFrame->setValue(0);
+ $untilFrame->setValue(0);
+ $priorityFromFrame->setValue(0);
+ $priorityToFrame->setValue(0);
+
+ # buttons on the last line
+ $align = $factory->createRight($layout);
+ $hbox = $factory->createHBox($align);
+ my $aboutButton = $factory->createPushButton($hbox, $self->loc->N("&About") );
+ $align = $factory->createRight($hbox);
+ $hbox = $factory->createHBox($align);
+ my $saveButton = $factory->createPushButton($hbox, $self->loc->N("&Save"));
+ my $quitButton = $factory->createPushButton($hbox, $self->loc->N("&Quit"));
+
+ # End Dialof layout
+
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ if ($widget == $quitButton) {
+ last;
+ }
+ elsif($widget == $aboutButton) {
+ my $translators = $self->loc->N("_: Translator(s) name(s) & email(s)\n");
+ $translators =~ s/\</\&lt\;/g;
+ $translators =~ s/\>/\&gt\;/g;
+ $self->sh_gui->AboutDialog({ name => $self->name,
+ version => $self->VERSION,
+ credits => $self->loc->N("Copyright (C) %s Mageia community", '2014'),
+ license => $self->loc->N("GPLv2"),
+ description => $self->loc->N("Log viewer is a systemd journal viewer"),
+ authors => $self->loc->N("<h3>Developers</h3>
+ <ul><li>%s</li>
+ <li>%s</li>
+ </ul>
+ <h3>Translators</h3>
+ <ul><li>%s</li></ul>",
+ "Angelo Naselli &lt;anaselli\@linux.it&gt;",
+ "Matteo Pasotti &lt;matteo.pasotti\@gmail.com&gt;",
+ $translators
+ ),
+ }
+ );
+ }
+ elsif($widget == $saveButton) {
+ if ($logView->lines()) {
+ $self->_save($logView);
+ }
+ else {
+ $self->sh_gui->warningMsgBox({text => $self->loc->N("Empty log found")});
+ }
+ }
+ elsif ($widget == $searchButton) {
+ yui::YUI::app()->busyCursor();
+ $dialog->startMultipleChanges();
+ $logView->clearText();
+ my %log_opts;
+ if ($lastBoot->value()) {
+ $log_opts{this_boot} = 1;
+ }
+ if ($unitsFrame->value()) {
+ $log_opts{unit} = $units->value();
+ }
+ if ($sinceFrame->value()) {
+ $log_opts{since} = $sinceDate->value() . " " . $sinceTime->value();
+ }
+ if ($untilFrame->value()) {
+ $log_opts{until} = $untilDate->value() . " " . $untilTime->value();
+# TODO check date until > date since
+ }
+ if ($priorityFromFrame->value() || $priorityToFrame->value()) {
+ my $prio = $priorityFrom->value();
+ $prio .= "..".$priorityTo->value() if ($priorityToFrame->value());
+ $log_opts{priority} = $prio;
+# TODO enabling right using checkBoxes
+ }
+ my $log = $self->_search(\%log_opts);
+print " log lines: ". scalar (@{$log}) ."\n";
+# TODO check on log line number what to do if too big? and adding a progress bar?
+ $self->_parse_content({'matching' => $matchingInputField->value(),
+ 'noMatching' => $notMatchingInputField->value(),
+ 'log' => $log,
+ 'logView' => $logView,
+ }
+ );
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+ yui::YUI::app()->normalCursor();
+ }
+ elsif ($widget == $lastBoot) {
+ yui::YUI::ui()->blockEvents();
+ if ($lastBoot->value()) {
+ #last boot overrrides until and since
+ $sinceFrame->setValue(0);
+ $untilFrame->setValue(0);
+ }
+ yui::YUI::ui()->unblockEvents();
+ }
+ elsif ($widget == $sinceFrame) {
+ yui::YUI::ui()->blockEvents();
+ if ($sinceFrame->value()) {
+ #disabling last boot that overrrides until and since
+ $lastBoot->setValue(0);
+ }
+ yui::YUI::ui()->unblockEvents();
+ }
+ elsif ($widget == $untilFrame) {
+ yui::YUI::ui()->blockEvents();
+ if ($untilFrame->value()) {
+ #disabling last boot that overrrides until and since
+ $lastBoot->setValue(0);
+ }
+ yui::YUI::ui()->unblockEvents();
+ }
+ elsif ($widget == $priorityFromFrame) {
+ if ($priorityToFrame->value() && !$priorityFromFrame->value()) {
+ yui::YUI::ui()->blockEvents();
+ $priorityToFrame->setValue(0) ;
+ yui::YUI::ui()->unblockEvents();
+ }
+ }
+ elsif ($widget == $priorityToFrame) {
+ if ($priorityToFrame->value() && !$priorityFromFrame->value()) {
+ yui::YUI::ui()->blockEvents();
+ $priorityFromFrame->setValue(1) ;
+ yui::YUI::ui()->unblockEvents();
+ }
+ }
+
+ }
+ }
+ $dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+}
+
+
+
+sub _warn_about_user_mode {
+ my $self = shift;
+
+ my $title = $self->loc->N("Running in user mode");
+ my $msg = $self->loc->N("You are launching this program as a normal user.\n".
+ "You will not be able to read system logs which you do not have rights to,\n".
+ "but you may still browse all the others.");
+
+ if(($EUID != 0) and (!$self->sh_gui->ask_OkCancel({title => $title, text => $msg}))) {
+ # TODO add Privileges?
+ return 0;
+ }
+
+ return 1;
+}
+
+
+## Save as
+#
+# $logView: log Widget
+#
+##
+sub _save {
+ my ($self, $logView) = @_;
+
+ yui::YUI::app()->busyCursor();
+
+ my $outFile = yui::YUI::app()->askForSaveFileName(home(), "*", $self->loc->N("Save as.."));
+ if ($outFile) {
+ open(OF, ">".$outFile);
+ print OF $logView->logText();
+ close OF;
+ }
+
+ yui::YUI::app()->normalCursor();
+}
+
+## Search call back
+sub _search {
+ my ($self, $log_opts) = @_;
+
+ my $log = ManaTools::Shared::JournalCtl->new(%{$log_opts});
+ my $all = $log->getLog();
+
+ return $all;
+}
+
+## _parse_content
+#
+# $info : HASH cotaining
+#
+# matching: string to match
+# notMatching: string to skip
+# log: ARRAY REF to log content
+# logViewer: logViewer Widget
+#
+##
+sub _parse_content {
+ my ($self, $info) = @_;
+
+ my $ey = "";
+ my $en = "";
+
+ if( exists($info->{'matching'} ) ){
+ $ey = $info->{'matching'};
+ }
+ if( exists($info->{'noMatching'} ) ){
+ $en = $info->{'noMatching'};
+ }
+
+ $ey =~ s/ OR /|/ if ($ey);
+ $ey =~ s/^\*$// if ($ey);
+ $en =~ s/^\*$/.*/ if ($en);
+
+ my $test;
+
+ if ($en && !$ey) {
+ $test = sub { $_[0] !~ /$en/ };
+ }
+ elsif ($ey && !$en) {
+ $test = sub { $_[0] =~ /$ey/ };
+ }
+ elsif ($ey && $en) {
+ $test = sub { $_[0] =~ /$ey/ && $_[0] !~ /$en/ };
+ }
+ else {
+ $test = sub { $_[0] };
+ }
+
+ foreach (@{$info->{log}}) {
+ $info->{logView}->appendLines($_) if $test->($_);
+ }
+
+}
+
+
+1
diff --git a/lib/ManaTools/Module/Proxy.pm b/lib/ManaTools/Module/Proxy.pm
new file mode 100644
index 0000000..ff2af3e
--- /dev/null
+++ b/lib/ManaTools/Module/Proxy.pm
@@ -0,0 +1,396 @@
+# vim: set et ts=4 sw=4:
+#*****************************************************************************
+#
+# Copyright (c) 2013-2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#*****************************************************************************
+
+package ManaTools::Module::Proxy;
+
+use Modern::Perl '2011';
+use autodie;
+use Moose;
+use POSIX qw(ceil);
+use English;
+use utf8;
+
+use yui;
+use ManaTools::Shared qw(trim);
+use ManaTools::Shared::GUI;
+use ManaTools::Shared::Proxy;
+
+# TODROP but provides network::network
+use lib qw(/usr/lib/libDrakX);
+use network::network;
+use MDK::Common::System qw(getVarsFromSh);
+
+extends qw( ManaTools::Module );
+
+
+has '+icon' => (
+ default => "/usr/share/mcc/themes/default/drakproxy-mdk.png"
+);
+
+has '+name' => (
+ default => "Proxymanager",
+);
+
+=head1 VERSION
+
+Version 1.0.0
+
+=cut
+
+our $VERSION = '1.0.0';
+
+has 'dialog' => (
+ is => 'rw',
+ init_arg => undef
+);
+
+has 'table' => (
+ is => 'rw',
+ init_arg => undef
+);
+
+has 'proxy' => (
+ is => 'rw',
+ isa => 'HashRef',
+ builder => "init_proxy"
+);
+
+has 'sh_gui' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_SharedUGUIInitialize'
+);
+
+has 'loc' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_localeInitialize'
+);
+
+
+sub _localeInitialize {
+ my $self = shift();
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'drakx-net') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+sub _SharedUGUIInitialize {
+ my $self = shift();
+
+ $self->sh_gui( ManaTools::Shared::GUI->new() );
+}
+
+#=============================================================
+
+=head2 init_proxy
+
+=head3 DESCRIPTION
+
+=over 4
+
+=item This method does initialize the proxy attribute provided by this class.
+
+=item $self->proxy is structured as follows:
+
+=over 6
+
+=item B<no_proxy> the string with the list of the excluded domains/addresses
+
+=item B<http_proxy> the url of the http proxy
+
+=item B<https_proxy> the url of the https proxy
+
+=item B<ftp_proxy> the url for the ftp proxy
+
+=back
+
+=back
+
+=cut
+
+#=============================================================
+
+sub init_proxy {
+ my %p = (
+ 'no_proxy' => '',
+ 'http_proxy' => '',
+ 'https_proxy' => '',
+ 'ftp_proxy' => '',
+ );
+ return \%p;
+}
+
+#=============================================================
+
+=head2 start
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method extends Module::start and is invoked to
+ start proxy manager
+
+=cut
+
+#=============================================================
+sub start {
+ my $self = shift;
+
+ if ($EUID != 0) {
+ $self->sh_gui->warningMsgBox({
+ title => $self->name,
+ text => $self->loc->N("root privileges required"),
+ });
+ return;
+ }
+
+ $self->_manageProxyDialog();
+};
+
+#=============================================================
+
+=head2 ask_for_X_restart
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method shows a message box warning the user
+ that a X server restart is required
+
+=cut
+
+#=============================================================
+
+sub ask_for_X_restart {
+ my $self = shift;
+
+ $self->sh_gui->warningMsgBox({title=>$self->loc->N("X Restart Required"),text=>$self->loc->N("You need to log out and back in again for changes to take effect"),richtext=>1});
+}
+
+#=============================================================
+
+=head2 validate
+
+=head3 INPUT
+
+ $self: this object
+
+ $proxy: the hash containing what returns from getVarFromSh
+ eventually modified by the user
+
+=head3 DESCRIPTION
+
+ This method returns true if the each value match
+ certain conditions like the leading http:// for http proxy
+ or https:// for the https proxy, etc.
+
+ $proxy is passed by reference thus $proxy->{no_proxy} value
+ is sanitized (trimmed).
+
+=cut
+
+#=============================================================
+
+sub validate {
+ my $self = shift;
+ my $proxy = shift;
+ my $retval = 1;
+ $proxy->{no_proxy} =~ s/\s//g;
+ # using commas rather than slashes
+ if($proxy->{http_proxy} !~ m,^($|http://),)
+ {
+ $self->sh_gui->warningMsgBox({title=>'Error',text=>$self->loc->N("Proxy should be http://..."),richtext=>0});
+ $retval = 0;
+ }
+ if($proxy->{https_proxy} !~ m,^($|https?://),)
+ {
+ $self->sh_gui->warningMsgBox({title=>'Error',text=>$self->loc->N("Proxy should be http://... or https://..."),richtext=>0});
+ $retval = 0;
+ }
+ if($proxy->{ftp_proxy} !~ m,^($|ftp://|http://),)
+ {
+ $self->sh_gui->warningMsgBox({title=>'Error',text=>$self->loc->N("URL should begin with 'ftp:' or 'http:'"),richtext=>0});
+ $retval = 0;
+ }
+ return $retval;
+}
+
+sub _manageProxyDialog {
+ my $self = shift;
+
+ ## TODO fix for manatools
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ my $appIcon = yui::YUI::app()->applicationIcon();
+ ## set new title to get it in dialog
+ my $newTitle = $self->loc->N("Proxies configuration");
+ yui::YUI::app()->setApplicationTitle($newTitle);
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+ my $label_width = 25;
+ my $inputfield_width = 45;
+ # getVarsFromSh returns an empty hash if no vars are defined
+ # possible alternatives:
+ # . Config::Auto::parse
+ my $proxy_curr_settings = { getVarsFromSh('/etc/profile.d/proxy.sh') };
+ my $httpsProxyEqualToHttpProxy = 0;
+ if((defined($proxy_curr_settings->{http_proxy}) && defined($proxy_curr_settings->{https_proxy}))&&
+ (($proxy_curr_settings->{http_proxy} eq $proxy_curr_settings->{https_proxy}) &&
+ ($proxy_curr_settings->{http_proxy} ne ""))){
+ $httpsProxyEqualToHttpProxy = 1;
+ }
+
+ #
+ # @layout
+ #
+ # +------------------------------+
+ # | +------------+-------------+ |
+ # | |LABELS | VALUES | |
+ # | | | | |
+ # | | | | |
+ # | | | | |
+ # | +------------+-------------+ |
+ # +------------------------------+
+
+ $self->dialog($factory->createMainDialog());
+ my $layout = $factory->createVBox($self->dialog);
+
+ my $hbox_header = $factory->createHBox($layout);
+ my $headLeft = $factory->createHBox($factory->createLeft($hbox_header));
+ my $headRight = $factory->createHBox($factory->createRight($hbox_header));
+
+ my $logoImage = $factory->createImage($headLeft, $appIcon);
+ my $labelAppDescription = $factory->createLabel($headRight,$newTitle);
+ $logoImage->setWeight($yui::YD_HORIZ,0);
+ $labelAppDescription->setWeight($yui::YD_HORIZ,3);
+
+ # app description
+ my $hbox_content = $factory->createHBox($layout);
+ $factory->createLabel($hbox_content, $self->loc->N("Here you can set up your proxies configuration (eg: http://my_caching_server:8080)"));
+
+ $hbox_content = $factory->createHBox($layout);
+
+ my $vbox_labels_flags = $factory->createVBox($hbox_content);
+ my $vbox_inputfields = $factory->createVBox($hbox_content);
+
+ # http proxy section
+ my $httpproxy_label = $factory->createLabel($vbox_labels_flags, $self->loc->N("HTTP proxy"));
+ my $http_proxy = $factory->createInputField($factory->createHBox($vbox_inputfields),"",0);
+ $http_proxy->setValue($proxy_curr_settings->{http_proxy}) if(defined($proxy_curr_settings->{http_proxy}));
+ $http_proxy->setWeight($yui::YD_HORIZ, 30);
+
+ # flag to setup the https proxy with the same value of the http proxy
+ my $ckbHttpEqHttps = $factory->createCheckBox($vbox_labels_flags, $self->loc->N("Use HTTP proxy for HTTPS connections"),$httpsProxyEqualToHttpProxy);
+ $ckbHttpEqHttps->setNotify(1);
+ # add a spacing as we have
+ $factory->createLabel($factory->createHBox($vbox_inputfields)," ");
+
+ # https proxy
+ $factory->createLabel($vbox_labels_flags, $self->loc->N("HTTPS proxy"));
+ my $https_proxy = $factory->createInputField($factory->createHBox($vbox_inputfields),"",0);
+ $https_proxy->setValue($proxy_curr_settings->{https_proxy}) if(defined($proxy_curr_settings->{https_proxy}));
+ $https_proxy->setWeight($yui::YD_HORIZ, 30);
+
+ # ftp proxy
+ $factory->createLabel($vbox_labels_flags, $self->loc->N("FTP proxy"));
+ my $ftp_proxy = $factory->createInputField($factory->createHBox($vbox_inputfields),"",0);
+ $ftp_proxy->setValue($proxy_curr_settings->{ftp_proxy}) if(defined($proxy_curr_settings->{ftp_proxy}));
+ $ftp_proxy->setWeight($yui::YD_HORIZ, 30);
+
+ # no-proxy list
+ $factory->createLabel($vbox_labels_flags, $self->loc->N("No proxy for (comma separated list):"));
+ my $no_proxy = $factory->createInputField($factory->createHBox($vbox_inputfields),"",0);
+ $no_proxy->setValue($proxy_curr_settings->{no_proxy}) if(defined($proxy_curr_settings->{no_proxy}));
+ $no_proxy->setWeight($yui::YD_HORIZ, 30);
+
+ my $hbox_filler = $factory->createHBox($layout);
+ $factory->createSpacing($hbox_filler,$yui::YD_VERT,2);
+
+ my $hbox_foot = $factory->createHBox($layout);
+ my $vbox_foot_left = $factory->createVBox($factory->createLeft($hbox_foot));
+ my $vbox_foot_right = $factory->createVBox($factory->createRight($hbox_foot));
+ my $aboutButton = $factory->createPushButton($vbox_foot_left,$self->loc->N("&About"));
+ my $cancelButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("&OK"));
+
+ # main loop
+ while(1) {
+ my $event = $self->dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+### Buttons and widgets ###
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }elsif ($widget == $aboutButton) {
+ $self->sh_gui->AboutDialog({
+ name => $appTitle,
+ version => $VERSION,
+ credits => "Copyright (c) 2013-2014 by Matteo Pasotti",
+ license => "GPLv2",
+ description => $self->loc->N("Graphical manager for proxies"),
+ authors => "Matteo Pasotti &lt;matteo.pasotti\@gmail.com&gt;"
+ }
+ );
+ }elsif ($widget == $okButton) {
+ # setup proxy attribute
+ my %_proxy = (
+ no_proxy => $no_proxy->value(),
+ http_proxy => $http_proxy->value(),
+ https_proxy => $https_proxy->value(),
+ ftp_proxy => $ftp_proxy->value()
+ );
+ if($self->validate(\%_proxy)) {
+ # validation succeded
+ $self->proxy(\%_proxy);
+ # save changes
+ network::network::proxy_configure($self->proxy);
+ $self->ask_for_X_restart();
+ last;
+ }
+ # validation failed
+ next;
+ }elsif ($widget == $ckbHttpEqHttps){
+ $https_proxy->setEnabled(!$ckbHttpEqHttps->isChecked());
+ }
+ }
+ }
+
+ $self->dialog->destroy() ;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+}
+
+1;
diff --git a/lib/ManaTools/Module/Services.pm b/lib/ManaTools/Module/Services.pm
new file mode 100644
index 0000000..dca0c0c
--- /dev/null
+++ b/lib/ManaTools/Module/Services.pm
@@ -0,0 +1,588 @@
+# vim: set et ts=4 sw=4:
+
+package ManaTools::Module::Services;
+
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ManaTools::Module::Services - This module aims to manage service
+ with GUI
+
+=head1 SYNOPSIS
+
+ my $serviceMan = ManaTools::Module::Services->new();
+ $serviceMan->start();
+
+=head1 DESCRIPTION
+
+ This module presents all the system service status and gives
+ the availability to administrator to stop, start and active at boot
+ them.
+
+ From the original code drakx services.
+
+=head1 SUPPORT
+
+ You can find documentation for this module with the perldoc command:
+
+ perldoc ManaTools::Module::Services
+
+=head1 SEE ALSO
+
+ ManaTools::Module
+
+=head1 AUTHOR
+
+Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2014-2015, Angelo Naselli.
+
+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
+
+=cut
+
+
+use Moose;
+use English;
+use Time::HiRes qw(usleep);
+
+use MDK::Common::String qw(formatAlaTeX);
+use MDK::Common::DataStructure qw(member);
+
+use yui;
+use ManaTools::Shared::GUI;
+use ManaTools::Shared::Locales;
+use ManaTools::Shared::Services;
+
+
+use File::Basename;
+
+extends qw( ManaTools::Module );
+
+has '+icon' => (
+ default => "/usr/share/mcc/themes/default/service-mdk.png",
+);
+
+has '_services' => (
+ traits => ['Array'],
+ is => 'rw',
+ isa => 'ArrayRef[Str]',
+ default => sub { [] },
+ init_arg => undef,
+ handles => {
+ all_services => 'elements',
+ add_service => 'push',
+ map_service => 'map',
+ service_count => 'count',
+ sorted_services => 'sort',
+ },
+);
+
+has '_xinetd_services' => (
+ traits => ['Array'],
+ is => 'rw',
+ isa => 'ArrayRef[Str]',
+ default => sub { [] },
+ init_arg => undef,
+ handles => {
+ all_xinetd_services => 'elements',
+ add_xinetd_service => 'push',
+ map_xinetd_service => 'map',
+ xinetd_service_count => 'count',
+ sorted_xinetd_services => 'sort',
+ },
+);
+
+has 'on_services' => (
+ traits => ['Array'],
+ is => 'rw',
+ isa => 'ArrayRef[Str]',
+ default => sub { [] },
+ init_arg => undef,
+ handles => {
+ all_on_services => 'elements',
+ add_on_service => 'push',
+ map_on_service => 'map',
+ on_service_count => 'count',
+ sorted_on_services => 'sort',
+ },
+);
+
+
+has 'sh_gui' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_SharedUGUIInitialize'
+);
+
+sub _SharedUGUIInitialize {
+ my $self = shift();
+
+ $self->sh_gui(ManaTools::Shared::GUI->new() );
+}
+
+has 'sh_services' => (
+ is => 'rw',
+ init_arg => undef,
+ lazy => 1,
+ builder => '_SharedServicesInitialize'
+);
+
+sub _SharedServicesInitialize {
+ my $self = shift();
+
+ $self->sh_services(ManaTools::Shared::Services->new() );
+}
+
+
+has 'loc' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_localeInitialize'
+);
+
+sub _localeInitialize {
+ my $self = shift();
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'libDrakX-standalone') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+=head1 VERSION
+
+Version 1.0.0
+
+=cut
+
+our $VERSION = '1.0.0';
+
+=head1 METHODS
+
+=cut
+
+#=============================================================
+
+=head2 BUILD
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ The BUILD method is called after a Moose object is created,
+ in this methods Services loads all the service information.
+
+=cut
+
+#=============================================================
+sub BUILD {
+ my $self = shift;
+
+ if (! $self->name) {
+ $self->name ($self->loc->N("adminService"));
+ }
+
+ $self->loadServices();
+}
+
+
+#=============================================================
+
+=head2 start
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method extends Module::start and is invoked to
+ start adminService
+
+=cut
+
+#=============================================================
+sub start {
+ my $self = shift;
+
+# if ($EUID != 0) {
+# $self->sh_gui->warningMsgBox({
+# title => $self->name,
+# text => $self->loc->N("root privileges required"),
+# });
+# return;
+# }
+
+ $self->_servicePanel();
+};
+
+
+#=============================================================
+
+=head2 loadServices
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This methonds load service info into local attributes such
+ as xinetd_services, on_services and all the available,
+ services
+
+=cut
+
+#=============================================================
+sub loadServices {
+ my $self = shift;
+
+ my $refresh = 1;
+ my ($l, $on_services) = $self->sh_services->services($refresh);
+ my @xinetd_services = map { $_->[0] } $self->sh_services->xinetd_services();
+
+ $self->_xinetd_services();
+ $self->_xinetd_services(\@xinetd_services);
+ $self->_services(\@$l);
+ $self->on_services(\@$on_services);
+
+}
+
+## _waitUnitStatus wait unit status is reached for
+## a while (10 secs max)
+sub _waitUnitStatus {
+ my ($self, $service, $running) = @_;
+
+ for (my $i=0; $i < 100; $i++) {
+ $self->loadServices();
+ if ($running) {
+ last if $self->sh_services->is_service_running($service);
+ }
+ else {
+ last if !$self->sh_services->is_service_running($service);
+ }
+ usleep(100);
+ }
+}
+
+## _serviceInfo sets service description accordingly to
+## selected service status
+## param
+## 'service' service name
+## 'infoPanel' service information widget
+sub _serviceInfo {
+ my ($self, $service, $infoPanel) = @_;
+
+ yui::YUI::ui()->blockEvents();
+ ## infoPanel
+ $infoPanel->setValue(MDK::Common::String::formatAlaTeX($self->sh_services->description($service)));
+ yui::YUI::ui()->unblockEvents();
+}
+
+
+sub _serviceStatusString {
+ my ($self, $serviceName) = @_;
+
+ my $started;
+
+ if (MDK::Common::DataStructure::member($serviceName, $self->all_xinetd_services)) {
+ $started = $self->loc->N("Start when requested");
+ }
+ else {
+ $started = ($self->sh_services->is_service_running($serviceName)? $self->loc->N("running") : $self->loc->N("stopped"));
+ }
+
+ return $started;
+}
+
+## _serviceStatus sets status label accordingly to selected item
+## param
+## 'service' yui CB table (service table)
+## 'item' selected item (service)
+sub _serviceStatus {
+ my ($self, $tbl, $item) = @_;
+
+ my $started = $self->_serviceStatusString($item->label());
+
+ # TODO add icon green/red led
+ my $cell = $tbl->toCBYTableItem($item)->cell(1);
+ if ($cell) {
+ $cell->setLabel($started);
+ $tbl->cellChanged($cell);
+ }
+}
+
+
+## fill service table with service info
+## param
+## 'tbl' yui table
+sub _fillServiceTable {
+ my ($self, $tbl) = @_;
+
+ $tbl->startMultipleChanges();
+ $tbl->deleteAllItems();
+ my $itemCollection = new yui::YItemCollection;
+ foreach (sort $self->all_services) {
+
+ my $serviceName = $_;
+
+ my $item = new yui::YCBTableItem($serviceName);
+ my $started = $self->_serviceStatusString($serviceName);
+
+ # TODO add icon green/red led
+ my $cell = new yui::YTableCell($started);
+ $item->addCell($cell);
+
+ $item->check(MDK::Common::DataStructure::member($serviceName, $self->all_on_services));
+ $item->setLabel($serviceName);
+ $itemCollection->push($item);
+ $item->DISOWN();
+ }
+ $tbl->addItems($itemCollection);
+ $tbl->doneMultipleChanges();
+}
+
+## draw service panel and manage it (main dialog)
+sub _servicePanel {
+ my $self = shift;
+
+ my $appTitle = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($self->name);
+ ## set icon if not already set by external launcher
+ yui::YUI::app()->setApplicationIcon($self->icon);
+
+ my $mageiaPlugin = "mga";
+ my $factory = yui::YUI::widgetFactory;
+ my $mgaFactory = yui::YExternalWidgets::externalWidgetFactory($mageiaPlugin);
+ $mgaFactory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($mgaFactory);
+
+ my $dialog = $factory->createMainDialog;
+ my $vbox = $factory->createVBox( $dialog );
+
+ #Line for logo and title
+ my $hbox_iconbar = $factory->createHBox($vbox);
+ my $head_align_left = $factory->createLeft($hbox_iconbar);
+ $hbox_iconbar = $factory->createHBox($head_align_left);
+ $factory->createImage($hbox_iconbar, $self->icon);
+
+ $factory->createHeading($hbox_iconbar, $self->loc->N("Manage system services by enabling or disabling them"));
+
+ my $frame = $factory->createFrame ($vbox, "");
+
+ my $frmVbox = $factory->createVBox( $frame );
+ my $hbox = $factory->createHBox( $frmVbox );
+
+ my $yTableHeader = new yui::YTableHeader();
+ $yTableHeader->addColumn($self->loc->N("Service"), $yui::YAlignBegin);
+ $yTableHeader->addColumn($self->loc->N("Status"), $yui::YAlignCenter);
+ $yTableHeader->addColumn($self->loc->N("On boot"), $yui::YAlignBegin);
+
+ ## service list (serviceBox)
+ my $serviceTbl = $mgaFactory->createCBTable($hbox, $yTableHeader, $yui::YCBTableCheckBoxOnLastColumn);
+
+ $self->_fillServiceTable($serviceTbl);
+
+ $serviceTbl->setImmediateMode(1);
+ $serviceTbl->setWeight(0, 50);
+
+ ## info panel (infoPanel)
+ $frame = $factory->createFrame ($hbox, $self->loc->N("Information"));
+ $frame->setWeight(0, 30);
+ $frmVbox = $factory->createVBox( $frame );
+ my $infoPanel = $factory->createRichText($frmVbox, "--------------"); #, 0, 0);
+ $infoPanel->setAutoScrollDown();
+
+ ### Service Start button ($startButton)
+ $hbox = $factory->createHBox( $frmVbox );
+ my $startButton = $factory->createPushButton($hbox, $self->loc->N("&Start"));
+
+ ### Service Stop button ($stopButton)
+ my $stopButton = $factory->createPushButton($hbox, $self->loc->N("S&top"));
+
+ # dialog buttons
+ $factory->createVSpacing($vbox, 1.0);
+ ## Window push buttons
+ $hbox = $factory->createHBox( $vbox );
+ my $align = $factory->createLeft($hbox);
+ $hbox = $factory->createHBox($align);
+ my $aboutButton = $factory->createPushButton($hbox, $self->loc->N("&About") );
+ $align = $factory->createRight($hbox);
+ $hbox = $factory->createHBox($align);
+
+ ### Service Refresh button ($refreshButton)
+ my $refreshButton = $factory->createPushButton($hbox, $self->loc->N("&Refresh"));
+ my $closeButton = $factory->createPushButton($hbox, $self->loc->N("&Quit") );
+
+ #first item status
+ my $item = $serviceTbl->selectedItem();
+ if ($item) {
+ $self->_serviceInfo($item->label(), $infoPanel);
+ if (MDK::Common::DataStructure::member($item->label(), $self->all_xinetd_services)) {
+ $stopButton->setDisabled();
+ $startButton->setDisabled();
+ }
+ else {
+ $stopButton->setEnabled(1);
+ $startButton->setEnabled(1);
+ }
+ }
+
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ my $wEvent = yui::toYWidgetEvent($event);
+
+ if ($widget == $closeButton) {
+ last;
+ }
+ elsif ($widget == $aboutButton) {
+ my $translators = $self->loc->N("_: Translator(s) name(s) & email(s)\n");
+ $translators =~ s/\</\&lt\;/g;
+ $translators =~ s/\>/\&gt\;/g;
+ $self->sh_gui->AboutDialog({ name => $self->name,
+ version => $self->VERSION,
+ credits => $self->loc->N("Copyright (C) %s Mageia community", '2013-2015'),
+ license => $self->loc->N("GPLv2"),
+ description => $self->loc->N("adminService is the Mageia service and daemon management tool\n
+ (from the original idea of Mandriva draxservice)."),
+ authors => $self->loc->N("<h3>Developers</h3>
+ <ul><li>%s</li>
+ <li>%s</li>
+ </ul>
+ <h3>Translators</h3>
+ <ul><li>%s</li></ul>",
+ "Angelo Naselli &lt;anaselli\@linux.it&gt;",
+ "Matteo Pasotti &lt;matteo.pasotti\@gmail.com&gt;",
+ $translators
+ ),
+ }
+ );
+ }
+ elsif ($widget == $serviceTbl) {
+
+ # service selection changed
+ $item = $serviceTbl->selectedItem();
+ if ($item) {
+ $self->_serviceInfo($item->label(), $infoPanel);
+ if (MDK::Common::DataStructure::member($item->label(), $self->all_xinetd_services)) {
+ $stopButton->setDisabled();
+ $startButton->setDisabled();
+ }
+ else {
+ $stopButton->setEnabled(1);
+ $startButton->setEnabled(1);
+ }
+ }
+# TODO fix libyui-mga-XXX item will always be changed after first one
+ if ($wEvent->reason() == $yui::YEvent::ValueChanged) {
+ $item = $serviceTbl->changedItem();
+ if ($item) {
+ yui::YUI::app()->busyCursor();
+ eval {
+ $self->sh_services->set_service($item->label(), $item->checked());
+ };
+ my $errors = $@;
+ $self->loadServices();
+ yui::YUI::app()->normalCursor();
+
+ if ($errors) {
+ $self->sh_gui->warningMsgBox({
+ title => $self->loc->N($item->checked() ? "Enabling %s" : "Disabling %s", $item->label()),
+ text => "$errors",
+ richtext => 1,
+ });
+ $dialog->startMultipleChanges();
+ $self->_fillServiceTable($serviceTbl);
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+ }
+ }
+ }
+ }
+ elsif ($widget == $startButton) {
+ $item = $serviceTbl->selectedItem();
+ if ($item) {
+ my $serviceName = $item->label();
+ yui::YUI::app()->busyCursor();
+ eval {
+ $self->sh_services->restart_or_start($serviceName);
+ $self->_waitUnitStatus($serviceName, 1);
+ };
+ my $errors = $@;
+ yui::YUI::app()->normalCursor();
+ $self->_serviceStatus($serviceTbl, $item);
+
+ $self->sh_gui->warningMsgBox({
+ title => $self->loc->N("Starting %s", $serviceName),
+ text => "$errors",
+ richtext => 1,
+ }) if $errors;
+ }
+ }
+ elsif ($widget == $stopButton) {
+ $item = $serviceTbl->selectedItem();
+ if ($item) {
+ my $serviceName = $item->label();
+ yui::YUI::app()->busyCursor();
+ eval {
+ $self->sh_services->stopService($serviceName);
+ $self->_waitUnitStatus($serviceName, 0);
+ };
+ my $errors = $@;
+ yui::YUI::app()->normalCursor();
+ $self->_serviceStatus($serviceTbl, $item);
+
+ $self->sh_gui->warningMsgBox({
+ title => $self->loc->N("Stopping %s", $serviceName),
+ text => "$errors",
+ richtext => 1,
+ }) if $errors;
+ }
+ }
+ elsif ($widget == $refreshButton) {
+ yui::YUI::app()->busyCursor();
+ $self->loadServices();
+ $dialog->startMultipleChanges();
+ $self->_fillServiceTable($serviceTbl);
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+ yui::YUI::app()->normalCursor();
+ }
+ }
+ }
+ $dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+}
+
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/lib/ManaTools/Module/Users.pm b/lib/ManaTools/Module/Users.pm
new file mode 100644
index 0000000..2ef9066
--- /dev/null
+++ b/lib/ManaTools/Module/Users.pm
@@ -0,0 +1,2637 @@
+๏ปฟ# vim: set et ts=4 sw=4:
+
+package ManaTools::Module::Users;
+
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ManaTools::Module::Users - This module aims to manage service
+ with GUI
+
+=head1 SYNOPSIS
+
+ my $userManager = ManaTools::Module::Users->new();
+ $userManager->start();
+
+=head1 DESCRIPTION
+
+ This module is a tool to manage users on the system.
+
+ From the original code adduserdrake and userdrake.
+
+=head1 SUPPORT
+
+ You can find documentation for this module with the perldoc command:
+
+ perldoc ManaTools::Module::Users
+
+=head1 SEE ALSO
+
+ ManaTools::Module
+
+=head1 AUTHOR
+
+Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2013-2015, Angelo Naselli.
+
+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
+
+=head1 VERSION
+
+Version 1.0.0
+
+=cut
+
+our $VERSION = '1.0.0';
+
+
+###############################################
+##
+## graphic related routines for managing user
+##
+###############################################
+
+use Moose;
+
+use POSIX qw(ceil);
+use Config::Auto;
+use File::ShareDir ':ALL';
+
+use utf8;
+use Sys::Syslog;
+use Glib;
+use English;
+use yui;
+use ManaTools::Shared;
+use ManaTools::Shared::GUI;
+use ManaTools::Shared::Locales;
+use ManaTools::Shared::Users;
+use MDK::Common::DataStructure qw(member);
+use feature 'state';
+
+extends qw( ManaTools::Module );
+
+has '+icon' => (
+ default => "/usr/share/icons/userdrake.png",
+);
+
+
+# main dialog
+has 'dialog' => (
+ is => 'rw',
+ init_arg => undef,
+);
+
+has 'widgets' => (
+ traits => ['Hash'],
+ default => sub { {} },
+ is => 'rw',
+ isa => 'HashRef',
+ handles => {
+ set_widget => 'set',
+ get_widget => 'get',
+ widget_pairs => 'kv',
+ },
+ init_arg => undef,
+);
+
+has 'action_menu' => (
+ traits => ['Hash'],
+ default => sub { {} },
+ is => 'rw',
+ isa => 'HashRef',
+ handles => {
+ set_action_menu => 'set',
+ get_action_menu => 'get',
+ action_menu_pairs => 'kv',
+ },
+ init_arg => undef,
+);
+
+
+has 'edit_tab_widgets' => (
+ traits => ['Hash'],
+ default => sub { {} },
+ is => 'rw',
+ isa => 'HashRef',
+ handles => {
+ set_edit_tab_widget => 'set',
+ get_edit_tab_widget => 'get',
+ edit_tab_pairs => 'kv',
+ },
+ init_arg => undef,
+);
+
+has 'sh_users' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_SharedUsersInitialize'
+);
+
+sub _SharedUsersInitialize {
+ my $self = shift();
+
+ $self->sh_users(ManaTools::Shared::Users->new() );
+}
+
+has 'sh_gui' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_SharedUGUIInitialize'
+);
+
+sub _SharedUGUIInitialize {
+ my $self = shift();
+
+ $self->sh_gui(ManaTools::Shared::GUI->new() );
+}
+
+has 'loc' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_localeInitialize'
+);
+
+
+sub _localeInitialize {
+ my $self = shift();
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'userdrake') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+
+#=============================================================
+
+=head1 METHODS
+
+=cut
+
+=head2 new - additional parameters
+
+=head3 config_file
+
+ optional parameter to set the configuration file name
+
+=cut
+
+has 'config_file' => (
+ is => 'rw',
+ isa => 'Str',
+ default => '/etc/sysconfig/manauser',
+);
+
+
+
+#=============================================================
+
+=head2 start
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method extends Module::start and is invoked to
+ start adminService
+
+=cut
+
+#=============================================================
+sub start {
+ my $self = shift;
+
+ $self->_manageUsersDialog();
+};
+
+# TODO move to Shared?
+sub _labeledFrameBox {
+ my ($parent, $label) = @_;
+
+ my $factory = yui::YUI::widgetFactory;
+
+ my $frame = $factory->createFrame($parent, $label);
+ $frame->setWeight( $yui::YD_HORIZ, 1);
+ $frame->setWeight( $yui::YD_VERT, 2);
+ $frame = $factory->createHVCenter( $frame );
+ $frame = $factory->createVBox( $frame );
+ return $frame;
+}
+
+# usefull local variable to avoid duplicating
+# translation point for user edit labels
+my %userEditLabel;
+# usefull local variable to avoid duplicating
+# translation point for group edit labels
+my %groupEditLabel;
+
+
+#=============================================================
+
+=head2 BUILD
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ The BUILD method is called after a Moose object is created,
+ Into this method additional data are initialized.
+
+=cut
+
+#=============================================================
+sub BUILD {
+ my $self = shift;
+
+ if (! $self->name) {
+ $self->name ($self->loc->N("manauser"));
+ }
+
+ %userEditLabel = (
+ user_data => $self->loc->N("User Data"),
+ account_info => $self->loc->N("Account Info"),
+ password_info => $self->loc->N("Password Info"),
+ groups => $self->loc->N("Groups"),
+ );
+ %groupEditLabel = (
+ group_data => $self->loc->N("Group Data"),
+ group_users => $self->loc->N("Group Users"),
+ );
+}
+
+
+#=============================================================
+
+=head2 ChooseGroup
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 OUTPUT
+
+ $choice: 0 or 1 (choice)
+ -1 cancel or exit
+
+=head3 DESCRIPTION
+
+creates a popup dialog to ask if adding user to an existing
+group or to the 'users' group
+
+=cut
+
+#=============================================================
+sub ChooseGroup {
+ my $self = shift;
+
+ my $choice = -1;
+
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($self->loc->N("Choose group"));
+
+ my $factory = yui::YUI::widgetFactory;
+
+ my $dlg = $factory->createPopupDialog();
+ my $layout = $factory->createVBox($dlg);
+
+
+ my $frame = _labeledFrameBox($layout, $self->loc->N("A group with this name already exists. What would you like to do?"));
+
+ my $rbg = $factory->createRadioButtonGroup( $frame );
+ $frame = $factory->createVBox( $rbg );
+ my $align = $factory->createLeft($frame);
+
+ my $rb1 = $factory->createRadioButton( $align, $self->loc->N("Add to the existing group"), 1);
+ $rb1->setNotify(1);
+ $rbg->addRadioButton( $rb1 );
+ $align = $factory->createLeft($frame);
+ my $rb2 = $factory->createRadioButton( $align, $self->loc->N("Add to the 'users' group"), 0);
+ $rb2->setNotify(1);
+ $rbg->addRadioButton( $rb2 );
+
+ my $hbox = $factory->createHBox($layout);
+ $align = $factory->createRight($hbox);
+ my $cancelButton = $factory->createPushButton($align, $self->loc->N("Cancel"));
+ my $okButton = $factory->createPushButton($hbox, $self->loc->N("Ok"));
+ while(1) {
+ my $event = $dlg->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ if ($widget == $okButton) {
+ $choice = $rb1->value() ? 0 : 1 ;
+ last;
+ }
+ }
+ }
+
+ destroy $dlg;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+
+ return $choice;
+}
+
+#=============================================================
+
+=head2 _deleteGroupDialog
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method open a dialog to delete the selected group.
+
+=cut
+
+#=============================================================
+sub _deleteGroupDialog {
+ my $self = shift;
+
+ my $item = $self->get_widget('table')->selectedItem();
+ if (! $item) {
+ return;
+ }
+
+ my $groupname = $item->label();
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($self->loc->N("Warning"));
+
+ my $factory = yui::YUI::widgetFactory;
+ my $dlg = $factory->createPopupDialog();
+ my $layout = $factory->createVBox($dlg);
+
+ my $align = $factory->createLeft($layout);
+
+ $factory->createLabel($align, $self->loc->N("Do you really want to delete the group %s?",
+ $groupname));
+
+ $align = $factory->createRight($layout);
+ my $hbox = $factory->createHBox($align);
+ my $cancelButton = $factory->createPushButton($hbox, $self->loc->N("Cancel"));
+ my $deleteButton = $factory->createPushButton($hbox, $self->loc->N("Delete"));
+
+ while(1) {
+ my $event = $dlg->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $deleteButton) {
+ my $username = $self->sh_users->isPrimaryGroup($groupname);
+ if (defined($username)) {
+ $self->sh_gui->msgBox({
+ text => $self->loc->N("%s is a primary group for user %s\n Remove the user first",
+ $groupname, $username
+ )
+ });
+ }
+ else {
+ if ($self->sh_users->deleteGroup($groupname)) {
+ Sys::Syslog::syslog('info|local1', $self->loc->N("Removing group: %s", $groupname));
+ }
+ $self->_refresh();
+ }
+ last;
+ }
+ }
+ }
+
+ destroy $dlg;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+}
+
+#=============================================================
+
+=head2 _deleteUserDialog
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method open a dialog to delete the selected user.
+ It also asks for additional information to be removed.
+
+=cut
+
+#=============================================================
+sub _deleteUserDialog {
+ my $self = shift;
+
+ my $item = $self->get_widget('table')->selectedItem();
+ if (! $item) {
+ return;
+ }
+ my $username = $item->label();
+
+ my $homedir = $self->sh_users->getUserHome($username);
+ return if !defined($homedir);
+
+
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($self->loc->N("Delete files or not?"));
+
+ my $factory = yui::YUI::widgetFactory;
+ my $dlg = $factory->createPopupDialog();
+ my $layout = $factory->createVBox($dlg);
+
+ my $align = $factory->createLeft($layout);
+ $factory->createLabel($align, $self->loc->N("Deleting user %s\nAlso perform the following actions\n",
+ $username));
+ $align = $factory->createLeft($layout);
+ my $checkhome = $factory->createCheckBox($align, $self->loc->N("Delete Home Directory: %s", $homedir), 0);
+ $align = $factory->createLeft($layout);
+ my $checkspool = $factory->createCheckBox($align, $self->loc->N("Delete Mailbox: /var/spool/mail/%s",
+ $username), 0);
+ $align = $factory->createRight($layout);
+ my $hbox = $factory->createHBox($align);
+ my $cancelButton = $factory->createPushButton($hbox, $self->loc->N("Cancel"));
+ my $deleteButton = $factory->createPushButton($hbox, $self->loc->N("Delete"));
+
+ if ($homedir !~ m!(?:/home|/var/spool)!) {
+ $checkhome->setDisabled();
+ $checkspool->setDisabled();
+ }
+
+
+ while(1) {
+ my $event = $dlg->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $deleteButton) {
+ Sys::Syslog::syslog('info|local1', $self->loc->N("Removing user: %s", $username));
+ my $option = undef;
+ $option->{clean_home} = $checkhome->isChecked() if $checkhome->isChecked();
+ $option->{clean_spool} = $checkspool->isChecked() if $checkspool->isChecked();
+
+ my $err = $self->sh_users->deleteUser($username, $option);
+ $self->sh_gui->msgBox({text => $err}) if (defined($err));
+
+ #remove added icon
+ $self->sh_users->removeKdmIcon($username);
+ $self->_refresh();
+ last;
+ }
+ }
+ }
+
+ destroy $dlg;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+
+}
+
+
+sub _addGroupDialog {
+ my $self = shift;
+
+ my $is_system = 0;
+
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($self->loc->N("Create New Group"));
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+ my $dlg = $factory->createPopupDialog();
+ my $layout = $factory->createVBox($dlg);
+
+ ## 'group name'
+ my $align = $factory->createRight($layout);
+ my $hbox = $factory->createHBox($align);
+ my $label = $factory->createLabel($hbox, $self->loc->N("Group Name:") );
+ my $groupName = $factory->createInputField($hbox, "", 0);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $groupName->setWeight($yui::YD_HORIZ, 2);
+
+ $factory->createVSpacing($layout, 1);
+
+ # Specify group id manually
+ $align = $factory->createLeft($layout);
+ $hbox = $factory->createHBox($align);
+ my $gidManually = $factory->createCheckBox($hbox, $self->loc->N("Specify group ID manually"), 0);
+ $factory->createHSpacing($hbox, 2);
+ my $GID = $factory->createIntField($hbox, $self->loc->N("GID"), 1, 65000, $self->sh_users->min_GID);
+ $GID->setEnabled($gidManually->value());
+ $gidManually->setNotify(1);
+
+ $hbox = $factory->createHBox($layout);
+ $align = $factory->createRight($hbox);
+ my $cancelButton = $factory->createPushButton($align, $self->loc->N("Cancel"));
+ my $okButton = $factory->createPushButton($hbox, $self->loc->N("Ok"));
+ while(1) {
+ my $event = $dlg->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $gidManually) {
+ # GID inserction enabled?
+ $GID->setEnabled($gidManually->value());
+ }
+ elsif ($widget == $okButton) {
+ ## check data
+ my $groupname = $groupName->value();
+ my ($continue, $errorString) = $self->sh_users->valid_groupname($groupname);
+ my $nm = $continue && $self->sh_users->groupNameExists($groupname);
+ if ($nm) {
+ $groupName->setValue("");
+ $errorString = $self->loc->N("Group already exists, please choose another Group Name");
+ $continue = 0;
+ }
+
+ my $gid = -1;
+ if ($continue && $gidManually->value()) {
+ if (($gid = $GID->value()) < $self->sh_users->min_GID) {
+ $errorString = "";
+ my $gidchoice = $self->sh_gui->ask_YesOrNo({ title => $self->loc->N(" Group Gid is < %n", $self->sh_users->min_GID),
+ text => $self->loc->N("Creating a group with a GID less than %d is not recommended.\n Are you sure you want to do this?\n\n",
+ $self->sh_users->min_GID
+ )
+ });
+ $continue = $gidchoice;
+ } else {
+ if ($self->sh_users->groupIDExists($gid)) {
+ $errorString = "";
+ my $gidchoice = $self->sh_gui->ask_YesOrNo({title => $self->loc->N(" Group ID is already used "),
+ text => $self->loc->N("Creating a group with a non unique GID?\n\n")});
+ $continue = $gidchoice;
+ }
+ }
+ }
+
+
+ if (!$continue) {
+ #--- raise error
+ $self->sh_gui->msgBox({text => $errorString}) if ($errorString);
+ }
+ else {
+ Sys::Syslog::syslog('info|local1', $self->loc->N("Adding group: %s ", $groupname));
+ my $groupParams = {
+ groupname => $groupname,
+ is_system => $is_system,
+ };
+ $groupParams->{gid} = $gid if $gid != -1;
+ $self->sh_users->addGroup($groupParams);
+ $self->_refresh();
+ last;
+ }
+ }
+ }
+ }
+ destroy $dlg;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+}
+
+
+#=============================================================
+
+=head2 _buildUserData
+
+=head3 INPUT
+
+ $self: this object
+ $layout : layout in wich drawing graphic user data
+
+=head3 OUTPUT
+
+ $userData: hash reference containing reference to graphical object
+ such as:
+ full_name, login_name, password, password1,
+ login_shell
+ full_name, login_name, password, password1,
+ weakness (icon), login_shell
+
+=head3 DESCRIPTION
+
+ This method is used by addUserDialog and _editUserDialog
+ to create User Data dialog
+=cut
+
+#=============================================================
+sub _buildUserData {
+ my ($self, $layout, $selected_shell) = @_;
+
+
+ my @shells = @{$self->sh_users->getUserShells()};
+
+ my $factory = yui::YUI::widgetFactory;
+
+ ## user 'full name'
+ my $align = $factory->createRight($layout);
+ my $hbox = $factory->createHBox($align);
+ my $label = $factory->createLabel($hbox, $self->loc->N("Full Name:") );
+ $factory->createHSpacing($hbox, 2.0);
+ my $fullName = $factory->createInputField($hbox, "", 0);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $fullName->setWeight($yui::YD_HORIZ, 2);
+
+ ## user 'login name'
+ $align = $factory->createRight($layout);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $self->loc->N("Login:") );
+ $factory->createHSpacing($hbox, 2.0);
+ my $loginName = $factory->createInputField($hbox, "", 0);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $loginName->setWeight($yui::YD_HORIZ, 2);
+ $loginName->setNotify(1);
+
+ ## user 'Password'
+ $align = $factory->createRight($layout);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $self->loc->N("Password:") );
+ my $weakness = undef;
+ if (yui::YUI::app()->hasImageSupport()) {
+ $factory->createHSpacing($hbox, 2.0);
+ my $file = File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/Blank16x16.png');
+ $weakness = $factory->createImage($hbox, $file);
+ }
+ else {
+ $factory->createHSpacing($hbox, 1.0);
+ $weakness = $factory->createLabel($hbox, " ");
+ $factory->createHSpacing($hbox, 1.0);
+ }
+ my $password = $factory->createInputField($hbox, "", 1);
+ $weakness->setWeight($yui::YD_HORIZ, 1);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $password->setWeight($yui::YD_HORIZ, 4);
+ # notify input to check weakness
+ $password->setNotify(1);
+
+ ## user 'confirm Password'
+ $align = $factory->createRight($layout);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $self->loc->N("Confirm Password:") );
+ $factory->createHSpacing($hbox, 2.0);
+ my $password1 = $factory->createInputField($hbox, "", 1);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $password1->setWeight($yui::YD_HORIZ, 2);
+
+ ## user 'Login Shell'
+ $align = $factory->createRight($layout);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $self->loc->N("Login Shell:") );
+ $factory->createHSpacing($hbox, 2.0);
+ my $loginShell = $factory->createComboBox($hbox, "", 0);
+ my $itemColl = new yui::YItemCollection;
+ foreach my $shell (@shells) {
+ my $item = new yui::YItem ($shell, 0);
+ $item->setSelected(1) if ($selected_shell && $selected_shell eq $shell);
+ $itemColl->push($item);
+ $item->DISOWN();
+ }
+ $loginShell->addItems($itemColl);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $loginShell->setWeight($yui::YD_HORIZ, 2);
+
+ my %userData = (
+ full_name => $fullName,
+ login_name => $loginName,
+ password => $password,
+ password1 => $password1,
+ weakness => $weakness,
+ login_shell => $loginShell,
+ );
+
+ return ( \%userData );
+}
+
+# get/set icon button name
+# if $icon_name is present it sets as "&Icon icon_name", so the shortcut is always the same
+# if $icon_name is not present it returns the previous set $label
+sub _iconButtonLabel {
+ my ($self, $icon_button, $icon_name) = @_;
+
+ state $label = "";
+
+ return if !$icon_button;
+ return if ref $icon_button ne "yui::YPushButton";
+
+ if ($icon_name) {
+ #set
+ $icon_button->setLabel($self->loc->N("&Icon (%s)", $icon_name));
+ $label = $icon_name;
+ }
+
+ return $label;
+}
+
+
+#=============================================================
+
+=head2 addUserDialog
+
+=head3 INPUT
+
+ $self: this object
+ $standalone: if set the application title is set
+ from the name set in costructor
+
+=head3 DESCRIPTION
+
+ This method creates and manages the dialog to add a new
+ user.
+
+=cut
+
+#=============================================================
+sub addUserDialog {
+ my $self = shift;
+ my $standalone = shift;
+
+ if ($EUID != 0) {
+ $self->sh_gui->warningMsgBox({
+ title => $self->name,
+ text => $self->loc->N("root privileges required"),
+ });
+ return;
+ }
+
+ my $dontcreatehomedir = 0;
+ my $is_system = 0;
+
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ if ($standalone) {
+ yui::YUI::app()->setApplicationTitle($self->name);
+ }
+ else {
+ yui::YUI::app()->setApplicationTitle($self->loc->N("Create New User"));
+ }
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+ my $dlg = $factory->createPopupDialog();
+ my $layout = $factory->createVBox($dlg);
+
+ my $userData = $self->_buildUserData($layout);
+
+ ##### add a separator
+ ## Create Home directory
+ my $align = $factory->createLeft($layout);
+ my $hbox = $factory->createHBox($align);
+ my $createHome = $factory->createCheckBox($hbox, $self->loc->N("Create Home Directory"), 1);
+ ## Home directory
+ $align = $factory->createLeft($layout);
+ $hbox = $factory->createHBox($align);
+ my $label = $factory->createLabel($hbox, $self->loc->N("Home Directory:") );
+ $factory->createHSpacing($hbox, 2.0);
+ my $homeDir = $factory->createInputField($hbox, "", 0);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $homeDir->setWeight($yui::YD_HORIZ, 2);
+
+ # Create private group
+ $align = $factory->createLeft($layout);
+ $hbox = $factory->createHBox($align);
+ my $createGroup = $factory->createCheckBox($hbox, $self->loc->N("Create a private group for the user"), 1);
+
+ # Specify user id manually
+ $align = $factory->createRight($layout);
+ $hbox = $factory->createHBox($align);
+ my $uidManually = $factory->createCheckBox($hbox, $self->loc->N("Specify user ID manually"), 0);
+ $factory->createHSpacing($hbox, 2.0);
+ my $UID = $factory->createIntField($hbox, $self->loc->N("UID"), 1, 65000, $self->sh_users->min_UID);
+ $UID->setEnabled($uidManually->value());
+ $uidManually->setNotify(1);
+# $uidManually->setWeight($yui::YD_HORIZ, 2);
+ $UID->setWeight($yui::YD_HORIZ, 1);
+
+ ## user 'icon'
+ $hbox = $factory->createHBox($layout);
+ $factory->createLabel($hbox, $self->loc->N("Click on icon to change it") );
+ my $iconFace = $self->sh_users->GetFaceIcon();
+ my $icon = $factory->createPushButton($hbox, "");
+ $icon->setIcon($self->sh_users->face2png($iconFace));
+ $self->_iconButtonLabel($icon, $iconFace);
+
+ $hbox = $factory->createHBox($layout);
+ $align = $factory->createRight($hbox);
+ my $cancelButton = $factory->createPushButton($align, $self->loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($hbox, $self->loc->N("&Ok"));
+
+ while(1) {
+ my $event = $dlg->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $icon) {
+ #remove shortcut from label
+ my $iconLabel = $self->_iconButtonLabel($icon);
+ my $nextIcon = $self->sh_users->GetFaceIcon($iconLabel, 1);
+ $self->_iconButtonLabel($icon, $nextIcon);
+ $icon->setIcon($self->sh_users->face2png($nextIcon));
+ }
+ elsif ($widget == $uidManually) {
+ # UID inserction enabled?
+ $UID->setEnabled($uidManually->value());
+ }
+ elsif ($widget == $userData->{ login_name }) {
+ my $username = $userData->{ login_name }->value();
+ $homeDir->setValue("/home/$username");
+ }
+ elsif ($widget == $userData->{password}) {
+ my $pass = $userData->{ password }->value();
+ $self->_checkWeaknessPassword($pass, $userData->{ weakness });
+ }
+ elsif ($widget == $okButton) {
+ ## check data
+ my $username = $userData->{ login_name }->value();
+ my ($continue, $errorString) = $self->sh_users->valid_username($username);
+ my $nm = $continue && $self->sh_users->userNameExists($username);
+ if ($nm) {
+ $userData->{ login_name }->setValue("");
+ $homeDir->setValue("");
+ $errorString = $self->loc->N("User already exists, please choose another User Name");
+ $continue = 0;
+ }
+ my $passwd = $continue && $userData->{ password }->value();
+ if ($continue && $passwd ne $userData->{ password1 }->value()) {
+ $errorString = $self->loc->N("Password Mismatch");
+ $continue = 0;
+ }
+ if ($self->sh_users->weakPasswordForSecurityLevel($passwd)) {
+ $errorString = $self->loc->N("This password is too simple. \n Good passwords should be > 6 characters");
+ $continue = 0;
+ }
+ my $homedir;
+ if ($continue && $createHome->value()) {
+ $homedir = $homeDir->value();
+ if ( -d $homedir) {
+ $errorString = $self->loc->N("Home directory <%s> already exists.\nPlease uncheck the home creation option, or change the directory path name", $homedir);
+ $continue = 0;
+ }
+ else {
+ $dontcreatehomedir = 0;
+ }
+ } else {
+ $dontcreatehomedir = 1;
+ }
+ my $uid = -1;
+ if ($continue && $uidManually->value()) {
+ if (($uid = $UID->value()) < $self->sh_users->min_UID) {
+ $errorString = "";
+ my $uidchoice = $self->sh_gui->ask_YesOrNo({title => $self->loc->N("User Uid is < %d", $self->sh_users->min_UID),
+ text => $self->loc->N("Creating a user with a UID less than %d is not recommended.\nAre you sure you want to do this?\n\n", $self->sh_users->min_UID)});
+ $continue = $uidchoice;
+ }
+ }
+ my $gid = undef;
+ if ($createGroup->value()) {
+ if ($continue) {
+ #Check if group exist
+ if ($self->sh_users->groupNameExists($username)) {
+ my $groupchoice = $self->ChooseGroup();
+ if ($groupchoice == 0 ) {
+ #You choose to put it in the existing group
+ $gid = $self->sh_users->groupID($username);
+ } elsif ($groupchoice == 1) {
+ # Put it in 'users' group
+ Sys::Syslog::syslog('info|local1', $self->loc->N("Putting %s to 'users' group",
+ $username));
+ $gid = $self->sh_users->Add2UsersGroup($username);
+ }
+ else {
+ $errorString = "";
+ $continue = 0;
+ }
+ } else {
+ #it's a new group: Add it
+ $gid = $self->sh_users->addGroup({
+ groupname => $username,
+ is_system => $is_system,
+ });
+ Sys::Syslog::syslog('info|local1', $self->loc->N("Creating new group: %s", $username));
+ }
+ }
+ } else {
+ $continue and $gid = $self->sh_users->Add2UsersGroup($username);
+ }
+
+ if (!$continue) {
+ #---rasie error
+ $self->sh_gui->msgBox({text => $errorString}) if ($errorString);
+ }
+ else {
+ ## OK let's create the user
+ print $self->loc->N("Adding user: ") . $username . " \n";
+ Sys::Syslog::syslog('info|local1', $self->loc->N("Adding user: %s", $username));
+ my $loginshell = $userData->{ login_shell }->value();
+ my $fullname = $userData->{ full_name }->value();
+ utf8::decode($fullname);
+
+ my $userParams = {
+ username => $username,
+ is_system => $is_system,
+ donotcreatehome => $dontcreatehomedir,
+ shell => $loginshell,
+ fullname => $fullname,
+ gid => $gid,
+ password => $passwd,
+ };
+ $userParams->{uid} = $uid if $uid != -1;
+ $userParams->{homedir} = $homedir if !$dontcreatehomedir;
+ $self->sh_users->addUser($userParams);
+
+ defined $icon->label() and
+ $self->sh_users->addKdmIcon($username, $icon->label());
+### TODO Migration wizard
+#
+# Refresh($sysfilter, $stringsearch);
+# transfugdrake::get_windows_disk()
+# and $in->ask_yesorno($self->loc->N("Migration wizard"),
+# $self->loc->N("Do you want to run the migration wizard in order to import Windows documents and settings in your Mageia distribution?"))
+# and run_program::raw({ detach => 1 }, 'transfugdrake');
+
+
+ last;
+ }
+ }
+ }
+ }
+
+ destroy $dlg;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+}
+
+#=============================================================
+
+=head2 _createUserTable
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+This function create the User table to be added to the replace
+point of the tab widget. Note this function is meant for internal
+use only
+
+=cut
+
+#=============================================================
+sub _createUserTable {
+ my $self = shift;
+
+ $self->dialog->startMultipleChanges();
+ $self->get_widget('replace_pnt')->deleteChildren();
+ my $parent = $self->get_widget('replace_pnt');
+ my $factory = yui::YUI::widgetFactory;
+ my $yTableHeader = new yui::YTableHeader();
+ $yTableHeader->addColumn($self->loc->N("User Name"), $yui::YAlignBegin);
+ $yTableHeader->addColumn($self->loc->N("User ID"), $yui::YAlignBegin);
+ $yTableHeader->addColumn($self->loc->N("Primary Group"), $yui::YAlignBegin);
+ $yTableHeader->addColumn($self->loc->N("Full Name"), $yui::YAlignBegin);
+ $yTableHeader->addColumn($self->loc->N("Login Shell"), $yui::YAlignBegin);
+ $yTableHeader->addColumn($self->loc->N("Home Directory"), $yui::YAlignBegin);
+ $yTableHeader->addColumn($self->loc->N("Status"), $yui::YAlignBegin);
+ $yTableHeader->DISOWN();
+
+ $self->set_widget(table => $factory->createTable($parent, $yTableHeader));
+
+ $self->get_widget('table')->setImmediateMode(1);
+ $self->get_widget('table')->DISOWN();
+ $self->get_widget('replace_pnt')->showChild();
+ $self->dialog->recalcLayout();
+ $self->dialog->doneMultipleChanges();
+ $self->_refreshUsers();
+}
+
+#=============================================================
+
+=head2 _createGroupTable
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+This function create the Group table to be added to the replace
+point of the tab widget. Note this function is meant for internal
+use only
+
+
+=cut
+
+#=============================================================
+sub _createGroupTable {
+ my $self = shift;
+
+
+ $self->dialog->startMultipleChanges();
+ $self->get_widget('replace_pnt')->deleteChildren();
+ my $parent = $self->get_widget('replace_pnt');
+ my $factory = yui::YUI::widgetFactory;
+ my $yTableHeader = new yui::YTableHeader();
+ $yTableHeader->addColumn($self->loc->N("Group Name"), $yui::YAlignBegin);
+ $yTableHeader->addColumn($self->loc->N("Group ID"), $yui::YAlignBegin);
+ $yTableHeader->addColumn($self->loc->N("Group Members"), $yui::YAlignBegin);
+ $yTableHeader->DISOWN();
+
+ $self->set_widget(table => $factory->createTable($parent, $yTableHeader));
+
+ $self->get_widget('table')->setImmediateMode(1);
+ $self->get_widget('table')->DISOWN();
+ $self->get_widget('replace_pnt')->showChild();
+ $self->dialog->recalcLayout();
+ $self->dialog->doneMultipleChanges();
+ $self->_refreshGroups();
+}
+
+
+
+
+#=============================================================
+
+=head2 _refreshUsers
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method refresh user info into User tab widget.
+ Note this function is meant for internal use only
+
+=cut
+
+#=============================================================
+sub _refreshUsers {
+ my $self = shift;
+
+ my $strfilt = $self->get_widget('filter')->value();
+ my $filterusers = $self->get_widget('filter_system')->isChecked();
+
+ my $usersInfo = $self->sh_users->getUsersInfo({
+ username_filter => $strfilt,
+ filter_system => $filterusers,
+ });
+
+
+ $self->dialog->startMultipleChanges();
+ #for some reasons QT send an event using table->selectItem()
+ # WA remove notification immediate
+ $self->get_widget('table')->setImmediateMode(0);
+ $self->get_widget('table')->deleteAllItems();
+
+ my $itemColl = new yui::YItemCollection;
+ foreach my $username (keys %{$usersInfo}) {
+ my $info = $usersInfo->{$username};
+ my $item = new yui::YTableItem (
+ "$username",
+ "$info->{uid}",
+ "$info->{group}",
+ "$info->{fullname}",
+ "$info->{shell}",
+ "$info->{home}",
+ "$info->{status}"
+ );
+
+ # TODO workaround to get first cell at least until we don't
+ # a cast from YItem
+ $item->setLabel( $username );
+ $itemColl->push($item);
+ $item->DISOWN();
+ }
+
+ $self->get_widget('table')->addItems($itemColl);
+ my $item = $self->get_widget('table')->selectedItem();
+ $self->get_widget('table')->selectItem($item, 0) if $item;
+ $self->dialog->recalcLayout();
+ $self->dialog->doneMultipleChanges();
+ $self->_refreshActions();
+ $self->get_widget('table')->setImmediateMode(1);
+}
+
+#=============================================================
+
+=head2 _refreshGroups
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ This method refresh group info into Group tab widget.
+ Note this function is meant for internal use only
+
+=cut
+
+#=============================================================
+sub _refreshGroups {
+ my $self = shift;
+
+ my $strfilt = $self->get_widget('filter')->value();
+ my $filtergroups = $self->get_widget('filter_system')->isChecked();
+
+ $self->dialog->startMultipleChanges();
+ #for some reasons QT send an event using table->selectItem()
+ # WA remove notification immediate
+ $self->get_widget('table')->setImmediateMode(0);
+ $self->get_widget('table')->deleteAllItems();
+
+ my $groupInfo = $self->sh_users->getGroupsInfo({
+ groupname_filter => $strfilt,
+ filter_system => $filtergroups,
+ });
+
+ my $itemColl = new yui::YItemCollection;
+ foreach my $groupname (keys %{$groupInfo}) {
+ my $info = $groupInfo->{$groupname};
+ my $listUbyG = join(',', @{$info->{members}});
+ my $item = new yui::YTableItem ("$groupname",
+ "$info->{gid}",
+ "$listUbyG");
+ $item->setLabel( $groupname );
+ $itemColl->push($item);
+ $item->DISOWN();
+ }
+
+ $self->get_widget('table')->addItems($itemColl);
+ my $item = $self->get_widget('table')->selectedItem();
+ $self->get_widget('table')->selectItem($item, 0) if $item;
+ $self->dialog->recalcLayout();
+ $self->dialog->doneMultipleChanges();
+ $self->_refreshActions();
+ $self->get_widget('table')->setImmediateMode(1);
+}
+
+
+#=============================================================
+
+=head2 _getUserInfo
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 OUTPUT
+
+ $userData: HASH reference containing:
+ username: username
+ full_name: full name of user
+ shell: shell used
+ homedir: home dir path
+ UID: User identifier
+ acc_check_exp: account expiration enabling
+ acc_expy: account expiration year
+ acc_expm: account expiration month
+ acc_expd: account expiration day
+ lockuser: account locked
+ pwd_check_exp: password expiration enabling
+ pwd_exp_min: days before changing password
+ is allowed
+ pwd_exp_max: days before changing password
+ is required
+ pwd_exp_warn: warning days before changing
+ pwd_exp_inact: days before account becomes
+ inact
+ members: Array containing groups the user
+ belongs to.
+ primary_group: primary group ID for the user
+
+=head3 DESCRIPTION
+
+ Retrieves the selected user info from the system
+ Note that acc_expy, acc_expm and acc_expd are valid if
+ acc_check_exp is enabled.
+ Note that pwd_exp_min, pwd_exp_max, pwd_exp_warn,
+ pwd_exp_inact are valid if pwd_check_exp is enabled.
+
+=cut
+
+#=============================================================
+
+sub _getUserInfo {
+ my $self = shift;
+
+ my $label = $self->_skipShortcut($self->get_widget('tabs')->selectedItem()->label());
+ if ($label ne $self->loc->N("Users") ) {
+ return undef;
+ }
+
+ my $item = $self->get_widget('table')->selectedItem();
+ if (! $item) {
+ return undef;
+ }
+
+ my %userData;
+ $userData{old_username} = $item->label();
+ $userData{username} = $item->label();
+
+ my $userInfo = $self->sh_users->getUserInfo($userData{username});
+
+ $userData{full_name} = $userInfo->{fullname};
+ $userData{shell} = $userInfo->{shell};
+ $userData{homedir} = $userInfo->{home};
+ $userData{UID} = $userInfo->{uid};
+
+ # default expiration time
+ my ($day, $mo, $ye) = (localtime())[3, 4, 5];
+ $userData{acc_expy} = $ye+1900;
+ $userData{acc_expm} = $mo+1;
+ $userData{acc_expd} = $day;
+ $userData{acc_check_exp} = 0;
+
+ my $expire = $userInfo->{expire};
+ if ($expire && $expire != -1) {
+ my $times = _TimeOfArray($expire, 1);
+ $userData{acc_expy} = $times->{year};
+ $userData{acc_expm} = $times->{month};
+ $userData{acc_expd} = $times->{dayint};
+ $userData{acc_check_exp} = 1;
+ }
+
+ # user password are not retrieved if admin wants
+ # to change it has to insert a new one
+ $userData{password} = undef;
+ $userData{password1} = undef;
+
+ $userData{lockuser} = $userInfo->{locked};
+ $userData{icon_face} = $self->sh_users->GetFaceIcon($userData{username});
+
+ $userData{pwd_check_exp} = 0;
+ $userData{pwd_exp_min} = $userInfo->{exp_min};
+ $userData{pwd_exp_max} = $userInfo->{exp_max};
+ $userData{pwd_exp_warn} = $userInfo->{exp_warn};
+ $userData{pwd_exp_inact} = $userInfo->{exp_inact};
+ if ($userData{pwd_exp_min} && $userData{pwd_exp_min} != -1 ||
+ $userData{pwd_exp_max} && $userData{pwd_exp_max} != 99999 ||
+ $userData{pwd_exp_warn} && $userData{pwd_exp_warn} != 7 && $userData{pwd_exp_warn} != -1 ||
+ $userData{pwd_exp_inact} && $userData{pwd_exp_inact} != -1) {
+ $userData{pwd_check_exp} = 1;
+ }
+
+ $userData{members} = $userInfo->{members};
+ $userData{primary_group} = $userInfo->{gid};
+
+ return \%userData;
+}
+
+#=============================================================
+
+=head2 _getUserInfo
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 OUTPUT
+
+ %groupData: selected group info as:
+ $groupname: group name
+ $members: users that are members of this group
+
+=head3 DESCRIPTION
+
+ Retrieves the selected group info from the system
+
+=cut
+
+#=============================================================
+
+sub _getGroupInfo {
+ my $self = shift;
+
+ my $label = $self->_skipShortcut($self->get_widget('tabs')->selectedItem()->label());
+ if ($label ne $self->loc->N("Groups") ) {
+ return undef;
+ }
+
+ my $item = $self->get_widget('table')->selectedItem();
+ if (! $item) {
+ return undef;
+ }
+
+ my %groupData;
+ $groupData{start_groupname} = $item->label();
+ $groupData{groupname} = $item->label();
+
+ $groupData{members} = $self->sh_users->groupMembers($groupData{groupname});
+
+ return %groupData;
+
+}
+
+sub _storeDataFromGroupEditPreviousTab {
+ my ($self, %groupData) = @_;
+
+ my $previus_tab = $self->get_edit_tab_widget('edit_tab_label');
+ if (!$previus_tab) {
+ return %groupData;
+ }
+ elsif ($previus_tab eq $groupEditLabel{group_data}) {
+ $groupData{groupname} = $self->get_edit_tab_widget('groupname')->value();
+ }
+ elsif ($previus_tab eq $groupEditLabel{group_users}) {
+ my $tbl = $self->get_edit_tab_widget('members');
+ $groupData{members} = undef;
+ my @members;
+ my $i;
+ for($i=0;$i<$tbl->itemsCount();$i++) {
+ push (@members, $tbl->item($i)->label()) if $tbl->toCBYTableItem($tbl->item($i))->checked();
+ }
+ $groupData{members} = [ @members ];
+ }
+
+ return %groupData;
+}
+
+
+sub _storeDataFromUserEditPreviousTab {
+ my ($self, $userData) = @_;
+
+ my $previus_tab = $self->get_edit_tab_widget('edit_tab_label');
+ if (!$previus_tab) {
+ return $userData;
+ }
+ elsif ($previus_tab eq $userEditLabel{user_data}) {
+ $userData->{full_name} = $self->get_edit_tab_widget('full_name')->value();
+ $userData->{username} = $self->get_edit_tab_widget('login_name')->value() ;
+ $userData->{shell} = $self->get_edit_tab_widget('login_shell')->value();
+ $userData->{homedir} = $self->get_edit_tab_widget('homedir')->value();
+ my $passwd = $self->get_edit_tab_widget('password')->value();
+ $userData->{password} = $passwd;
+ $passwd = $self->get_edit_tab_widget('password1')->value();
+ $userData->{password1} = $passwd;
+ }
+ elsif ($previus_tab eq $userEditLabel{account_info}) {
+ $userData->{acc_check_exp} = $self->get_edit_tab_widget('acc_check_exp')->value();
+ $userData->{acc_expy} = $self->get_edit_tab_widget('acc_expy')->value();
+ $userData->{acc_expm} = $self->get_edit_tab_widget('acc_expm')->value();
+ $userData->{acc_expd} = $self->get_edit_tab_widget('acc_expd')->value();
+ $userData->{lockuser} = $self->get_edit_tab_widget('lockuser')->value();
+ $userData->{icon_face} = $self->_iconButtonLabel($self->get_edit_tab_widget('icon_face'));
+ }
+ elsif ($previus_tab eq $userEditLabel{password_info}) {
+ $userData->{pwd_check_exp} = $self->get_edit_tab_widget('pwd_check_exp')->value();
+ $userData->{pwd_exp_min} = $self->get_edit_tab_widget('pwd_exp_min')->value();
+ $userData->{pwd_exp_max} = $self->get_edit_tab_widget('pwd_exp_max')->value();
+ $userData->{pwd_exp_warn} = $self->get_edit_tab_widget('pwd_exp_warn')->value();
+ $userData->{pwd_exp_inact} = $self->get_edit_tab_widget('pwd_exp_inact')->value();
+ }
+ elsif ($previus_tab eq $userEditLabel{groups}) {
+ my $tbl = $self->get_edit_tab_widget('members');
+ $userData->{members} = undef;
+ my @members;
+ my $i;
+ for($i=0;$i<$tbl->itemsCount();$i++) {
+ push (@members, $tbl->item($i)->label()) if $tbl->toCBYTableItem($tbl->item($i))->checked();
+ }
+ $userData->{members} = \@members;
+
+ if ($self->get_edit_tab_widget('primary_group')->selectedItem()) {
+ my $groupname = $self->get_edit_tab_widget('primary_group')->selectedItem()->label();
+ my $primgroup = $self->sh_users->groupID($groupname);
+ $userData->{primary_group} = $primgroup;
+ }
+ else {
+ $userData->{primary_group} = -1;
+ }
+ }
+
+ return $userData;
+}
+
+#=============================================================
+
+=head2 _userDataTabWidget
+
+=head3 INPUT
+
+ $self: this object
+ $dialog: YUI dialog that owns the YUI replace point
+ $replace_pnt: YUI replace point, needed to add a new tab
+ widget
+ $userData: hash reference containing user data info, tabs are
+ removed and added again on selection, so
+ data must be saved outside of widgets.
+
+=head3 OUTPUT
+
+ $userDataWidget: hash containing new YUI widget objects
+ such as:
+ returned onject from _buildUserData and
+ homedir.
+
+=head3 DESCRIPTION
+
+ This internal method removes old tab widget saving its
+ relevant data into userData and creates new selected table
+ to be shown.
+
+=cut
+
+#=============================================================
+sub _userDataTabWidget {
+ my ($self, $dialog, $replace_pnt, $userData) = @_;
+
+ my $factory = yui::YUI::widgetFactory;
+
+ $dialog->startMultipleChanges();
+
+ $replace_pnt->deleteChildren();
+ my $layout = $factory->createVBox($replace_pnt);
+ my $userDataWidget = $self->_buildUserData($layout, $userData->{shell});
+
+ ## user 'login name'
+ my $align = $factory->createRight($layout);
+ my $hbox = $factory->createHBox($align);
+ my $label = $factory->createLabel($hbox, $self->loc->N("Home:") );
+ $factory->createHSpacing($hbox, 2.0);
+ $userDataWidget->{homedir} = $factory->createInputField($hbox, "", 0);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $userDataWidget->{homedir}->setWeight($yui::YD_HORIZ, 2);
+
+ # fill data into widgets
+ ##
+ # full_name, login_name, password, password1,
+ # login_shell
+ $userDataWidget->{full_name}->setValue($userData->{full_name});
+ $userDataWidget->{login_name}->setValue($userData->{username});
+ yui::YUI::ui()->blockEvents();
+ $userDataWidget->{password}->setValue($userData->{password}) if $userData->{password};
+ yui::YUI::ui()->unblockEvents();
+ $userDataWidget->{password1}->setValue($userData->{password1}) if $userData->{password1};
+ $userDataWidget->{homedir}->setValue($userData->{homedir});
+
+ $replace_pnt->showChild();
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+
+ return $userDataWidget;
+}
+
+
+#=============================================================
+
+=head2 _groupDataTabWidget
+
+=head3 INPUT
+
+ $self: this object
+ $dialog: YUI dialog that owns the YUI replace point
+ $replace_pnt: YUI replace point, needed to add a new tab
+ widget
+ %groupData: hash containing group data info, tabs are
+ removed and added again on selection, so
+ data must be saved outside of widgets.
+ $previus_tab: previous tab widget label, needed to store
+ group data from the old tab before removing
+ it, if user changed something.
+
+=head3 OUTPUT
+
+ %groupDataWidget: hash containing new YUI widget objects
+ such as:
+ groupname.
+
+=head3 DESCRIPTION
+
+ This internal method removes old tab widget saving its
+ relevant data into groupData and creates new selected table
+ to be shown.
+
+=cut
+
+#=============================================================
+sub _groupDataTabWidget {
+ my ($self, $dialog, $replace_pnt, %groupData) = @_;
+
+ my $factory = yui::YUI::widgetFactory;
+
+ $dialog->startMultipleChanges();
+
+ $replace_pnt->deleteChildren();
+ my $layout = $factory->createVBox($replace_pnt);
+
+ my %groupDataWidget;
+
+ ## user 'login name'
+ my $align = $factory->createRight($layout);
+ my $hbox = $factory->createHBox($align);
+ my $label = $factory->createLabel($hbox, $self->loc->N("Group Name:") );
+ $groupDataWidget{groupname} = $factory->createInputField($hbox, "", 0);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $groupDataWidget{groupname}->setWeight($yui::YD_HORIZ, 2);
+
+ $groupDataWidget{groupname}->setValue($groupData{groupname});
+
+ $replace_pnt->showChild();
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+
+ return %groupDataWidget;
+}
+
+
+sub _userAccountInfoTabWidget {
+ my ($self, $dialog, $replace_pnt, $userData) = @_;
+
+ my $factory = yui::YUI::widgetFactory;
+
+ $dialog->startMultipleChanges();
+
+ $replace_pnt->deleteChildren();
+ my $layout = $factory->createVBox($replace_pnt);
+
+ my %userAccountWidget;
+ $userAccountWidget{acc_check_exp} = $factory->createCheckBoxFrame($layout, $self->loc->N("Enable account expiration"), 1);
+ my $align = $factory->createRight($userAccountWidget{acc_check_exp});
+ my $hbox = $factory->createHBox($align);
+ my $label = $factory->createLabel($hbox, $self->loc->N("Account expires (YYYY-MM-DD):"));
+ $userAccountWidget{acc_expy} = $factory->createIntField($hbox, "", 1970, 9999, $userData->{acc_expy});
+ $userAccountWidget{acc_expm} = $factory->createIntField($hbox, "", 1, 12, $userData->{acc_expm});
+ $userAccountWidget{acc_expd} = $factory->createIntField($hbox, "", 1, 31, $userData->{acc_expd});
+ $userAccountWidget{acc_check_exp}->setValue($userData->{acc_check_exp});
+ $label->setWeight($yui::YD_HORIZ, 2);
+ $align = $factory->createLeft($layout);
+ $userAccountWidget{lockuser} = $factory->createCheckBox($align, $self->loc->N("Lock User Account"), $userData->{lockuser});
+
+ $align = $factory->createLeft($layout);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $self->loc->N("Click on the icon to change it"));
+ $userAccountWidget{icon_face} = $factory->createPushButton($hbox, "");
+ $userAccountWidget{icon_face}->setIcon($self->sh_users->face2png($userData->{icon_face}));
+ $self->_iconButtonLabel($userAccountWidget{icon_face}, $userData->{icon_face});
+
+ $replace_pnt->showChild();
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+
+ return \%userAccountWidget;
+}
+
+
+sub _userPasswordInfoTabWidget {
+ my ($self, $dialog, $replace_pnt, $userData) = @_;
+
+ my $factory = yui::YUI::widgetFactory;
+
+ $dialog->startMultipleChanges();
+
+ $replace_pnt->deleteChildren();
+ my $layout = $factory->createVBox($replace_pnt);
+
+ my %userPasswordWidget;
+
+ my $userInfo = $self->sh_users->getUserInfo($userData->{username});
+ my $lastchg = $userInfo->{last_change};
+
+ my $align = $factory->createLeft($layout);
+ my $hbox = $factory->createHBox($align);
+ my $label = $factory->createLabel($hbox, $self->loc->N("User last changed password on: "));
+ my $dayStr = $factory->createLabel($hbox, "");
+ my $month = $factory->createLabel($hbox, "");
+ my $dayInt = $factory->createLabel($hbox, "");
+ my $year = $factory->createLabel($hbox, "");
+ if ($lastchg) {
+ my $times = _TimeOfArray($lastchg, 0);
+ $dayStr->setValue($times->{daystr});
+ $month->setValue($times->{month});
+ $dayInt->setValue($times->{dayint});
+ $year->setValue($times->{year});
+ }
+
+ $userPasswordWidget{pwd_check_exp} = $factory->createCheckBoxFrame($layout, $self->loc->N("Enable Password Expiration"), 1);
+ $layout = $factory->createVBox($userPasswordWidget{pwd_check_exp});
+ $align = $factory->createLeft($layout);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $self->loc->N("Days before change allowed:"));
+ $userPasswordWidget{pwd_exp_min} = $factory->createInputField($hbox, "", 0);
+ $userPasswordWidget{pwd_exp_min}->setValue("$userData->{pwd_exp_min}");
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $userPasswordWidget{pwd_exp_min}->setWeight($yui::YD_HORIZ, 2);
+
+ $align = $factory->createLeft($layout);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $self->loc->N("Days before change required:"));
+ $userPasswordWidget{pwd_exp_max} = $factory->createInputField($hbox, "", 0);
+ $userPasswordWidget{pwd_exp_max}->setValue("$userData->{pwd_exp_max}");
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $userPasswordWidget{pwd_exp_max}->setWeight($yui::YD_HORIZ, 2);
+
+ $align = $factory->createLeft($layout);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $self->loc->N("Days warning before change:"));
+ $userPasswordWidget{pwd_exp_warn} = $factory->createInputField($hbox, "", 0);
+ $userPasswordWidget{pwd_exp_warn}->setValue("$userData->{pwd_exp_warn}");
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $userPasswordWidget{pwd_exp_warn}->setWeight($yui::YD_HORIZ, 2);
+
+ $align = $factory->createLeft($layout);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $self->loc->N("Days before account inactive:"));
+ $userPasswordWidget{pwd_exp_inact} = $factory->createInputField($hbox, "", 0);
+ $userPasswordWidget{pwd_exp_inact}->setValue("$userData->{pwd_exp_inact}");
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $userPasswordWidget{pwd_exp_inact}->setWeight($yui::YD_HORIZ, 2);
+
+ $userPasswordWidget{pwd_check_exp}->setValue($userData->{pwd_check_exp});
+
+ $replace_pnt->showChild();
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+
+ return \%userPasswordWidget;
+}
+
+sub _groupUsersTabWidget {
+ my ($self, $dialog, $replace_pnt, %groupData) = @_;
+
+ my $factory = yui::YUI::widgetFactory;
+ my $mageiaPlugin = "mga";
+ my $mgaFactory = yui::YExternalWidgets::externalWidgetFactory($mageiaPlugin);
+ $mgaFactory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($mgaFactory);
+
+ $dialog->startMultipleChanges();
+
+ $replace_pnt->deleteChildren();
+
+ my %groupUsersWidget;
+
+ my $layout = _labeledFrameBox($replace_pnt, $self->loc->N("Select the users to join this group:"));
+
+ my $yTableHeader = new yui::YTableHeader();
+ $yTableHeader->addColumn("", $yui::YAlignBegin);
+ $yTableHeader->addColumn($self->loc->N("User"), $yui::YAlignBegin);
+
+ $groupUsersWidget{members} = $mgaFactory->createCBTable($layout, $yTableHeader, $yui::YCBTableCheckBoxOnFirstColumn);
+
+ my $users = $self->sh_users->getUsers();
+ my @susers = sort(@$users);
+
+ my $itemCollection = new yui::YItemCollection;
+ my $members = $groupData{members};
+ foreach my $user (@susers) {
+ my $item = new yui::YCBTableItem($user);
+ $item->check(MDK::Common::DataStructure::member($user, @$members));
+ $item->setLabel($user);
+ $itemCollection->push($item);
+ $item->DISOWN();
+ }
+ $groupUsersWidget{members}->addItems($itemCollection);
+
+ $replace_pnt->showChild();
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+
+ return %groupUsersWidget;
+}
+
+sub _userGroupsTabWidget {
+ my ($self, $dialog, $replace_pnt, $userData) = @_;
+
+ my $factory = yui::YUI::widgetFactory;
+ my $mageiaPlugin = "mga";
+ my $mgaFactory = yui::YExternalWidgets::externalWidgetFactory($mageiaPlugin);
+ $mgaFactory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($mgaFactory);
+
+ $dialog->startMultipleChanges();
+
+ $replace_pnt->deleteChildren();
+
+ my %userGroupsWidget;
+ my $userInfo = $self->sh_users->getUserInfo($userData->{username});
+ my $lastchg = $userInfo->{last_change};
+
+ my $layout = _labeledFrameBox($replace_pnt, $self->loc->N("Select groups that the user will be member of:"));
+
+ my $yTableHeader = new yui::YTableHeader();
+ $yTableHeader->addColumn("", $yui::YAlignBegin);
+ $yTableHeader->addColumn($self->loc->N("Group"), $yui::YAlignBegin);
+
+ $userGroupsWidget{members} = $mgaFactory->createCBTable($layout, $yTableHeader, $yui::YCBTableCheckBoxOnFirstColumn);
+
+ my $grps = $self->sh_users->getGoups();
+ my @sgroups = sort @$grps;
+
+ my $itemCollection = new yui::YItemCollection;
+ my $members = $userData->{members};
+ foreach my $group (@sgroups) {
+ my $item = new yui::YCBTableItem($group);
+ $item->check(MDK::Common::DataStructure::member($group, @$members));
+ $item->setLabel($group);
+ $itemCollection->push($item);
+ $item->DISOWN();
+ }
+ $userGroupsWidget{members}->addItems($itemCollection);
+ $userGroupsWidget{members}->setNotify(1);
+ my $primgroup = '';
+ if ($userData->{primary_group} != -1) {
+ $primgroup = $self->sh_users->groupName($userData->{primary_group});
+ }
+
+ my $align = $factory->createLeft($layout);
+ my $hbox = $factory->createHBox($align);
+ my $label = $factory->createLabel($hbox, $self->loc->N("Primary Group"));
+ $userGroupsWidget{primary_group} = $factory->createComboBox($hbox, "", 0);
+ my $itemColl = new yui::YItemCollection;
+ foreach my $member (@$members) {
+ my $item = new yui::YItem ($member, 0);
+ $item->setSelected(1) if ($item->label() eq $primgroup);
+ $itemColl->push($item);
+ $item->DISOWN();
+ }
+ $userGroupsWidget{primary_group}->addItems($itemColl);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $userGroupsWidget{primary_group}->setWeight($yui::YD_HORIZ, 2);
+
+ $replace_pnt->showChild();
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+
+ return \%userGroupsWidget;
+}
+
+sub _groupEdit_Ok {
+ my ($self, %groupData) = @_;
+
+ # update last changes if any
+ %groupData = $self->_storeDataFromGroupEditPreviousTab(%groupData);
+
+ my ($continue, $errorString) = $self->sh_users->valid_groupname($groupData{groupname});
+ if (!$continue) {
+ $self->sh_gui->msgBox({text => $errorString}) if ($errorString);
+ return $continue;
+ }
+
+ my $groupInfo = {
+ groupname => $groupData{groupname},
+ members => $groupData{members},
+ };
+
+ if ($groupData{start_groupname} ne $groupData{groupname}) {
+ $groupInfo->{old_groupname} = $groupData{start_groupname};
+ }
+
+ my $retval = $self->sh_users->modifyGroup($groupInfo);
+
+ if (!$retval->{status}) {
+ $self->sh_gui->msgBox({text => $retval->{error}} );
+ }
+
+ $self->_refresh();
+
+ return 1;
+}
+
+sub _userEdit_Ok {
+ my ($self, $userData) = @_;
+
+ # update last changes if any
+ $self->_storeDataFromUserEditPreviousTab($userData);
+
+ my ($continue, $errorString) = $self->sh_users->valid_username($userData->{username});
+ if (!$continue) {
+ $self->sh_gui->msgBox({text => $errorString}) if ($errorString);
+ return $continue;
+ }
+
+ if ( $userData->{password} ne $userData->{password1}) {
+ $self->sh_gui->msgBox({text => $self->loc->N("Password Mismatch")});
+ return 0;
+ }
+
+ if ($userData->{password} ne '') {
+ if ($self->sh_users->weakPasswordForSecurityLevel($userData->{password})) {
+ $self->sh_gui->msgBox({text => $self->loc->N("This password is too simple. \n Good passwords should be > 6 characters")});
+ return 0;
+ }
+ }
+
+ if ($userData->{primary_group} == -1) {
+ $self->sh_gui->msgBox({ text => $self->loc->N("Please select at least one group for the user")});
+ return 0;
+ }
+
+ my $userInfo = {
+ username => $userData->{username},
+ fullname => $userData->{full_name},
+ homedir => $userData->{homedir},
+ shell => $userData->{shell},
+ members => $userData->{members},
+ gid => $userData->{primary_group},
+ lockuser => $userData->{lockuser},
+ };
+
+ $userInfo->{old_username} = $userData->{old_username} if $userData->{username} ne $userData->{old_username};
+ $userInfo->{password} = $userData->{password} if $userData->{password} ne '';
+
+
+ if ($userData->{acc_check_exp}) {
+ my $yr = $userData->{acc_expy};
+ my $mo = $userData->{acc_expm};
+ my $dy = $userData->{acc_expd};
+ if (!_ValidInt($yr, $dy, $mo)) {
+ $self->sh_gui->msgBox({text => $self->loc->N("Please specify Year, Month and Day \n for Account Expiration ")});
+ return 0;
+ }
+ $userInfo->{acc_expires} = {
+ exp_y => $yr,
+ exp_m => $mo,
+ exp_d => $dy,
+ };
+ }
+
+ if ($userData->{pwd_check_exp}) {
+ my $allowed = int($userData->{pwd_exp_min});
+ my $required = int($userData->{pwd_exp_max});
+ my $warning = int($userData->{pwd_exp_warn});
+ my $inactive = int($userData->{pwd_exp_inact});
+ if (!$allowed || !$required || !$warning || !$inactive) {
+ $self->sh_gui->msgBox({text => $self->loc->N("Please fill up all fields in password aging\n")});
+ return 0;
+ }
+ $userInfo->{password_expires} = {
+ exp_min => $allowed,
+ exp_max => $required,
+ exp_warn => $warning,
+ exp_inact => $inactive,
+ };
+ }
+
+ $self->sh_users->modifyUser($userInfo);
+
+
+ defined $userData->{icon_face} and $self->sh_users->addKdmIcon($userData->{username}, $userData->{icon_face});
+ $self->_refresh();
+
+ return 1;
+}
+
+
+# check the password and set the widget accordingly
+sub _checkWeaknessPassword {
+ my ($self, $password, $weakness_widget) = @_;
+
+ my $strongp = $self->sh_users->strongPassword($password);
+ if (yui::YUI::app()->hasImageSupport()) {
+ my $file = File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/Warning_Shield_Grey16x16.png');
+ if ($strongp) {
+ $file = File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/Checked_Shield_Green16x16.png');
+ }
+ $weakness_widget->setImage($file);
+ }
+ else {
+ # For ncurses set a label
+ $weakness_widget->setValue(($strongp ? $self->loc->N("Strong") : $self->loc->N("Weak")));
+ }
+}
+
+sub _editUserDialog {
+ my $self = shift;
+
+ my $dontcreatehomedir = 0;
+ my $is_system = 0;
+
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($self->loc->N("Edit User"));
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+ my $dlg = $factory->createPopupDialog();
+ my $layout = $factory->createVBox($dlg);
+
+ my %tabs;
+ if ($optional->hasDumbTab()) {
+ my $hbox = $factory->createHBox($layout);
+ my $align = $factory->createHCenter($hbox);
+ $tabs{widget} = $optional->createDumbTab($align);
+
+ $tabs{user_data} = new yui::YItem($userEditLabel{user_data});
+ $tabs{user_data}->setSelected();
+ $tabs{used} = $tabs{user_data}->label();
+ $tabs{widget}->addItem( $tabs{user_data} );
+ $tabs{user_data}->DISOWN();
+
+ $tabs{account_info} = new yui::YItem($userEditLabel{account_info});
+ $tabs{widget}->addItem( $tabs{account_info} );
+ $tabs{account_info}->DISOWN();
+
+ $tabs{password_info} = new yui::YItem($userEditLabel{password_info});
+ $tabs{widget}->addItem( $tabs{password_info} );
+ $tabs{password_info}->DISOWN();
+
+ $tabs{groups} = new yui::YItem($userEditLabel{groups});
+ $tabs{widget}->addItem( $tabs{groups} );
+ $tabs{groups}->DISOWN();
+
+ my $vbox = $factory->createVBox($tabs{widget});
+ $align = $factory->createLeft($vbox);
+ $tabs{replace_pnt} = $factory->createReplacePoint($align);
+
+ $hbox = $factory->createHBox($vbox);
+ $align = $factory->createRight($hbox);
+ my $cancelButton = $factory->createPushButton($align, $self->loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($hbox, $self->loc->N("&Ok"));
+
+ my $userData = $self->_getUserInfo();
+ # userData here should be tested because it could be undef
+
+ # Useful entry point for the current edit user/group tab widget
+ $self->set_edit_tab_widget( %{$self->_userDataTabWidget($dlg, $tabs{replace_pnt}, $userData)} );
+ $self->set_edit_tab_widget( edit_tab_label => $userEditLabel{user_data});
+
+ while(1) {
+ my $event = $dlg->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::MenuEvent) {
+ ### MENU ###
+ my $item = $event->item();
+ if ($item->label() eq $tabs{user_data}->label()) {
+ $self->_storeDataFromUserEditPreviousTab($userData);
+ my $edit_tab = $self->_userDataTabWidget($dlg, $tabs{replace_pnt}, $userData );
+ $self->edit_tab_widgets( {} );
+ $self->set_edit_tab_widget(%{$edit_tab});
+ $self->set_edit_tab_widget( edit_tab_label => $userEditLabel{user_data});
+ }
+ elsif ($item->label() eq $tabs{account_info}->label()) {
+ $self->_storeDataFromUserEditPreviousTab($userData);
+ my $edit_tab = $self->_userAccountInfoTabWidget($dlg, $tabs{replace_pnt}, $userData );
+ $self->edit_tab_widgets( {} );
+ $self->set_edit_tab_widget(%{$edit_tab});
+ $self->set_edit_tab_widget( edit_tab_label => $userEditLabel{account_info});
+ }
+ elsif ($item->label() eq $tabs{password_info}->label()) {
+ $self->_storeDataFromUserEditPreviousTab($userData);
+ my $edit_tab = $self->_userPasswordInfoTabWidget($dlg, $tabs{replace_pnt}, $userData );
+ $self->edit_tab_widgets( {} );
+ $self->set_edit_tab_widget(%{$edit_tab});
+ $self->set_edit_tab_widget( edit_tab_label => $userEditLabel{password_info});
+ }
+ elsif ($item->label() eq $tabs{groups}->label()) {
+ $self->_storeDataFromUserEditPreviousTab($userData);
+ my $edit_tab = $self->_userGroupsTabWidget($dlg, $tabs{replace_pnt}, $userData );
+ $self->edit_tab_widgets( {} );
+ $self->set_edit_tab_widget(%{$edit_tab});
+ $self->set_edit_tab_widget( edit_tab_label => $userEditLabel{groups});
+ }
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### widget
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $self->get_edit_tab_widget('password')) {
+ my $pass = $self->get_edit_tab_widget('password')->value();
+ $self->_checkWeaknessPassword($pass, $self->get_edit_tab_widget('weakness'));
+ }
+ elsif ($widget == $okButton) {
+ ## save changes
+ if ($self->_userEdit_Ok($userData)) {
+ last;
+ }
+ }
+# last: managing tab widget events
+ else {
+ my $current_tab = $self->get_edit_tab_widget('edit_tab_label');
+ if ($current_tab && $current_tab eq $userEditLabel{account_info}) {
+ if ($widget == $self->get_edit_tab_widget('icon_face')) {
+ my $iconLabel = $self->_iconButtonLabel($self->get_edit_tab_widget('icon_face'));
+ my $nextIcon = $self->sh_users->GetFaceIcon($iconLabel, 1);
+ $self->_iconButtonLabel($self->get_edit_tab_widget('icon_face'), $nextIcon);
+ $self->get_edit_tab_widget('icon_face')->setIcon($self->sh_users->face2png($nextIcon));
+ }
+ }
+ elsif ($current_tab && $current_tab eq $userEditLabel{groups}) {
+ if ($widget == $self->get_edit_tab_widget('members')) {
+ my $item = $self->get_edit_tab_widget('members')->changedItem();
+ if ($item) {
+ if ($item->checked()) {
+ # add it to possible primary groups
+ my $pgItem = new yui::YItem ($item->label(), 0);
+ $self->get_edit_tab_widget('primary_group')->addItem($pgItem);
+ }
+ else {
+ # remove it to possible primary groups
+ $dlg->startMultipleChanges();
+ my $itemColl = new yui::YItemCollection;
+ my $tbl = $self->get_edit_tab_widget('members');
+ for(my $i=0;$i < $tbl->itemsCount();$i++) {
+ if ($tbl->toCBYTableItem($tbl->item($i))->checked()) {
+ my $pgItem = new yui::YItem ($tbl->item($i)->label(), 0);
+ my $primgroup = $self->sh_users->groupName($userData->{primary_group});
+ $pgItem->setSelected(1) if ($pgItem->label() eq $primgroup);
+
+ $itemColl->push($pgItem);
+ $pgItem->DISOWN();
+ }
+ }
+ $self->get_edit_tab_widget('primary_group')->deleteAllItems();
+ $self->get_edit_tab_widget('primary_group')->addItems($itemColl);
+ $dlg->recalcLayout();
+ $dlg->doneMultipleChanges();
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+ else {
+ $self->sh_gui->warningMsgBox({text => $self->loc->N("Cannot create tab widgets")});
+ }
+
+ destroy $dlg;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+
+}
+
+sub _editGroupDialog {
+ my $self = shift;
+
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($self->loc->N("Edit Group"));
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+ my $dlg = $factory->createPopupDialog();
+ my $layout = $factory->createVBox($dlg);
+
+ my %tabs;
+ if ($optional->hasDumbTab()) {
+ my $hbox = $factory->createHBox($layout);
+ my $align = $factory->createHCenter($hbox);
+ $tabs{widget} = $optional->createDumbTab($align);
+
+ $tabs{group_data} = new yui::YItem($groupEditLabel{group_data});
+ $tabs{group_data}->setSelected();
+ $tabs{widget}->addItem( $tabs{group_data} );
+ $tabs{group_data}->DISOWN();
+
+ $tabs{group_users} = new yui::YItem($groupEditLabel{group_users});
+ $tabs{widget}->addItem( $tabs{group_users} );
+ $tabs{group_users}->DISOWN();
+
+ my $vbox = $factory->createVBox($tabs{widget});
+ $align = $factory->createLeft($vbox);
+ $tabs{replace_pnt} = $factory->createReplacePoint($align);
+
+ $hbox = $factory->createHBox($vbox);
+ $align = $factory->createRight($hbox);
+ my $cancelButton = $factory->createPushButton($align, $self->loc->N("Cancel"));
+ my $okButton = $factory->createPushButton($hbox, $self->loc->N("Ok"));
+
+ my %groupData = $self->_getGroupInfo();
+ # groupData here should be tested because it could be undef
+
+# %groupData: selected group info as:
+# $groupname: group name
+# $members: users that are members of this group
+
+
+ # Useful entry point for the current edit user/group tab widget
+ $self->set_edit_tab_widget( $self->_groupDataTabWidget($dlg, $tabs{replace_pnt}, %groupData) );
+ $self->set_edit_tab_widget( edit_tab_label => $groupEditLabel{group_data});
+
+ while(1) {
+ my $event = $dlg->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::MenuEvent) {
+ ### MENU ###
+ my $item = $event->item();
+ if ($item->label() eq $tabs{group_data}->label()) {
+ %groupData = $self->_storeDataFromGroupEditPreviousTab(%groupData);
+ my %edit_tab = $self->_groupDataTabWidget($dlg, $tabs{replace_pnt}, %groupData );
+ $self->edit_tab_widgets( {} );
+ $self->set_edit_tab_widget(%edit_tab);
+ $self->set_edit_tab_widget( edit_tab_label => $groupEditLabel{group_data});
+ }
+ elsif ($item->label() eq $tabs{group_users}->label()) {
+ %groupData = $self->_storeDataFromGroupEditPreviousTab(%groupData);
+ my %edit_tab = $self->_groupUsersTabWidget($dlg, $tabs{replace_pnt}, %groupData );
+ $self->edit_tab_widgets( {} );
+ $self->set_edit_tab_widget(%edit_tab);
+ $self->set_edit_tab_widget( edit_tab_label => $groupEditLabel{group_users});
+ }
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### widget
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $okButton) {
+ ## save changes
+ if ($self->_groupEdit_Ok(%groupData)) {
+ last;
+ }
+ }
+ }
+ }
+
+ }
+ else {
+ $self->sh_gui->warningMsgBox({text => $self->loc->N("Cannot create tab widgets")});
+ }
+
+ destroy $dlg;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+
+}
+
+sub _editUserOrGroup {
+ my $self = shift;
+
+ # TODO item management avoid label if possible
+ my $label = $self->_skipShortcut($self->get_widget('tabs')->selectedItem()->label());
+ if ($label eq $self->loc->N("Users") ) {
+ $self->_editUserDialog();
+ }
+ else {
+ $self->_editGroupDialog();
+ }
+ $self->_refresh();
+}
+
+
+sub _deleteUserOrGroup {
+ my $self = shift;
+
+ # TODO item management avoid label if possible
+ my $label = $self->_skipShortcut($self->get_widget('tabs')->selectedItem()->label());
+ if ($label eq $self->loc->N("Users") ) {
+ $self->_deleteUserDialog();
+ $self->_refresh();
+ }
+ else {
+ $self->_deleteGroupDialog();
+ $self->_refresh();
+ }
+}
+
+
+sub _refresh {
+ my $self = shift;
+
+ # TODO item management avoid label if possible
+ my $label = $self->_skipShortcut($self->get_widget('tabs')->selectedItem()->label());
+ if ($label eq $self->loc->N("Users") ) {
+ $self->_refreshUsers();
+ }
+ else {
+ $self->_refreshGroups();
+ }
+# TODO xguest
+# RefreshXguest(1);
+}
+
+# TODO context menu creation is missed in libyui
+sub _contextMenuActions {
+ my $self = shift;
+
+ my $item = $self->get_widget('table')->selectedItem();
+ if ($item) {
+ }
+}
+
+sub _refreshActions {
+ my $self = shift;
+
+ my $item = $self->get_widget('table')->selectedItem();
+ $self->dialog->startMultipleChanges();
+ $self->get_widget('action_menu')->deleteAllItems();
+
+ # do we need to undef them first?
+ $self->set_action_menu(
+ add_user => undef,
+ add_group => undef,
+ edit => undef,
+ del => undef,
+ inst => undef,
+ );
+ $self->set_action_menu(
+ add_user => new yui::YMenuItem($self->loc->N("Add User")),
+ add_group => new yui::YMenuItem($self->loc->N("Add Group")),
+ edit => new yui::YMenuItem($self->loc->N("&Edit")),
+ del => new yui::YMenuItem($self->loc->N("&Delete")),
+ inst => new yui::YMenuItem($self->loc->N("Install guest account")),
+ );
+
+ my $itemColl = new yui::YItemCollection;
+ for my $pair ( $self->action_menu_pairs ) {
+ my $menuItem = $pair->[1];
+ if ($pair->[0] eq 'edit' || $pair->[0] eq 'del') {
+ if ($item) {
+ $itemColl->push($menuItem);
+ }
+ }
+ else {
+ $itemColl->push($menuItem);
+ }
+ $menuItem->DISOWN();
+ }
+ $self->get_widget('action_menu')->addItems($itemColl);
+ $self->get_widget('action_menu')->rebuildMenuTree();
+ if ($item) {
+ $self->get_widget('edit')->setEnabled();
+ $self->get_widget('del')->setEnabled();
+ }
+ else {
+ $self->get_widget('edit')->setDisabled();
+ $self->get_widget('del')->setDisabled();
+ }
+
+ $self->dialog->doneMultipleChanges();
+}
+
+sub _showAboutDialog {
+ my $self = shift;
+
+ my $translators = $self->loc->N("_: Translator(s) name(s) & email(s)\n");
+ $translators =~ s/\</\&lt\;/g;
+ $translators =~ s/\>/\&gt\;/g;
+ $self->sh_gui->AboutDialog({
+ name => $self->loc->N("manauser"),
+ version => $self->VERSION,
+ credits => $self->loc->N("Copyright (C) %s Mageia community", '2013-2015'),
+ license => $self->loc->N("GPLv2"),
+ description => $self->loc->N("manauser is a Mageia user management tool \n(from the original idea of Mandriva userdrake)."),
+ authors => $self->loc->N("<h3>Developers</h3>
+ <ul><li>%s</li>
+ <li>%s</li>
+ </ul>
+ <h3>Translators</h3>
+ <ul><li>%s</li></ul>",
+ "Angelo Naselli &lt;anaselli\@linux.it&gt;",
+ "Matteo Pasotti &lt;matteo.pasotti\@gmail.com&gt;",
+ $translators
+ ),
+ });
+}
+sub _manageUsersDialog {
+ my $self = shift;
+
+ if ($EUID != 0) {
+ $self->sh_gui->warningMsgBox({
+ title => $self->name,
+ text => $self->loc->N("root privileges required"),
+ });
+ return;
+ }
+
+ ## TODO fix for manatools
+ my $pixdir = '/usr/share/userdrake/pixmaps/';
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($self->name);
+ ## set icon if not already set by external launcher
+ yui::YUI::app()->setApplicationIcon($self->icon);
+
+
+ my $factory = yui::YUI::widgetFactory;
+ my $optional = yui::YUI::optionalWidgetFactory;
+
+
+ $self->dialog($factory->createMainDialog());
+ my $layout = $factory->createVBox($self->dialog);
+
+ my $hbox_headbar = $factory->createHBox($layout);
+ my $head_align_left = $factory->createLeft($hbox_headbar);
+ my $head_align_right = $factory->createRight($hbox_headbar);
+ my $headbar = $factory->createHBox($head_align_left);
+ my $headRight = $factory->createHBox($head_align_right);
+
+ my %fileMenu = (
+ widget => $factory->createMenuButton($headbar,$self->loc->N("File")),
+ refresh => new yui::YMenuItem($self->loc->N("Refresh")),
+ quit => new yui::YMenuItem($self->loc->N("&Quit")),
+ );
+
+ $fileMenu{ widget }->addItem($fileMenu{ refresh });
+ $fileMenu{ widget }->addItem($fileMenu{ quit });
+ $fileMenu{ widget }->rebuildMenuTree();
+
+ my $actionMenu = $factory->createMenuButton($headbar, $self->loc->N("Actions"));
+ $actionMenu->DISOWN();
+
+ my %helpMenu = (
+ widget => $factory->createMenuButton($headRight, $self->loc->N("&Help")),
+ help => new yui::YMenuItem($self->loc->N("Help")),
+ report_bug => new yui::YMenuItem($self->loc->N("Report Bug")),
+ about => new yui::YMenuItem($self->loc->N("&About")),
+ );
+
+ while ( my ($key, $value) = each(%helpMenu) ) {
+ if ($key ne 'widget' ) {
+ $helpMenu{ widget }->addItem($value);
+ }
+ }
+ $helpMenu{ widget }->rebuildMenuTree();
+
+ my $hbox = $factory->createHBox($layout);
+ $hbox = $factory->createHBox($factory->createLeft($hbox));
+ $self->set_widget(
+ add_user => $factory->createIconButton($hbox, $pixdir . 'user_add.png', $self->loc->N("Add User")),
+ add_group => $factory->createIconButton($hbox, $pixdir . 'group_add.png', $self->loc->N("Add Group")),
+ edit => $factory->createIconButton($hbox, $pixdir . 'user_conf.png', $self->loc->N("Edit")),
+ del => $factory->createIconButton($hbox, $pixdir . 'user_del.png', $self->loc->N("Delete")),
+ refresh => $factory->createIconButton($hbox, $pixdir . 'refresh.png', $self->loc->N("Refresh")),
+ action_menu => $actionMenu,
+ );
+
+
+ $hbox = $factory->createHBox($layout);
+ $head_align_left = $factory->createLeft($hbox);
+
+ my $sysfilter = 1;
+ if (-e $self->config_file) {
+ my $prefs = Config::Auto::parse($self->config_file);
+ $sysfilter = ($prefs->{FILTER} eq 'true' or $prefs->{FILTER} eq 'true' or $prefs->{FILTER} eq '1');
+ }
+ $self->set_widget(filter_system => $factory->createCheckBox($head_align_left, $self->loc->N("Filter system users"),
+ $sysfilter));
+ $factory->createHSpacing($hbox, 3);
+ $head_align_right = $factory->createRight($hbox);
+ $headRight = $factory->createHBox($head_align_right);
+ $factory->createLabel($headRight, $self->loc->N("Search:"));
+ $self->set_widget(filter => $factory->createInputField($headRight, "", 0));
+ $self->set_widget(apply_filter => $factory->createPushButton($headRight, $self->loc->N("Apply filter")));
+ $self->get_widget('filter')->setWeight($yui::YD_HORIZ, 2);
+ $self->get_widget('apply_filter')->setWeight($yui::YD_HORIZ, 1);
+ $self->get_widget('filter_system')->setNotify(1);
+
+ my %tabs;
+ if ($optional->hasDumbTab()) {
+ $hbox = $factory->createHBox($layout);
+ my $align = $factory->createHCenter($hbox);
+ $self->set_widget(tabs => $optional->createDumbTab($align));
+ }
+
+ $hbox = $factory->createHBox($layout);
+ my $align = $factory->createLeft($hbox);
+ my $aboutButton = $factory->createPushButton($align, $self->loc->N("&About") );
+ $align = $factory->createRight($hbox);
+ my $quitButton = $factory->createPushButton($align, $self->loc->N("&Quit") );
+
+ if ($optional->hasDumbTab()) {
+ $tabs{users} = new yui::YItem($self->loc->N("Users"));
+ $tabs{users}->setSelected();
+ $self->get_widget('tabs')->addItem( $tabs{users} );
+ $tabs{users}->DISOWN();
+ $tabs{groups} = new yui::YItem($self->loc->N("Groups"));
+ $self->get_widget('tabs')->addItem( $tabs{groups} );
+ $tabs{groups}->DISOWN();
+ my $vbox = $factory->createVBox($self->get_widget('tabs'));
+ $align = $factory->createLeft($vbox);
+ $self->set_widget(replace_pnt => $factory->createReplacePoint($align));
+ $self->_createUserTable();
+ $self->get_widget('table')->setImmediateMode(1);
+ $self->get_widget('table')->DISOWN();
+ }
+
+ $self->_refreshActions();
+
+ # main loop
+ while(1) {
+ my $event = $self->dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::MenuEvent) {
+### MENU ###
+ my $item = $event->item();
+ my $menuLabel = $item->label();
+ if ($menuLabel eq $fileMenu{ quit }->label()) {
+ last;
+ }
+ elsif ($menuLabel eq $helpMenu{about}->label()) {
+ $self->_showAboutDialog();
+ }
+ elsif ($menuLabel eq $self->get_action_menu('add_user')->label()) {
+ $self->addUserDialog();
+ $self->_refresh();
+ }
+ elsif ($menuLabel eq $self->get_action_menu('add_group')->label()) {
+ $self->_addGroupDialog();
+ $self->_refresh();
+ }
+ elsif ($menuLabel eq $self->get_action_menu('del')->label()) {
+ $self->_deleteUserOrGroup();
+ }
+ elsif ($menuLabel eq $self->get_action_menu('edit')->label()) {
+ $self->_editUserOrGroup();
+ }
+ elsif ($self->get_widget('tabs') && $menuLabel eq $tabs{groups}->label()) {
+ $self->_createGroupTable();
+ }
+ elsif ($self->get_widget('tabs') && $menuLabel eq $tabs{users}->label()) {
+ $self->_createUserTable();
+ }
+ elsif ($menuLabel eq $fileMenu{refresh}->label()) {
+ $self->_refresh();
+ }
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+### Buttons and widgets ###
+ my $widget = $event->widget();
+ if ($widget == $quitButton) {
+ last;
+ }
+ elsif ($widget == $aboutButton) {
+ $self->_showAboutDialog();
+ }
+ elsif ($widget == $self->get_widget('add_user')) {
+ $self->addUserDialog();
+ $self->_refresh();
+ }
+ elsif ($widget == $self->get_widget('del')) {
+ $self->_deleteUserOrGroup();
+ }
+ elsif ($widget == $self->get_widget('table')) {
+ $self->_refreshActions();
+ my $wEvent = yui::YMGAWidgetFactory::getYWidgetEvent($event);
+ if ($wEvent && $wEvent->reason() == $yui::YEvent::Activated) {
+ $self->_editUserOrGroup();
+ }
+ }
+ elsif ($widget == $self->get_widget('add_group')) {
+ $self->_addGroupDialog();
+ $self->_refresh();
+ }
+ elsif ($widget == $self->get_widget('edit')) {
+ $self->_editUserOrGroup();
+ }
+ elsif ( $widget == $self->get_widget('filter_system') ||
+ $widget == $self->get_widget('refresh') ||
+ $widget == $self->get_widget('apply_filter') ) {
+ $self->_refresh();
+ }
+ }
+ }
+
+ $self->dialog->destroy() ;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+}
+
+#=============================================================
+
+=head2 _skipShortcut
+
+=head3 INPUT
+
+ $self: this object
+ $label: an item label to be cleaned by keyboard shortcut "&"
+
+=head3 OUTPUT
+
+ $label: cleaned label
+
+=head3 DESCRIPTION
+
+ This internal method is a workaround to label that are
+ changed by "&" due to keyborad shortcut.
+
+=cut
+
+#=============================================================
+sub _skipShortcut {
+ my ($self, $label) = @_;
+
+ $label =~ s/&// if ($label);
+
+ return ($label);
+}
+
+
+sub _ValidInt {
+ foreach my $i (@_) { $i =~ /\d+/ or return 0 }
+ return 1;
+}
+
+
+sub _TimeOfArray {
+ my ($reltime, $cm) = @_;
+ my $h; my %mth = (Jan => 1, Feb => 2, Mar => 3, Apr => 4, May => 5, Jun => 6, Jul => 7, Aug => 8, Sep => 9, Oct => 10, Nov => 11, Dec => 12);
+ my $_t = localtime($reltime * 24 * 60 * 60) =~ /(\S+)\s+(\S+)\s+(\d+)\s+(\S+)\s+(\d+)/;
+ $h->{daystr} = $1;
+ $h->{month} = $2;
+ $h->{dayint} = $3;
+ $h->{year} = $5;
+ $cm and $h->{month} = $mth{$2};
+ $h;
+}
+
+
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/lib/ManaTools/Privileges.pm b/lib/ManaTools/Privileges.pm
new file mode 100644
index 0000000..5833900
--- /dev/null
+++ b/lib/ManaTools/Privileges.pm
@@ -0,0 +1,61 @@
+# vim: set et ts=4 sw=4:
+# Copyright 2012-2015 Matteo Pasotti
+#
+# This file is part of ManaTools
+#
+# ManaTools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# ManaTools 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 ManaTools. If not, see <http://www.gnu.org/licenses/>.
+
+package ManaTools::Privileges;
+
+use strict;
+use warnings;
+use diagnostics;
+require Exporter;
+use base qw(Exporter);
+use English qw(-no_match_vars);
+
+our @EXPORT = qw(is_root_capability_required
+ ask_for_authentication);
+
+my $wrappers = { "sudo" => "/usr/bin/sudo",
+ "pkit" => "/usr/bin/pkexec",
+ "chlp" => "/usr/bin/consolehelper"
+ };
+
+my $wrapper = 0;
+
+sub is_root_capability_required {
+ return $EUID != 0;
+}
+
+sub ask_for_authentication {
+ my $wrapper_id = shift;
+ $wrapper = $wrappers->{$wrapper_id} if(defined($wrappers->{$wrapper_id}));
+ my ($command, @args) = wrap_command($0, @ARGV);
+ unshift(@args,$command->[1]);
+ unshift(@args, '-n') if($wrapper_id eq "sudo"); # let sudo die if password is needed
+ exec { $command->[0] } $command->[1], @args or die ("command %s missing", $command->[0]);
+}
+
+sub wrap_command {
+ my ($app, @args) = @_;
+ return ([$wrapper, $app], @args);
+}
+
+sub get_wrapper {
+ my $id = shift;
+ return $wrappers->{$id} if(defined($wrappers->{$id}));
+}
+
+1;
diff --git a/lib/ManaTools/Rpmdragora/.perl_checker b/lib/ManaTools/Rpmdragora/.perl_checker
new file mode 100644
index 0000000..202e053
--- /dev/null
+++ b/lib/ManaTools/Rpmdragora/.perl_checker
@@ -0,0 +1 @@
+Basedir ..
diff --git a/lib/ManaTools/Rpmdragora/edit_urpm_sources.pm b/lib/ManaTools/Rpmdragora/edit_urpm_sources.pm
new file mode 100644
index 0000000..c756964
--- /dev/null
+++ b/lib/ManaTools/Rpmdragora/edit_urpm_sources.pm
@@ -0,0 +1,2243 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Rpmdragora::edit_urpm_sources;
+#*****************************************************************************
+#
+# Copyright (c) 2002 Guillaume Cottenceau
+# Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com>
+# Copyright (c) 2002-2007 Mandriva Linux
+# Copyright (c) 2014 - 2015 Angelo Naselli <anaselli@linux.it>
+#
+# 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 File::ShareDir ':ALL';
+use File::HomeDir qw(home);
+
+use MDK::Common::Func qw(if_ each_index);
+use MDK::Common::Math qw(max);
+use MDK::Common::File qw(cat_ output);
+use MDK::Common::DataStructure qw(member put_in_hash uniq);
+use MDK::Common::Various qw(to_bool);
+
+use ManaTools::Shared;
+use ManaTools::Shared::Locales;
+use ManaTools::rpmdragora;
+use ManaTools::Rpmdragora::init;
+use ManaTools::Rpmdragora::open_db;
+use ManaTools::Rpmdragora::formatting;
+use ManaTools::Shared::GUI;
+use URPM::Signature;
+use urpm::media;
+use urpm::download;
+use urpm::lock;
+
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(run);
+
+
+my $urpm;
+my ($mainw, $list_tv, $something_changed);
+
+my %col = (
+ mainw => {
+ is_enabled => 0,
+ is_update => 1,
+ type => 2,
+ name => 3,
+ activatable => 4
+ },
+);
+
+my $loc = ManaTools::rpmdragora::locale();
+
+sub get_medium_type {
+ my ($medium) = @_;
+ my %medium_type = (
+ cdrom => $loc->N("CD-ROM"),
+ ftp => $loc->N("FTP"),
+ file => $loc->N("Local"),
+ http => $loc->N("HTTP"),
+ https => $loc->N("HTTPS"),
+ nfs => $loc->N("NFS"),
+ removable => $loc->N("Removable"),
+ rsync => $loc->N("rsync"),
+ ssh => $loc->N("NFS"),
+ );
+ return $loc->N("Mirror list") if $medium->{mirrorlist};
+ return $medium_type{$1} if $medium->{url} =~ m!^([^:]*)://!;
+ return $loc->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() {
+ $::expert && distro_type(0) eq 'updates' ? interactive_msg(
+ $loc->N("Choose media type"),
+ $loc->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 => $loc->N("Full set of sources"), no => $loc->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 0;
+ my $distro = $ManaTools::rpmdragora::mageia_release;
+ my ($mirror) = choose_mirror($urpm, message =>
+ $loc->N("This will attempt to install all official sources corresponding to your
+distribution (%s).\n
+I need to contact the Mageia website to get the mirror list.
+Please check that your network is currently running.\n
+Is it ok to continue?", $distro),
+ transient => $::main_window,
+ ) or return 0;
+ ref $mirror or return 0;
+ my $wait = wait_msg($loc->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 0;
+ warn_for_network_need(undef, transient => $::main_window) or return 0;
+ my $wait = wait_msg($loc->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;
+}
+
+## Internal routine that builds input fields needed to manage
+## the selected media type to be added
+## return HASH reference with the added widgets
+sub _build_add_dialog {
+ my $options = shift;
+
+ die "replace point is needed" if !defined ($options->{replace_pnt});
+ die "dialog is needed" if !defined ($options->{dialog});
+ die "selected item is needed" if !defined ($options->{selected});
+ die "media info is needed" if !defined ($options->{info});
+
+ my %widgets;
+ my $factory = yui::YUI::widgetFactory;
+
+ $options->{dialog}->startMultipleChanges();
+ $options->{replace_pnt}->deleteChildren();
+
+ # replace widgets
+ my $vbox = $factory->createVBox( $options->{replace_pnt} );
+ my $hbox = $factory->createHBox($vbox);
+ $factory->createHSpacing($hbox, 1.0);
+ my $label = $factory->createLabel($hbox,
+ $options->{info}->{$options->{selected}}->{url}
+ );
+
+ $factory->createHSpacing($hbox, 3.0);
+ $widgets{url} = $factory->createInputField($hbox, "", 0);
+ $widgets{url}->setWeight($yui::YD_HORIZ, 2);
+ if (defined($options->{info}->{$options->{selected}}->{dirsel})) {
+ $widgets{dirsel} = $factory->createPushButton($hbox, $loc->N("Browse..."));
+ }
+ elsif (defined($options->{info}->{$options->{selected}}->{loginpass})) {
+ $hbox = $factory->createHBox($vbox);
+ $factory->createHSpacing($hbox, 1.0);
+ $label = $factory->createLabel($hbox, $loc->N("Login:") );
+ $factory->createHSpacing($hbox, 1.0);
+ $widgets{login} = $factory->createInputField($hbox, "", 0);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $widgets{login}->setWeight($yui::YD_HORIZ, 3);
+ $hbox = $factory->createHBox($vbox);
+ $factory->createHSpacing($hbox, 1.0);
+ $label = $factory->createLabel($hbox, $loc->N("Password:") );
+ $factory->createHSpacing($hbox, 1.0);
+ $widgets{pass} = $factory->createInputField($hbox, "", 1);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $widgets{pass}->setWeight($yui::YD_HORIZ, 3);
+
+ }
+ # recalc layout
+ $options->{replace_pnt}->showChild();
+ $options->{dialog}->recalcLayout();
+ $options->{dialog}->doneMultipleChanges();
+
+ return \%widgets;
+}
+
+sub add_callback() {
+
+ my $retVal = 0;
+
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($loc->N("Add a medium"));
+
+ my $factory = yui::YUI::widgetFactory;
+
+ my $dialog = $factory->createPopupDialog();
+ my $minSize = $factory->createMinSize( $dialog, 60, 5 );
+ my $vbox = $factory->createVBox( $minSize );
+
+ $factory->createVSpacing($vbox, 0.5);
+
+ my $hbox = $factory->createHBox( $factory->createLeft($vbox) );
+ $factory->createHeading($hbox, $loc->N("Adding a medium:"));
+ $factory->createVSpacing($vbox, 0.5);
+
+ $hbox = $factory->createHBox($vbox);
+ $factory->createHSpacing($hbox, 1.0);
+ my $label = $factory->createLabel($hbox, $loc->N("Type of medium:") );
+ my $media_type = $factory->createComboBox($hbox, "", 0);
+ $media_type->setWeight($yui::YD_HORIZ, 2);
+
+ my %radios_infos = (
+ local => { name => $loc->N("Local files"), url => $loc->N("Medium path:"), dirsel => 1 },
+ ftp => { name => $loc->N("FTP server"), url => $loc->N("URL:"), loginpass => 1 },
+ rsync => { name => $loc->N("RSYNC server"), url => $loc->N("URL:") },
+ http => { name => $loc->N("HTTP server"), url => $loc->N("URL:") },
+ removable => { name => $loc->N("Removable device (CD-ROM, DVD, ...)"),
+ url => $loc->N("Path or mount point:"), dirsel => 1 },
+ );
+ my @radios_names_ordered = qw(local ftp rsync http removable);
+
+ my $itemColl = new yui::YItemCollection;
+ foreach my $elem (@radios_names_ordered) {
+ my $it = new yui::YItem($radios_infos{$elem}->{'name'}, 0);
+ if ($elem eq $radios_names_ordered[0]) {
+ $it->setSelected(1);
+ }
+ $itemColl->push($it);
+ $it->DISOWN();
+ }
+ $media_type->addItems($itemColl);
+ $media_type->setNotify(1);
+
+ $hbox = $factory->createHBox($vbox);
+ $factory->createHSpacing($hbox, 1.0);
+ $label = $factory->createLabel($hbox, $loc->N("Medium name:") );
+ $factory->createHSpacing($hbox, 1.0);
+ my $media_name = $factory->createInputField($hbox, "", 0);
+ $media_name->setWeight($yui::YD_HORIZ, 2);
+
+ # per function layout (replace point)
+ my $align = $factory->createLeft($vbox);
+ my $replace_pnt = $factory->createReplacePoint($align);
+
+ my $add_widgets = _build_add_dialog({replace_pnt => $replace_pnt, dialog => $dialog,
+ info => \%radios_infos, selected => $radios_names_ordered[0]}
+ );
+ # check-boxes
+ $hbox = $factory->createHBox($factory->createLeft($vbox));
+ $factory->createHSpacing($hbox, 1.3);
+ my $dist_media = $factory->createCheckBox($hbox, $loc->N("Create media for a whole distribution"), 0);
+ $hbox = $factory->createHBox($factory->createLeft($vbox));
+ $factory->createHSpacing($hbox, 1.3);
+ my $update_media = $factory->createCheckBox($hbox, $loc->N("Tag this medium as an update medium"), 0);
+ $dist_media->setNotify(1);
+
+ # Last line buttons
+ $factory->createVSpacing($vbox, 0.5);
+ $hbox = $factory->createHBox($vbox);
+ my $cancelButton = $factory->createPushButton($hbox, $loc->N("&Cancel"));
+ $factory->createHSpacing($hbox, 3.0);
+ my $okButton = $factory->createPushButton($hbox, $loc->N("&Ok"));
+
+ $cancelButton->setDefaultButton(1);
+
+ # dialog event loop
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### widget
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $media_type) {
+ my $item = $media_type->selectedItem();
+ my $sel = $item ? $item->index() : 0 ;
+ $add_widgets = _build_add_dialog({replace_pnt => $replace_pnt, dialog => $dialog,
+ info => \%radios_infos, selected => $radios_names_ordered[$sel]}
+ );
+ }
+ elsif ($widget == $dist_media) {
+ $update_media->setEnabled(!$dist_media->value());
+ }
+ elsif ($widget == $okButton) {
+ my $item = $media_type->selectedItem();
+ my $sel = $item ? $item->index() : 0 ;
+ my $info = $radios_infos{$radios_names_ordered[$sel]};
+ my $name = $media_name->value();
+ my $url = $add_widgets->{url}->value();
+ $name eq '' || $url eq '' and interactive_msg('rpmdragora', $loc->N("You need to fill up at least the two first entries.")), next;
+ if (member($name, map { $_->{name} } @{$urpm->{media}})) {
+ interactive_msg('rpmdragora',
+ $loc->N("There is already a medium called <%s>,\ndo you really want to replace it?", $name),
+ yesno => 1) or next;
+ }
+
+ my %i = (
+ name => $name,
+ url => $url,
+ distrib => $dist_media->value() ? 1 : 0,
+ update => $update_media->value() ? 1 : undef,
+ );
+ my %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",
+ defined($add_widgets->{login})
+ ?
+ $add_widgets->{login}->value() . ':' . ( $add_widgets->{pass}->value() ?
+ $add_widgets->{pass}->value() :
+ '')
+ :
+ '',
+ $i{url};
+
+ if ($i{distrib}) {
+ add_medium_and_check(
+ $urpm,
+ { nolock => 1, distrib => 1 },
+ $i{name}, $make_url{$radios_names_ordered[$sel]}, 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{$radios_names_ordered[$sel]}, $i{hdlist}, update => $i{update},
+ );
+ }
+
+ $retVal = 1;
+ last;
+ }
+ else {
+ my $item = $media_type->selectedItem();
+ my $sel = $item ? $item->index() : 0 ;
+ if (defined($radios_infos{$radios_names_ordered[$sel]}->{dirsel}) &&
+ defined($add_widgets->{dirsel}) ) {
+ if ($widget == $add_widgets->{dirsel}) {
+ my $dir = yui::YUI::app()->askForExistingDirectory(home(),
+ $radios_infos{$radios_names_ordered[$sel]}->{url}
+ );
+ $add_widgets->{url}->setValue($dir) if ($dir);
+ }
+ }
+ }
+ }
+ }
+### End ###
+
+ $dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+
+ return $retVal;
+
+}
+
+sub options_callback() {
+
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($loc->N("Global options for package installation"));
+
+ my $factory = yui::YUI::widgetFactory;
+
+ my $dialog = $factory->createPopupDialog();
+ my $minSize = $factory->createMinSize( $dialog, 50, 5 );
+ my $vbox = $factory->createVBox( $minSize );
+
+ my $hbox = $factory->createHBox($vbox);
+ $factory->createHSpacing($hbox, 1.0);
+ my $label = $factory->createLabel($hbox, $loc->N("Verify RPMs to be installed:") );
+ $factory->createHSpacing($hbox, 3.5);
+ my $verify_rpm = $factory->createComboBox($hbox, "", 0);
+ $verify_rpm->setWeight($yui::YD_HORIZ, 2);
+ my @verif = ($loc->N("never"), $loc->N("always"));
+ my $verify_rpm_value = $urpm->{global_config}{'verify-rpm'} || 0;
+
+ my $itemColl = new yui::YItemCollection;
+ my $cnt = 0;
+ foreach my $elem (@verif) {
+ my $it = new yui::YItem($elem, 0);
+ if ($cnt == $verify_rpm_value) {
+ $it->setSelected(1);
+ }
+ $itemColl->push($it);
+ $it->DISOWN();
+ $cnt++;
+ }
+ $verify_rpm->addItems($itemColl);
+
+ $hbox = $factory->createHBox($vbox);
+ $factory->createHSpacing($hbox, 1.0);
+ $label = $factory->createLabel($hbox, $loc->N("Download program to use:") );
+ $factory->createHSpacing($hbox, 4.0);
+ my $downloader_entry = $factory->createComboBox($hbox, "", 0);
+ $downloader_entry->setWeight($yui::YD_HORIZ, 2);
+
+ my @comboList = urpm::download::available_ftp_http_downloaders() ;
+ my $downloader = $urpm->{global_config}{downloader} || $comboList[0];
+
+ if (scalar(@comboList) > 0) {
+ $itemColl = new yui::YItemCollection;
+ foreach my $elem (@comboList) {
+ my $it = new yui::YItem($elem, 0);
+ if ($elem eq $downloader) {
+ $it->setSelected(1);
+ }
+ $itemColl->push($it);
+ $it->DISOWN();
+ }
+ $downloader_entry->addItems($itemColl);
+ }
+
+ $hbox = $factory->createHBox($vbox);
+ $factory->createHSpacing($hbox, 1.0);
+ $label = $factory->createLabel($hbox, $loc->N("XML meta-data download policy:") );
+ my $xml_info_policy = $factory->createComboBox($hbox, "", 0);
+ $xml_info_policy->setWeight($yui::YD_HORIZ, 2);
+
+ my @xml_info_policies = ('', 'never', 'on-demand', 'update-only', 'always');
+ my @xml_info_policiesL = ('', $loc->N("Never"), $loc->N("On-demand"), $loc->N("Update-only"), $loc->N("Always"));
+ my $xml_info_policy_value = $urpm->{global_config}{'xml-info'};
+
+ $itemColl = new yui::YItemCollection;
+ $cnt = 0;
+ foreach my $elem (@xml_info_policiesL) {
+ my $it = new yui::YItem($elem, 0);
+ if ($xml_info_policy_value && $xml_info_policy_value eq $xml_info_policies[$cnt]) {
+ $it->setSelected(1);
+ }
+ $itemColl->push($it);
+ $it->DISOWN();
+ $cnt++;
+ }
+ $xml_info_policy->addItems($itemColl);
+
+ ### TODO tips ###
+ #tip =>
+ #join("\n",
+ #$loc->N("For remote media, specify when XML meta-data (file lists, changelogs & information) are downloaded."),
+ #'',
+ #$loc->N("Never"),
+ #$loc->N("For remote media, XML meta-data are never downloaded."),
+ #'',
+ #$loc->N("On-demand"),
+ #$loc->N("(This is the default)"),
+ #$loc->N("The specific XML info file is downloaded when clicking on package."),
+ #'',
+ #$loc->N("Update-only"),
+ #$loc->N("Updating media implies updating XML info files already required at least once."),
+ #'',
+ #$loc->N("Always"),
+ #$loc->N("All XML info files are downloaded when adding or updating media."),
+
+ $factory->createVSpacing($vbox, 0.5);
+
+
+ ### last line buttons
+ $factory->createVSpacing($vbox, 0.5);
+ $hbox = $factory->createHBox($vbox);
+ my $cancelButton = $factory->createPushButton($hbox, $loc->N("&Cancel"));
+ $factory->createHSpacing($hbox, 3.0);
+ my $okButton = $factory->createPushButton($hbox, $loc->N("&Ok"));
+
+ $cancelButton->setDefaultButton(1);
+
+ # dialog event loop
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### widget
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $okButton) {
+ my $changed = 0;
+ my $item = $verify_rpm->selectedItem();
+ if ($item->index() != $verify_rpm_value) {
+ $changed = 1;
+ $urpm->{global_config}{'verify-rpm'} = $item->index();
+ }
+ $item = $downloader_entry->selectedItem();
+ if ($item->label() ne $downloader) {
+ $changed = 1;
+ $urpm->{global_config}{downloader} = $item->label();
+ }
+ $item = $xml_info_policy->selectedItem();
+ if ($xml_info_policies[$item->index()] ne $xml_info_policy_value) {
+ $changed = 1;
+ $urpm->{global_config}{'xml-info'} = $xml_info_policies[$item->index()];
+ }
+ if ($changed) {
+ urpm::media::write_config($urpm);
+ $urpm = fast_open_urpmi_db();
+ }
+
+ last;
+ }
+ }
+ }
+
+ ### End ###
+
+ $dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+
+}
+
+#=============================================================
+
+=head2 remove_callback
+
+=head3 INPUT
+
+$selection: YItemCollection (selected items)
+
+
+=head3 DESCRIPTION
+
+Remove the selected medias
+
+=cut
+
+#=============================================================
+
+sub remove_callback {
+ my $selection = shift;
+
+ my @rows;
+ for (my $it = 0; $it < $selection->size(); $it++) {
+ my $item = $selection->get($it);
+ push @rows, $item->index();
+ }
+ @rows == 0 and return 0;
+ interactive_msg(
+ $loc->N("Source Removal"),
+ @rows == 1 ?
+ $loc->N("Are you sure you want to remove source \"%s\"?", $urpm->{media}[$rows[0]]{name}) :
+ $loc->N("Are you sure you want to remove the following sources?") . "\n\n" .
+ format_list(map { $urpm->{media}[$_]{name} } @rows),
+ yesno => 1, scroll => 1,
+ ) or return 0;
+
+ my $wait = wait_msg($loc->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;
+}
+
+
+#=============================================================
+
+=head2 upwards_callback
+
+=head3 INPUT
+
+$table: Mirror table (YTable)
+
+=head3 DESCRIPTION
+
+Move selected item to high priority level
+
+=cut
+
+#=============================================================
+sub upwards_callback {
+ my $table = shift;
+
+ ## get the first
+ my $item = $table->selectedItem();
+ !$item and return 0;
+ return 0 if ($item->index() == 0);
+ my $row = $item->index();
+ my @media = ( $urpm->{media}[$row-1], $urpm->{media}[$row]);
+ $urpm->{media}[$row] = $media[0];
+ $urpm->{media}[$row-1] = $media[1];
+
+ urpm::media::write_config($urpm);
+ $urpm = fast_open_urpmi_db();
+ return $row - 1;
+}
+
+#=============================================================
+
+=head2 downwards_callback
+
+=head3 INPUT
+
+$table: Mirror table (YTable)
+
+=head3 DESCRIPTION
+
+Move selected item to low priority level
+
+=cut
+
+#=============================================================
+sub downwards_callback {
+ my $table = shift;
+
+ ## get the first
+ my $item = $table->selectedItem();
+ !$item and return 0;
+ my $row = $item->index();
+ return $row if ($row >= $table->itemsCount()-1);
+
+ my @media = ( $urpm->{media}[$row], $urpm->{media}[$row+1]);
+ $urpm->{media}[$row+1] = $media[0];
+ $urpm->{media}[$row] = $media[1];
+
+ urpm::media::write_config($urpm);
+ $urpm = fast_open_urpmi_db();
+ return $row + 1;
+}
+
+#- returns 1 if something changed to force readMedia()
+sub edit_callback {
+ my $table = shift;
+
+ my $changed = 0;
+ ## get the first
+ my $item = $table->selectedItem();
+ !$item and return 0;
+ my $row = $item->index();
+
+ 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 $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($loc->N("Edit a medium"));
+
+ my $factory = yui::YUI::widgetFactory;
+
+ my $dialog = $factory->createPopupDialog();
+ my $minSize = $factory->createMinSize( $dialog, 80, 5 );
+ my $vbox = $factory->createVBox( $minSize );
+
+ my $hbox = $factory->createHBox( $factory->createLeft($vbox) );
+ $factory->createHeading($hbox, $loc->N("Editing medium \"%s\":", $medium->{name}));
+ $factory->createVSpacing($vbox, 1.0);
+
+ $hbox = $factory->createHBox($vbox);
+ $factory->createHSpacing($hbox, 1.0);
+ my $label = $factory->createLabel($hbox, $loc->N("URL:"));
+ my $url_entry = $factory->createInputField($hbox, "", 0);
+ $url_entry->setWeight($yui::YD_HORIZ, 2);
+ $url_entry->setValue($verbatim_medium->{url} || $verbatim_medium->{mirrorlist});
+
+ $hbox = $factory->createHBox($vbox);
+ $factory->createHSpacing($hbox, 1.0);
+ $label = $factory->createLabel($hbox, $loc->N("Downloader:") );
+ my $downloader_entry = $factory->createComboBox($hbox, "", 0);
+ $downloader_entry->setWeight($yui::YD_HORIZ, 2);
+
+ my @comboList = urpm::download::available_ftp_http_downloaders() ;
+ my $downloader = $verbatim_medium->{downloader} || $urpm->{global_config}{downloader} || $comboList[0];
+
+ if (scalar(@comboList) > 0) {
+ my $itemColl = new yui::YItemCollection;
+ foreach my $elem (@comboList) {
+ my $it = new yui::YItem($elem, 0);
+ if ($elem eq $downloader) {
+ $it->setSelected(1);
+ }
+ $itemColl->push($it);
+ $it->DISOWN();
+ }
+ $downloader_entry->addItems($itemColl);
+ }
+ $factory->createVSpacing($vbox, 0.5);
+
+ my $url = $url_entry->value();
+
+ $hbox = $factory->createHBox($factory->createLeft($vbox));
+ $factory->createHSpacing($hbox, 1.0);
+
+ my $tableItem = yui::toYTableItem($item);
+ # enabled cell 0, updates cell 1
+ my $cellEnabled = $tableItem->cell(0)->label() ? 1 : 0;
+ my $enabled = $factory->createCheckBox($hbox, $loc->N("Enabled"), $cellEnabled);
+ my $cellUpdates = $tableItem->cell(1)->label() ? 1 : 0;
+ my $update = $factory->createCheckBox($hbox, $loc->N("Updates"), $cellUpdates);
+ $update->setDisabled() if (!$::expert);
+
+ $factory->createVSpacing($vbox, 0.5);
+ $hbox = $factory->createHBox($vbox);
+ my $cancelButton = $factory->createPushButton($hbox, $loc->N("&Cancel"));
+ $factory->createHSpacing($hbox, 3.0);
+ my $saveButton = $factory->createPushButton($hbox, $loc->N("&OK"));
+ $factory->createHSpacing($hbox, 3.0);
+ my $proxyButton = $factory->createPushButton($hbox, $loc->N("&Proxy..."));
+
+ $cancelButton->setDefaultButton(1);
+
+ # dialog event loop
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### widget
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $saveButton) {
+ if ($cellEnabled != $enabled->value()) {
+ $urpm->{media}[$row]{ignore} = !$urpm->{media}[$row]{ignore} || undef;
+ $changed = 1;
+ }
+ if ($cellUpdates != $update->value()) {
+ $urpm->{media}[$row]{update} = !$urpm->{media}[$row]{update} || undef;
+ $changed = 1;
+ }
+ if ( $changed ) {
+ urpm::media::write_config($urpm);
+ }
+
+ my ($m_name, $m_update) = map { $medium->{$_} } qw(name update);
+ # TODO check if really changed first
+ $url = $url_entry->value();
+ $downloader = $downloader_entry->value();
+ $url =~ m|^removable://| and (
+ interactive_msg(
+ $loc->N("You need to insert the medium to continue"),
+ $loc->N("In order to save the changes, you need to insert the medium in the drive."),
+ yesno => 1, text => { yes => $loc->N("Ok"), no => $loc->N("Cancel") }
+ ) or return 0
+ );
+ my $saved_proxy = urpm::download::get_proxy($m_name);
+ undef $saved_proxy if !defined $saved_proxy->{http_proxy} && !defined $saved_proxy->{ftp_proxy};
+ urpm::media::select_media($urpm, $m_name);
+ if (my ($media) = grep { $_->{name} eq $m_name } @{$urpm->{media}}) {
+ MDK::Common::DataStructure::put_in_hash($media, {
+ ($verbatim_medium->{mirrorlist} ? 'mirrorlist' : 'url') => $url,
+ name => $m_name,
+ if_($m_update && $m_update ne $media->{update} || $m_update, update => $m_update),
+ if_($saved_proxy && $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);
+ update_sources_noninteractive($urpm, [ $m_name ]);
+ } else {
+ urpm::media::remove_selected_media($urpm);
+ add_medium_and_check($urpm, { nolock => 1, proxy => $saved_proxy }, $m_name, $url, undef, update => $m_update, if_($downloader, downloader => $downloader));
+ }
+ last;
+ }
+ elsif ($widget == $proxyButton) {
+ proxy_callback($medium);
+ }
+ }
+ }
+### End ###
+
+ $dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+
+ return $changed;
+}
+
+sub update_callback() {
+ update_sources_interactive($urpm, transient => $::main_window, nolock => 1);
+}
+
+#=============================================================
+
+=head2 proxy_callback
+
+=head3 INPUT
+
+$medium: the medium which proxy is going to be modified
+
+=head3 DESCRIPTION
+
+Set or change the proxy settings for the given media.
+Note that Ok button saves the changes.
+
+=cut
+
+#=============================================================
+sub proxy_callback {
+ my ($medium) = @_;
+ my $medium_name = $medium ? $medium->{name} : '';
+
+ my ($proxy, $proxy_user) = readproxy($medium_name);
+ my ($user, $pass) = $proxy_user =~ /^([^:]*):(.*)$/;
+
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($loc->N("Configure proxies"));
+
+ my $factory = yui::YUI::widgetFactory;
+
+ my $dialog = $factory->createPopupDialog();
+ my $minSize = $factory->createMinSize( $dialog, 80, 5 );
+ my $vbox = $factory->createVBox( $minSize );
+
+ my $hbox = $factory->createHBox( $factory->createLeft($vbox) );
+ $factory->createHeading($hbox,
+ $medium_name
+ ? $loc->N("Proxy settings for media \"%s\"", $medium_name)
+ : $loc->N("Global proxy settings"));
+ $factory->createVSpacing($vbox, 0.5);
+
+ $hbox = $factory->createHBox($vbox);
+ $factory->createHSpacing($hbox, 1.0);
+ my $label = $factory->createLabel($hbox, $loc->N("If you need a proxy, enter the hostname and an optional port (syntax: <proxyhost[:port]>):"));
+ $factory->createVSpacing($vbox, 0.5);
+
+ my ($proxybutton, $proxyentry, $proxyuserbutton, $proxyuserentry, $proxypasswordentry);
+
+ $hbox = $factory->createHBox($factory->createLeft($vbox));
+ $proxybutton = $factory->createCheckBoxFrame($hbox, $loc->N("Enable proxy"), 1);
+ my $frm_vbox = $factory->createVBox( $proxybutton );
+ my $align = $factory->createRight($frm_vbox);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $loc->N("Proxy hostname:") );
+ $proxyentry = $factory->createInputField($hbox, "", 0);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $proxyentry->setWeight($yui::YD_HORIZ, 2);
+ $proxyuserbutton = $factory->createCheckBoxFrame($factory->createLeft($frm_vbox),
+ $loc->N("You may specify a username/password for the proxy authentication:"), 1);
+ $proxyentry->setValue($proxy) if $proxy;
+
+ $frm_vbox = $factory->createVBox( $proxyuserbutton );
+
+ ## proxy user name
+ $align = $factory->createRight($frm_vbox);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $loc->N("User:") );
+ $proxyuserentry = $factory->createInputField($hbox, "", 0);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $proxyuserentry->setWeight($yui::YD_HORIZ, 2);
+ $proxyuserentry->setValue($user) if $user;
+
+ ## proxy user password
+ $align = $factory->createRight($frm_vbox);
+ $hbox = $factory->createHBox($align);
+ $label = $factory->createLabel($hbox, $loc->N("Password:") );
+ $proxypasswordentry = $factory->createInputField($hbox, "", 1);
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $proxypasswordentry->setWeight($yui::YD_HORIZ, 2);
+ $proxypasswordentry->setValue($pass) if $pass;
+
+ $proxyuserbutton->setValue ($proxy_user ? 1 : 0);
+ $proxybutton->setValue ($proxy ? 1 : 0);
+
+ # dialog low level buttons
+ $factory->createVSpacing($vbox, 0.5);
+ $hbox = $factory->createHBox($vbox);
+ my $okButton = $factory->createPushButton($hbox, $loc->N("&Ok"));
+ $factory->createHSpacing($hbox, 3.0);
+ my $cancelButton = $factory->createPushButton($hbox, $loc->N("&Cancel"));
+
+ $cancelButton->setDefaultButton(1);
+
+ # dialog event loop
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### widget
+ my $widget = $event->widget();
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $okButton) {
+ $proxy = $proxybutton->value() ? $proxyentry->value() : '';
+ $proxy_user = $proxyuserbutton->value()
+ ? ($proxyuserentry->value() . ':' . $proxypasswordentry->value()) : '';
+
+ writeproxy($proxy, $proxy_user, $medium_name);
+ last;
+ }
+ }
+ }
+
+### End ###
+ $dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+
+}
+
+sub parallel_read_sysconf() {
+ my @conf;
+ foreach (MDK::Common::File::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 => $loc->N("Ok"), clicked => sub { $w->{retval} = 1; $get_value->(); Gtk2->main_quit }),
+ gtknew('Button', text => $loc->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 ? $loc->N("Add a parallel group") : $loc->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_($loc->N("Add a medium limit"), $loc->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_($loc->N("Add a host"), $loc->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 => $loc->N("Editing parallel group \"%s\":", $edited->{name}))
+ ),
+ 1, create_packtable(
+ {},
+ [ $loc->N("Group name:"), $name_entry = gtkentry($edited->{name}) ],
+ [ $loc->N("Protocol:"), gtknew('HBox', children_tight => [
+ @protocols = gtkradio($edited->{protocol}, @protocols_names) ]) ],
+ [ $loc->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($loc->N("Add"))), clicked => sub { $add_media->() }),
+ gtksignal_connect(Gtk2::Button->new(but($loc->N("Remove"))), clicked => sub {
+ remove_from_list($medias, $edited->{medias}, $medias_ls);
+ }) ]) ]) ],
+ [ $loc->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($loc->N("Add"))), clicked => sub { $add_host->() }),
+ gtksignal_connect(Gtk2::Button->new(but($loc->N("Remove"))), clicked => sub {
+ remove_from_list($hosts, $hosts_list, $hosts_ls);
+ }) ]) ]) ]
+ ),
+ 0, gtknew('HSeparator'),
+ 0, gtkpack(
+ gtknew('HButtonBox'),
+ gtksignal_connect(
+ gtknew('Button', text => $loc->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 => $loc->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($loc->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)) } $loc->N("Group"), $loc->N("Protocol"), $loc->N("Media limit");
+ $list->append_column(my $commandcol = Gtk2::TreeViewColumn->new_with_attributes($loc->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}}) : $loc->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($loc->N("Remove"))),
+ clicked => sub { remove_parallel(selrow($list), $conf); $reread->() },
+ ),
+ gtksignal_connect(
+ Gtk2::Button->new(but($loc->N("Edit..."))),
+ clicked => sub {
+ my $row = selrow($list);
+ $row != -1 and edit_parallel($row, $conf);
+ $reread->();
+ },
+ ),
+ gtksignal_connect(
+ Gtk2::Button->new(but($loc->N("Add..."))),
+ clicked => sub { edit_parallel(-1, $conf) and $reread->() },
+ )
+ )
+ ),
+ 0, gtknew('HSeparator'),
+ 0, gtkpack(
+ gtknew('HButtonBox'),
+ gtknew('Button', text => $loc->N("Ok"), clicked => sub { Gtk2->main_quit })
+ )
+ )
+ );
+ $w->main;
+}
+
+sub keys_callback() {
+ my $changed = 0;
+ my $appTitle = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($loc->N("Manage keys for digital signatures of packages"));
+
+ my $factory = yui::YUI::widgetFactory;
+
+ my $dialog = $factory->createPopupDialog();
+ my $minSize = $factory->createMinSize( $dialog, 80, 20 );
+ my $vbox = $factory->createVBox( $minSize );
+
+ my $hbox_headbar = $factory->createHBox($vbox);
+ my $head_align_left = $factory->createLeft($hbox_headbar);
+ my $head_align_right = $factory->createRight($hbox_headbar);
+ my $headbar = $factory->createHBox($head_align_left);
+ my $headRight = $factory->createHBox($head_align_right);
+
+
+ my $hbox_content = $factory->createHBox($vbox);
+ my $leftContent = $factory->createLeft($hbox_content);
+ $leftContent->setWeight($yui::YD_HORIZ,3);
+
+ my $frame = $factory->createFrame ($leftContent, "");
+
+ my $frmVbox = $factory->createVBox( $frame );
+ my $hbox = $factory->createHBox( $frmVbox );
+
+ ## media list
+ my $yTableHeader = new yui::YTableHeader();
+ $yTableHeader->addColumn($loc->N("Medium"), $yui::YAlignBegin);
+ my $multiselection = 0;
+ my $mediaTbl = $factory->createTable($hbox, $yTableHeader, $multiselection);
+ $mediaTbl->setKeepSorting(1);
+ $mediaTbl->setImmediateMode(1);
+
+ my $itemColl = new yui::YItemCollection;
+ foreach (@{$urpm->{media}}) {
+ my $name = $_->{name};
+
+ my $item = new yui::YTableItem ($name);
+ # NOTE row is $item->index()
+ $item->setLabel( $name );
+ $itemColl->push($item);
+ $item->DISOWN();
+ }
+ $mediaTbl->addItems($itemColl);
+
+ ## key list
+ $leftContent = $factory->createLeft($hbox_content);
+ $leftContent->setWeight($yui::YD_HORIZ,3);
+ $frame = $factory->createFrame ($leftContent, "");
+ $frmVbox = $factory->createVBox( $frame );
+ $hbox = $factory->createHBox( $frmVbox );
+ $yTableHeader = new yui::YTableHeader();
+ $yTableHeader->addColumn($loc->N("Keys"), $yui::YAlignBegin);
+ $multiselection = 0;
+ my $keyTbl = $factory->createTable($hbox, $yTableHeader, $multiselection);
+ $keyTbl->setKeepSorting(1);
+
+ my ($current_medium, $current_medium_nb, @keys);
+
+ ### internal subroutines
+ 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->();
+ };
+
+ $read_conf->();
+
+ my $key_name = sub {
+ exists $urpm->{keys}{$_[0]} ? $urpm->{keys}{$_[0]}{name}
+ : $loc->N("no name found, key doesn't exist in rpm keyring!");
+ };
+
+ my $sel_changed = sub {
+ my $item = $mediaTbl->selectedItem();
+ if ($item) {
+ $current_medium = $item->label();
+ $current_medium_nb = $item->index();
+
+ yui::YUI::app()->busyCursor();
+ yui::YUI::ui()->blockEvents();
+ $dialog->startMultipleChanges();
+
+ $keyTbl->deleteAllItems();
+ my $itemColl = new yui::YItemCollection;
+ foreach ( @{$keys[$current_medium_nb]} ) {
+ my $it = new yui::YTableItem (sprintf("%s (%s)", $_, $key_name->($_)));
+ # NOTE row is $item->index()
+ $it->setLabel( $_ );
+ $itemColl->push($it);
+ $it->DISOWN();
+ }
+ $keyTbl->addItems($itemColl);
+
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+ yui::YUI::ui()->unblockEvents();
+ yui::YUI::app()->normalCursor();
+ }
+ };
+
+ my $add_key = sub {
+ my $sh_gui = ManaTools::Shared::GUI->new();
+ my $item = $mediaTbl->selectedItem();
+ if ($item) {
+ $current_medium = $item->label();
+ $current_medium_nb = $item->index();
+ my @list;
+ my %key;
+ foreach (keys %{$urpm->{keys}}) {
+ my $k = sprintf("%s (%s)", $_, $key_name->($_));
+ $key{$k} = $_;
+ push @list, $k;
+ }
+
+ my $choice = $sh_gui->ask_fromList({
+ title => $loc->N("Add a key"),
+ header => $loc->N("Choose a key to add to the medium %s", $current_medium),
+ list => \@list,
+ });
+ if ($choice) {
+ my $k = $key{$choice};
+ $urpm->{media}[$current_medium_nb]{'key-ids'} = join(',', sort(uniq(@{$keys[$current_medium_nb]}, $k)));
+ $write->();
+ return 1;
+ }
+ }
+ return 0;
+ };
+
+ my $remove_key = sub {
+ my $sh_gui = ManaTools::Shared::GUI->new();
+ my $keyItem = $keyTbl->selectedItem();
+ my $mediaItem = $mediaTbl->selectedItem();
+ if ($keyItem && $mediaItem) {
+ $current_medium = $mediaItem->label();
+ $current_medium_nb = $mediaItem->index();
+ my $current_key = $keyItem->label();
+ my $current_keyVal = yui::toYTableItem($keyItem)->cell(0)->label();
+ my $choice = $sh_gui->ask_YesOrNo({
+ title => $loc->N("Remove a key"),
+ text => $loc->N("Are you sure you want to remove the key <br>%s<br> from medium %s?<br>(name of the key: %s)",
+ $current_keyVal, $current_medium, $current_key
+ ),
+ richtext => 1,
+ });
+ if ($choice) {
+ $urpm->{media}[$current_medium_nb]{'key-ids'} = join(',',
+ difference2(\@{$keys[$current_medium_nb]}, [ $current_key ])
+ );
+ $write->();
+ return 1;
+ }
+ }
+
+ return 0;
+
+ };
+
+
+ #### end subroutines
+ $sel_changed->();
+
+ my $rightContent = $factory->createRight($hbox_content);
+ $rightContent->setWeight($yui::YD_HORIZ,1);
+ my $topContent = $factory->createTop($rightContent);
+ my $vbox_commands = $factory->createVBox($topContent);
+ $factory->createVSpacing($vbox_commands, 1.0);
+ my $addButton = $factory->createPushButton($factory->createHBox($vbox_commands), $loc->N("Add"));
+ my $remButton = $factory->createPushButton($factory->createHBox($vbox_commands), $loc->N("Remove"));
+
+ # dialog buttons
+ $factory->createVSpacing($vbox, 1.0);
+ $hbox = $factory->createHBox( $vbox );
+
+ ### Close button
+ my $closeButton = $factory->createPushButton($hbox, $loc->N("&Quit") );
+
+ ### dialog event loop
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+ my $changed = 0;
+ my $selection_changed = 0;
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ my $wEvent = yui::toYWidgetEvent($event);
+
+ if ($widget == $closeButton) {
+ last;
+ }
+ elsif ($widget == $addButton) {
+ $changed = $add_key->();
+ $sel_changed->() if $changed;
+ }
+ elsif ($widget == $remButton) {
+ $changed = $remove_key->();
+ $sel_changed->() if $changed;
+ }
+ elsif ($widget == $mediaTbl) {
+ $sel_changed->();
+ }
+ }
+ }
+
+ $dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+
+ return $changed;
+}
+
+#=============================================================
+
+=head2 readMedia
+
+=head3 INPUT
+
+$name: optional parameter, the media called name has to be
+ updated
+
+=head3 OUTPUT
+
+$itemColl: yui::YItemCollection containing media data to
+ be added to the YTable
+
+=head3 DESCRIPTION
+
+This method reads the configured media and add their info
+to the collection
+
+=cut
+
+#=============================================================
+sub readMedia {
+ my ($name) = @_;
+ if (defined $name) {
+ urpm::media::select_media($urpm, $name);
+ update_sources_check(
+ $urpm,
+ { nolock => 1 },
+ $loc->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();
+
+ my $itemColl = new yui::YItemCollection;
+ foreach (grep { ! $_->{external} } @{$urpm->{media}}) {
+ my $name = $_->{name};
+
+ my $item = new yui::YTableItem (($_->{ignore} ? "" : "X"),
+ ($_->{update} ? "X" : ""),
+ get_medium_type($_),
+ $name);
+ ## NOTE anaselli: next lines add check icon to cells, but they are 8x8, a dimension should
+ ## be evaluated by font size, so disabled atm
+# my $cell = $item->cell(0); # Checked
+# my $checkedIcon = File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/Check_8x8.png');
+#
+# $cell->setIconName($checkedIcon) if (!$_->{ignore});
+# $cell = $item->cell(1); # Updates
+# $cell->setIconName($checkedIcon) if ($_->{update});
+ ## end icons on cells
+
+ # TODO manage to_bool($::expert)
+ # row # is $item->index()
+ $item->setLabel( $name );
+ $itemColl->push($item);
+ $item->DISOWN();
+ }
+
+ return $itemColl;
+}
+
+#=============================================================
+
+=head2 selectRow
+
+=head3 INPUT
+
+$itemCollection: YItem collection in which to find the item that
+ has to be selected
+$row: line to be selected
+
+=head3 DESCRIPTION
+
+Select item at row position
+=cut
+
+#=============================================================
+
+sub selectRow {
+ my ($itemCollection, $row) = @_;
+
+ return if !$itemCollection;
+
+ for (my $it = 0; $it < $itemCollection->size(); $it++) {
+ my $item = $itemCollection->get($it);
+ if ($it == $row) {
+ $item->setSelected(1);
+ return;
+ }
+ }
+}
+
+#=============================================================
+
+=head2 _showMediaStatus
+
+=head3 INPUT
+
+$info: HASH reference containing
+ item => selected item
+ updates => updates checkbox widget
+ enabled => enabled checkbox widget
+
+=head3 DESCRIPTION
+
+This internal function enables/disables checkboxes according to the
+passed item value.
+
+=cut
+
+#=============================================================
+sub _showMediaStatus {
+ my $info = shift;
+
+ die "Updates checkbox is mandatory" if !defined($info->{updates}) || !$info->{updates};
+ die "Enabled checkbox is mandatory" if !defined($info->{enabled}) || !$info->{enabled};
+
+ if (defined($info->{item})) {
+ my $tableItem = yui::toYTableItem($info->{item});
+ # enabled cell 0, updates cell 1
+ my $cellEnabled = $tableItem && $tableItem->cell(0)->label() ? 1 : 0;
+ my $cellUpdates = $tableItem && $tableItem->cell(1)->label() ? 1 : 0;
+ $info->{enabled}->setValue($cellEnabled);
+ $info->{updates}->setValue($cellUpdates);
+ }
+ else {
+ $info->{enabled}->setDisabled();
+ $info->{updates}->setDisabled();
+ }
+}
+
+sub mainwindow() {
+
+ my $something_changed = 0;
+ my $appTitle = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($loc->N("Configure media"));
+ ## set icon if not already set by external launcher TODO
+ yui::YUI::app()->setApplicationIcon("/usr/share/mcc/themes/default/rpmdrake-mdk.png");
+
+ my $mageiaPlugin = "mga";
+ my $factory = yui::YUI::widgetFactory;
+
+ my $dialog = $factory->createMainDialog;
+ my $vbox = $factory->createVBox( $dialog );
+
+ my $hbox_headbar = $factory->createHBox($vbox);
+ my $head_align_left = $factory->createLeft($hbox_headbar);
+ my $head_align_right = $factory->createRight($hbox_headbar);
+ my $headbar = $factory->createHBox($head_align_left);
+ my $headRight = $factory->createHBox($head_align_right);
+
+ my %fileMenu = (
+ widget => $factory->createMenuButton($headbar,$loc->N("File")),
+ update => new yui::YMenuItem($loc->N("Update")),
+ add_media => new yui::YMenuItem($loc->N("Add a specific media mirror")),
+ custom => new yui::YMenuItem($loc->N("Add a custom medium")),
+ quit => new yui::YMenuItem($loc->N("&Quit")),
+ );
+
+ my @ordered_menu_lines = qw(update add_media custom quit);
+ foreach (@ordered_menu_lines) {
+ $fileMenu{ widget }->addItem($fileMenu{ $_ });
+ }
+ $fileMenu{ widget }->rebuildMenuTree();
+
+ my %optionsMenu = (
+ widget => $factory->createMenuButton($headbar, $loc->N("&Options")),
+ global => new yui::YMenuItem($loc->N("Global options")),
+ man_keys => new yui::YMenuItem($loc->N("Manage keys")),
+ parallel => new yui::YMenuItem($loc->N("Parallel")),
+ proxy => new yui::YMenuItem($loc->N("Proxy")),
+ );
+ @ordered_menu_lines = qw(global man_keys parallel proxy);
+ foreach (@ordered_menu_lines) {
+ $optionsMenu{ widget }->addItem($optionsMenu{ $_ });
+ }
+ $optionsMenu{ widget }->rebuildMenuTree();
+
+ my %helpMenu = (
+ widget => $factory->createMenuButton($headRight, $loc->N("&Help")),
+ help => new yui::YMenuItem($loc->N("Manual")),
+ report_bug => new yui::YMenuItem($loc->N("Report Bug")),
+ about => new yui::YMenuItem($loc->N("&About")),
+ );
+ @ordered_menu_lines = qw(help report_bug about);
+ foreach (@ordered_menu_lines) {
+ $helpMenu{ widget }->addItem($helpMenu{ $_ });
+ }
+ $helpMenu{ widget }->rebuildMenuTree();
+
+# my %contextMenu = (
+# enable => $loc->N("Enable/Disable"),
+# update => $loc->N("Check as updates"),
+# );
+# @ordered_menu_lines = qw(enable update);
+# my $itemColl = new yui::YItemCollection;
+# foreach (@ordered_menu_lines) {
+# # last if (!$::expert && $_ eq "update");
+# my $item = new yui::YMenuItem($contextMenu{$_});
+# $item->DISOWN();
+# $itemColl->push($item);
+# }
+# yui::YUI::app()->openContextMenu($itemColl) or die "Cannot create contextMenu";
+
+ my $hbox_content = $factory->createHBox($vbox);
+ my $leftContent = $factory->createLeft($hbox_content);
+ $leftContent->setWeight($yui::YD_HORIZ,3);
+
+ my $frame = $factory->createFrame ($leftContent, "");
+
+ my $frmVbox = $factory->createVBox( $frame );
+ my $hbox = $factory->createHBox( $frmVbox );
+
+ my $yTableHeader = new yui::YTableHeader();
+ $yTableHeader->addColumn($loc->N("Enabled"), $yui::YAlignCenter);
+ $yTableHeader->addColumn($loc->N("Updates"), $yui::YAlignCenter);
+ $yTableHeader->addColumn($loc->N("Type"), $yui::YAlignBegin);
+ $yTableHeader->addColumn($loc->N("Medium"), $yui::YAlignBegin);
+
+ ## mirror list
+ my $multiselection = 1;
+ my $mirrorTbl = $factory->createTable($hbox, $yTableHeader, $multiselection);
+ $mirrorTbl->setKeepSorting(1);
+ $mirrorTbl->setImmediateMode(1);
+
+ my $itemCollection = readMedia();
+ selectRow($itemCollection, 0); #default selection
+ $mirrorTbl->addItems($itemCollection);
+
+ my $rightContent = $factory->createRight($hbox_content);
+ $rightContent->setWeight($yui::YD_HORIZ,1);
+ my $topContent = $factory->createTop($rightContent);
+ my $vbox_commands = $factory->createVBox($topContent);
+ $factory->createVSpacing($vbox_commands, 1.0);
+ my $remButton = $factory->createPushButton($factory->createHBox($vbox_commands), $loc->N("Remove"));
+ my $edtButton = $factory->createPushButton($factory->createHBox($vbox_commands), $loc->N("Edit"));
+ my $addButton = $factory->createPushButton($factory->createHBox($vbox_commands), $loc->N("Add"));
+
+ $hbox = $factory->createHBox( $vbox_commands );
+ my $item = $mirrorTbl->selectedItem();
+ $factory->createHSpacing($hbox, 1.0);
+ my $enabled = $factory->createCheckBox($factory->createLeft($hbox), $loc->N("Enabled"));
+ my $update = $factory->createCheckBox($factory->createLeft($hbox), $loc->N("Updates"));
+ _showMediaStatus({item => $item, enabled => $enabled, updates => $update});
+ $update->setNotify(1);
+ $enabled->setNotify(1);
+ $update->setDisabled() if (!$::expert);
+
+ $hbox = $factory->createHBox( $vbox_commands );
+ ## TODO icon and label for ncurses
+ my $upIcon = File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/Up_16x16.png');
+ my $downIcon = File::ShareDir::dist_file(ManaTools::Shared::distName(), 'images/Down_16x16.png');
+ my $upButton = $factory->createPushButton($factory->createHBox($hbox), $loc->N("Up"));
+ my $downButton = $factory->createPushButton($factory->createHBox($hbox), $loc->N("Down"));
+ $upButton->setIcon($upIcon);
+ $downButton->setIcon($downIcon);
+
+ $addButton->setWeight($yui::YD_HORIZ,1);
+ $edtButton->setWeight($yui::YD_HORIZ,1);
+ $remButton->setWeight($yui::YD_HORIZ,1);
+ $upButton->setWeight($yui::YD_HORIZ,1);
+ $downButton->setWeight($yui::YD_HORIZ,1);
+
+
+ # dialog buttons
+ $factory->createVSpacing($vbox, 1.0);
+ ## Window push buttons
+ $hbox = $factory->createHBox( $vbox );
+ my $align = $factory->createLeft($hbox);
+ $hbox = $factory->createHBox($align);
+
+ my $helpButton = $factory->createPushButton($hbox, $loc->N("Help") );
+ $align = $factory->createRight($hbox);
+ $hbox = $factory->createHBox($align);
+
+ ### Close button
+ my $closeButton = $factory->createPushButton($hbox, $loc->N("&Quit") );
+
+ ### dialog event loop
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+ my $changed = 0;
+ my $selection_changed = 0;
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::MenuEvent) {
+ ### MENU ###
+ my $item = $event->item();
+ my $menuLabel = $item->label();
+ if ($menuLabel eq $fileMenu{ quit }->label()) {
+ last;
+ }
+ elsif ($menuLabel eq $helpMenu{ about }->label()) {
+ my $translators = $loc->N("_: Translator(s) name(s) & email(s)\n");
+ $translators =~ s/\</\&lt\;/g;
+ $translators =~ s/\>/\&gt\;/g;
+ my $sh_gui = ManaTools::Shared::GUI->new();
+ $sh_gui->AboutDialog({ name => "Rpmdragora",
+ version => $VERSION,
+ credits => $loc->N("Copyright (C) %s Mageia community", '2013-2014'),
+ license => $loc->N("GPLv2"),
+ description => $loc->N("Rpmdragora is the Mageia package management tool."),
+ authors => $loc->N("<h3>Developers</h3>
+ <ul><li>%s</li>
+ <li>%s</li>
+ </ul>
+ <h3>Translators</h3>
+ <ul><li>%s</li></ul>",
+ "Angelo Naselli &lt;anaselli\@linux.it&gt;",
+ "Matteo Pasotti &lt;matteo.pasotti\@gmail.com&gt;",
+ $translators
+ ),
+ }
+ );
+ }
+ elsif ($menuLabel eq $fileMenu{ update }->label()) {
+ update_callback();
+ }
+ elsif ($menuLabel eq $fileMenu{ add_media }->label()) {
+ $changed = easy_add_callback_with_mirror();
+ }
+ elsif ($menuLabel eq $fileMenu{ custom }->label()) {
+ $changed = add_callback();
+ }
+ elsif ($menuLabel eq $optionsMenu{ proxy }->label()) {
+ proxy_callback();
+ }
+ elsif ($menuLabel eq $optionsMenu{ global }->label()) {
+ options_callback();
+ }
+ elsif ($menuLabel eq $optionsMenu{ man_keys }->label()) {
+ $changed = keys_callback();
+ }
+ elsif ($menuLabel eq $optionsMenu{ parallel }->label()) {
+# parallel_callback();
+ }
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ my $wEvent = yui::toYWidgetEvent($event);
+
+ if ($widget == $closeButton) {
+ last;
+ }
+ elsif ($widget == $helpButton) {
+ }
+ elsif ($widget == $upButton) {
+ yui::YUI::app()->busyCursor();
+ yui::YUI::ui()->blockEvents();
+ $dialog->startMultipleChanges();
+
+ my $row = upwards_callback($mirrorTbl);
+
+ $mirrorTbl->deleteAllItems();
+ my $itemCollection = readMedia();
+ selectRow($itemCollection, $row);
+ $mirrorTbl->addItems($itemCollection);
+
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+ yui::YUI::ui()->unblockEvents();
+ yui::YUI::app()->normalCursor();
+ }
+ elsif ($widget == $downButton) {
+ yui::YUI::app()->busyCursor();
+ yui::YUI::ui()->blockEvents();
+ $dialog->startMultipleChanges();
+
+ my $row = downwards_callback($mirrorTbl);
+
+ $mirrorTbl->deleteAllItems();
+ my $itemCollection = readMedia();
+ selectRow($itemCollection, $row);
+ $mirrorTbl->addItems($itemCollection);
+
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+ yui::YUI::ui()->unblockEvents();
+ yui::YUI::app()->normalCursor();
+ }
+ elsif ($widget == $edtButton) {
+ my $item = $mirrorTbl->selectedItem();
+ if ($item && edit_callback($mirrorTbl) ) {
+ my $row = $item->index();
+ yui::YUI::app()->busyCursor();
+ yui::YUI::ui()->blockEvents();
+
+ $dialog->startMultipleChanges();
+
+ my $ignored = $urpm->{media}[$row]{ignore};
+ my $itemCollection = readMedia();
+ if (!$ignored && $urpm->{media}[$row]{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}[$row]{ignore} = !$urpm->{media}[$row]{ignore} || undef;
+ urpm::media::write_config($urpm);
+ #- Enabling this media failed, force update
+ interactive_msg('rpmdragora',
+ $loc->N("This medium needs to be updated to be usable. Update it now?"),
+ yesno => 1,
+ ) and $itemCollection = readMedia($urpm->{media}[$row]{name});
+ }
+ $mirrorTbl->deleteAllItems();
+ selectRow($itemCollection, $row);
+ $mirrorTbl->addItems($itemCollection);
+
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+ yui::YUI::ui()->unblockEvents();
+ yui::YUI::app()->normalCursor();
+ $selection_changed = 1; # to align $enabled and $update status
+ }
+ }
+ elsif ($widget == $remButton) {
+ my $sel = $mirrorTbl->selectedItems();
+ $changed = remove_callback($sel);
+ }
+ elsif ($widget == $addButton) {
+ $changed = easy_add_callback();
+ }
+ elsif ($widget == $update) {
+ my $item = $mirrorTbl->selectedItem();
+ if ($item) {
+ yui::YUI::app()->busyCursor();
+ my $row = $item->index();
+ $urpm->{media}[$row]{update} = !$urpm->{media}[$row]{update} || undef;
+ urpm::media::write_config($urpm);
+ yui::YUI::ui()->blockEvents();
+ $dialog->startMultipleChanges();
+ $mirrorTbl->deleteAllItems();
+ my $itemCollection = readMedia();
+ selectRow($itemCollection, $row);
+ $mirrorTbl->addItems($itemCollection);
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+ yui::YUI::ui()->unblockEvents();
+ yui::YUI::app()->normalCursor();
+ }
+ }
+ elsif ($widget == $enabled) {
+ ## TODO same as $edtButton after edit_callback
+ my $item = $mirrorTbl->selectedItem();
+ if ($item) {
+ my $row = $item->index();
+ yui::YUI::app()->busyCursor();
+ yui::YUI::ui()->blockEvents();
+
+ $dialog->startMultipleChanges();
+
+ $urpm->{media}[$row]{ignore} = !$urpm->{media}[$row]{ignore} || undef;
+ urpm::media::write_config($urpm);
+ my $ignored = $urpm->{media}[$row]{ignore};
+ my $itemCollection = readMedia();
+ if (!$ignored && $urpm->{media}[$row]{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}[$row]{ignore} = !$urpm->{media}[$row]{ignore} || undef;
+ urpm::media::write_config($urpm);
+ #- Enabling this media failed, force update
+ interactive_msg('rpmdragora',
+ $loc->N("This medium needs to be updated to be usable. Update it now?"),
+ yesno => 1,
+ ) and $itemCollection = readMedia($urpm->{media}[$row]{name});
+ }
+ $mirrorTbl->deleteAllItems();
+ selectRow($itemCollection, $row);
+ $mirrorTbl->addItems($itemCollection);
+
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+ yui::YUI::ui()->unblockEvents();
+ yui::YUI::app()->normalCursor();
+ }
+ }
+ elsif ($widget == $mirrorTbl) {
+ $selection_changed = 1;
+ }
+ }
+ if ($changed) {
+ yui::YUI::app()->busyCursor();
+ yui::YUI::ui()->blockEvents();
+
+ $dialog->startMultipleChanges();
+
+ $mirrorTbl->deleteAllItems();
+ my $itemCollection = readMedia();
+ selectRow($itemCollection, 0); #default selection
+ $mirrorTbl->addItems($itemCollection);
+
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+ yui::YUI::app()->normalCursor();
+ $selection_changed = 1;
+ }
+ if ($selection_changed) {
+ yui::YUI::ui()->blockEvents();
+ my $item = $mirrorTbl->selectedItem();
+ _showMediaStatus({item => $item, enabled => $enabled, updates => $update});
+
+ my $sel = $mirrorTbl->selectedItems();
+ if ($sel->size() == 0 || $sel->size() > 1 ) {
+ $remButton->setEnabled(($sel->size() == 0) ? 0 : 1);
+ $edtButton->setEnabled(0);
+ $upButton->setEnabled(0);
+ $downButton->setEnabled(0);
+ $enabled->setEnabled(0);
+ $update->setEnabled(0);
+ }
+ else {
+ $remButton->setEnabled(1);
+ $edtButton->setEnabled(1);
+ $upButton->setEnabled(1);
+ $downButton->setEnabled(1);
+ $enabled->setEnabled(1);
+ $update->setEnabled(1) if $::expert;
+ }
+
+ yui::YUI::ui()->unblockEvents();
+ }
+ }
+ $dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+
+ return $something_changed;
+}
+
+
+sub OLD_mainwindow() {
+ undef $something_changed;
+ $mainw = ugtk2->new($loc->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},
+ [ $loc->N("/_File"), undef, undef, undef, '<Branch>' ],
+ [ $loc->N("/_File") . $loc->N("/_Update"), $loc->N("<control>U"), sub { update_callback() and $reread_media->() }, undef, '<Item>', ],
+ [ $loc->N("/_File") . $loc->N("/Add a specific _media mirror"), $loc->N("<control>M"), sub { easy_add_callback_with_mirror() and $reread_media->() }, undef, '<Item>' ],
+ [ $loc->N("/_File") . $loc->N("/_Add a custom medium"), $loc->N("<control>A"), sub { add_callback() and $reread_media->() }, undef, '<Item>' ],
+ [ $loc->N("/_File") . $loc->N("/Close"), $loc->N("<control>W"), sub { Gtk2->main_quit }, undef, '<Item>', ],
+ [ $loc->N("/_Options"), undef, undef, undef, '<Branch>' ],
+ [ $loc->N("/_Options") . $loc->N("/_Global options"), $loc->N("<control>G"), \&options_callback, undef, '<Item>' ],
+ [ $loc->N("/_Options") . $loc->N("/Manage _keys"), $loc->N("<control>K"), \&keys_callback, undef, '<Item>' ],
+ [ $loc->N("/_Options") . $loc->N("/_Parallel"), $loc->N("<control>P"), \&parallel_callback, undef, '<Item>' ],
+ [ $loc->N("/_Options") . $loc->N("/P_roxy"), $loc->N("<control>R"), \&proxy_callback, undef, '<Item>' ],
+ if_($0 =~ /edit-urpm-sources/,
+ [ $loc->N("/_Help"), undef, undef, undef, '<Branch>' ],
+ [ $loc->N("/_Help") . $loc->N("/_Report Bug"), undef, sub { run_drakbug('edit-urpm-sources.pl') }, undef, '<Item>' ],
+ [ $loc->N("/_Help") . $loc->N("/_Help"), undef, sub { rpmdragora::open_help('sources') }, undef, '<Item>' ],
+ [ $loc->N("/_Help") . $loc->N("/_About..."), undef, sub {
+ my $license = MDK::Common::String::formatAlaTeX(translate($::license));
+ $license =~ s/\n/\n\n/sg; # nicer formatting
+ my $w = gtknew('AboutDialog', name => $loc->N("Rpmdragora"),
+ version => $rpmdragora::distro_version,
+ copyright => $loc->N("Copyright (C) %s by Mandriva", '2002-2008'),
+ license => $license, wrap_license => 1,
+ comments => $loc->N("Rpmdragora is the Mageia package management tool."),
+ website => 'http://www.mageia.org/',
+ website_label => $loc->N("Mageia"),
+ authors => 'Thierry Vignaud <vignaud@mandriva.com>',
+ artists => 'Hรฉlรจne Durosini <ln@mandriva.com>',
+ translator_credits =>
+ #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith <jsmith@nowhere.com>")
+ $loc->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, '<Item>'
+ ]
+ ),
+ );
+
+ 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($loc->N("Enabled"),
+ my $tr = Gtk2::CellRendererToggle->new,
+ 'active' => $col{mainw}{is_enabled}));
+ $list_tv->append_column(Gtk2::TreeViewColumn->new_with_attributes($loc->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($loc->N("Type"),
+ Gtk2::CellRendererText->new,
+ 'text' => $col{mainw}{type}));
+ $list_tv->append_column(Gtk2::TreeViewColumn->new_with_attributes($loc->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',
+ $loc->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 },
+ $loc->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, $loc->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($loc->N("Remove"))),
+ clicked => sub { remove_callback() and $reread_media->() },
+ ),
+ gtksignal_connect(
+ $edit_button = Gtk2::Button->new(but($loc->N("Edit"))),
+ clicked => sub {
+ my $name = edit_callback(); defined $name and $reread_media->($name);
+ }
+ ),
+ gtksignal_connect(
+ Gtk2::Button->new(but($loc->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($loc->N("Help"))), clicked => sub { rpmdragora::open_help('sources') }),
+ gtksignal_connect(Gtk2::Button->new(but($loc->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',
+ $loc->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;
+}
+
+sub readproxy (;$) {
+ my $proxy = get_proxy($_[0]);
+ ($proxy->{http_proxy} || $proxy->{ftp_proxy} || '',
+ defined $proxy->{user} ? "$proxy->{user}:$proxy->{pwd}" : '');
+}
+
+sub writeproxy {
+ my ($proxy, $proxy_user, $o_media_name) = @_;
+ my ($user, $pwd) = split /:/, $proxy_user;
+ set_proxy_config(user => $user, $o_media_name);
+ set_proxy_config(pwd => $pwd, $o_media_name);
+ set_proxy_config(http_proxy => $proxy, $o_media_name);
+ set_proxy_config(ftp_proxy => $proxy, $o_media_name);
+ dump_proxy_config();
+}
+
+1;
diff --git a/lib/ManaTools/Rpmdragora/formatting.pm b/lib/ManaTools/Rpmdragora/formatting.pm
new file mode 100644
index 0000000..83a960a
--- /dev/null
+++ b/lib/ManaTools/Rpmdragora/formatting.pm
@@ -0,0 +1,209 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Rpmdragora::formatting;
+#*****************************************************************************
+#
+# Copyright (c) 2002 Guillaume Cottenceau
+# Copyright (c) 2002-2006 Thierry Vignaud <tvignaud@mandriva.com>
+# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
+# Copyright (c) 2005, 2006 Mandriva SA
+# Copyright (c) 2013 - 2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+# Copyright (c) 2014 - 2015 Angelo Naselli <anaselli@linux.it>
+#
+# 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 MDK::Common::Various qw(internal_error);
+use ManaTools::Shared::Locales;
+use ManaTools::rpmdragora;
+use urpm::msg;
+
+
+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
+ );
+
+my $loc = ManaTools::rpmdragora::locale();
+
+sub escape_text_for_TextView_markup_format {
+ my ($str) = @_;
+ return '' if !$str;
+
+ my %rules = ('&' => '&amp;',
+ '<' => '&lt;',
+ '>' => '&gt;',
+ );
+ eval { $str =~ s!([&<>])!$rules{$1}!g }; #^(&(amp|lt|gt);)!!) {
+ if (my $err = $@) {
+ MDK::Common::Various::internal_error("$err\n$str");
+ }
+ $str;
+}
+
+# from rpmtools, #37482:
+sub ensure_utf8 {
+ return '' if !$_[0];
+
+ 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) = @_;
+ return '' if !$description;
+
+ ensure_utf8($description);
+ my $t = "";
+ my $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 => $loc->N("None (installed)") } if !defined($p->id); # if installed
+ URPM::pkg2media($urpm->{media}, $p) || { name => $loc->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 { $loc->to_utf8(POSIX::strftime("%c", localtime($_[0]))) }
+
+our $spacing = " ";
+sub format_changelog_string {
+ my ($installed_version, $string) = @_;
+
+ #- preprocess changelog for faster TextView insert reaction
+ my $version;
+ my $highlight;
+ my $chl = [ map {
+ my %attrs;
+ if (/^\*/) {
+ ($version) = /(\S*-\S*)\s*$/;
+ $highlight = $installed_version ne $loc->N("(none)") && 0 < URPM::rpmvercmp($version, $installed_version);
+ if ($highlight) {
+ "<b><i>" . $_ . "</i></b>";
+ }
+ else {
+ "<b>" . $_ . "</b>";
+ }
+ }
+ else {
+ "$spacing" . $_ . "\n";
+ }
+ } split("\n", $string) ];
+
+ return $chl;
+}
+
+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) = @_;
+ '<i>' . escape_text_for_TextView_markup_format($name) . '</i>';
+}
+
+sub format_name_n_summary {
+ my ($name, $summary) = @_;
+ join("\n", '<b>' . $name . '</b>', escape_text_for_TextView_markup_format($summary));
+}
+
+sub format_header {
+ my ($str) = @_;
+ '<big>' . escape_text_for_TextView_markup_format($str) . '</big>';
+}
+
+sub format_field {
+ my ($str) = @_;
+ '<b>' . escape_text_for_TextView_markup_format($str) . '</b>';
+}
+
+sub format_size {
+ my ($size) = @_;
+ $size >= 0 ?
+ $loc->N("%s of additional disk space will be used.", formatXiB($size)) :
+ $loc->N("%s of disk space will be freed.", formatXiB(-$size));
+}
+
+sub format_filesize {
+ my ($filesize) = @_;
+ $filesize ? $loc->N("%s of packages will be retrieved.", formatXiB($filesize)) : ();
+}
+
+sub format_list {
+ return join("\n", map { s/^(\s)/ $1/mg; "- $_" } sort { uc($a) cmp uc($b) } @_);
+}
+
+1;
diff --git a/lib/ManaTools/Rpmdragora/gui.pm b/lib/ManaTools/Rpmdragora/gui.pm
new file mode 100644
index 0000000..0cf0882
--- /dev/null
+++ b/lib/ManaTools/Rpmdragora/gui.pm
@@ -0,0 +1,1828 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Rpmdragora::gui;
+#*****************************************************************************
+#
+# Copyright (c) 2002 Guillaume Cottenceau
+# Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com>
+# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
+# Copyright (c) 2005-2007 Mandriva SA
+# Copyright (c) 2013 - 2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+# Copyright (c) 2014 - 2015 Angelo Naselli <anaselli@linux.it>
+#
+# 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 matteo or anaselli
+############################################################
+
+use strict;
+use Sys::Syslog;
+
+use utf8;
+
+use MDK::Common::Func qw(before_leaving find any if_);
+use MDK::Common::DataStructure qw(difference2 member intersection);
+use MDK::Common::Various qw(chomp_ to_bool);
+use MDK::Common::String qw(formatAlaTeX);
+use MDK::Common::Math qw(sum);
+use MDK::Common::System qw(list_passwd);
+
+use ManaTools::rpmdragora;
+use ManaTools::Rpmdragora::open_db;
+use ManaTools::Rpmdragora::formatting;
+use ManaTools::Rpmdragora::init;
+use ManaTools::Rpmdragora::icon qw(get_icon_path);
+use ManaTools::Rpmdragora::pkg;
+use ManaTools::Shared;
+use ManaTools::Shared::GUI;
+use ManaTools::Shared::Locales;
+use ManaTools::Shared::RunProgram qw(get_stdout raw);
+use yui;
+use feature 'state';
+use Carp;
+
+use Exporter;
+our @ISA = qw(Exporter);
+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
+ group_has_parent
+ group_parent
+ groups_tree
+ is_locale_available
+ node_state
+ pkgs_provider
+ real_quit
+ reset_search
+ set_node_state
+ sort_callback
+ switch_pkg_list_mode
+ toggle_all
+ toggle_nodes
+ fast_toggle
+ setInfoOnWidget
+ );
+
+my $loc = ManaTools::rpmdragora::locale();
+my $shared_gui = ManaTools::Shared::GUI->new() ;
+
+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) = ($loc->N("Search results"), $loc->N("Search results (none)"));
+
+our %hidden_info = (
+ details => "details",
+ changelog => "changelog",
+ files => "file",
+ new_deps => "new_deps",
+
+);
+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}, $loc->N("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 = $loc->N($pkgs->{$key}{pkg}->summary);
+ require utf8;
+ utf8::valid($summary) ? $summary : @{[]};
+}
+
+sub get_advisory_link {
+ my ($update_descr) = @_;
+
+ my $webref = "<br /><a href=\"". $update_descr->{URL} ."\">". $loc->N("Security advisory") ."</a><br />";
+
+ [ $webref ];
+}
+
+sub get_description {
+ my ($pkg, $update_descr) = @_;
+
+ my $descr = ($pkg->{description} ||
+ $update_descr->{description} ||
+ '<i>'. $loc->N("No description").'</i>');
+ $descr =~ s|\n|<br />|g;
+
+ return $descr . "<br />";
+}
+
+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 = $loc->N("It is <b>not supported</b> by Mageia.");
+ my $dangerous = $loc->N("It may <b>break</b> your system.");
+ my $s = "";
+ $s .= $loc->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",
+ $loc->N("This package contains a new version that was backported."),
+ $unsupported, $dangerous, $s);
+ } elsif (member('testing', @media_types)) {
+ return join("\n",
+ $loc->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) ?
+ $loc->N("This is an official update which is supported by Mageia.")
+ : ($loc->N("This is an unofficial update."), $unsupported))
+ ,
+ $s);
+ } else {
+ $s .= $loc->N("This is an official package supported by Mageia") . "\n" if member('official', @media_types);
+ return $s if $s;
+ }
+
+ return;
+}
+
+sub get_main_text {
+ my ($medium, $fullname, $name, $summary, $is_update, $update_descr) = @_;
+
+ my $txt = get_string_from_keywords($medium, $fullname);
+ my $notice = format_field($loc->N("Notice: ")) . $txt if $txt;
+ ensure_utf8($notice);
+
+ my $hdr = "<h2>" . format_header(join(' - ', $name, $summary)) . "</h2>\n";
+ ensure_utf8($hdr);
+
+ my $update = MDK::Common::Func::if_($is_update, # is it an update?
+ format_field($loc->N("Importance: ")) . format_update_field($update_descr->{importance}),
+ format_field($loc->N("Reason for update: ")) . format_update_field(rpm_description($update_descr->{pre})),
+ );
+ ensure_utf8($update);
+
+ my $main_text = $hdr;
+ $main_text .= "&nbsp;&nbsp;&nbsp;" . $notice . "<br/>" if $notice;
+ $main_text .= "&nbsp;&nbsp;&nbsp;" . $update . "<br/>" if $update;
+
+ return $main_text;
+}
+
+sub get_details {
+ my ($pkg, $upkg, $installed_version, $raw_medium) = @_;
+ my @details = ();
+ push @details, format_field($loc->N("Version: ")) . $upkg->EVR;
+ push @details, format_field($loc->N("Currently installed version: ")) . $installed_version if($upkg->flag_installed);
+ push @details, format_field($loc->N("Group: ")) . translate_group($upkg->group);
+ push @details, format_field($loc->N("Architecture: ")) . $upkg->arch;
+ push @details, format_field($loc->N("Size: ")) . $loc->N("%s KB", int($upkg->size/1024));
+ push @details, eval { format_field($loc->N("Medium: ")) . $raw_medium->{name} };
+
+ my @link = get_url_link($upkg, $pkg);
+ push @details, join("<br />&nbsp;&nbsp;&nbsp;",@link) if(@link);
+ unshift @details, "<br />&nbsp;&nbsp;&nbsp;";
+ join("<br />&nbsp;&nbsp;&nbsp;", @details);
+}
+
+sub get_new_deps {
+ my ($urpm, $upkg) = @_;
+
+ my $deps = slow_func(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 = $loc->N("All dependencies installed.") if !@deps;
+ return \@deps;
+ });
+ return $deps;
+}
+
+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 ($url ? 1 : 0) });
+ }
+
+ return if !$url;
+
+ my @a;
+ push @a, format_field($loc->N("URL: ")) . ${spacing} . "<a href=\"". $url ."\">". $url ."</a>";
+ @a;
+}
+
+sub files_format {
+ my ($files) = @_;
+
+ return '<tt>' . $spacing . "<br />&nbsp;&nbsp;&nbsp;" . join("<br />&nbsp;&nbsp;&nbsp;", @$files) . '</tt>';
+}
+
+#=============================================================
+
+=head2 format_link
+
+=head3 INPUT
+
+ $description: Description to be shown as link
+ $url: to be reach when click on $description link
+
+=head3 OUTPUT
+
+ $webref: href HTML tag
+
+=head3 DESCRIPTION
+
+ This function returns an href string to be published
+
+=cut
+
+#=============================================================
+sub format_link {
+ my ($description, $url) = @_;
+
+ my $webref = "<a href=\"". $url ."\">". $description ."</a>";
+
+ return $webref;
+}
+
+sub _format_pkg_simplifiedinfo {
+ my ($pkgs, $key, $urpm, $descriptions, $options) = @_;
+ 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 $dummy_string = get_main_text($raw_medium, $key, $name, $summary, $is_update, $update_descr);
+ my $s;
+ push @$s, $dummy_string;
+ push @$s, get_advisory_link($update_descr) if $is_update;
+
+ push @$s, get_description($pkg, $update_descr);
+
+ my $installed_version = eval { find_installed_version($upkg) };
+
+ #push @$s, [ gtkadd(gtkshow(my $details_exp = Gtk2::Expander->new(format_field($loc->N("Details:")))),
+ # gtknew('TextView', text => get_details($pkg, $upkg, $installed_version, $raw_medium))) ];
+ my $detail_link = format_link(format_field($loc->N("Details:")), $hidden_info{details} );
+ if ($options->{details}) {
+ my $details = get_details($pkg, $upkg, $installed_version, $raw_medium);
+ utf8::encode($details);
+ $detail_link .= "\n" . $details;
+ }
+ push @$s, join("\n", $detail_link, "\n");
+
+ #push @$s, [ build_expander($pkg, $loc->N("Files:"), 'files', sub { files_format($pkg->{files}) }) ];
+ my $files_link = format_link(format_field($loc->N("Files:")), $hidden_info{files} );
+ if ($options->{files}) {
+ my $wait = ManaTools::rpmdragora::wait_msg();
+ if (!$pkg->{files}) {
+ extract_header($pkg, $urpm, 'files', $installed_version);
+ }
+ my $files = $pkg->{files} ? files_format($pkg->{files}) : $loc->N("(Not available)");
+ utf8::encode($files);
+ $files_link .= "\n\n" . $files;
+ ManaTools::rpmdragora::remove_wait_msg($wait);
+ }
+ push @$s, join("\n", $files_link, "\n");
+
+ #push @$s, [ build_expander($pkg, $loc->N("Changelog:"), 'changelog', sub { $pkg->{changelog} }, $installed_version) ];
+ my $changelog_link = format_link(format_field($loc->N("Changelog:")), $hidden_info{changelog} );
+ if ($options->{changelog}) {
+ my $wait = ManaTools::rpmdragora::wait_msg();
+ my @changelog = $pkg->{changelog} ? @{$pkg->{changelog}} : ( $loc->N("(Not available)") );
+ if (!$pkg->{changelog} || !scalar @{$pkg->{changelog}} ) {
+ # my ($pkg, $label, $type, $get_data, $o_installed_version) = @_;
+ extract_header($pkg, $urpm, 'changelog', $installed_version);
+ @changelog = $pkg->{changelog} ? @{$pkg->{changelog}} : ( $loc->N("(Not available)") );
+ }
+ utf8::encode(\@changelog);
+
+ $changelog_link .= "<br />&nbsp;&nbsp;&nbsp;" . join("<br />&nbsp;&nbsp;&nbsp;", @changelog);
+ $changelog_link =~ s|\n||g;
+ ManaTools::rpmdragora::remove_wait_msg($wait);
+ }
+ push @$s, join("\n\n", $changelog_link, "\n");
+
+ my $deps_link = format_link(format_field($loc->N("New dependencies:")), $hidden_info{new_deps} );
+ if ($options->{new_deps}) {
+ if ($upkg->id) { # If not installed
+ $deps_link .= "<br />&nbsp;&nbsp;&nbsp;" . join("<br />&nbsp;&nbsp;&nbsp;", @{get_new_deps($urpm, $upkg)});
+ # push @$s, get_new_deps($urpm, $upkg);
+ }
+ }
+ push @$s, join("\n", $deps_link, "\n");
+
+ return $s;
+}
+
+sub warn_if_no_pkg {
+ my ($name) = @_;
+ my ($short_name) = split_fullname($name);
+ state $warned;
+ if (!$warned) {
+ $warned = 1;
+ interactive_msg($loc->N("Warning"),
+ join("\n",
+ $loc->N("The package \"%s\" was found.", $name),
+ $loc->N("However this package is not in the package list."),
+ $loc->N("You may want to update your urpmi database."),
+ '',
+ $loc->N("Matching packages:"),
+ '',
+ join("\n", sort map {
+ #-PO: this is list fomatting: "- <package_name> (medium: <medium_name>)"
+ #-PO: eg: "- rpmdragora (medium: "Main Release"
+ $loc->N("- %s (medium: %s)", $_, pkg2medium($pkgs->{$_}{pkg}, $urpm)->{name});
+ } grep { /^$short_name/ } keys %$pkgs),
+ ),
+ scroll => 1,
+ );
+ }
+ return 'XXX';
+}
+
+#
+# @method node_state
+#
+=pod
+
+=head1 node_state(pkgname)
+
+=over 4
+
+=item returns the state of the node (pkg) querying an urpm object from $pkgs->{$pkgname}
+
+=over 6
+
+=item I<to_install>
+
+=item I<to_remove>
+
+=item I<to_update>
+
+=item I<installed>
+
+=item I<uninstalled>
+
+=back
+
+=back
+
+=cut
+
+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, @table_item_list);
+
+#
+# @method set_node_state
+#
+
+=pod
+
+=head1 set_node_state($tblItem, $state, $detail_list)
+
+=over 4
+
+=item setup the table row by adding a cell representing the state of the package
+
+=item see node_state
+
+=over 6
+
+=item B<$tblItem> , YTableItem instance
+
+=item B<$state> , string containing the state of the package from node_state
+
+=item B<$detail_list> , reference to the YCBTable
+
+=back
+
+=back
+
+=cut
+
+sub set_node_state {
+ my ($tblItem, $state, $detail_list) = @_;
+ return if $state eq 'XXX' || !$state;
+
+ if ($detail_list) {
+ $detail_list->parent()->parent()->startMultipleChanges();
+ $tblItem->addCell($state,"/usr/share/rpmdrake/icons/state_$state.png") if(ref $tblItem eq "yui::YCBTableItem");
+ if(to_bool(member($state, qw(base installed to_install)))){
+ # it should be parent()->setChecked(1)
+ $detail_list->checkItem($tblItem, 1);
+ # $tblItem->setSelected(1);
+ }else{
+ $detail_list->checkItem($tblItem, 0);
+ # $tblItem->setSelected(0);
+ }
+# if(!to_bool($state ne 'base')){
+# #$iter->cell(0)->setLabel('-');
+# $tblItem->cell(0)->setLabel('-');
+# }
+ $detail_list->parent()->parent()->doneMultipleChanges();
+ }
+ else {
+ # no item list means we use just the item to add state information
+ $tblItem->addCell($state,"/usr/share/rpmdrake/icons/state_$state.png") if(ref $tblItem eq "yui::YCBTableItem");
+ $tblItem->check(to_bool(member($state, qw(base installed to_install))));
+# $tblItem->cell(0)->setLabel('-') if !to_bool($state ne 'base');
+ }
+}
+
+sub set_leaf_state {
+ my ($leaf, $state, $detail_list) = @_;
+ # %ptree is a hash using the pkg name as key and a monodimensional array (?) as value
+ # were it is stored the index of the item into the table
+ my $nodeIndex = $ptree{$leaf}[0];
+ my $node = itemAt($detail_list,$nodeIndex);
+ set_node_state($node, $state, $detail_list);
+}
+
+#=============================================================
+
+=head2 grep_unselected
+
+=head3 INPUT
+
+ $l: ARRAY reference containing the list of package fullnames
+
+=head3 OUTPUT
+
+ \@result: ARRAY reference containing the list of packages
+ that are not selected
+
+=head3 DESCRIPTION
+
+ Function returning the list of not selected packages
+
+=cut
+
+#=============================================================
+sub grep_unselected {
+ my $l = shift;
+
+ my @result = grep { exists $pkgs->{$_} && !$pkgs->{$_}{selected} } @{$l} ;
+ return \@result;
+}
+
+my %groups_tree = ();
+
+#
+# @method add_parent
+#
+
+=pod
+
+=head1 add_parent($tree, $root, $state)
+
+=over 4
+
+=item populates the treeview with the rpm package groups
+
+=over 6
+
+=item B<$tree> , YTree for the group of the rpm packages
+
+=item B<$root> , string containing a path-like sequence (e.g. "foo|bar")
+
+=item B<$state> , not used currently (from the old impl.)
+
+=back
+
+=back
+
+=cut
+
+sub add_parent {
+ my ($tree, $root, $state) = @_;
+ $tree or return undef;
+ #$root or return undef;
+ my $parent = 0;
+
+ carp "WARNING TODO: add_parent to be removed (called with " . $root . ")\n";
+
+ my @items = split('\|', $root);
+ my $i = 0;
+ my $parentItem = undef;
+ my $rootItem = undef;
+ for my $item (@items) {
+ chomp $item;
+ $item = trim($item);
+ my $treeItem;
+ if($i == 0){
+ $parent = $item;
+ $treeItem = new yui::YTreeItem($item,get_icon_path($item,0),0);
+ $rootItem = $treeItem;
+ $parentItem = $treeItem;
+ if(!defined($groups_tree{$parent})) {
+ $groups_tree{$parent}{parent} = $treeItem;
+ $groups_tree{$parent}{children} = ();
+# $tree->addItem($groups_tree{$parent}{'parent'});
+ }
+ }else{
+ #if(any { $_ ne $item } @{$groups_tree{$parent}{'children'}}){
+ # push @{$groups_tree{$parent}{'children'}}, $item;
+ #}
+ $parentItem = new yui::YTreeItem($parentItem, $item,get_icon_path($item,$parent),0);
+ if(!defined($groups_tree{$parent}{'children'}{$item})){
+# $treeItem = new yui::YTreeItem($treeItem, $item,get_icon_path($item,$parent),0);
+ $treeItem = $parentItem;
+ $groups_tree{$parent}{'children'}{$item} = $treeItem;
+ $groups_tree{$parent}{'parent'}->addChild($treeItem);
+ }
+ }
+ $i++;
+ }
+ $tree->addItem($rootItem) if $rootItem;
+ $tree->rebuildTree();
+}
+
+
+#=============================================================
+
+=head2 add_tree_item
+
+=head3 INPUT
+
+=item B<$tree> YTree for the group of the rpm packages
+
+=item B<$item> string containing a group (to be added as leaf node)
+
+=item B<$selected> if the new item is selected
+
+
+=head3 DESCRIPTION
+
+ This function add a group to the tree view
+
+=cut
+
+#=============================================================
+sub add_tree_item {
+ my ($tree, $item, $selected) = @_;
+
+ $tree or return undef;
+
+ $tree->startMultipleChanges();
+ my $treeItem = new yui::YTreeItem($item, get_icon_path($item, 0), 0);
+ $treeItem->setSelected($selected);
+
+ $tree->addItem($treeItem);
+ $tree->rebuildTree();
+ $tree->doneMultipleChanges();
+}
+
+#=============================================================
+
+=head2 add_package_item
+
+=head3 INPUT
+
+=item B<$item_list>: reference to a YItemCollection
+
+=item B<$pkg_name>: package name
+
+=item B<$select>: select given package
+
+=head3 DESCRIPTION
+
+ populates the item list for the table view with the given rpm package
+
+=cut
+
+#=============================================================
+sub add_package_item {
+ my ($item_list, $pkg_name, $select) = @_;
+
+ return if !$pkg_name;
+
+ return if ref($item_list) ne "yui::YItemCollection";
+
+ my $state = node_state($pkg_name) or return;
+
+ my $iter;
+ if (is_a_package($pkg_name)) {
+ my ($name, $version, $release, $arch) = split_fullname($pkg_name);
+
+ $name = "" if !defined($name);
+ $version = "" if !defined($version);
+ $release = "" if !defined($release);
+ $arch = "" if !defined($arch);
+
+ my $newTableItem = new yui::YCBTableItem(
+ $name,
+ get_summary($pkg_name),
+ $version,
+ $release,
+ $arch
+ );
+
+ $newTableItem->setSelected($select);
+
+ set_node_state($newTableItem, $state);
+
+ $item_list->push($newTableItem);
+
+ $newTableItem->DISOWN();
+ }
+ else {
+ carp $pkg_name . " is not a leaf package and that is not managed!";
+ }
+
+}
+
+#
+# @method add_node
+#
+
+=pod
+
+=head1 add_node($leaf, $root, $options)
+
+=over 4
+
+=item populates the tableview with the rpm packages or the treeview with the package groups
+
+=over 6
+
+=item B<$leaf> , could be the name of a package or the name of a group o packages
+
+=item B<$root> , string containing a path-like sequence (e.g. "foo|bar")
+
+=item B<$state> , the string with the state of the package if leaf is the name of a package
+
+=back
+
+=back
+
+=cut
+
+sub add_node {
+ my ($leaf, $root, $o_options) = @_;
+ my $state = node_state($leaf) or return;
+ if ($leaf) {
+ my $iter;
+ if (is_a_package($leaf)) {
+carp "TODO: add_node is_a_package(\$leaf)" . $leaf . "\n";
+
+ my ($name, $version, $release, $arch) = split_fullname($leaf);
+ #OLD $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,
+ # ]);
+ $name = "" if(!defined($name));
+ $version = "" if(!defined($version));
+ $release = "" if(!defined($release));
+ $arch = "" if(!defined($arch));
+ #my $newTableItem = new yui::YTableItem(format_name_n_summary($name, get_summary($leaf)),
+ my $newTableItem = new yui::YCBTableItem($name,
+ get_summary($leaf),
+ $version,
+ $release,
+ $arch);
+ $w->{detail_list}->addItem($newTableItem);
+ set_node_state($newTableItem, $state, $w->{detail_list});
+ # $ptree{$leaf} = [ $newTableItem->label() ];
+ $ptree{$leaf} = [ $newTableItem->index() ];
+ $table_item_list[$newTableItem->index()] = $leaf;
+ $newTableItem->DISOWN();
+ } else {
+ carp "TODO: add_node !is_a_package(\$leaf) not MANAGED\n";
+# $iter = $w->{tree_model}->append_set(add_parent($w->{tree},$root, $state), [ $grp_columns{label} => $leaf ]);
+ #push @{$wtree{$leaf}}, $iter;
+ }
+ } else {
+ carp "TODO: add_node !\$leaf not MANAGED\n";
+
+# 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="" if(!defined($prev_label));
+ $prev_label ne $new_label and $w->{status}->setText($prev_label = $new_label);
+ }
+}
+
+#=============================================================
+
+=head2 treeview_children
+
+=head3 INPUT
+
+ $tbl: YCBTable widget containing the package list shown
+
+=head3 OUTPUT
+
+ \@l: ARRAY reference containing the list
+ of YItem contained into YCBTable
+
+=head3 DESCRIPTION
+
+ This functions just returns the YCBTable content such as all the
+ YItem objects
+
+=cut
+
+#=============================================================
+sub treeview_children {
+ my ($tbl) = @_;
+ my $it;
+ my @l;
+ my $i=0;
+ # using iterators
+ for ($it = $tbl->itemsBegin(); $it != $tbl->itemsEnd(); ) {
+ my $item = $tbl->YItemIteratorToYItem($it);
+ push @l, $item;
+ $it = $tbl->nextItem($it);
+ $i++;
+ if ($i == $tbl->itemsCount()) {
+ last;
+ }
+ }
+
+ return \@l;
+}
+
+#=============================================================
+
+=head2 children
+
+=head3 INPUT
+
+ $tbl: YCBTable object
+ @table_item_list: array containing package fullnames
+
+=head3 OUTPUT
+
+ \@result: ARRAY reference containing package fullnames
+
+=head3 DESCRIPTION
+
+ This Function return the list of package fullnames
+
+=cut
+
+#=============================================================
+
+sub children {
+ my ($tbl, $table_item_list) = @_;
+ # map { $w->{detail_list}->get($_, $pkg_columns{text}) } treeview_children($w->{detail_list});
+ # map { $table_item_list[$_->index()] } treeview_children($w->{detail_list});
+ my $children_list = treeview_children($tbl);
+ my @result;
+ for my $child(@{$children_list}){
+ push @result, $table_item_list->[$child->index()];
+ }
+ return \@result;
+}
+
+sub itemAt {
+ my ($table, $index) = @_;
+ return $table->item($index);
+ #return bless ($table->item($index),'yui::YTableItem');
+ #foreach my $item(treeview_children($table)){
+ # if($item->index() == $index){
+ # print "\n== item label ".$item->label()."\n";
+ # return bless ($item, 'yui::YTableItem');
+ # }
+ #}
+}
+
+
+#=============================================================
+
+=head2 toggle_all
+
+=head3 INPUT
+
+ $common: HASH reference containing (### TODO ###)
+ widgets => {
+ detail_list: YTable reference (?)
+ }
+ table_item_list => array containing package fullnames
+ partialsel_unsel => (?)
+
+ set_state_callback => set state callback invoked by
+ toggle_nodes if needed. if undef
+ set_leaf_state is used.
+ $_val: value to be set (so it seems not a toggle! unused?)
+
+=head3 DESCRIPTION
+
+This method (should) check -or un-check if already checked- all
+the packages
+
+=cut
+
+#=============================================================
+sub toggle_all {
+ my ($common, $_val) = @_;
+ my $w = $common->{widgets};
+
+ my $l = children($w->{detail_list}, $common->{table_item_list}) or return;
+
+ my $set_state = $common->{set_state_callback} ? $common->{set_state_callback} : \&set_leaf_state;
+ 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}, $w->{detail_list_model}, \&set_leaf_state, node_state($p[0]), @p);
+
+ toggle_nodes($w->{detail_list}, $w->{detail_list}, $set_state, node_state($p->[0]), @{$p});
+ update_size($common);
+}
+
+sub fast_toggle {
+ my ($item) = @_;
+ #gtkset_mousecursor_wait($w->{w}{rwindow}->window);
+ #my $_cleaner = before_leaving { gtkset_mousecursor_normal($w->{w}{rwindow}->window) };
+ my $name = $common->{table_item_list}[$item->index()];
+ my $urpm_obj = $pkgs->{$name}{pkg};
+ if ($urpm_obj->flag_base) {
+ interactive_msg($loc->N("Warning"), $loc->N("Removing package %s would break your system", $name));
+ return '';
+ }
+ if ($urpm_obj->flag_skip) {
+ interactive_msg($loc->N("Warning"), $loc->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 ($ManaTools::Rpmdragora::pkg::need_restart && !$priority_up_alread_warned) {
+ $priority_up_alread_warned = 1;
+ interactive_msg($loc->N("Warning"), '<b>' . $loc->N("Rpmdragora or one of its priority dependencies needs to be updated first. Rpmdragora will then restart.") . '</b>' . "\n\n");
+ }
+ # toggle_nodes($w->{tree}->window, $w->{detail_list_model}, \&set_leaf_state, $w->{detail_list_model}->get($iter, $pkg_columns{state}),
+
+ my $old_status = node_state($name);
+
+ #$DB::single = 1;
+
+# my $old_state;
+# if($item->checked()){
+# $old_state = "to_install";
+# }else{
+# $old_state = "to_remove";
+# }
+ toggle_nodes($w->{tree}, $w->{detail_list}, \&set_leaf_state, $old_status, $name);
+ 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};
+
+ $common->{table_item_list} = \@table_item_list;
+
+ $w->{detail_list} ||= $w->{tree};
+ #$w->{detail_list_model} ||= $w->{tree_model};
+
+ $common->{add_parent} = \&add_parent;
+ my $clear_all_caches = sub {
+ %ptree = %wtree = ();
+ @table_item_list = ();
+ };
+ $common->{clear_all_caches} = $clear_all_caches;
+ $common->{delete_all} = sub {
+ $clear_all_caches->();
+ %groups_tree = ();
+ };
+ $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) = @_;
+ carp "WARNING TODO delete_category to be removed!";
+ exists $wtree{$cat} or return;
+ %ptree = ();
+ update_size($common);
+ return;
+ };
+ $common->{add_nodes} = sub {
+ my (@nodes) = @_;
+ carp "TODO ==================> ADD NODES - add packages (" . scalar(@nodes) . ") \n";
+ yui::YUI::app()->busyCursor();
+
+ my $lastItem = $w->{detail_list}->selectedItem() ? $w->{detail_list}->selectedItem()->label() : "";
+ $w->{detail_list}->startMultipleChanges();
+ $w->{detail_list}->deleteAllItems();
+ my $itemColl = new yui::YItemCollection;
+
+ @table_item_list = ();
+ my $index = 0;
+ foreach(@nodes){
+ add_package_item($itemColl, $_->[0], ($lastItem eq $_->[0]));
+ warn "Unmanaged param " . $_->[2] if defined $_->[2];
+ $ptree{$_->[0]} = [ $index ];
+ $index++;
+ push @table_item_list, $_->[0];
+ }
+
+ update_size($common);
+ $w->{detail_list}->addItems($itemColl);
+ $w->{detail_list}->doneMultipleChanges();
+ yui::YUI::app()->normalCursor();
+ };
+
+ $common->{rebuild_tree}->();
+ update_size($common);
+ $common->{initial_selection} and toggle_nodes($w->{tree}->window, $w->{detail_list}, \&set_leaf_state, undef, @{$common->{initial_selection}});
+ my $_b = before_leaving { $clear_all_caches->() };
+ $common->{init_callback}->() if $common->{init_callback};
+
+ $w->{w};
+}
+
+our $find_entry;
+
+sub reset_search() {
+ return if !$common;
+
+# TODO $common->{delete_category}->($_) foreach $results_ok, $results_none;
+ # clear package list:
+ yui::YUI::app()->busyCursor();
+ my $wdgt = $common->{widgets};
+ $wdgt->{detail_list}->startMultipleChanges();
+ $wdgt->{detail_list}->deleteAllItems();
+ $wdgt->{detail_list}->doneMultipleChanges();
+ yui::YUI::app()->normalCursor();
+}
+
+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} && !$ManaTools::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 = MDK::Common::Func::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($loc->N("More information on package..."), get_info($_[0]), scroll => 1) };
+ $choices = [ sort { $a->name cmp $b->name } @$choices ];
+ my @choices = interactive_list_($loc->N("Please choose"), (scalar(@$choices) == 1 ?
+ $loc->N("The following package is needed:") : $loc->N("One of the following packages is needed:")),
+ [ map { urpm_name($_) } @$choices ], $callback, nocancel => 1);
+ defined $choices[0] ? $choices->[$choices[0]] : undef;
+}
+
+#=============================================================
+
+=head2 info_details
+
+=head3 INPUT
+
+ $info_detail_selected: string to get more info details
+ (see %hidden_info)
+ $info_options: reference to info options that are going to changed
+ based on passed $info_detail_selected
+
+=head3 OUTPUT
+
+ [0, 1]: 0 if $info_detail_selected not valid, 1 otherwise
+
+=head3 DESCRIPTION
+
+ This function change the info_options accordingly to the string passed
+ returning 0 if the string is not managed (see %hidden_info)
+
+=cut
+
+#=============================================================
+sub info_details {
+ my ($info_detail_selected, $info_options) = @_;
+
+ foreach my $k (keys %hidden_info) {
+ if ($info_detail_selected eq $hidden_info{$k}) {
+ $info_options->{$k} = $info_options->{$k} ? 0 : 1;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+#=============================================================
+
+=head2 setInfoOnWidget
+
+=head3 INPUT
+
+ $pckgname: full name of the package
+ $infoWidget: YRichText object to fill
+
+=head3 DESCRIPTION
+
+ This function writes on a YRichText object package info
+
+=cut
+
+#=============================================================
+sub setInfoOnWidget {
+ my ($pkgname, $infoWidget, $options) = @_;
+
+ return if( ref $infoWidget ne "yui::YRichText");
+
+ $infoWidget->setValue("");
+
+ my $info_text = "";
+
+ my @data = get_info($pkgname, $options);
+ for(@{$data[0]}){
+ if(ref $_ ne "ARRAY"){
+ $info_text .= $_ . "<br />&nbsp;&nbsp;&nbsp;";
+ }else{
+ for my $subitem(@{$_}) {
+ $info_text .= "<br />" . "<br />&nbsp;&nbsp;&nbsp;" . $subitem;
+ }
+ }
+ }
+ # change \n to <br/>
+ $info_text =~ s|\n|<br/>|g;
+
+ $infoWidget->setValue($info_text);
+}
+
+
+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;
+
+ my $appTitle = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($title);
+# TODO icon if needed
+# yui::YUI::app()->setApplicationIcon($which_icon);
+
+ my $factory = yui::YUI::widgetFactory;
+
+ ## | [msg-label] |
+ ## | |
+ ## | pkg-list | info on selected pkg |(1)
+ ## | |
+ ## | [cancel] [ok] |
+ ####
+ # (1) info on pkg list:
+ # [ label info ]
+ # sub info on click (Details, Files, Changelog, New dependencies)
+
+ my $dialog = $factory->createPopupDialog;
+ my $vbox = $factory->createVBox( $dialog );
+ my $msgBox = $factory->createLabel($vbox, $msg, 1);
+# my $hbox = $factory->createHBox( $vbox );
+ my $pkgList = $factory->createSelectionBox( $vbox, $loc->N("Select package") );
+ $factory->createVSpacing($vbox, 1);
+
+# my $frame = $factory->createFrame ($hbox, $loc->N("Information on packages"));
+# my $frmVbox = $factory->createVBox( $frame );
+ my $infoBox = $factory->createRichText($vbox, "", 0);
+ $pkgList->setWeight($yui::YD_HORIZ, 2);
+ $infoBox->setWeight($yui::YD_HORIZ, 3);
+ $factory->createVSpacing($vbox, 1);
+ my $hbox = $factory->createHBox( $vbox );
+ my $align = $factory->createRight($hbox);
+ my $cancelButton = $factory->createPushButton($align, $loc->N("&Cancel"));
+ my $okButton = $factory->createPushButton($hbox, $loc->N("&Ok"));
+
+ # adding packages to the list
+ my $itemColl = new yui::YItemCollection;
+ foreach my $p (map { scalar(urpm::select::translate_why_removed_one($urpm, $urpm->{state}, $_)) } @deps) {
+ my $item = new yui::YTableItem ("$p");
+ $item->setLabel( $p );
+ $itemColl->push($item);
+ $item->DISOWN();
+ }
+ $pkgList->addItems($itemColl);
+ $pkgList->setImmediateMode(1);
+ my $item = $pkgList->selectedItem();
+ if ($item) {
+ my $pkg = $item->label();
+ setInfoOnWidget($pkg, $infoBox);
+ }
+
+ my $retval = 0;
+ my $info_options = {};
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::MenuEvent) {
+ my $item = $event->item();
+ if (!$item) {
+ #URL emitted or at least a ref into RichText widget
+ my $url = yui::toYMenuEvent($event)->id ();
+ if (ManaTools::Rpmdragora::gui::info_details($url, $info_options) ) {
+ $item = $pkgList->selectedItem();
+ my $pkg = $item->label();
+ ManaTools::Rpmdragora::gui::setInfoOnWidget($pkg, $infoBox, $info_options);
+ }
+ else {
+ # default it's really a URL
+ ManaTools::Rpmdragora::gui::run_browser($url);
+ }
+ }
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### widget
+ my $widget = $event->widget();
+ if ($widget == $pkgList) {
+ #change info
+ $item = $pkgList->selectedItem();
+ $info_options = {};
+ if ( $item ) {
+ my $pkg = $item->label();
+ setInfoOnWidget($pkg, $infoBox);
+ }
+ }
+ elsif ($widget == $okButton) {
+ $retval = 1;
+ last;
+ }
+ elsif ($widget == $cancelButton) {
+ last;
+ }
+ }
+ }
+
+ destroy $dialog;
+
+ # restore original title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+
+ return $retval;
+}
+
+# set_state <- package-fullname, node_state = {to_install, to_remove,...}, list=YTable
+sub toggle_nodes {
+ my ($widget, $detail_list, $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($loc->N("Checking dependencies of package..."), 0);
+
+ my $warn_about_additional_packages_to_remove = sub {
+ my ($msg) = @_;
+ statusbar_msg_remove($bar_id);
+ deps_msg($loc->N("Some additional packages need to be removed"),
+ MDK::Common::String::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(sub { @remove = closure_removal(@nodes) });
+ @nodes_with_deps = grep { !$pkgs->{$_}{selected} && !/^basesystem/ } @remove;
+ $warn_about_additional_packages_to_remove->(
+ $loc->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($loc->N("Some packages cannot be removed"),
+ $loc->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 {
+ @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->(
+ $loc->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;
+ @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($loc->N("Additional packages needed"),
+ MDK::Common::String::formatAlaTeX($loc->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($loc->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 = MDK::Common::Func::find { $_ eq $cant } @ask_unselect;
+ $unsel
+ ? join("\n", urpm::select::translate_why_unselected($urpm, $urpm->{state}, $unsel))
+ : ($pkgs->{$_}{pkg}->flag_skip ? $loc->N("%s (belongs to the skip list)", $cant) : $cant);
+ } @cant;
+ my $count = @reasons;
+ interactive_msg(
+ ($count == 1 ? $loc->N("One package cannot be installed") : $loc->N("Some packages cannot be installed")),
+ ($count == 1 ?
+ $loc->N("Sorry, the following package cannot be selected:\n\n%s", format_list(@reasons))
+ : $loc->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;
+ @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($loc->N("Some packages need to be removed"),
+ $loc->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};
+ Sys::Syslog::syslog('info|local1', "can't be removed: $_");
+ } else {
+ $pkgs->{$_}{selected} = $new_state;
+ }
+ # invoke set_leaf_state($pkgname, node_state, )
+ # node_state = {to_install, to_remove,...}
+
+ $set_state->($_, node_state($_), $detail_list);
+ 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()) {
+ return interactive_msg($loc->N("Some packages are selected."), $loc->N("Some packages are selected.") . "\n" . $loc->N("Do you really want to quit?"), yesno => 1);
+ }
+
+ return 1;
+}
+
+sub do_action__real {
+ my ($options, $callback_action, $o_info) = @_;
+ require urpm::sys;
+ if (!urpm::sys::check_fs_writable()) {
+ $urpm->{fatal}(1, $loc->N("Error: %s appears to be mounted read-only.", $urpm::sys::mountpoint));
+ return 1;
+ }
+ if (!$ManaTools::Rpmdragora::pkg::need_restart && !is_there_selected_packages()) {
+ interactive_msg($loc->N("You need to select some packages first."), $loc->N("You need to select some packages first."));
+ return 1;
+ }
+ my $size_added = MDK::Common::Math::sum(map { MDK::Common::Func::if_($_->flag_selected && !$_->flag_installed, $_->size) } @{$urpm->{depslist}});
+ if ($MODE eq 'install' && $size_free - $size_added/1024 < 50*1024) {
+ interactive_msg($loc->N("Too many packages are selected"),
+ $loc->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}, MDK::Common::Func::if_($ManaTools::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};
+ $o_info->setValue("") 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($loc->N("Fatal error"),
+ $loc->N("A fatal error occurred: %s.", $err));
+ }
+ $res;
+}
+
+sub translate_group {
+ join('/', map { $loc->N($_) } split m|/|, $_[0]);
+}
+
+sub ctreefy {
+ join('|', map { $loc->N($_) } split m|/|, $_[0]);
+}
+
+sub _build_tree {
+ my ($tree, $pkg_by_group_hash, @pkg_name_and_group_list) = @_;
+
+ print "TODO ====> BUILD TREE\n";
+
+ yui::YUI::app()->busyCursor();
+
+ #- we populate all the groups tree at first
+ %{$pkg_by_group_hash} = ();
+ # better loop on packages, create groups tree and push packages in the proper place:
+ my @groups = ();
+ foreach my $pkg (@pkg_name_and_group_list) {
+ my $grp = $pkg->[1];
+ # no state for groups (they're not packages and thus have no state)
+ push @groups, $grp;
+
+ $pkg_by_group_hash->{$grp} ||= [];
+ push @{$pkg_by_group_hash->{$grp}}, $pkg;
+ }
+
+ my $tree_hash = ManaTools::Shared::pathList2hash({
+ paths => \@groups,
+ separator => '|',
+ });
+
+ $tree->startMultipleChanges();
+ my $selected = $tree->selectedItem();
+ my $groupname = group_path_name($selected) if $selected;
+
+ $tree->deleteAllItems() if $tree->hasItems();
+
+ # TODO fixing geti icon api to get a better hash from the module
+ my %icons = ();
+ foreach my $group (@groups) {
+ next if defined($icons{$group});
+ my @items = split('\|', $group);
+ if (scalar(@items) > 1) {
+ $icons{$items[0]} = get_icon_path($items[0], 0);
+ $icons{$group} = get_icon_path($items[1], $items[0])
+ }
+ else {
+ $icons{$group} = get_icon_path($group, 0);
+ }
+ }
+
+ my $itemColl = new yui::YItemCollection;
+ $shared_gui->hashTreeToYItemCollection({
+ collection => $itemColl,
+ hash_tree => $tree_hash,
+ icons => \%icons,
+ default_item_separator => '|',
+ default_item => $groupname,
+ });
+
+ $tree->addItems($itemColl);
+ $tree->doneMultipleChanges();
+ $tree->rebuildTree();
+ yui::YUI::app()->normalCursor();
+}
+
+
+sub build_tree {
+ my ($tree, $pkg_by_group_hash, $options, $force_rebuild, $flat, $mode) = @_;
+ state $old_mode;
+ $mode = $options->{rmodes}{$mode} || $mode;
+ $old_mode = '' if(!defined($old_mode));
+ return if $old_mode eq $mode && !$force_rebuild;
+ $old_mode = $mode;
+ undef $force_rebuild;
+ my @elems;
+ my $wait; $wait = statusbar_msg($loc->N("Please wait, listing packages...")) if $MODE ne 'update';
+
+ 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) {
+ _build_tree($tree, $pkg_by_group_hash, ['', $loc->N("(none)")]);
+# add_node('', $loc->N("(none)"), { nochild => 1 });
+ state $explanation_only_once;
+ $explanation_only_once or interactive_msg($loc->N("No update"),
+ $loc->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;
+ }
+ }
+ if (scalar @keys) {
+ # 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_($_) } ManaTools::Shared::RunProgram::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) {
+ carp "WARNING: TODO \$flat not tested\n";
+ _build_tree($tree, $pkg_by_group_hash, map {[$_->[0], '']} $sortmethods{$::mode->[0] || 'flat'}->(@elems));
+# add_node($_->[0], '') foreach $sortmethods{$::mode->[0] || 'flat'}->(@elems);
+ }
+ else {
+ if ($::mode->[0] eq 'by_source') {
+ _build_tree($tree, $pkg_by_group_hash, $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, $pkg_by_group_hash, map {
+ my $pkg = $pkgs->{$_->[0]}{pkg};
+ [ $_->[0], $pkg->flag_installed ?
+ (!$pkg->flag_skip && $pkg->flag_upgrade ? $loc->N("Upgradable") : $loc->N("Installed"))
+ : $loc->N("Addable") ];
+ } $sortmethods{flat}->(@elems));
+ }
+ else {
+ _build_tree($tree, $pkg_by_group_hash, @elems);
+ }
+ }
+ }
+ statusbar_msg_remove($wait) if defined $wait;
+}
+
+#=============================================================
+
+=head2 get_info
+
+=head3 INPUT
+
+ $key: package full name
+ $options: HASH reference containing:
+ details => show details
+ changelog => show changelog
+ files => show files
+ new_deps => show new dependencies
+
+=head3 DESCRIPTION
+
+ return a string with all the package info
+
+=cut
+
+#=============================================================
+sub get_info {
+ my ($key, $options) = @_;
+ #- the package information hasn't been loaded. Instead of rescanning the media, just give up.
+ exists $pkgs->{$key} or return [ [ $loc->N("Description not available for this package\n") ] ];
+ #- get the description if needed:
+ exists $pkgs->{$key}{description} or slow_func(sub { extract_header($pkgs->{$key}, $urpm, 'info', find_installed_version($pkgs->{$key}{pkg})) });
+ _format_pkg_simplifiedinfo($pkgs, $key, $urpm, $descriptions, $options);
+}
+
+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};
+ ManaTools::Shared::RunProgram::raw({ detach => 1, as_user => 1 }, 'www-browser', $url);
+}
+
+#=============================================================
+
+=head2 run_browser
+
+=head3 INPUT
+
+ $url: url to be passed to the configured browser
+
+=head3 DESCRIPTION
+
+ This function calls the browser with the given URL
+
+=cut
+
+#=============================================================
+sub run_browser {
+ my $url = shift;
+
+ my ($user) = grep { $_->[2] eq $ENV{USERHELPER_UID} } MDK::Common::System::list_passwd();
+ local $ENV{HOME} = $user->[7] if $user && $ENV{USERHELPER_UID};
+ ManaTools::Shared::RunProgram::raw({ detach => 1, as_user => 1 }, 'www-browser', $url);
+}
+
+#=============================================================
+
+=head2 group_path_name
+
+=head3 INPUT
+
+ $treeItem: YTreeItem object
+
+=head3 OUTPUT
+
+ $fullname: completed path group name
+
+=head3 DESCRIPTION
+
+ This function returns the path name treeItem group name (e.g. foo|bar)
+
+=cut
+
+#=============================================================
+sub group_path_name {
+ my $treeItem = shift;
+
+ my $fullname = $treeItem->label();
+ my $it = $treeItem;
+ while ($it = $it->parent()) {
+ $fullname = join("|", $it->label(), $fullname);
+ }
+
+ return $fullname;
+}
+
+sub groups_tree {
+ carp "DEPRECATED groups_tree: do not use it any more!";
+
+ return %groups_tree;
+}
+
+sub group_has_parent {
+ my ($group) = shift;
+ carp "DEPRECATED group_has_parent: do not use it any more!";
+ return 0 if(!defined($group));
+ return defined($groups_tree{$group}{parent});
+}
+
+sub group_parent {
+ my ($group) = shift;
+
+ carp "DEPRECATED group_parent: do not use it any more!";
+ # if group is a parent itself return it
+ # who use group_parent have to take care of the comparison
+ # between a group and its parent
+ # e.g. group System has groups_tree{'System'}{parent}->label() = 'System'
+ return $groups_tree{$group}{parent} if(group_has_parent($group));
+ for my $sup (keys %groups_tree){
+ for my $item(keys %{$groups_tree{$sup}{children}}){
+ if(defined($group) && ($item eq $group)){
+ return $groups_tree{$sup}{parent};
+ }
+ }
+ }
+ return undef;
+}
+
+1;
diff --git a/lib/ManaTools/Rpmdragora/gurpm.pm b/lib/ManaTools/Rpmdragora/gurpm.pm
new file mode 100644
index 0000000..20cac6e
--- /dev/null
+++ b/lib/ManaTools/Rpmdragora/gurpm.pm
@@ -0,0 +1,298 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Rpmdragora::gurpm;
+
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ ManaTools::Rpmdragora::gurpm - Module that shows the urpmi
+ progress status
+
+=head1 SYNOPSIS
+
+ my %option = (title => "Urpmi action ivoked", text => "Please wait", );
+ my $gurpmi = ManaTools::Rpmdragora::gurpm->new(%option);
+ $gurpmi->progress(45);
+
+ #add to an existing dialog
+ %option = (title => "Urpmi action ivoked", text => "Please wait", main_dialog => $dialog, parent => $parent_container);
+ $gurpmi = ManaTools::Rpmdragora::gurpm->new(%option);
+ $gurpmi->progress(20);
+
+=head1 DESCRIPTION
+
+ This class is used to show the progress of an urpmi operation on
+ its progress bar. It can be istantiated as a popup dialog or used
+ to add label and progress bar into a YLayoutBox container.
+
+=head1 SUPPORT
+
+ You can find documentation for this module with the perldoc command:
+
+ perldoc ManaTools::Rpmdragora::gurpm
+
+=head1 AUTHOR
+
+ Angelo Naselli <anaselli@linux.it>
+
+ Matteo Pasotti <matteo.pasotti@gmail.com>
+
+=head1 COPYRIGHT and LICENSE
+
+ Copyright (c) 2002 Guillaume Cottenceau
+ Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com>
+ Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
+ Copyright (c) 2005-2007 Mandriva SA
+ Copyright (c) 2013 - 2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+ Copyright (C) 2015, Angelo Naselli <anaselli@linux.it>
+
+ 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
+
+=cut
+
+
+use Moose;
+use Carp;
+use Time::HiRes;
+
+use yui;
+use feature 'state';
+
+
+has 'title' => (
+ is => 'rw',
+ isa => 'Str',
+);
+
+has 'text' => (
+ is => 'rw',
+ isa => 'Str',
+);
+
+has 'main_dialog' => (
+ is => 'rw',
+ isa => 'yui::YDialog',
+);
+
+has 'parent' => (
+ is => 'rw',
+ isa => 'yui::YReplacePoint',
+);
+
+has 'label_widget' => (
+ is => 'rw',
+ isa => 'yui::YLabel',
+ init_arg => undef,
+);
+
+has 'progressbar' => (
+ is => 'rw',
+ isa => 'yui::YProgressBar',
+ init_arg => undef,
+);
+
+#=============================================================
+
+=head2 BUILD
+
+=head3 DESCRIPTION
+
+ The BUILD method is called after a Moose object is created,
+ in this methods Services loads all the service information.
+
+=cut
+
+#=============================================================
+sub BUILD {
+ my $self = shift;
+
+ my $factory = yui::YUI::widgetFactory;
+ my $vbox;
+
+ if (! $self->main_dialog) {
+ if ($self->parent) {
+ carp "WARNING: parent parameter is skipped without main_dialog set\n" ;
+ $self->parent(undef);
+ }
+ $self->main_dialog($factory->createPopupDialog());
+ $vbox = $factory->createVBox($self->main_dialog);
+ }
+ else {
+ die "parent parameter is mandatory with main_dialog" if !$self->parent;
+ $self->main_dialog->startMultipleChanges();
+ $self->parent->deleteChildren();
+ $vbox = $factory->createVBox($self->parent);
+ $factory->createVSpacing($vbox, 0.5);
+ }
+
+ $self->label_widget( $factory->createLabel($vbox, $self->text) );
+ $self->label_widget->setStretchable( $yui::YD_HORIZ, 1 );
+ $self->progressbar( $factory->createProgressBar($vbox, "") );
+
+ if ($self->parent) {
+ $factory->createVSpacing($vbox, 0.5);
+ $self->parent->showChild();
+ $self->main_dialog->recalcLayout();
+ $self->main_dialog->doneMultipleChanges();
+ }
+
+ $self->main_dialog->pollEvent();
+ $self->flush();
+}
+
+
+#=============================================================
+
+=head2 flush
+
+=head3 DESCRIPTION
+
+ Polls a dialog event to refresh the dialog
+
+=cut
+
+#=============================================================
+sub flush {
+ my ($self) = @_;
+
+ $self->main_dialog->startMultipleChanges();
+ $self->main_dialog->recalcLayout();
+ $self->main_dialog->doneMultipleChanges();
+
+ if ($self->main_dialog->isTopmostDialog()) {
+ $self->main_dialog->waitForEvent(10);
+ $self->main_dialog->pollEvent();
+ }
+ else {
+ carp "This dialog is not a top most dialog\n";
+ }
+ yui::YUI::app()->redrawScreen();
+}
+
+#=============================================================
+
+=head2 label
+
+=head3 INPUT
+
+ $text: text to be shown on label
+
+=head3 DESCRIPTION
+
+ Sets the label text
+
+=cut
+
+#=============================================================
+sub label {
+ my ($self, $text) = @_;
+
+ $self->main_dialog->startMultipleChanges();
+ $self->label_widget->setValue($text) if $text;
+ $self->main_dialog->doneMultipleChanges();
+
+ $self->flush();
+}
+
+#=============================================================
+
+=head2 progress
+
+=head3 INPUT
+
+ $value: integer value in the range 0..100
+
+=head3 DESCRIPTION
+
+ Sets the progress bar percentage value
+
+=cut
+
+#=============================================================
+sub progress {
+ my ($self, $value) = @_;
+ state $time = 0;
+
+ $value = 0 if !defined($value) || $value < 0;
+ $value = 100 if 100 < $value;
+
+ $self->progressbar->setValue($value);
+ return if Time::HiRes::clock_gettime() - $time < 0.333;
+ $time = Time::HiRes::clock_gettime();
+
+ $self->flush();
+}
+
+#=============================================================
+
+=head2 DEMOLISH
+
+=head3 INPUT
+
+ $val: boolean value indicating whether or not this method
+ was called as part of the global destruction process
+ (when the Perl interpreter exits)
+
+=head3 DESCRIPTION
+
+ Moose provides a hook for object destruction with the
+ DEMOLISH method as it does for construtor with BUILD
+
+=cut
+
+#=============================================================
+sub DEMOLISH {
+ my ($self, $val) = @_;
+
+ $self->main_dialog->destroy if !$self->parent;
+}
+
+# TODO cancel button cannot be easily managed in libyui polling events
+# removed atm
+#
+# sub validate_cancel {
+# my ($self, $cancel_msg, $cancel_cb) = @_;
+# $self->{main_dialog}->startMultipleChanges();
+# 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->flush();
+# }
+#
+# 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/lib/ManaTools/Rpmdragora/icon.pm b/lib/ManaTools/Rpmdragora/icon.pm
new file mode 100644
index 0000000..6c3887d
--- /dev/null
+++ b/lib/ManaTools/Rpmdragora/icon.pm
@@ -0,0 +1,234 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Rpmdragora::icon;
+#*****************************************************************************
+#
+# Copyright (c) 2002 Guillaume Cottenceau
+# Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com>
+# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
+# Copyright (c) 2005-2007 Mandriva SA
+# Copyright (c) 2013 - 2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+# Copyright (c) 2014 - 2015 Angelo Naselli <anaselli@linux.it>
+#
+# 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;
+
+use ManaTools::rpmdragora;
+use ManaTools::Shared::Locales;
+
+my $loc = ManaTools::rpmdragora::locale();
+
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(get_icon_path);
+#- /usr/share/rpmlint/config (duplicates are normal, so that we are not too far away from .py)
+my %group_icons = (
+ $loc->N("All") => 'system_section',
+ $loc->N("Accessibility") => 'accessibility_section',
+ $loc->N("Archiving") => 'archiving_section',
+ join('|', $loc->N("Archiving"), $loc->N("Backup")) => 'backup_section',
+ join('|', $loc->N("Archiving"), $loc->N("Cd burning")) => 'cd_burning_section',
+ join('|', $loc->N("Archiving"), $loc->N("Compression")) => 'compression_section',
+ join('|', $loc->N("Archiving"), $loc->N("Other")) => 'other_archiving',
+ $loc->N("Communications") => 'communications_section',
+ join('|', $loc->N("Communications"), $loc->N("Bluetooth")) => 'communications_bluetooth_section',
+ join('|', $loc->N("Communications"), $loc->N("Dial-Up")) => 'communications_dialup_section',
+ join('|', $loc->N("Communications"), $loc->N("Fax")) => 'communications_fax_section',
+ join('|', $loc->N("Communications"), $loc->N("Mobile")) => 'communications_mobile_section',
+ join('|', $loc->N("Communications"), $loc->N("Radio")) => 'communications_radio_section',
+ join('|', $loc->N("Communications"), $loc->N("Serial")) => 'communications_serial_section',
+ join('|', $loc->N("Communications"), $loc->N("Telephony")) => 'communications_phone_section',
+ $loc->N("Databases") => 'databases_section',
+ $loc->N("Development") => 'development_section',
+ join('|', $loc->N("Development"), $loc->N("Basic")) => '',
+ join('|', $loc->N("Development"), $loc->N("C")) => '',
+ join('|', $loc->N("Development"), $loc->N("C++")) => '',
+ join('|', $loc->N("Development"), $loc->N("C#")) => '',
+ join('|', $loc->N("Development"), $loc->N("Databases")) => 'databases_section',
+ join('|', $loc->N("Development"), $loc->N("Debug")) => '',
+ join('|', $loc->N("Development"), $loc->N("Erlang")) => '',
+ join('|', $loc->N("Development"), $loc->N("GNOME and GTK+")) => 'gnome_section',
+ join('|', $loc->N("Development"), $loc->N("Java")) => '',
+ join('|', $loc->N("Development"), $loc->N("KDE and Qt")) => 'kde_section',
+ join('|', $loc->N("Development"), $loc->N("Kernel")) => '',
+ join('|', $loc->N("Development"), $loc->N("OCaml")) => '',
+ join('|', $loc->N("Development"), $loc->N("Other")) => '',
+ join('|', $loc->N("Development"), $loc->N("Perl")) => '',
+ join('|', $loc->N("Development"), $loc->N("PHP")) => '',
+ join('|', $loc->N("Development"), $loc->N("Python")) => '',
+ join('|', $loc->N("Development"), $loc->N("Tools")) => 'development_tools_section',
+ join('|', $loc->N("Development"), $loc->N("X11")) => '',
+ $loc->N("Documentation") => 'documentation_section',
+ $loc->N("Editors") => 'editors_section',
+ $loc->N("Education") => 'education_section',
+ $loc->N("Emulators") => 'emulators_section',
+ $loc->N("File tools") => 'file_tools_section',
+ $loc->N("Games") => 'amusement_section',
+ join('|', $loc->N("Games"), $loc->N("Adventure")) => 'adventure_section',
+ join('|', $loc->N("Games"), $loc->N("Arcade")) => 'arcade_section',
+ join('|', $loc->N("Games"), $loc->N("Boards")) => 'boards_section',
+ join('|', $loc->N("Games"), $loc->N("Cards")) => 'cards_section',
+ join('|', $loc->N("Games"), $loc->N("Other")) => 'other_amusement',
+ join('|', $loc->N("Games"), $loc->N("Puzzles")) => 'puzzle_section',
+ join('|', $loc->N("Games"), $loc->N("Shooter")) => 'shooter_section',
+ join('|', $loc->N("Games"), $loc->N("Simulation")) => 'simulation_section',
+ join('|', $loc->N("Games"), $loc->N("Sports")) => 'sport_section',
+ join('|', $loc->N("Games"), $loc->N("Strategy")) => 'strategy_section',
+ $loc->N("Geography") => 'geography_section',
+ $loc->N("Graphical desktop") => 'graphical_desktop_section',
+ join('|', $loc->N("Graphical desktop"),
+ #-PO: This is a package/product name. Only translate it if needed:
+ $loc->N("Enlightenment")) => 'enlightment_section',
+ join('|', $loc->N("Graphical desktop"),
+ #-PO: This is a package/product name. Only translate it if needed:
+ $loc->N("GNOME")) => 'gnome_section',
+ join('|', $loc->N("Graphical desktop"),
+ #-PO: This is a package/product name. Only translate it if needed:
+ $loc->N("Icewm")) => 'icewm_section',
+ join('|', $loc->N("Graphical desktop"),
+ #-PO: This is a package/product name. Only translate it if needed:
+ $loc->N("KDE")) => 'kde_section',
+ join('|', $loc->N("Graphical desktop"), $loc->N("Other")) => 'more_applications_other_section',
+ join('|', $loc->N("Graphical desktop"),
+ #-PO: This is a package/product name. Only translate it if needed:
+ $loc->N("WindowMaker")) => 'windowmaker_section',
+ join('|', $loc->N("Graphical desktop"),
+ #-PO: This is a package/product name. Only translate it if needed:
+ $loc->N("Xfce")) => 'xfce_section',
+ $loc->N("Graphics") => 'graphics_section',
+ join('|', $loc->N("Graphics"), $loc->N("3D")) => 'graphics_3d_section',
+ join('|', $loc->N("Graphics"), $loc->N("Editors and Converters")) => 'graphics_editors_section',
+ join('|', $loc->N("Graphics"), $loc->N("Utilities")) => 'graphics_utilities_section',
+ join('|', $loc->N("Graphics"), $loc->N("Photography")) => 'graphics_photography_section',
+ join('|', $loc->N("Graphics"), $loc->N("Scanning")) => 'graphics_scanning_section',
+ join('|', $loc->N("Graphics"), $loc->N("Viewers")) => 'graphics_viewers_section',
+ $loc->N("Monitoring") => 'monitoring_section',
+ $loc->N("Networking") => 'networking_section',
+ join('|', $loc->N("Networking"), $loc->N("File transfer")) => 'file_transfer_section',
+ join('|', $loc->N("Networking"), $loc->N("IRC")) => 'irc_section',
+ join('|', $loc->N("Networking"), $loc->N("Instant messaging")) => 'instant_messaging_section',
+ join('|', $loc->N("Networking"), $loc->N("Mail")) => 'mail_section',
+ join('|', $loc->N("Networking"), $loc->N("News")) => 'news_section',
+ join('|', $loc->N("Networking"), $loc->N("Other")) => 'other_networking',
+ join('|', $loc->N("Networking"), $loc->N("Remote access")) => 'remote_access_section',
+ join('|', $loc->N("Networking"), $loc->N("WWW")) => 'networking_www_section',
+ $loc->N("Office") => 'office_section',
+ join('|', $loc->N("Office"), $loc->N("Dictionary")) => 'office_dictionary_section',
+ join('|', $loc->N("Office"), $loc->N("Finance")) => 'finances_section',
+ join('|', $loc->N("Office"), $loc->N("Management")) => 'timemanagement_section',
+ join('|', $loc->N("Office"), $loc->N("Organizer")) => 'timemanagement_section',
+ join('|', $loc->N("Office"), $loc->N("Utilities")) => 'office_accessories_section',
+ join('|', $loc->N("Office"), $loc->N("Spreadsheet")) => 'spreadsheet_section',
+ join('|', $loc->N("Office"), $loc->N("Suite")) => 'office_suite',
+ join('|', $loc->N("Office"), $loc->N("Word processor")) => 'wordprocessor_section',
+ $loc->N("Publishing") => 'publishing_section',
+ $loc->N("Sciences") => 'sciences_section',
+ join('|', $loc->N("Sciences"), $loc->N("Astronomy")) => 'astronomy_section',
+ join('|', $loc->N("Sciences"), $loc->N("Biology")) => 'biology_section',
+ join('|', $loc->N("Sciences"), $loc->N("Chemistry")) => 'chemistry_section',
+ join('|', $loc->N("Sciences"), $loc->N("Computer science")) => 'computer_science_section',
+ join('|', $loc->N("Sciences"), $loc->N("Geosciences")) => 'geosciences_section',
+ join('|', $loc->N("Sciences"), $loc->N("Mathematics")) => 'mathematics_section',
+ join('|', $loc->N("Sciences"), $loc->N("Other")) => 'other_sciences',
+ join('|', $loc->N("Sciences"), $loc->N("Physics")) => 'physics_section',
+ $loc->N("Security") => 'security_section',
+ $loc->N("Shells") => 'shells_section',
+ $loc->N("Sound") => 'sound_section',
+ join('|', $loc->N("Sound"), $loc->N("Editors and Converters")) => 'sound_editors_section',
+ join('|', $loc->N("Sound"), $loc->N("Midi")) => 'sound_midi_section',
+ join('|', $loc->N("Sound"), $loc->N("Mixers")) => 'sound_mixers_section',
+ join('|', $loc->N("Sound"), $loc->N("Players")) => 'sound_players_section',
+ join('|', $loc->N("Sound"), $loc->N("Utilities")) => 'sound_utilities_section',
+ $loc->N("System") => 'system_section',
+ join('|', $loc->N("System"), $loc->N("Base")) => 'system_section',
+ join('|', $loc->N("System"), $loc->N("Boot and Init")) => 'boot_init_section',
+ join('|', $loc->N("System"), $loc->N("Cluster")) => 'parallel_computing_section',
+ join('|', $loc->N("System"), $loc->N("Configuration")) => 'configuration_section',
+ join('|', $loc->N("System"), $loc->N("Fonts")) => 'chinese_section',
+ join('|', $loc->N("System"), $loc->N("Fonts"), $loc->N("True type")) => '',
+ join('|', $loc->N("System"), $loc->N("Fonts"), $loc->N("Type1")) => '',
+ join('|', $loc->N("System"), $loc->N("Fonts"), $loc->N("X11 bitmap")) => '',
+ join('|', $loc->N("System"), $loc->N("Internationalization")) => 'chinese_section',
+ join('|', $loc->N("System"), $loc->N("Kernel and hardware")) => 'hardware_configuration_section',
+ join('|', $loc->N("System"), $loc->N("Libraries")) => 'system_section',
+ join('|', $loc->N("System"), $loc->N("Networking")) => 'networking_configuration_section',
+ join('|', $loc->N("System"), $loc->N("Packaging")) => 'packaging_section',
+ join('|', $loc->N("System"), $loc->N("Printing")) => 'printing_section',
+ join('|', $loc->N("System"), $loc->N("Servers")) => 'servers_section',
+ join('|', $loc->N("System"),
+ #-PO: This is a package/product name. Only translate it if needed:
+ $loc->N("X11")) => 'x11_section',
+ $loc->N("Terminals") => 'terminals_section',
+ $loc->N("Text tools") => 'text_tools_section',
+ $loc->N("Toys") => 'toys_section',
+ $loc->N("Video") => 'video_section',
+ join('|', $loc->N("Video"), $loc->N("Editors and Converters")) => 'video_editors_section',
+ join('|', $loc->N("Video"), $loc->N("Players")) => 'video_players_section',
+ join('|', $loc->N("Video"), $loc->N("Television")) => 'video_television_section',
+ join('|', $loc->N("Video"), $loc->N("Utilities")) => 'video_utilities_section',
+
+ # for Mageia Choice:
+ $loc->N("Workstation") => 'system_section',
+ join('|', $loc->N("Workstation"), $loc->N("Configuration")) => 'configuration_section',
+ join('|', $loc->N("Workstation"), $loc->N("Console Tools")) => 'interpreters_section',
+ join('|', $loc->N("Workstation"), $loc->N("Documentation")) => 'documentation_section',
+ join('|', $loc->N("Workstation"), $loc->N("Game station")) => 'amusement_section',
+ join('|', $loc->N("Workstation"), $loc->N("Internet station")) => 'networking_section',
+ join('|', $loc->N("Workstation"), $loc->N("Multimedia station")) => 'multimedia_section',
+ join('|', $loc->N("Workstation"), $loc->N("Network Computer (client)")) => 'other_networking',
+ join('|', $loc->N("Workstation"), $loc->N("Office Workstation")) => 'office_section',
+ join('|', $loc->N("Workstation"), $loc->N("Scientific Workstation")) => 'sciences_section',
+ $loc->N("Graphical Environment") => 'graphical_desktop_section',
+
+ join('|', $loc->N("Graphical Environment"), $loc->N("GNOME Workstation")) => 'gnome_section',
+ join('|', $loc->N("Graphical Environment"), $loc->N("IceWm Desktop")) => 'icewm_section',
+ join('|', $loc->N("Graphical Environment"), $loc->N("KDE Workstation")) => 'kde_section',
+ join('|', $loc->N("Graphical Environment"), $loc->N("Other Graphical Desktops")) => 'more_applications_other_section',
+ $loc->N("Development") => 'development_section',
+ join('|', $loc->N("Development"), $loc->N("Development")) => 'development_section',
+ join('|', $loc->N("Development"), $loc->N("Documentation")) => 'documentation_section',
+ $loc->N("Server") => 'servers_section',
+ join('|', $loc->N("Server"), $loc->N("DNS/NIS")) => 'networking_section',
+ join('|', $loc->N("Server"), $loc->N("Database")) => 'databases_section',
+ join('|', $loc->N("Server"), $loc->N("Firewall/Router")) => 'networking_section',
+ join('|', $loc->N("Server"), $loc->N("Mail")) => 'mail_section',
+ join('|', $loc->N("Server"), $loc->N("Mail/Groupware/News")) => 'mail_section',
+ join('|', $loc->N("Server"), $loc->N("Network Computer server")) => 'networking_section',
+ join('|', $loc->N("Server"), $loc->N("Web/FTP")) => 'networking_www_section',
+
+ );
+
+sub get_icon_path {
+ my ($group, $parent) = @_;
+
+ my $path = $parent ? '/usr/share/icons/mini/' : '/usr/share/icons/';
+ my $icon_path = "";
+ if(defined($group_icons{$group})){
+ $icon_path = join('', $path, $group_icons{$group}, '.png');
+ }elsif(defined($group_icons{$parent."\|".$group})){
+ $icon_path = join('', $path, $group_icons{$parent."\|".$group}, '.png');
+ }else{
+ $icon_path = join('', $path, 'applications_section', '.png');
+ }
+ unless(-e $icon_path){
+ $icon_path = join('', $path, 'applications_section', '.png');
+ }
+ return $icon_path;
+}
+
+1;
diff --git a/lib/ManaTools/Rpmdragora/init.pm b/lib/ManaTools/Rpmdragora/init.pm
new file mode 100644
index 0000000..f6d570d
--- /dev/null
+++ b/lib/ManaTools/Rpmdragora/init.pm
@@ -0,0 +1,181 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Rpmdragora::init;
+#*****************************************************************************
+#
+# Copyright (c) 2002 Guillaume Cottenceau
+# Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com>
+# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
+# Copyright (c) 2005-2007 Mandriva SA
+# Copyright (c) 2013 - 2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+# Copyright (c) 2014 - 2015 Angelo Naselli <anaselli@linux.it>
+#
+# 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 English;
+BEGIN { $::no_global_argv_parsing = 1 }
+require urpm::args;
+use MDK::Common::Various qw(chomp_);
+
+use ManaTools::Privileges;
+use ManaTools::Shared::Locales;
+
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = qw(init
+ warn_about_user_mode
+ $MODE
+ $VERSION
+ $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 $loc = ManaTools::Shared::Locales->new(domain_name => 'rpmdrake');
+
+ my $basename = sub { local $_ = shift; s|/*\s*$||; s|.*/||; $_ };
+ any { /^--?h/ } @ARGV and do {
+ printf join("\n", $loc->N("Usage: %s [OPTION]...", $basename->($0)),
+$loc->N(" --auto assume default answers to questions"),
+$loc->N(" --changelog-first display changelog before filelist in the description window"),
+$loc->N(" --media=medium1,.. limit to given media"),
+$loc->N(" --merge-all-rpmnew propose to merge all .rpmnew/.rpmsave files found"),
+$loc->N(" --mode=MODE set mode (install (default), remove, update)"),
+$loc->N(" --justdb update the database, but do not modify the filesystem"),
+$loc->N(" --no-confirmation don't ask first confirmation question in update mode"),
+$loc->N(" --no-media-update don't update media at startup"),
+$loc->N(" --no-verify-rpm don't verify package signatures"),
+if_($0 !~ /MageiaUpdate/, $loc->N(" --parallel=alias,host be in parallel mode, use \"alias\" group, use \"host\" machine to show needed deps")),
+$loc->N(" --rpm-root=path use another root for rpm installation"),
+$loc->N(" --urpmi-root use another root for urpmi db & rpm installation"),
+$loc->N(" --run-as-root force to run as root"),
+$loc->N(" --search=pkg run search for \"pkg\""),
+$loc->N(" --test only verify if the installation can be achieved correctly"),
+chomp_($loc->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.0.0";
+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 ManaTools::Privileges::is_root_capability_required();
+$::noborderWhenEmbedded = 1;
+
+require ManaTools::rpmdragora;
+
+our $changelog_first = $ManaTools::rpmdragora::changelog_first_config->[0];
+$changelog_first = 1 if $rpmdragora_options{'changelog-first'};
+
+sub warn_about_user_mode() {
+ my $loc = ManaTools::Shared::Locales->new(domain_name => 'rpmdrake');
+ my $title = $loc->N("Running in user mode");
+ my $msg = $loc->N("You are launching this program as a normal user.\n".
+ "You will not be able to perform modifications on the system,\n".
+ "but you may still browse the existing database.");
+
+ if(($EUID != 0) and (!ManaTools::rpmdragora::interactive_msg($title, $msg))) {
+ return 0;
+ }
+ return 1;
+}
+
+sub init() {
+ URPM::bind_rpm_textdomain_codeset();
+}
+
+1;
diff --git a/lib/ManaTools/Rpmdragora/open_db.pm b/lib/ManaTools/Rpmdragora/open_db.pm
new file mode 100644
index 0000000..7bfa7bf
--- /dev/null
+++ b/lib/ManaTools/Rpmdragora/open_db.pm
@@ -0,0 +1,171 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Rpmdragora::open_db;
+#*****************************************************************************
+#
+# Copyright (c) 2002 Guillaume Cottenceau
+# Copyright (c) 2002-2014 Thierry Vignaud <thierry.vignaud@gmail.com>
+# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
+# Copyright (c) 2005-2007 Mandriva SA
+# Copyright (c) 2012-2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+# Copyright (c) 2014-2015 Angelo Naselli <anaselli@linux.it>
+#
+# 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 Sys::Syslog;
+
+use MDK::Common::File qw(cat_ mkdir_p);
+use MDK::Common::Func;
+use ManaTools::rpmdragora;
+use URPM;
+use urpm;
+use urpm::args;
+use urpm::select;
+use urpm::media;
+use urpm::mirrors;
+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
+ );
+
+my $loc = ManaTools::rpmdragora::locale();
+
+
+# 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;
+ Sys::Syslog::syslog('info|local1', "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($loc->N("Fatal error"),
+ $loc->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 = '/etc/product.id';
+ $path = $::rpmdragora_options{'urpmi-root'}[0] . $path if defined($::rpmdragora_options{'urpmi-root'}[0]);
+ $res = urpm::mirrors::parse_LDAP_namespace_structure(cat_($path))->{branch} eq 'Devel';
+ return $res;
+}
+
+sub get_backport_media {
+ my ($urpm) = @_;
+ grep { $_->{name} =~ /backport/i &&
+ $_->{name} !~ /debug|sources|testing/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, MDK::Common::Func::if_($searchmedia, searchmedia => $searchmedia), %urpmi_options);
+ $urpm;
+}
+
+1;
diff --git a/lib/ManaTools/Rpmdragora/pkg.pm b/lib/ManaTools/Rpmdragora/pkg.pm
new file mode 100644
index 0000000..ba0b18b
--- /dev/null
+++ b/lib/ManaTools/Rpmdragora/pkg.pm
@@ -0,0 +1,1085 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Rpmdragora::pkg;
+#*****************************************************************************
+#
+# Copyright (c) 2002 Guillaume Cottenceau
+# Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com>
+# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
+# Copyright (c) 2005-2007 Mandriva SA
+# Copyright (c) 2012-2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+# Copyright (c) 2014-2015 Angelo Naselli <anaselli@linux.it>
+#
+# 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 Sys::Syslog;
+
+use MDK::Common::Func; #qw(before_leaving any);
+use MDK::Common::DataStructure; # qw (uniq difference2 member add2hash put_in_hash);
+use MDK::Common::System qw(arch);
+use MDK::Common::File; # qw(cat_);
+use MDK::Common::Various qw(chomp_);
+
+use POSIX qw(_exit ceil);
+use URPM;
+use utf8;
+use ManaTools::rpmdragora;
+use ManaTools::Rpmdragora::open_db;
+use ManaTools::Rpmdragora::gurpm;
+use ManaTools::Rpmdragora::formatting;
+use ManaTools::Rpmdragora::rpmnew;
+use ManaTools::Shared::RunProgram qw(run get_stdout);
+use ManaTools::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 urpm::util;
+use Carp;
+
+my $loc = ManaTools::rpmdragora::locale();
+
+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
+ );
+
+our $priority_up_alread_warned;
+
+sub sort_packages_biarch {
+ sort {
+ my ($na, $aa) = $a =~ /^(.*-[^-]+-[^-]+)\.([^.-]+)$/;
+ my ($nb, $ab) = $b =~ /^(.*-[^-]+-[^-]+)\.([^.-]+)$/;
+ !defined($na) ?
+ (!defined($nb) ? 0 : 1) :
+ (!defined($nb) ? -1 : $na cmp $nb || ($ab =~ /64$/) <=> ($aa =~ /64$/));
+ } @_;
+}
+
+sub sort_packages_monoarch {
+ sort { uc($a) cmp uc($b) } @_;
+}
+
+*sort_packages = MDK::Common::System::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($_); $_ } ManaTools::Shared::RunProgram::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 { MDK::Common::Various::chomp_($_) } run_rpm("rpm -ql $name");
+ MDK::Common::DataStructure::add2hash($pkg, { files => [ @files ? @files : $loc->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 = MDK::Common::Func::before_leaving { $bar_id and statusbar_msg_remove($bar_id) };
+ my $dir = urpm::file_from_local_url($medium->{url});
+
+ print "p->filename: ". $p->filename."\n";
+ $local_source = "$dir/" . $p->filename if $dir;
+ print "local_source: " . ($local_source ? $local_source : "") . "\n";
+
+ if ($local_source && -e $local_source) {
+ $bar_id = statusbar_msg($loc->N("Getting information from %s...", $dir), 0);
+ $urpm->{log}("getting information from rpms from $dir");
+ } else {
+ my $gurpm;
+ $bar_id = statusbar_msg($loc->N("Getting '%s' from XML meta-data...", $xml_info), 0);
+ my $_gurpm_clean_guard = MDK::Common::Func::before_leaving { undef $gurpm };
+ if (my $xml_info_file = eval { urpm::media::any_xml_info($urpm, $medium, $xml_info, undef, sub {
+ $gurpm ||= ManaTools::Rpmdragora::gurpm->new(
+ text => $loc->N("Please wait"),
+ );
+ 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 $@;
+ MDK::Common::DataStructure::put_in_hash($xml_info_pkgs{$name} ||= {}, $nodes{$name});
+ } else {
+ if ($xml_info eq 'info') {
+ $urpm->{info}($loc->N("No xml info for medium \"%s\", only partial result for package %s", $medium->{name}, $name));
+ } else {
+ $urpm->{error}($loc->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 ($local_source && -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 = $loc->N("(none)") if !@files;
+ MDK::Common::DataStructure::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') {
+ MDK::Common::DataStructure::add2hash($pkg, { description => rpm_description($xml_info_pkgs{$name}{description}),
+ url => $xml_info_pkgs{$name}{url}
+ });
+ } elsif ($xml_info eq 'files') {
+ my @files = map { MDK::Common::Various::chomp_($loc->to_utf8($_)) } split("\n", $xml_info_pkgs{$name}{files});
+ MDK::Common::DataStructure::add2hash($pkg, { files => scalar(@files) ? \@files : [ $loc->N("(none)") ] });
+ } elsif ($xml_info eq 'changelog') {
+ MDK::Common::DataStructure::add2hash($pkg, {
+ changelog => format_changelog_changelogs($o_installed_version,
+ @{$xml_info_pkgs{$name}{changelogs}})
+ });
+ }
+ } else {
+ goto header_non_available;
+ }
+ return;
+ header_non_available:
+ MDK::Common::DataStructure::add2hash($pkg, { summary => $p->summary || $loc->N("(Not available)"), description => undef });
+ }
+}
+
+sub find_installed_version {
+ my ($p) = @_;
+
+ my $version = $loc->N("(none)");
+ open_rpm_db()->traverse_tag_find('name', $p->name, sub { $version = $_[0]->EVR; return ($version ? 1 : 0) }) if $p;
+ return $version;
+}
+
+my $canceled;
+sub download_callback {
+ my ($gurpm, $mode, $file, $percent, $total, $eta, $speed) = @_;
+ $canceled = 0;
+
+ if ($mode eq 'start') {
+ $gurpm->label($loc->N("Downloading package `%s'...", urpm::util::basename($file)));
+# $gurpm->validate_cancel(but($loc->N("Cancel")), sub { $canceled = 1 });
+ } elsif ($mode eq 'progress') {
+ $gurpm->label(
+ join("\n",
+ $loc->N("Downloading package `%s'...", urpm::util::basename($file)),
+ (defined $total && defined $eta ?
+ $loc->N(" %s%% of %s completed, ETA = %s, speed = %s", $percent, $total, $eta, $speed)
+ : $loc->N(" %s%% completed, speed = %s", $percent, $speed)
+ ) =~ /^\s*(.*)/
+ ),
+ );
+ #$gurpm->progress($percenti/100);
+ $gurpm->progress(ceil($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 0 if $::MODE ne 'update';
+ return 0 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;
+ if (!$::rpmdragora_options{'no-confirmation'}) {
+ my $choice = interactive_msg($loc->N("Confirmation"),
+ $loc->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, dont_ask_again => 1,
+# TODO widget => gtknew('CheckButton', text => $loc->N("Do not ask me next time"),
+# active_ref => \$::rpmdragora_options{'no-confirmation'}
+# )
+ );
+ $::rpmdragora_options{'no-confirmation'} = $choice->{dont_ask_again};
+ return(-1) if !$choice->{value};
+ }
+ writeconf();
+ urpm::media::select_media($urpm, map { $_->{name} } @update_medias);
+ update_sources($urpm, noclean => 1, medialist => [ map { $_->{name} } @update_medias ]);
+ }
+ } else {
+ if (MDK::Common::Func::any { $_->{update} } @{$urpm->{media}}) {
+ interactive_msg($loc->N("Already existing update media"),
+$loc->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\".", $loc->N("Enabled"), $ManaTools::rpmdragora::myname_update));
+ return (-1);
+ }
+ my ($mirror) = choose_mirror($urpm, transient => $w->{real_window} || $::main_window,
+ message => join("\n\n",
+ $loc->N("You have no configured update media. MageiaUpdate cannot operate without any update media."),
+ $loc->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($loc->N("How to choose manually your mirror"),
+$loc->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.", $ManaTools::rpmdragora::myname_update)), return (-1);
+ add_distrib_update_media($urpm, $mirror, only_updates => 0);
+ }
+ return 0;
+}
+
+
+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 = 5;
+ $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;
+ $prev_stage = 0 if(!defined($prev_stage));
+ if ($prev_stage + 1 < $new_stage) {
+ $prev_stage = $new_stage;
+ $gurpm->progress(ceil($new_stage));
+ }
+}
+
+
+sub get_installed_packages {
+ my ($urpm, $db, $all_pkgs, $gurpm) = @_;
+
+ $urpm->{global_config}{'prohibit-remove'} = '' if(!defined($urpm->{global_config}{'prohibit-remove'}));
+ my @base = ("basesystem", split /,\s*/, $urpm->{global_config}{'prohibit-remove'});
+ my (%base, %basepackages, @installed_pkgs, @processed_base);
+ reset_pbar_count(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 MDK::Common::DataStructure::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 => \&ManaTools::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 => \&ManaTools::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 = MDK::Common::DataStructure::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;
+
+ myexit (-1) if (warn_about_media($w, %options) == -1);
+
+ my $gurpm = ManaTools::Rpmdragora::gurpm->new(
+ text => $loc->N("Please wait"),
+ );
+ my $_gurpm_clean_guard = MDK::Common::Func::before_leaving { undef $gurpm };
+
+ my $urpm = open_urpmi_db(update => $probe_only_for_updates && !is_it_a_devel_distro());
+
+ my $_drop_lock = MDK::Common::Func::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($loc->N("Reading updates description"));
+ #- parse the description file
+ my $update_descr = urpm::get_updates_description($urpm, @update_medias);
+ $gurpm->progress(100);
+
+ my $_unused = $loc->N("Please wait, finding available packages...");
+
+# # find out installed packages:
+
+ init_progress_bar($urpm);
+
+ $gurpm->label($loc->N("Please wait, listing base packages..."));
+ $gurpm->progress(ceil($level));
+
+ my $db = eval { open_rpm_db() };
+ if (my $err = $@) {
+ interactive_msg($loc->N("Error"), $loc->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($loc->N("Please wait, finding installed packages..."));
+ $level = 33;
+ $gurpm->progress(ceil($level));
+ reset_pbar_count(66);
+ 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($loc->N("Please wait, finding available packages..."));
+ $level = 66;
+ $gurpm->progress(ceil($level));
+
+ 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
+ $level = 70;
+ $gurpm->progress(ceil($level));
+
+ my %l;
+ reset_pbar_count(100);
+ 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($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;
+ if (defined($medium->{start}) || defined($medium->{end})) {
+ 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 = MDK::Common::DataStructure::uniq(MDK::Common::DataStructure::difference2(\@installable_pkgs, \@updates));
+
+ my @meta_pkgs = grep { /^task-|^basesystem/ } keys %all_pkgs;
+
+ my @gui_pkgs = map { chomp; $_ } MDK::Common::File::cat_('/usr/share/rpmdrake/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 && MDK::Common::DataStructure::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 = shift;
+ return if !$urpm->{readmes};
+
+ my %Readmes = %{$urpm->{readmes}};
+ return if ! scalar keys %Readmes;
+
+ my $appTitle = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($loc->N("Upgrade information"));
+ my $factory = yui::YUI::widgetFactory;
+
+ ## | [msg-label] |
+ ## | |
+ ## | pkg-list |
+ ## | |
+ ## | info on selected pkg |(1)
+ ## | |
+ ## | [ok] |
+ ####
+ # (1) info on pkg list:
+ # selected package readmi.urpmi
+
+ my $dialog = $factory->createPopupDialog;
+ my $vbox = $factory->createVBox( $dialog );
+ my $msgBox = $factory->createLabel($vbox, $loc->N("These packages come with upgrade information"), 1);
+ my $tree = $factory->createTree($vbox, $loc->N("Select a package"));
+ $factory->createVSpacing($vbox, 1);
+ my $infoBox = $factory->createRichText($vbox, "", 0);
+ $tree->setWeight($yui::YD_HORIZ, 2);
+ $infoBox->setWeight($yui::YD_HORIZ, 4);
+ $tree->setWeight($yui::YD_VERT, 10);
+ $infoBox->setWeight($yui::YD_VERT, 10);
+ $factory->createVSpacing($vbox, 1);
+ my $hbox = $factory->createHBox( $vbox );
+ my $align = $factory->createHCenter($hbox);
+ my $okButton = $factory->createPushButton($align, $loc->N("&Ok"));
+ $okButton->setDefaultButton(1);
+
+ # adding packages to the list
+ my $itemColl = new yui::YItemCollection;
+ foreach my $f (sort keys %Readmes) {
+ my $item = new yui::YTreeItem ("$Readmes{$f}");
+ my $child = new yui::YTreeItem ($item, "$f");
+ $child->DISOWN();
+ $itemColl->push($item);
+ $item->DISOWN();
+ }
+ $tree->addItems($itemColl);
+ $tree->setImmediateMode(1);
+
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### widget
+ my $widget = $event->widget();
+ if ($widget == $tree) {
+ my $content = "";
+ my $item = $tree->selectedItem();
+ if ($item && !$item->hasChildren()) {
+ my $filename = $tree->currentItem()->label();
+ $content = scalar MDK::Common::File::cat_($filename);
+ $content = $loc->N("(none)") if !$content; # should not happen
+ ensure_utf8($content);
+ $content =~ s/\n/<br>/g;
+ }
+ $infoBox->setValue($content);
+ }
+ elsif ($widget == $okButton) {
+ last;
+ }
+ }
+ }
+
+ destroy $dialog;
+
+ # restore original title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+
+ return;
+}
+
+sub perform_parallel_install {
+ my ($urpm, $group, $w, $statusbar_msg_id) = @_;
+ my @pkgs = map { MDK::Common::Func::if_($_->flag_requested, urpm_name($_)) } @{$urpm->{depslist}};
+
+ my @error_msgs;
+ my $res = !ManaTools::Shared::RunProgram::run('urpmi', '2>', \@error_msgs, '-v', '--X', '--parallel', $group, @pkgs);
+
+ if ($res) {
+ $$statusbar_msg_id = statusbar_msg(
+ #$loc->N("Everything installed successfully"),
+ $loc->N("All requested packages were installed successfully."),
+ );
+ } else {
+ interactive_msg(
+ $loc->N("Problem during installation"),
+ $loc->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($loc->N("Installation failed"),
+ $loc->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 = MDK::Common::Func::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($loc->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 => \&ManaTools::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(
+ $loc->N("Unable to get source packages."),
+ $loc->N("Unable to get source packages, sorry. %s",
+ @error_msgs ? $loc->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 { MDK::Common::Func::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 ? '<b>' . $loc->N("Rpmdragora or one of its priority dependencies needs to be updated first. Rpmdragora will then restart.") . '</b>' . "\n\n" : '') .
+ ($loc->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 ? $loc->N("Confirmation") : $loc->N("Some packages need to be removed")),
+ join("\n\n",
+ ($r ?
+ (!$to_install ? ($loc->P("Remove one package?", "Remove %d packages?", $remove_count, $remove_count), $r) :
+ (($remove_count == 1 ?
+ $loc->N("The following package has to be removed for others to be upgraded:")
+ : $loc->N("The following packages have to be removed for others to be upgraded:")), $r), MDK::Common::Func::if_($to_install, $to_install))
+ : $to_install),
+ format_size($size),
+ format_filesize($filesize),
+ $loc->N("Is it ok to continue?")),
+ scroll => 1,
+ yesno => 1, min_size => {lines => 18},) or return 1;
+
+ my $_umount_guard = MDK::Common::Func::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 = ManaTools::Rpmdragora::gurpm->new(
+ text => $loc->N("Please wait"),
+ title => $loc->N("Initializing..."),
+ );
+ # my $_gurpm_clean_guard = MDK::Common::Func::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($loc->N("Orphan packages"), $loc->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') {
+ print(1 ? $loc->N("Preparing package installation...") : $loc->N("Preparing package installation transaction..."));
+ $gurpm->label($loc->N("Preparing package installation..."));
+ } elsif (defined $pkg) {
+ $something_installed = 1;
+ print($loc->N("Installing package `%s' (%s/%s)...", $pkg->name, $transaction_progress_nb, scalar(@{$transaction->{upgrade}}))."\n" . $loc->N("Total: %s/%s", $progress_nb, $install_count));
+ $gurpm->label($loc->N("Installing package `%s' (%s/%s)...", $pkg->name, ++$transaction_progress_nb, scalar(@{$transaction->{upgrade}}))
+ . "\n" . $loc->N("Total: %s/%s", ++$progress_nb, $install_count));
+ }
+ } elsif ($subtype eq 'progress') {
+ $gurpm->progress($total ? ceil(($amount/$total)*100) : 100);
+ # print("Progress: ".($total ? ($amount/$total)*100 : 100)."\n");
+ }
+ };
+
+ # 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(
+ $loc->N("Change medium"),
+ $loc->N("Please insert the medium named \"%s\"", $medium),
+ yesno => 1, text => { no => $loc->N("Cancel"), yes => $loc->N("Ok") },
+ );
+ },
+ pre_check_sig => sub { $gurpm->label($loc->N("Verifying package signatures...")) },
+ check_sig => sub { $gurpm->progress(ceil(++$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(
+ $loc->N("Warning"), "$msg\n\n$msg2", yesno => 1, MDK::Common::Func::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($loc->N("Warning"), join("\n", values %$need_restart_formatted), scroll => 1);
+ },
+ trans_error_summary => sub {
+ my ($nok, $errors) = @_;
+ interactive_msg(
+ $loc->N("Problem during installation"),
+ MDK::Common::Func::if_($nok, $loc->N("%d installation transactions failed", $nok) . "\n\n") .
+ $loc->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($loc->N("Warning"),
+ join("\n\n", values %$need_restart_formatted));
+ },
+ success_summary => sub {
+ if (!($done || @to_remove)) {
+ interactive_msg($loc->N("Error"),
+ $loc->N("Unrecoverable error: no package found for installation, sorry."));
+ return;
+ }
+ my $id = statusbar_msg($loc->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);
+ rpmnew_dialog($loc->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($loc->N("All requested packages were installed successfully."), 1);
+ statusbar_msg($loc->N("Looking for \"README\" files..."), 1);
+ _display_READMEs_if_needed($urpm);
+ },
+ already_installed_or_not_installable => sub {
+ my ($msg1, $msg2) = @_;
+ my $msg = join("\n", @$msg1, @$msg2);
+ return if !$msg; # workaround missing state
+ interactive_msg($loc->N("Error"), $msg);
+ },
+ },
+ );
+
+ #- restart rpmdragora if needed, keep command line for that.
+ if ($need_restart && !$exit_code && $something_installed) {
+ Sys::Syslog::syslog('info|local1', "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=/ } @ManaTools::Rpmdragora::init::ARGV_copy);
+ # remove "--emmbedded <id>" from argv:
+ my $i = 0;
+ foreach (@argv) {
+ splice @argv, $i, 2 if /^--embedded$/;
+ $i++;
+ }
+ alarm(0);
+ # reMDK::Common::DataStructure::member not to ask again questions and the like:
+ writeconf();
+ exec($0, @argv);
+ exit(0);
+ }
+
+ my $_s1 = $loc->N("RPM transaction %d/%d", 0, 0);
+ my $_s2 = $loc->N("Unselect all");
+ my $_s3 = $loc->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($loc->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 { MDK::Common::Func::if_($pkgs->{$_}{selected}, $pkgs->{$_}{urpm_name}) } keys %$pkgs;
+ return if !@toremove;
+ my $gurpm = ManaTools::Rpmdragora::gurpm->new(
+ text => $loc->N("Please wait")
+ );
+ my $_gurpm_clean_guard = MDK::Common::Func::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(ceil(min(99, scalar($progress/@toremove)*100)));
+ #gtkflush();
+ };
+
+ my @results;
+ slow_func_statusbar(
+ $loc->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(
+ $loc->N("Problem during removal"),
+ $loc->N("There was a problem during the removal of packages:\n\n%s", join("\n", @results)),
+ MDK::Common::Func::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($loc->N("Information"), $msg, scroll => 1);
+ }
+ }
+ return 0;
+ }
+}
+
+1;
diff --git a/lib/ManaTools/Rpmdragora/rpmnew.pm b/lib/ManaTools/Rpmdragora/rpmnew.pm
new file mode 100644
index 0000000..659c0b2
--- /dev/null
+++ b/lib/ManaTools/Rpmdragora/rpmnew.pm
@@ -0,0 +1,363 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Rpmdragora::rpmnew;
+#*****************************************************************************
+#
+# Copyright (c) 2002 Guillaume Cottenceau
+# Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com>
+# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
+# Copyright (c) 2005-2007 Mandriva SA
+# Copyright (c) 2013 - 2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+# Copyright (c) 2014 - 2015 Angelo Naselli <anaselli@linux.it>
+#
+# 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 Text::Diff;
+use MDK::Common::Math qw(sum);
+use MDK::Common::File qw(renamef);
+use MDK::Common::Various qw(chomp_);
+
+use ManaTools::rpmdragora;
+use ManaTools::Rpmdragora::init;
+use ManaTools::Rpmdragora::pkg;
+use ManaTools::Rpmdragora::open_db;
+use ManaTools::Rpmdragora::formatting;
+
+use yui;
+
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT = qw(rpmnew_dialog do_merge_if_needed);
+
+my $loc = ManaTools::rpmdragora::locale();
+
+# /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
+);
+
+
+#=============================================================
+
+=head2 _rpmnewFile
+
+=head3 INPUT
+
+ $filename: configuration file name
+
+=head3 OUTPUT
+
+ $rpmnew_filename: new configuration file name
+
+=head3 DESCRIPTION
+
+ This function evaluate the new configration file generated by
+ the update
+
+=cut
+
+#=============================================================
+sub _rpmnewFile ($) {
+ my $filename = shift;
+
+ my ($rpmnew, $rpmsave) = ("$filename.rpmnew", "$filename.rpmsave");
+ 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;
+
+ return $rpmnew;
+}
+
+#=============================================================
+
+=head2 _performDiff
+
+=head3 INPUT
+
+ $file: configuration file name
+ $diffBox: YWidget that will show the difference
+
+=head3 DESCRIPTION
+
+ This function gets the configuration file perfomrs
+ the difference with the new one and shows it into the
+ $diffBox
+
+=cut
+
+#=============================================================
+sub _performDiff ($$) {
+ my ($file, $diffBox) = @_;
+
+ my $rpmnew = _rpmnewFile($file);
+
+ 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 = diff $file, $rpmnew, { STYLE => "Unified" };
+ $diff = $loc->N("(none)") if !$diff;
+
+ my @lines = split ("\n", $diff);
+ ## adding color lines to diff
+ foreach my $line (@lines) {
+ if (substr ($line, 0, 1) eq "+") {
+ $line =~ s|^\+(.*)|<font color="green">+$1</font>|;
+ }
+ elsif (substr ($line, 0, 1) eq "-") {
+ $line =~ s|^\-(.*)|<font color="red">-$1</font>|;
+ }
+ elsif (substr ($line, 0, 1) eq "@") {
+ $line =~ s|(.*)|<font color="blue">$1</font>|;
+ }
+ else {
+ $line =~ s|(.*)|<font color="black">$1</font>|;
+ }
+ }
+ $diff = join("<br>", @lines);
+
+ ensure_utf8($diff);
+ $diffBox->setValue($diff);
+
+ return;
+}
+
+#=============================================================
+
+=head2 rpmnew_dialog
+
+=head3 INPUT
+
+ $msg: message to be shown during the choice
+ %o2r: HASH containing {
+ the package name => ARRAY of configration files
+ }
+
+=head3 OUTPUT
+
+ 1 if nothing to do or 0 after user interaction
+
+=head3 DESCRIPTION
+
+ This function shows the configuration files difference and
+ asks for action to be performed
+
+=cut
+
+#=============================================================
+sub rpmnew_dialog {
+ my ($msg, %p2r) = @_;
+
+ @{$p2r{$_}} = grep { !$ignores_rpmnew{$_} } @{$p2r{$_}} foreach keys %p2r;
+ my $sum_rpmnew = MDK::Common::Math::sum(map { int @{$p2r{$_}} } keys %p2r);
+ $sum_rpmnew == 0 and return 1;
+
+ my $appTitle = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($loc->N("Installation finished"));
+
+ my $factory = yui::YUI::widgetFactory;
+
+ ## | [msg-label] |
+ ## | |
+ ## | pkg-tree |
+ ## | |
+ ## | info on selected pkg |(1)
+ ## | Remove( ) Use ( ) Do nothing (*) |
+ ## | |
+ ## | [ok] |
+ ####
+ # (1) info on pkg list:
+ # selected configuration file diff between rpmnew/rpmsave and used one
+
+ my $dialog = $factory->createPopupDialog;
+ my $vbox = $factory->createVBox( $dialog );
+ my $msgBox = $factory->createLabel($vbox, $msg, 1);
+ $factory->createVSpacing($vbox, 1);
+ # Tree for groups
+ my $tree = $factory->createTree($vbox, $loc->N("Select a package"));
+ $tree->setWeight($yui::YD_VERT,10);
+ $factory->createVSpacing($vbox, 1);
+ my $infoBox = $factory->createRichText($vbox, "", 0);
+ $infoBox->setWeight($yui::YD_HORIZ, 20);
+ $infoBox->setWeight($yui::YD_VERT, 20);
+ $factory->createVSpacing($vbox, 1);
+ my $radiobuttongroup = $factory->createRadioButtonGroup($vbox);
+ my $rbbox = $factory->createHBox($radiobuttongroup);
+ my $rdnBtn = {
+ remove_rpmnew => $loc->N("Remove new file"),
+ use_rpmnew => $loc->N("Use new file"),
+ do_onthing => $loc->N("Do nothing"),
+ };
+
+ my %radiobutton = ();
+ my @rdnbtn_order = ('remove_rpmnew', 'use_rpmnew', 'do_onthing');
+ foreach my $btn_name (@rdnbtn_order) {
+ $radiobutton{$btn_name} = $factory->createRadioButton($rbbox, $rdnBtn->{$btn_name});
+ $radiobutton{$btn_name}->setValue(1) if $btn_name eq 'do_onthing';
+ $radiobutton{$btn_name}->setNotify(1);
+ $radiobuttongroup->addRadioButton($radiobutton{$btn_name});
+ }
+ $radiobuttongroup->setEnabled(0);
+ my $hbox = $factory->createHBox( $vbox );
+ my $align = $factory->createHCenter($hbox);
+ my $okButton = $factory->createPushButton($align, $loc->N("&Ok"));
+ $okButton->setDefaultButton(1);
+
+ # adding packages to the list
+ my $itemColl = new yui::YItemCollection;
+ my $num = 0;
+ my %file_action = ();
+ foreach my $p (sort keys %p2r) {
+ if (scalar @{$p2r{$p}}) {
+ my $item = new yui::YTreeItem ("$p");
+ foreach my $f (@{$p2r{$p}}) {
+ my $child = new yui::YTreeItem ($item, "$f");
+ $child->DISOWN();
+ $file_action{$f} = 'do_onthing';
+ }
+ $itemColl->push($item);
+ $item->DISOWN();
+ }
+ }
+
+ $tree->addItems($itemColl);
+ $tree->setImmediateMode(1);
+ $tree->rebuildTree();
+
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ ### widget
+ my $widget = $event->widget();
+ if ($widget == $tree) {
+ #change info
+ my $item = $tree->selectedItem();
+ if ($item && !$item->hasChildren()) {
+ my $filename = $tree->currentItem()->label();
+ _performDiff($filename, $infoBox);
+ $radiobuttongroup->setEnabled(1);
+ #$radiobuttongroup->uncheckOtherButtons ($radiobutton{$file_action{$filename}});
+ yui::YUI::ui()->blockEvents();
+ $radiobutton{$file_action{$filename}}->setValue(1);
+ yui::YUI::ui()->unblockEvents();
+ }
+ else {
+ $infoBox->setValue("");
+ $radiobuttongroup->setEnabled(0);
+ }
+ }
+ elsif ($widget == $okButton) {
+ # TODO
+ foreach my $file (keys %file_action) {
+ my $rpmnew = _rpmnewFile($file);
+ if ($file_action{$file} eq 'remove_rpmnew') {
+ eval { unlink "$rpmnew"};
+ }
+ elsif ($file_action{$file} eq 'use_rpmnew') {
+ MDK::Common::File::renamef($rpmnew, $file);
+ }
+ # else do_onthing
+ }
+ last;
+ }
+ else {
+ # radio buttons
+ RDNBTN: foreach my $btn_name (@rdnbtn_order) {
+ if ($widget == $radiobutton{$btn_name}) {
+ my $filename = $tree->currentItem()->label();
+ $file_action{$filename} = $btn_name;
+ last RDNBTN;
+ }
+ }
+ }
+ }
+ }
+
+ destroy $dialog;
+
+ # restore original title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+
+ return 0;
+}
+
+
+#=============================================================
+
+=head2 do_merge_if_needed
+
+=head3 DESCRIPTION
+
+ This function look for new configuration file versions
+ and ask for actions using the rpmnew_dialog
+
+=cut
+
+#=============================================================
+sub do_merge_if_needed() {
+ if ($rpmdragora_options{'merge-all-rpmnew'}) {
+ my %pkg2rpmnew;
+ my $wait = wait_msg($loc->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";
+ remove_wait_msg($wait);
+
+ rpmnew_dialog('', %pkg2rpmnew) and print "Nothing to do.\n";
+ }
+
+ return;
+}
+
+1;
diff --git a/lib/ManaTools/SettingsReader.pm b/lib/ManaTools/SettingsReader.pm
new file mode 100644
index 0000000..318aefd
--- /dev/null
+++ b/lib/ManaTools/SettingsReader.pm
@@ -0,0 +1,101 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::SettingsReader;
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ManaTools::SettingsReader - This module allows to load an XML configuration file
+
+=head1 SYNOPSIS
+
+ use ManaTools::SettingsReader;
+
+ my $settings = new ManaTools::SettingsReader($fileName);
+
+=head1 DESCRIPTION
+
+ This module allows to load a configuration file returning a Hash references with its content.
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command:
+
+perldoc ManaTools::SettingsReader
+
+=head1 SEE ALSO
+
+ XML::Simple
+
+=head1 AUTHOR
+
+ Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+ Copyright (C) 2012-2015, Angelo Naselli.
+
+ This file is part of ManaTools
+
+ ManaTools is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 2 of the License, or
+ (at your option) any later version.
+
+ ManaTools 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 ManaTools. If not, see <http://www.gnu.org/licenses/>.
+
+=head1 FUNCTIONS
+
+=cut
+
+
+use strict;
+use warnings;
+use diagnostics;
+use XML::Simple;
+use Data::Dumper;
+
+#=============================================================
+
+=head2 new
+
+=head3 INPUT
+
+ $fileName: File to be loaded
+
+=head3 OUTPUT
+
+ $settings: Hash reference containing read settings
+
+=head3 DESCRIPTION
+
+ The constructor just loads the given files and return its representation
+ into a hash reference.
+
+=cut
+
+#=============================================================
+
+sub new {
+ my ($class, $fileName) = @_;
+
+ my $self = {
+ settings => 0,
+ };
+ bless $self, 'ManaTools::SettingsReader';
+
+ die "File " . $fileName . " not found" if (! -e $fileName);
+
+ my $xml = new XML::Simple (KeyAttr=>[]);
+ $self->{settings} = $xml->XMLin($fileName);
+
+ return $self->{settings};
+}
+
+
+1;
diff --git a/lib/ManaTools/Shared.pm b/lib/ManaTools/Shared.pm
new file mode 100644
index 0000000..6a40539
--- /dev/null
+++ b/lib/ManaTools/Shared.pm
@@ -0,0 +1,376 @@
+# vim: set et ts=4 sw=4:
+# Copyright 2012-2015 Angelo Naselli <anaselli@linux.it>
+# Copyright 2013-2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+#
+# This file is part of ManaTools
+#
+# ManaTools is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# ManaTools 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 ManaTools. If not, see <http://www.gnu.org/licenses/>.
+
+package ManaTools::Shared;
+
+=head1 NAME
+
+ManaTools::Shared - ManaTools::Shared contains all the shared routines
+ needed by ManaTools and modules
+
+=head1 SYNOPSIS
+
+
+
+=head1 DESCRIPTION
+
+This module collects all the routines shared between ManaTools and its modules.
+
+=head1 EXPORT
+
+ trim
+ md5sum
+ pathList2hash
+ distName
+ apcat
+ inArray
+ disable_x_screensaver
+ enable_x_screensaver
+ isProcessRunning
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command:
+
+ perldoc ManaTools::Shared
+
+=head1 AUTHOR
+
+Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2013, Angelo Naselli.
+Copyright (C) 2014, Matteo Pasotti.
+
+This file is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 2 of the License, or
+(at your option) any later version.
+
+This file 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 file. If not, see <http://www.gnu.org/licenses/>.
+
+=head1 FUNCTIONS
+
+=cut
+
+use strict;
+use warnings;
+use diagnostics;
+
+use Digest::MD5;
+
+use yui;
+use base qw(Exporter);
+
+# TODO move GUI dialogs to Shared::GUI
+our @EXPORT_OK = qw(
+ trim
+ md5sum
+ pathList2hash
+ distName
+ apcat
+ inArray
+ disable_x_screensaver
+ enable_x_screensaver
+ isProcessRunning
+);
+
+
+=head1 VERSION
+
+Version 0.01
+
+=cut
+
+our $VERSION = '0.01';
+
+
+
+#=============================================================
+
+=head2 apcat
+
+=head3 PARAMETERS
+
+$filename the name of the file to read
+
+=head3 OUTPUT
+
+depending from the context it returns the content
+of the file as an array or a string
+
+=head3 DESCRIPTION
+
+This function return the content of $filename or false/0
+if it fails
+
+=cut
+
+#=============================================================
+
+sub apcat {
+ my $fn = shift();
+ my $fh = undef;
+ my @content = ();
+ open($fh, "<", $fn) || return 0;
+ while(<$fh>)
+ {
+ push(@content, $_);
+ }
+ return (wantarray() ? @content : join('',@content));
+}
+
+
+#=============================================================
+
+=head2 distName
+
+=head3 OUTPUT
+
+$distname: name of the distributed package
+
+=head3 DESCRIPTION
+
+This function return the distname, useful to retrieve data
+with File::ShareDir::dist_file and must be the same as into
+Makefile.PL (e.g. manatools)
+
+=cut
+
+#=============================================================
+
+sub distName {
+ return "manatools";
+}
+
+
+#=============================================================
+
+=head2 trim
+
+=head3 INPUT
+
+ $st: String to be trimmed
+
+=head3 OUTPUT
+
+ $st: trimmed string
+
+=head3 DESCRIPTION
+
+This function trim the given string.
+
+=cut
+
+#=============================================================
+
+sub trim {
+ my ($st) = shift;
+ $st =~s /^\s+//g;
+ $st =~s /\s+$//g;
+ return $st;
+}
+
+#=============================================================
+
+=head2 inArray
+
+=head3 INPUT
+
+ $item: item to search
+ $arr: array container
+
+=head3 OUTPUT
+
+ true: if the array contains the item
+
+=head3 DESCRIPTION
+
+This method returns if an item is into the array container
+
+=cut
+
+#=============================================================
+sub inArray {
+ my ($item, $arr) = @_;
+
+ return grep( /^$item$/, @{$arr} );
+}
+
+
+#=============================================================
+
+=head2 md5sum
+
+=head3 INPUT
+
+ $filename: file for md5 calculation
+
+=head3 OUTPUT
+
+ md5 sum
+
+=head3 DESCRIPTION
+
+ compute MD5 for the given file
+
+=cut
+
+#=============================================================
+
+sub md5sum {
+ my @files = @_;
+
+ my @md5 = map {
+ my $sum;
+ if (open(my $FILE, $_)) {
+ binmode($FILE);
+ $sum = Digest::MD5->new->addfile($FILE)->hexdigest;
+ close($FILE);
+ }
+ $sum;
+ } @files;
+ return wantarray() ? @md5 : $md5[0];
+}
+
+#=============================================================
+
+=head2 pathList2hash
+
+=head3 INPUT
+
+ $param : HASH ref containing
+ paths => ARRAY of string containing path like strings
+ separator => item separator inside a single path
+ (default separator is /)
+
+=head3 OUTPUT
+
+ \%tree: HASH reference containing the same structur passed as ARRAY
+ in a tree view form, leaves are undef.
+
+=head3 DESCRIPTION
+
+ This function return a tree representation of the given array.
+
+=cut
+
+#=============================================================
+
+sub pathList2hash {
+ my ($param) = @_;
+
+ die "array of path is missing" if ! exists $param->{paths};
+ my $separator = '/';
+ $separator = $param->{separator} if $param->{separator};
+ if ($separator eq "/" || $separator eq "|") {
+ $separator = '\\' . $separator;
+ }
+
+ my %tree;
+ for (@{$param->{paths}})
+ {
+ my $last = \\%tree;
+ $last = \$$last->{$_} for split /$separator/;
+ }
+
+ return \%tree;
+}
+
+#=============================================================
+
+=head2 disable_x_screensaver
+
+=head3 DESCRIPTION
+
+ if exists /usr/bin/xset disable screensaver
+
+=cut
+
+#=============================================================
+sub disable_x_screensaver() {
+ if (-e '/usr/bin/xset') {
+ $ENV{PATH} = "/usr/bin:/usr/sbin";
+ system ("/usr/bin/xset s off");
+ system ("/usr/bin/xset -dpms");
+ }
+}
+
+#=============================================================
+
+=head2 enable_x_screensaver
+
+=head3 DESCRIPTION
+
+ if exists /usr/bin/xset enables screensaver
+
+=cut
+
+#=============================================================
+sub enable_x_screensaver() {
+ if (-e '/usr/bin/xset') {
+ $ENV{PATH} = "/usr/bin:/usr/sbin";
+ system ("/usr/bin/xset +dpms");
+ system ("/usr/bin/xset s on");
+ system ("/usr/bin/xset s reset");
+ }
+}
+
+#=============================================================
+
+=head2 isProcessRunning
+
+=head3 INPUT
+
+ $name: Process name
+ $o_user: user who the process belongs to
+
+=head3 OUTPUT
+
+ $pid: process identifier
+
+=head3 DESCRIPTION
+
+ Function returns the process identifier if the given
+ process is running
+
+=cut
+
+#=============================================================
+sub isProcessRunning {
+ my ($name, $o_user) = @_;
+ my $user = $o_user || $ENV{USER};
+ my @proc = `ps -o '%P %p %c' -u $user`;
+ shift (@proc);
+ foreach (@proc) {
+ my ($ppid, $pid, $n) = /^\s*(\d+)\s+(\d+)\s+(.*)/;
+ return $pid if $n eq $name && $ppid != 1 && $pid != $$;
+ }
+ return;
+}
+
+1; # End of ManaTools::Shared
+
diff --git a/lib/ManaTools/Shared/Firewall.pm b/lib/ManaTools/Shared/Firewall.pm
new file mode 100644
index 0000000..f5a6c45
--- /dev/null
+++ b/lib/ManaTools/Shared/Firewall.pm
@@ -0,0 +1,35 @@
+# vim: set et ts=4 sw=4:
+#*****************************************************************************
+#
+# Copyright (c) 2013-2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#*****************************************************************************
+package ManaTools::Shared::Firewall;
+
+use Moose;
+use diagnostics;
+use utf8;
+
+use lib qw(/usr/lib/libDrakX);
+use network::nfs;
+use network::network;
+use network::tools;
+
+sub _initialize {
+ my $self = shift();
+}
+
+1;
diff --git a/lib/ManaTools/Shared/GUI.pm b/lib/ManaTools/Shared/GUI.pm
new file mode 100644
index 0000000..6c6144d
--- /dev/null
+++ b/lib/ManaTools/Shared/GUI.pm
@@ -0,0 +1,1118 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Shared::GUI;
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+Shared::GUI - Shared graphic routines
+
+=head1 SYNOPSIS
+
+ my $gui = ManaTools::Shared::GUI->new();
+ my $yesPressed = $gui->ask_YesOrNo($title, $text);
+
+=head1 DESCRIPTION
+
+ This module contains a collection of dialogs or widgets that can be used in more
+ graphics modules.
+
+=head1 EXPORT
+
+exported
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command:
+
+perldoc Shared::GUI
+
+
+=head1 AUTHOR
+
+Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2014-2015, Angelo Naselli.
+Copyright (C) 2015, Matteo Pasotti <matteo.pasotti@gmail.com>.
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2, as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, write to the Free Software
+Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+
+=head1 METHODS
+
+=cut
+
+
+use Moose;
+
+use diagnostics;
+use yui;
+
+use ManaTools::Shared qw(pathList2hash);
+
+use ManaTools::Shared::Locales;
+
+has 'loc' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_localeInitialize'
+);
+
+sub _localeInitialize {
+ my $self = shift();
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'libDrakX-standalone') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+
+#=============================================================
+
+=head2 warningMsgBox
+
+=head3 INPUT
+
+ $info: HASH, information to be passed to the dialog.
+ title => dialog title
+ text => string to be swhon into the dialog
+ richtext => 1 if using rich text
+
+=head3 DESCRIPTION
+
+ This function creates an Warning dialog and show the message
+ passed as input.
+
+=cut
+
+#=============================================================
+sub warningMsgBox {
+ my ($self, $info) = @_;
+
+ return 0 if ( ! $info );
+
+ my $retVal = 0;
+ yui::YUI::widgetFactory;
+ my $factory = yui::YExternalWidgets::externalWidgetFactory("mga");
+ $factory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($factory);
+ my $dlg = $factory->createDialogBox($yui::YMGAMessageBox::B_ONE,
+ $yui::YMGAMessageBox::D_WARNING);
+
+ $dlg->setTitle($info->{title}) if (exists $info->{title});
+ my $rt = (exists $info->{richtext}) ? $info->{richtext} : 0;
+ $dlg->setText($info->{text}, $rt) if (exists $info->{text});
+
+ $dlg->setButtonLabel($self->loc->N("Ok"), $yui::YMGAMessageBox::B_ONE );
+# $dlg->setMinSize(50, 5);
+
+ $retVal = $dlg->show();
+
+ $dlg = undef;
+
+ return 1;
+}
+
+#=============================================================
+
+=head2 infoMsgBox
+
+=head3 INPUT
+
+ $info: HASH, information to be passed to the dialog.
+ title => dialog title
+ text => string to be swhon into the dialog
+ richtext => 1 if using rich text
+
+=head3 DESCRIPTION
+
+ This function creates an Info dialog and show the message
+ passed as input.
+
+=cut
+
+#=============================================================
+
+sub infoMsgBox {
+ my ($self, $info) = @_;
+
+ return 0 if ( ! $info );
+
+ my $retVal = 0;
+ yui::YUI::widgetFactory;
+ my $factory = yui::YExternalWidgets::externalWidgetFactory("mga");
+ $factory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($factory);
+ my $dlg = $factory->createDialogBox($yui::YMGAMessageBox::B_ONE,
+ $yui::YMGAMessageBox::D_INFO);
+
+ $dlg->setTitle($info->{title}) if (exists $info->{title});
+ my $rt = (exists $info->{richtext}) ? $info->{richtext} : 0;
+ $dlg->setText($info->{text}, $rt) if (exists $info->{text});
+
+ $dlg->setButtonLabel($self->loc->N("Ok"), $yui::YMGAMessageBox::B_ONE );
+# $dlg->setMinSize(50, 5);
+
+ $retVal = $dlg->show();
+
+ $dlg = undef;
+
+ return 1;
+}
+
+#=============================================================
+
+=head2 msgBox
+
+=head3 INPUT
+
+ $info: HASH, information to be passed to the dialog.
+ title => dialog title
+ text => string to be swhon into the dialog
+ richtext => 1 if using rich text
+
+=head3 DESCRIPTION
+
+ This function creates a dialog and show the message passed as input.
+
+=cut
+
+#=============================================================
+
+sub msgBox {
+ my ($self, $info) = @_;
+
+ return 0 if ( ! $info );
+
+ my $retVal = 0;
+ yui::YUI::widgetFactory;
+ my $factory = yui::YExternalWidgets::externalWidgetFactory("mga");
+ $factory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($factory);
+ my $dlg = $factory->createDialogBox($yui::YMGAMessageBox::B_ONE);
+
+ $dlg->setTitle($info->{title}) if (exists $info->{title});
+ my $rt = (exists $info->{richtext}) ? $info->{richtext} : 0;
+ $dlg->setText($info->{text}, $rt) if (exists $info->{text});
+
+ $dlg->setButtonLabel($self->loc->N("Ok"), $yui::YMGAMessageBox::B_ONE );
+# $dlg->setMinSize(50, 5);
+
+ $retVal = $dlg->show();
+
+ $dlg = undef;
+
+ return 1;
+}
+
+#=============================================================
+
+=head2 ask_OkCancel
+
+=head3 INPUT
+
+ $info: HASH, information to be passed to the dialog.
+ title => dialog title
+ text => string to be swhon into the dialog
+ richtext => 1 if using rich text
+
+=head3 OUTPUT
+
+ 0: Cancel button has been pressed
+ 1: Ok button has been pressed
+
+=head3 DESCRIPTION
+
+ This function create an OK-Cancel dialog with a 'title' and a
+ 'text' passed as parameters.
+
+=cut
+
+#=============================================================
+
+sub ask_OkCancel {
+ my ($self, $info) = @_;
+
+ return 0 if ( ! $info );
+
+ my $retVal = 0;
+ yui::YUI::widgetFactory;
+ my $factory = yui::YExternalWidgets::externalWidgetFactory("mga");
+ $factory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($factory);
+ my $dlg = $factory->createDialogBox($yui::YMGAMessageBox::B_TWO);
+
+ $dlg->setTitle($info->{title}) if (exists $info->{title});
+ my $rt = (exists $info->{richtext}) ? $info->{richtext} : 0;
+ $dlg->setText($info->{text}, $rt) if (exists $info->{text});
+
+ $dlg->setButtonLabel($self->loc->N("Ok"), $yui::YMGAMessageBox::B_ONE );
+ $dlg->setButtonLabel($self->loc->N("Cancel"), $yui::YMGAMessageBox::B_TWO);
+ $dlg->setDefaultButton($yui::YMGAMessageBox::B_ONE);
+ $dlg->setMinSize(50, 5);
+
+ $retVal = $dlg->show() == $yui::YMGAMessageBox::B_ONE ? 1 : 0;
+
+ $dlg = undef;
+
+ return $retVal;
+}
+
+#=============================================================
+
+=head2 ask_YesOrNo
+
+=head3 INPUT
+
+ $info: HASH, information to be passed to the dialog.
+ title => dialog title
+ text => string to be swhon into the dialog
+ richtext => 1 if using rich text
+ default_button => (optional) 1: "Yes" (any other values "No")
+
+=head3 OUTPUT
+
+ 0: "No" button has been pressed
+ 1: "Yes" button has been pressed
+
+=head3 DESCRIPTION
+
+ This function create a Yes-No dialog with a 'title' and a
+ question 'text' passed as parameters.
+
+=cut
+
+#=============================================================
+
+sub ask_YesOrNo {
+ my ($self, $info) = @_;
+
+ return 0 if ( ! $info );
+
+ my $retVal = 0;
+ yui::YUI::widgetFactory;
+ my $factory = yui::YExternalWidgets::externalWidgetFactory("mga");
+ $factory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($factory);
+ my $dlg = $factory->createDialogBox($yui::YMGAMessageBox::B_TWO);
+
+ $dlg->setTitle($info->{title}) if (exists $info->{title});
+ my $rt = (exists $info->{richtext}) ? $info->{richtext} : 0;
+ $dlg->setText($info->{text}, $rt) if (exists $info->{text});
+
+ $dlg->setButtonLabel($self->loc->N("Yes"), $yui::YMGAMessageBox::B_ONE );
+ $dlg->setButtonLabel($self->loc->N("No"), $yui::YMGAMessageBox::B_TWO);
+ if (exists $info->{default_button} && $info->{default_button} == 1) {
+ $dlg->setDefaultButton($yui::YMGAMessageBox::B_ONE);
+ }
+ else {
+ $dlg->setDefaultButton($yui::YMGAMessageBox::B_TWO);
+ }
+ $dlg->setMinSize(50, 5);
+
+ $retVal = $dlg->show() == $yui::YMGAMessageBox::B_ONE ? 1 : 0;
+
+ $dlg = undef;
+
+ return $retVal;
+}
+
+
+#=============================================================
+
+=head2 ask_TwoConfigurableButtons
+
+=head3 INPUT
+
+$info: HASH, information to be passed to the dialog.
+ title => dialog title
+ text => string to be swhon into the dialog
+ richtext => 1 if using rich text
+ button_one => caption for the first button
+ button_two => caption for the second button
+ default_button => (optional) 1: "First button"
+
+=head3 OUTPUT
+
+ 0: "Button One Caption" button has been pressed
+ 1: "Button Two Caption" button has been pressed
+
+=head3 DESCRIPTION
+
+This function create a two-buttons dialog with a 'title', a
+question 'text' and a label for each button passed as parameters.
+
+=cut
+
+#=============================================================
+
+sub ask_TwoConfigurableButtons {
+ my ($self, $info) = @_;
+
+ return 0 if ( ! $info );
+
+ my $retVal = 0;
+ yui::YUI::widgetFactory;
+ my $factory = yui::YExternalWidgets::externalWidgetFactory("mga");
+ $factory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($factory);
+ my $dlg = $factory->createDialogBox($yui::YMGAMessageBox::B_TWO);
+
+ $dlg->setTitle($info->{title}) if (exists $info->{title});
+ my $rt = (exists $info->{richtext}) ? $info->{richtext} : 0;
+ $dlg->setText($info->{text}, $rt) if (exists $info->{text});
+
+ $dlg->setButtonLabel($info->{button_one}, $yui::YMGAMessageBox::B_ONE );
+ $dlg->setButtonLabel($info->{button_two}, $yui::YMGAMessageBox::B_TWO);
+ if (exists $info->{default_button} && $info->{default_button} == 1) {
+ $dlg->setDefaultButton($yui::YMGAMessageBox::B_ONE);
+ }
+ else {
+ $dlg->setDefaultButton($yui::YMGAMessageBox::B_TWO);
+ }
+ $dlg->setMinSize(50, 5);
+
+ $retVal = $dlg->show() == $yui::YMGAMessageBox::B_ONE ? 1 : 0;
+
+ $dlg = undef;
+
+ return $retVal;
+}
+
+#=============================================================
+
+=head2 arrayListToYItemCollection
+
+=head3 INPUT
+
+ $listInfo: HASH reference containing
+ default_item => Selected item (if any)
+ item_list => ARRAY reference containing the item list
+
+=head3 OUTPUT
+
+ $itemList: YItemCollection containing the item list passed
+
+=head3 DESCRIPTION
+
+ This method returns a YItemCollection containing the item list passed with default item
+ the "default_item"
+
+=cut
+
+#=============================================================
+
+sub arrayListToYItemCollection {
+ my ($self, $listInfo) = @_;
+
+ die "Item list is mandatory" if !($listInfo->{item_list});
+ # TODO check type
+ die "Not empty item list is mandatory" if (scalar @{$listInfo->{item_list}} < 1);
+
+
+ my $itemColl = new yui::YItemCollection;
+ foreach (@{$listInfo->{item_list}}) {
+ my $item = new yui::YItem ($_, 0);
+ $itemColl->push($item);
+ $item->DISOWN();
+ if ($listInfo->{default_item} && $listInfo->{default_item} eq $item->label()) {
+ $item->setSelected(1);
+ }
+ }
+
+ return $itemColl;
+}
+
+
+#=============================================================
+
+=head2 ask_fromList
+
+=head3 INPUT
+
+ $info: HASH, information to be passed to the dialog.
+ title => dialog title
+ header => combobox header
+ default_item => selected item if any
+ list => item list
+ default_button => (optional) 1: Select (any other values Cancel)
+
+=head3 OUTPUT
+
+ undef: if Cancel button has been pressed
+ selected item: if Select button has been pressed
+
+=head3 DESCRIPTION
+
+ This function create a dialog with a combobox in which to
+ choose an item from a given list.
+
+=cut
+
+#=============================================================
+
+sub ask_fromList {
+ my ($self, $info) = @_;
+
+ die "Missing dialog information" if (!$info);
+ die "Title is mandatory" if (! exists $info->{title});
+ die "Header is mandatory" if (! exists $info->{header});
+ die "List is mandatory" if (! exists $info->{list} );
+ my $list = $info->{list};
+ die "At least one element is mandatory into list" if (scalar(@$list) < 1);
+
+ my $choice = undef;
+ my $factory = yui::YUI::widgetFactory;
+
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($info->{title});
+
+ my $dlg = $factory->createPopupDialog($yui::YDialogNormalColor);
+ my $layout = $factory->createVBox($dlg);
+
+ my $combo = $factory->createComboBox($layout, $info->{header}, 0);
+
+ my $listInfo;
+ $listInfo->{default_item} = $info->{default_item} if $info->{default_item};
+ $listInfo->{item_list} = $info->{list};
+ my $itemColl = $self->arrayListToYItemCollection($listInfo);
+ $combo->addItems($itemColl);
+
+ my $align = $factory->createRight($layout);
+ my $hbox = $factory->createHBox($align);
+ my $selectButton = $factory->createPushButton($hbox, $self->loc->N("Select"));
+ my $cancelButton = $factory->createPushButton($hbox, $self->loc->N("Cancel"));
+
+ if (exists $info->{default_button} ) {
+ my $dflBtn = ($info->{default_button} == 1) ? $selectButton : $cancelButton;
+ $dlg->setDefaultButton($selectButton);
+ }
+
+ while (1) {
+ my $event = $dlg->waitForEvent();
+
+ my $eventType = $event->eventType();
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $selectButton) {
+ my $item = $combo->selectedItem();
+ $choice = $item->label() if ($item);
+ last;
+ }
+ }
+ }
+
+ destroy $dlg;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+
+ return $choice;
+}
+
+#=============================================================
+
+=head2 ask_multiple_fromList
+
+=head3 INPUT
+
+$info: HASH, information to be passed to the dialog.
+ title => dialog title
+ header => combobox header
+ default_item => selected item if any
+ list => item list
+ default_button => (optional) 1: Select (any other values Cancel)
+
+=head3 LIST
+
+list is an array of hashes like this
+
+ {
+ id => unique identifier for this particular item,
+ text => "descriptive text"
+ val => reference to the boolean value
+ }
+
+
+=head3 OUTPUT
+
+ undef: if Cancel button has been pressed
+ selected items: ArrayRef of the selected ids, if Select button has been pressed
+
+=head3 DESCRIPTION
+
+This function create a dialog with variable checkboxes in which to
+choose the items from a given list.
+
+Warning: to use only for a reduced set of items because of no scroll available
+
+=cut
+
+#=============================================================
+
+sub ask_multiple_fromList {
+ my ($self, $info) = @_;
+
+ die "Missing dialog information" if (!$info);
+ die "Title is mandatory" if (! exists $info->{title});
+ die "Header is mandatory" if (! exists $info->{header});
+ die "List is mandatory" if (! exists $info->{list} );
+ die "At least one element is mandatory into list" if (scalar(@{$info->{list}}) < 1);
+
+ my $selections = [];
+ my $factory = yui::YUI::widgetFactory;
+
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($info->{title});
+
+ my $dlg = $factory->createPopupDialog($yui::YDialogNormalColor);
+ my $layout = $factory->createVBox($dlg);
+
+ my @ckbox_array = ();
+
+ for my $item(@{$info->{list}})
+ {
+ my $ckbox = $factory->createCheckBox(
+ $factory->createLeft($factory->createHBox($layout)),
+ $item->{text},
+ ${$item->{val}}
+ );
+ $ckbox->setNotify(1);
+ push @ckbox_array, {
+ id => $item->{id},
+ widget => \$ckbox,
+ };
+ $ckbox->DISOWN();
+ }
+
+ my $align = $factory->createRight($layout);
+ my $hbox = $factory->createHBox($align);
+ my $selectButton = $factory->createPushButton($hbox, $self->loc->N("Select"));
+ my $cancelButton = $factory->createPushButton($hbox, $self->loc->N("Cancel"));
+
+ if (exists $info->{default_button} ) {
+ my $dflBtn = ($info->{default_button} == 1) ? $selectButton : $cancelButton;
+ $dlg->setDefaultButton($selectButton);
+ }
+
+ while (1) {
+ my $event = $dlg->waitForEvent();
+
+ my $eventType = $event->eventType();
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+
+ if ($widget == $cancelButton) {
+ $selections = undef;
+ last;
+ }
+ elsif ($widget == $selectButton) {
+ foreach my $ckbox (@ckbox_array)
+ {
+ if(${$ckbox->{widget}}->value())
+ {
+ # yui::YUI::ui()->blockEvents();
+ push @{$selections}, $ckbox->{id};
+ # yui::YUI::ui()->unblockEvents();
+ }
+ }
+ last;
+ }
+ }
+ }
+
+ destroy $dlg;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+
+ return $selections;
+}
+
+#=============================================================
+
+=head2 AboutDialog
+
+=head3 INPUT
+
+ $info: HASH containing optional information needed to get info for dialog.
+ name => the application name
+ version => the application version
+ license => the application license, the short length one (e.g. GPLv2, GPLv3, LGPLv2+, etc)
+ authors => the string providing the list of authors; it could be html-formatted
+ description => the string providing a brief description of the application
+ logo => the string providing the file path for the application logo (high-res image)
+ icon => the string providing the file path for the application icon (low-res image)
+ credits => the application credits, they can be html-formatted
+ information => other extra informations, they can be html-formatted
+ dialog_mode => 1: classic style dialog, any other as tabbed style dialog
+
+=head3 DESCRIPTION
+
+ About dialog implementation, this dialog can be used by
+ modules, to show authors, license, credits, etc.
+
+=cut
+
+#=============================================================
+
+sub AboutDialog {
+ my ($self, $info) = @_;
+
+ die "Missing dialog information" if (!$info);
+
+
+ yui::YUI::widgetFactory;
+ my $factory = yui::YExternalWidgets::externalWidgetFactory("mga");
+ $factory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($factory);
+
+ my $name = (exists $info->{name}) ? $info->{name} : "";
+ my $version = (exists $info->{version}) ? $info->{version} : "";
+ my $license = (exists $info->{license}) ? $info->{license} : "";
+ my $authors = (exists $info->{authors}) ? $info->{authors} : "";
+ my $description = (exists $info->{description}) ? $info->{description} : "";
+ my $logo = (exists $info->{logo}) ? $info->{logo} : "";
+ my $icon = (exists $info->{icon}) ? $info->{icon} : "";
+ my $credits = (exists $info->{credits}) ? $info->{credits} : "";
+ my $information = (exists $info->{information}) ? $info->{information} : "";
+ my $dialog_mode = $yui::YMGAAboutDialog::TABBED;
+ if (exists $info->{dialog_mode}) {
+ $dialog_mode = $yui::YMGAAboutDialog::CLASSIC if ($info->{dialog_mode} == 1);
+ }
+
+ my $dlg = $factory->createAboutDialog($name, $version, $license,
+ $authors, $description, $logo,
+ $icon, $credits, $information
+ );
+
+ $dlg->show($dialog_mode);
+
+ $dlg = undef;
+
+ return 1;
+}
+
+#=============================================================
+
+=head2 hashTreeToYItemCollection
+
+=head3 INPUT
+
+ $treeInfo: HASH reference containing
+ parent ==> YItem parent (if not root object)
+ collection ==> YItemCollection (mandatory)
+ default_item ==> Selected item (if any)
+ default_item_separator ==> If default item is passed and is a path like string
+ the separator is needed to match the selected item, using
+ the full pathname instead leaf (e.g. root/subroot/leaf).
+ Default separator is also needed if '$treeInfo->{icons} entry is passed
+ to match the right icon to set (e.g. using the full pathname).
+ hash_tree ==> HASH reference containing the path tree representation
+ icons ==> HASH reference containing item icons e.g.
+ {
+ root => 'root_icon_pathname',
+ root/subroot => 'root_subroot_icon_pathname',
+ ....
+ }
+ Do not add it if no icons are wanted.
+ default_icon ==> icon pathname to a default icon for all the items that are
+ not into $treeInfo->{icons} or if $treeInfo->{icons} is not
+ defined. Leave undef if no default icon is wanted
+
+=head3 DESCRIPTION
+
+ This function add to the given $treeInfo->{collection} new tree items from
+ the the given $treeInfo->{hash_tree}
+
+=cut
+
+#=============================================================
+
+sub hashTreeToYItemCollection {
+ my ($self, $treeInfo) = @_;
+
+ die "Collection is mandatory" if !($treeInfo->{collection});
+ die "Hash tree is mandatory" if !($treeInfo->{hash_tree});
+
+ my $treeLine = $treeInfo->{parent};
+ my $item;
+ foreach my $key (sort keys %{$treeInfo->{hash_tree}}) {
+ if ($treeInfo->{parent}) {
+ $item = new yui::YTreeItem ($treeLine, $key);
+ $item->DISOWN();
+ }
+ else {
+ if ($treeLine) {
+ if ( $treeLine->label() eq $key) {
+ $item = $treeLine;
+ }
+ else {
+ $treeInfo->{collection}->push($treeLine);
+ $item = $treeLine = new yui::YTreeItem ($key);
+ $item->DISOWN();
+ }
+ }
+ else {
+ $item = $treeLine = new yui::YTreeItem ($key);
+ $item->DISOWN();
+ }
+ }
+
+ # building full path name
+ my $label = $key;
+ if (exists $treeInfo->{default_item_separator}) {
+ my $parent = $item;
+ while($parent = $parent->parent()) {
+ $label = $parent->label() . $treeInfo->{default_item_separator} . $label ;
+ }
+ }
+ my $icon = undef;
+ $icon = $treeInfo->{default_icon} if defined($treeInfo->{default_icon});
+ $icon = $treeInfo->{icons}->{$label} if defined($treeInfo->{icons}) && defined($treeInfo->{icons}->{$label});
+
+ $item->setIconName($icon) if $icon;
+
+ ### select item
+ if ($treeInfo->{default_item}) {
+ if ($treeInfo->{default_item} eq $label) {
+ $item->setSelected(1) ;
+ $item->setOpen(1);
+ my $parent = $item;
+ while($parent = $parent->parent()) {
+ $parent->setOpen(1);
+ }
+ }
+ }
+
+ if ($treeInfo->{hash_tree}->{$key} && keys %{$treeInfo->{hash_tree}->{$key}}) {
+ my %tf;
+ $tf{collection} = $treeInfo->{collection};
+ $tf{parent} = $item;
+ $tf{default_item} = $treeInfo->{default_item} if $treeInfo->{default_item};
+ $tf{default_item_separator} = $treeInfo->{default_item_separator} if $treeInfo->{default_item_separator};
+ $tf{hash_tree} = $treeInfo->{hash_tree}->{$key};
+ $tf{icons} = $treeInfo->{icons};
+ $self->hashTreeToYItemCollection(\%tf);
+ }
+ else {
+ if (! $treeInfo->{parent}) {
+ $treeInfo->{collection}->push($treeLine);
+ $treeLine = undef;
+ }
+ }
+ }
+ if (! $treeInfo->{parent}) {
+ $treeInfo->{collection}->push($treeLine) if $treeLine;
+ }
+}
+
+
+#=============================================================
+
+=head2 ask_fromTreeList
+
+=head3 INPUT
+
+ $info: HASH, information to be passed to the dialog.
+ title => dialog title
+ header => TreeView header
+ list => path item list
+ min_size => minimum dialog size in the libYUI meaning
+ HASH {width => w, height => h}
+ default_item => selected item if any
+ item_separator => item separator default "/"
+ skip_path => if set item is returned without its original path,
+ just as a leaf (default use full path)
+ any_item_selection => allow to select any item, not just leaves (default just leaves)
+ default_button => (optional) 1: Select (any other values Cancel)
+
+=head3 OUTPUT
+
+ undef: if Cancel button has been pressed
+ selected item: if Select button has been pressed
+
+=head3 DESCRIPTION
+
+ This function create a dialog with a combobox in which to
+ choose an item from a given list.
+
+=cut
+
+#=============================================================
+
+sub ask_fromTreeList {
+ my ($self, $info) = @_;
+
+ die "Missing dialog information" if (!$info);
+ die "Title is mandatory" if (! exists $info->{title});
+ die "Header is mandatory" if (! exists $info->{header});
+ die "List is mandatory" if (! exists $info->{list} );
+ my $list = $info->{list};
+ die "At least one element is mandatory into list" if (scalar(@$list) < 1);
+
+ my $choice = undef;
+ my $factory = yui::YUI::widgetFactory;
+
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($info->{title});
+ my $minWidth = 80;
+ my $minHeight = 25;
+
+ if (exists $info->{min_size}) {
+ $minWidth = $info->{min_size}->{width} if $info->{min_size}->{width};
+ $minHeight = $info->{min_size}->{height} if $info->{min_size}->{height};
+ }
+
+ my $dlg = $factory->createPopupDialog($yui::YDialogNormalColor);
+ my $minSize = $factory->createMinSize( $dlg, $minWidth, $minHeight );
+ my $layout = $factory->createVBox($minSize);
+
+ my $treeWidget = $factory->createTree($layout, $info->{header});
+
+ my $treeInfo;
+ $treeInfo->{collection} = new yui::YItemCollection;
+ $treeInfo->{default_item} = $info->{default_item} if $info->{default_item};
+ if ($treeInfo->{default_item} && $info->{item_separator}) {
+ if (index($treeInfo->{default_item}, $info->{item_separator}) != -1) {
+ $treeInfo->{default_item_separator} = $info->{item_separator};
+ }
+ }
+ my $list2Convert;
+ $list2Convert->{paths} = $info->{list};
+ $list2Convert->{separator} = $info->{item_separator} if $info->{item_separator};
+ $treeInfo->{hash_tree} = ManaTools::Shared::pathList2hash($list2Convert);
+
+ $self->hashTreeToYItemCollection($treeInfo);
+ $treeWidget->addItems($treeInfo->{collection});
+
+ my $align = $factory->createRight($layout);
+ my $hbox = $factory->createHBox($align);
+ my $selectButton = $factory->createPushButton($hbox, $self->loc->N("Select"));
+ my $cancelButton = $factory->createPushButton($hbox, $self->loc->N("Cancel"));
+
+ if (exists $info->{default_button} ) {
+ my $dflBtn = ($info->{default_button} == 1) ? $selectButton : $cancelButton;
+ $dlg->setDefaultButton($selectButton);
+ }
+
+ while (1) {
+ my $event = $dlg->waitForEvent();
+
+ my $eventType = $event->eventType();
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $selectButton) {
+ my $item = $treeWidget->selectedItem();
+ my $getChoice = 1;
+ if (!exists $info->{any_item_selection} || $info->{any_item_selection} != 0) {
+ if ($item) {
+ $getChoice = (!$item->hasChildren());
+ }
+ }
+ if ($info->{skip_path} && $info->{skip_path} != 0) {
+ $choice = $item->label() if ($item && $getChoice);
+ }
+ else {
+ if ($getChoice) {
+ my $separator = exists $info->{item_separator} ? $info->{item_separator} : '/';
+ if ($item) {
+ $choice = $item->label();
+ my $parent = $item;
+ while($parent = $parent->parent()) {
+ $choice = $parent->label() . $separator . $choice ;
+ }
+ }
+ }
+ }
+
+ last;
+ }
+ }
+ }
+
+ destroy $dlg;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+
+ return $choice;
+}
+
+
+#=============================================================
+
+=head2 select_fromList
+
+=head3 INPUT
+
+ $info: HASH, information to be passed to the dialog.
+ title => dialog title
+ info_label => optional info text
+ header => column header hash reference{
+ text_column => text column header
+ check_column =>
+ }
+ list => item list hash reference
+ containing {
+ text => item text
+ selected => 0 ur undefined means unchecked
+ }
+
+=head3 OUTPUT
+
+ selection: list of selected items
+
+=head3 DESCRIPTION
+
+ This function create a dialog cotaining a table with a list of
+ items to be checked. The list of the checked items is returned.
+
+=cut
+
+#=============================================================
+
+sub select_fromList {
+ my ($self, $info) = @_;
+
+ die "Missing dialog information" if (!$info);
+ die "Title is mandatory" if (! exists $info->{title});
+ die "Header is mandatory" if (! exists $info->{header});
+ die "Header text column is mandatory" if (! $info->{header}->{text_column});
+ die "List is mandatory" if (! exists $info->{list} );
+ my $list = $info->{list};
+ die "At least one element is mandatory into list" if (scalar(@$list) < 1);
+
+ my $selection = [];
+
+ my $mageiaPlugin = "mga";
+ my $factory = yui::YUI::widgetFactory;
+ my $mgaFactory = yui::YExternalWidgets::externalWidgetFactory($mageiaPlugin);
+ $mgaFactory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($mgaFactory);
+
+ ## push application title
+ my $appTitle = yui::YUI::app()->applicationTitle();
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($info->{title});
+
+ my $dlg = $factory->createPopupDialog($yui::YDialogNormalColor);
+ my $layout = $factory->createVBox($dlg);
+
+ if ($info->{info_label}) {
+ $factory->createLabel($layout, $info->{info_label});
+ }
+
+ my $yTableHeader = new yui::YTableHeader();
+ $yTableHeader->addColumn($info->{header}->{text_column}, $yui::YAlignBegin);
+ $yTableHeader->addColumn($info->{header}->{check_column} || '', $yui::YAlignBegin);
+
+ ## service list (serviceBox)
+ my $selectionTable = $mgaFactory->createCBTable(
+ $layout,
+ $yTableHeader,
+ $yui::YCBTableCheckBoxOnLastColumn
+ );
+ $selectionTable->setImmediateMode(1);
+ $selectionTable->setWeight($yui::YD_HORIZ, 75);
+
+ $selectionTable->startMultipleChanges();
+ $selectionTable->deleteAllItems();
+ my $itemCollection = new yui::YItemCollection;
+ ## NOTE do not sort to preserve item indexes
+ foreach (@{$list}) {
+ my $text = $_->{text} || die "item text is mandatory";
+
+ my $item = new yui::YCBTableItem($text);
+ $item->check( $_->{checked} );
+ $itemCollection->push($item);
+ $item->DISOWN();
+ }
+ $selectionTable->addItems($itemCollection);
+ $selectionTable->doneMultipleChanges();
+
+ my $align = $factory->createRight($layout);
+ my $hbox = $factory->createHBox($align);
+ $factory->createVSpacing($hbox, 1.0);
+ my $okButton = $factory->createPushButton($hbox, $self->loc->N("Ok"));
+ $dlg->setDefaultButton($okButton);
+ $dlg->recalcLayout();
+
+ while (1) {
+ my $event = $dlg->waitForEvent();
+
+ my $eventType = $event->eventType();
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+
+ if ($widget == $okButton) {
+ last;
+ }
+ elsif ($widget == $selectionTable) {
+ my $wEvent = yui::toYWidgetEvent($event);
+ if ($wEvent->reason() == $yui::YEvent::ValueChanged) {
+ my $item = $selectionTable->changedItem();
+ if ($item) {
+ my $index = $item->index();
+ $list->[$index]->{checked} = $item->checked();
+ }
+ }
+ }
+ }
+ }
+
+ destroy $dlg;
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle);
+
+ foreach (@{$list}) {
+ push @{$selection}, $_->{text} if $_->{checked};
+ }
+
+ return $selection;
+}
+
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+
+1;
+
diff --git a/lib/ManaTools/Shared/Hosts.pm b/lib/ManaTools/Shared/Hosts.pm
new file mode 100644
index 0000000..d3b7fc9
--- /dev/null
+++ b/lib/ManaTools/Shared/Hosts.pm
@@ -0,0 +1,216 @@
+# vim: set et ts=4 sw=4:
+#*****************************************************************************
+#
+# Copyright (c) 2013-2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#*****************************************************************************
+package ManaTools::Shared::Hosts;
+
+use Moose;
+use diagnostics;
+use Config::Hosts;
+use Net::DBus;
+use utf8;
+
+# costants by Config::Hosts
+my $is_ip = 1;
+my $is_host = -1;
+my $is_none = 0;
+
+has 'configHosts' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_initialize'
+);
+
+has 'dbusConnectionParams' => (
+ is => 'ro',
+ isa => 'HashRef',
+ builder => '_initDBusConnectionParams',
+);
+
+sub _initialize {
+ my $self = shift();
+ $self->configHosts(Config::Hosts->new());
+}
+
+sub _initDBusConnectionParams {
+ my $self = shift();
+ my %dbusConnParams = ( 'servicePath' => 'org.freedesktop.hostname1', 'objectPath' => '/org/freedesktop/hostname1' );
+ return \%dbusConnParams;
+}
+
+=pod
+
+=head2 _getHosts
+
+=head3 OUTPUT
+
+ @result: array of hashes; each one of them represent a host definition from the hosts configuration file
+
+ NOTE: the 'hosts' item into each hash is an array: it contains the hostname and -eventually- the aliases
+
+=head3 DESCRIPTION
+
+retrieve data from the hosts file (/etc/hosts) using the Config::Hosts module
+
+=cut
+
+sub _getHosts {
+ my $self = shift();
+ # $self->configHosts(Config::Hosts->new());
+ my $hosts = $self->configHosts->read_hosts();
+ my @result = ();
+ while( my ($key, $value) = each(%{$hosts})){
+ if($self->configHosts->determine_ip_or_host($key) == $is_ip){
+ my $tmp = {};
+ $tmp = $self->configHosts->query_host($key);
+ $tmp->{'ip'} = $key;
+ push @result,$tmp;
+ }
+ }
+ return @result;
+}
+
+sub _insertHost {
+ my $self = shift();
+ # remember that the order matters!
+ my $ip = shift();
+ my @host_definitions = @_;
+ # $self->configHosts = Config::Hosts->new();
+ return $self->configHosts->insert_host(ip => $ip, hosts => @host_definitions);
+}
+
+sub _dropHost {
+ my $self = shift();
+ my $host_ip = shift();
+ return $self->configHosts->delete_host($host_ip);
+}
+
+sub _modifyHost {
+ my $self = shift();
+ my $host_ip = shift();
+ my @host_definitions = @_;
+ return $self->configHosts->update_host($host_ip, hosts => @host_definitions);
+}
+
+sub _writeHosts {
+ my $self = shift();
+ return $self->configHosts->write_hosts();
+}
+
+sub _dbus_connection {
+ my $self = shift();
+ my %params = %{$self->dbusConnectionParams()};
+ my $bus = Net::DBus->system;
+ my $service = $bus->get_service($params{'servicePath'});
+ my $object = $service->get_object($params{'objectPath'});
+ return $object;
+}
+
+sub _dbus_inquiry {
+ my $self = shift();
+ my $required_field = shift();
+ my $object = $self->_dbus_connection();
+ my %params = %{$self->dbusConnectionParams()};
+ my $properties = $object->GetAll($params{'servicePath'});
+ return $properties->{$required_field} if(defined($properties->{$required_field}));
+ return 0;
+}
+
+sub _dbus_setup {
+ my $self = shift();
+ my $attribute = shift();
+ my $value = shift();
+ my $object = $self->_dbus_connection();
+ if($attribute eq "Hostname")
+ {
+ $object->SetHostname($value,1);
+ }
+ elsif($attribute eq "PrettyHostname")
+ {
+ $object->SetPrettyHostname($value,1);
+ }
+ elsif($attribute eq "StaticHostname")
+ {
+ $object->SetStaticHostname($value,1);
+ }
+ elsif($attribute eq "Chassis")
+ {
+ $object->SetChassis($value,1);
+ }
+ elsif($attribute eq "IconName")
+ {
+ $object->SetIconName($value,1);
+ }
+}
+
+sub _getLocalHostName {
+ my $self = shift();
+ return $self->_dbus_inquiry('Hostname');
+}
+
+sub _getLocalPrettyHostName {
+ my $self = shift();
+ return $self->_dbus_inquiry('PrettyHostname');
+}
+
+sub _getLocalStaticHostName {
+ my $self = shift();
+ return $self->_dbus_inquiry('StaticHostname');
+}
+
+sub _getLocalChassis {
+ my $self = shift();
+ return $self->_dbus_inquiry('Chassis');
+}
+
+sub _getLocalIconName {
+ my $self = shift();
+ return $self->_dbus_inquiry('IconName');
+}
+
+sub _setLocalHostName {
+ my $self = shift();
+ my $hostname = shift();
+ $self->_dbus_setup('Hostname',$hostname);
+}
+
+sub _setLocalPrettyHostName {
+ my $self = shift();
+ my $value = shift();
+ $self->_dbus_setup('PrettyHostname',$value);
+}
+
+sub _setLocalStaticHostName {
+ my $self = shift();
+ my $value = shift();
+ $self->_dbus_setup('StaticHostname',$value);
+}
+
+sub _setLocalIconName {
+ my $self = shift();
+ my $value = shift();
+ $self->_dbus_setup('IconName',$value);
+}
+
+sub _setLocalChassis {
+ my $self = shift();
+ my $value = shift();
+ $self->_dbus_setup('Chassis',$value);
+}
+
+1;
diff --git a/lib/ManaTools/Shared/JournalCtl.pm b/lib/ManaTools/Shared/JournalCtl.pm
new file mode 100644
index 0000000..1194538
--- /dev/null
+++ b/lib/ManaTools/Shared/JournalCtl.pm
@@ -0,0 +1,141 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Shared::JournalCtl;
+
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ManaTools::Shared::JournalCtl - journalctl perl wrapper
+
+=head1 SYNOPSIS
+
+ my $log = ManaTools::Shared::JournalCtl->new();
+ my @log_content = $log->getLog();
+
+=head1 DESCRIPTION
+
+This module wraps journalctl allowing some running options and provides the
+output log content.
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command:
+
+perldoc ManaTools::Shared::JournalCtl
+
+
+=head1 AUTHOR
+
+Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2014-2015, Angelo Naselli.
+
+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.
+
+=head1 METHODS
+
+=cut
+
+use Moose;
+
+use diagnostics;
+
+
+has 'this_boot' => (
+ is => 'rw',
+ isa => 'Int',
+ default => 0,
+);
+
+has 'since' => (
+ is => 'rw',
+ isa => 'Str',
+ default => "",
+);
+
+has 'until' => (
+ is => 'rw',
+ isa => 'Str',
+ default => "",
+);
+
+has 'priority' => (
+ is => 'rw',
+ isa => 'Str',
+ default => "",
+);
+
+has 'unit' => (
+ is => 'rw',
+ isa => 'Str',
+ default => "",
+);
+
+#=============================================================
+
+=head2 getLog
+
+=head3 INPUT
+
+Input_Parameter: in_par_description
+
+=head3 OUTPUT
+
+\@content: ARRAYREF containing the log content.
+
+=head3 DESCRIPTION
+
+This methods gets the log using the provided options
+
+=cut
+
+#=============================================================
+
+sub getLog {
+ my $self = shift;
+
+ my $params = "--no-pager -q";
+ if ($self->this_boot == 1) {
+ $params .= " -b";
+ }
+ if ($self->since ne "") {
+ $params .= " --since=" . '"' . $self->since . '"';
+ }
+ if ($self->until ne "") {
+ $params .= " --until=" . '"' . $self->until .'"';
+ }
+ if ($self->unit ne "") {
+ $params .= " --unit=" . $self->unit;
+ }
+ if ($self->priority ne "") {
+ $params .= " --priority=" . $self->priority;
+ }
+
+ $ENV{'PATH'} = '/usr/sbin:/usr/bin';
+ my $jctl = "/usr/bin/journalctl " . $params;
+
+ # TODO remove or add to log
+ print " Running " . $jctl . "\n";
+ my @content = `$jctl`;
+
+ return \@content;
+}
+
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+
+1;
diff --git a/lib/ManaTools/Shared/Locales.pm b/lib/ManaTools/Shared/Locales.pm
new file mode 100644
index 0000000..ec54614
--- /dev/null
+++ b/lib/ManaTools/Shared/Locales.pm
@@ -0,0 +1,280 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Shared::Locales;
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ManaTools::Shared::Locales - Class to manage locales
+
+=head1 SYNOPSIS
+
+use ManaTools::Shared::Locales;
+
+my $obj = ManaTools::Shared::Locales->new(domain_name => 'this_domain');
+
+print $obj->N("test string %d", 1) . "\n";
+
+=head1 DESCRIPTION
+
+This class wraps Locale::gettext to manage localization
+
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command:
+
+perldoc ManaTools::Shared::Locales
+
+=head1 SEE ALSO
+
+Locale::gettext Text::Iconv and gettext
+
+=head1 AUTHOR
+
+Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2014-2015, Angelo Naselli.
+
+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
+
+=head1 FUNCTIONS
+
+=cut
+
+
+use Moose;
+use diagnostics;
+use utf8;
+use Locale::gettext;
+use Text::Iconv;
+
+
+#=============================================================
+
+=head2 new
+
+=head3 INPUT
+
+ hash ref containing
+ domain_name: gettext domain name (default mpan)
+ dir_name: gettext optional catalog directory (default undef)
+ codeset: gettext codeset (default UTF8)
+
+=head3 DESCRIPTION
+
+ new is inherited from Moose, to create a Locales object
+
+=cut
+
+#=============================================================
+
+has 'domain_name' => (
+ is => 'rw',
+ default => 'mpan',
+);
+
+has 'dir_name' => (
+ is => 'rw',
+ default => undef,
+);
+
+has 'codeset' => (
+ is => 'rw',
+ default => 'UTF8',
+);
+
+has 'domain' => (
+ is => 'rw',
+ init_arg => undef,
+);
+
+#=============================================================
+
+=head2 BUILD
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ The BUILD method is called after a Moose object is created.
+ This method initilaizes gettext domain.
+
+=cut
+
+#=============================================================
+sub BUILD {
+ my $self = shift;
+
+ if ($^V ge v5.20.0) {
+ require POSIX;
+ POSIX::setlocale (POSIX::LC_ALL (), '');
+ }
+
+ $self->domain(Locale::gettext->domain_raw($self->domain_name));
+ $self->domain->dir($self->dir_name) if $self->dir_name;
+ $self->domain->codeset($self->codeset);
+}
+
+
+#=============================================================
+
+=head2 P
+
+=head3 INPUT
+
+ $self : this object
+ $s_singular: msg id singular
+ $s_plural: msg id plural
+ $nb: value for plural
+
+=head3 OUTPUT
+
+ locale string
+
+=head3 DESCRIPTION
+
+ returns the given string localized (see dngettext)
+
+=cut
+
+#=============================================================
+sub P {
+ my ($self, $s_singular, $s_plural, $nb, @para) = @_;
+
+ sprintf($self->domain->nget($s_singular, $s_plural, $nb), @para);
+}
+
+#=============================================================
+
+=head2 N
+
+=head3 INPUT
+
+ $self : this object
+ $s: msg id
+
+=head3 OUTPUT
+
+ locale string
+
+=head3 DESCRIPTION
+
+ returns the given string localized (see dgettext)
+
+=cut
+
+#=============================================================
+sub N {
+ my ($self, $s, @para) = @_;
+
+ sprintf($self->domain->get($s), @para);
+}
+
+#=============================================================
+
+=head2 N_
+
+=head3 INPUT
+
+ $self : this object
+ $s: msg id
+
+=head3 OUTPUT
+
+ msg id
+
+=head3 DESCRIPTION
+
+ returns the given string
+
+=cut
+
+#=============================================================
+sub N_ {
+ my $self = shift;
+
+ $_[0];
+}
+
+
+#=============================================================
+
+=head2 from_utf8
+
+=head3 INPUT
+
+ $self: this object
+ $s: string to be converted
+
+=head3 OUTPUT\
+
+ $converted: converted string
+
+=head3 DESCRIPTION
+
+ convert from utf-8 to current locale
+
+=cut
+
+#=============================================================
+sub from_utf8 {
+ my ($self, $s) = @_;
+
+ my $converter = Text::Iconv->new("utf-8", undef);
+ my $converted = $converter->convert($s);
+
+ return $converted;
+}
+
+
+#=============================================================
+
+=head2 to_utf8
+
+=head3 INPUT
+
+ $self: this object
+ $s: string to be converted
+
+=head3 OUTPUT\
+
+ $converted: converted string
+
+=head3 DESCRIPTION
+
+ convert to utf-8 from current locale
+
+=cut
+
+#=============================================================
+sub to_utf8 {
+ my ($self, $s) = @_;
+
+ my $converter = Text::Iconv->new(undef, "utf-8");
+ my $converted = $converter->convert($s);
+
+ return $converted;
+}
+
+
+
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+
+1;
diff --git a/lib/ManaTools/Shared/Proxy.pm b/lib/ManaTools/Shared/Proxy.pm
new file mode 100644
index 0000000..6accbb7
--- /dev/null
+++ b/lib/ManaTools/Shared/Proxy.pm
@@ -0,0 +1,36 @@
+# vim: set et ts=4 sw=4:
+#*****************************************************************************
+#
+# Copyright (c) 2013-2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#*****************************************************************************
+package ManaTools::Shared::Proxy;
+
+use Moose;
+use diagnostics;
+use utf8;
+
+has 'network' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_initialize'
+);
+
+sub _initialize {
+ my $self = shift();
+}
+
+1;
diff --git a/lib/ManaTools/Shared/RunProgram.pm b/lib/ManaTools/Shared/RunProgram.pm
new file mode 100644
index 0000000..042f559
--- /dev/null
+++ b/lib/ManaTools/Shared/RunProgram.pm
@@ -0,0 +1,352 @@
+package ManaTools::Shared::RunProgram;
+
+use strict;
+use MDK::Common;
+use Sys::Syslog;
+use MDK::Common::File qw(cat_);
+
+use Exporter;
+our @ISA = qw(Exporter);
+our @EXPORT_OK = qw(
+ set_default_timeout
+ run_or_die
+ rooted_or_die
+ get_stdout
+ get_stdout_raw
+ rooted_get_stdout
+ run
+ raw
+ rooted
+);
+
+=head1 SYNOPSYS
+
+B<rManaTools::Shared::RunProgram> enables to:
+
+=over 4
+
+=item * run programs in foreground or in background,
+
+=item * to retrieve their stdout or stderr
+
+=item * ...
+
+=back
+
+Most functions exits in a normal form & a rooted one. e.g.:
+
+=over 4
+
+=item * C<run()> & C<rooted()>
+
+=item * C<get_stdout()> & C<rooted_get_stdout()>
+
+=back
+
+Most functions exits in a normal form & one that die. e.g.:
+
+=over 4
+
+=item * C<run()> & C<run_or_die()>
+
+=item * C<rooted()> & C<rooted_or_die()>
+
+=back
+
+=head1 Functions
+
+=over
+
+=cut
+
+1;
+
+my $default_timeout = 10 * 60;
+
+=item set_default_timeout($seconds)
+
+Alters defaults timeout (eg for harddrake service)
+
+=cut
+
+sub set_default_timeout {
+ my ($seconds) = @_;
+ $default_timeout = $seconds;
+}
+
+=item run_or_die($name, @args)
+
+Runs $name with @args parameterXs. Dies if it exit code is not 0.
+
+=cut
+
+sub run_or_die {
+ my ($name, @args) = @_;
+ run($name, @args) or die "$name failed\n";
+}
+
+=item rooted_or_die($root, $name, @args)
+
+Similar to run_or_die() but runs in chroot in $root
+
+=cut
+
+sub rooted_or_die {
+ my ($root, $name, @args) = @_;
+ rooted($root, $name, @args) or die "$name failed\n";
+}
+
+=item get_stdout($name, @args)
+
+Similar to run_or_die() but return stdout of program:
+
+=over 4
+
+=item * a list of lines in list context
+
+=item * a string of concatenated lines in scalar context
+
+=back
+
+=cut
+
+sub get_stdout {
+ my ($name, @args) = @_;
+ my @r;
+ run($name, '>', \@r, @args) or return;
+ wantarray() ? @r : join('', @r);
+}
+
+=item get_stdout_raw($options, $name, @args)
+
+Similar to get_stdout() but allow to pass options to raw()
+
+=cut
+
+sub get_stdout_raw {
+ my ($options, $name, @args) = @_;
+ my @r;
+ raw($options, $name, '>', \@r, @args) or return;
+ wantarray() ? @r : join('', @r);
+}
+
+=item rooted_get_stdout($root, $name, @args)
+
+Similar to get_stdout() but runs in chroot in $root
+
+=cut
+
+sub rooted_get_stdout {
+ my ($root, $name, @args) = @_;
+ my @r;
+ rooted($root, $name, '>', \@r, @args) or return;
+ wantarray() ? @r : join('', @r);
+}
+
+=item run($name, @args)
+
+Runs $name with @args parameters.
+
+=cut
+
+sub run {
+ raw({}, @_);
+}
+
+=item rooted($root, $name, @args)
+
+Similar to run() but runs in chroot in $root
+
+=cut
+
+sub rooted {
+ my ($root, $name, @args) = @_;
+ raw({ root => $root }, $name, @args);
+}
+
+=item raw($options, $name, @args)
+
+The function used by all the other, making every combination possible.
+Runs $name with @args parameters. $options is a hash ref that can contains:
+
+=over 4
+
+=item * B<root>: $name will be chrooted in $root prior to run
+
+=item * B<as_user>: $name will be run as $ENV{USERHELPER_UID} or with the UID of parent process. Implies I<setuid>
+
+=item * B<sensitive_arguments>: parameters will be hidden in logs (b/c eg there's a password)
+
+=item * B<detach>: $name will be run in the background. Default is foreground
+
+=item * B<chdir>: $name will be run in a different default directory
+
+=item * B<setuid>: contains a getpwnam(3) struct ; $name will be with droped privileges ;
+make sure environment is set right and keep a copy of the X11 cookie
+
+=item * B<timeout>: execution of $name will be aborted after C<timeout> seconds
+
+=back
+
+eg:
+
+=over 4
+
+=item * C<< ManaTools::Shared::RunProgram::raw({ root => $::prefix, sensitive_arguments => 1 }, "echo -e $user->{password} | cryptsetup luksFormat $device"); >>
+
+=item * C<< ManaTools::Shared::RunProgram::raw({ detach => 1 }, '/etc/rc.d/init.d/dm', '>', '/dev/null', '2>', '/dev/null', 'restart'); >>
+
+=back
+
+=cut
+
+sub raw {
+ my ($options, $name, @args) = @_;
+ my $root = $options->{root} || '';
+ my $real_name = ref($name) ? $name->[0] : $name;
+
+ my ($stdout_raw, $stdout_mode, $stderr_raw, $stderr_mode);
+ ($stdout_mode, $stdout_raw, @args) = @args if $args[0] =~ /^>>?$/;
+ ($stderr_mode, $stderr_raw, @args) = @args if $args[0] =~ /^2>>?$/;
+
+ my $home;
+ if ($options->{as_user}) {
+ my $uid;
+ $uid = $ENV{USERHELPER_UID} && getpwuid($ENV{USERHELPER_UID});
+ $uid ||= _get_parent_uid();
+ $options->{setuid} = getpwnam($uid) if $uid;
+ my ($full_user) = grep { $_->[2] eq $uid } list_passwd();
+ $home = $full_user->[7] if $full_user;
+ }
+ local $ENV{HOME} = $home if $home;
+
+ my $args = $options->{sensitive_arguments} ? '<hidden arguments>' : join(' ', @args);
+ Sys::Syslog::syslog('info|local1', "running: $real_name $args" . ($root ? " with root $root" : ""));
+
+ return if $root && $<;
+
+ $root ? ($root .= '/') : ($root = '');
+
+ my $tmpdir = sub {
+ my $dir = $< != 0 ? "$ENV{HOME}/tmp" : -d '/root' ? '/root/tmp' : '/tmp';
+ -d $dir or mkdir($dir, 0700);
+ $dir;
+ };
+ my $stdout = $stdout_raw && (ref($stdout_raw) ? $tmpdir->() . "/.drakx-stdout.$$" : "$root$stdout_raw");
+ my $stderr = $stderr_raw && (ref($stderr_raw) ? $tmpdir->() . "/.drakx-stderr.$$" : "$root$stderr_raw");
+
+ #- checking if binary exist to avoid clobbering stdout file
+ my $rname = $real_name =~ /(.*?)[\s\|]/ ? $1 : $real_name;
+ if (! ($rname =~ m!^/!
+ ? -x "$root$rname" || $root && -l "$root$rname" #- handle non-relative symlink which can be broken when non-rooted
+ : whereis_binary($rname, $root))) {
+ Sys::Syslog::syslog('warning', "program not found: $real_name");
+
+ return;
+ }
+
+ if (my $pid = fork()) {
+ if ($options->{detach}) {
+ $pid;
+ } else {
+ my $ok;
+ add2hash_($options, { timeout => $default_timeout });
+ eval {
+ local $SIG{ALRM} = sub { die "ALARM" };
+ my $remaining = $options->{timeout} && $options->{timeout} ne 'never' && alarm($options->{timeout});
+ waitpid $pid, 0;
+ $ok = $? == -1 || ($? >> 8) == 0;
+ alarm $remaining;
+ };
+ if ($@) {
+ Sys::Syslog::syslog('warning', "ERROR: killing runaway process (process=$real_name, pid=$pid, args=@args, error=$@)");
+ kill 9, $pid;
+ return;
+ }
+
+ if ($stdout_raw && ref($stdout_raw)) {
+ if (ref($stdout_raw) eq 'ARRAY') {
+ @$stdout_raw = cat_($stdout);
+ } else {
+ $$stdout_raw = cat_($stdout);
+ }
+ unlink $stdout;
+ }
+ if ($stderr_raw && ref($stderr_raw)) {
+ if (ref($stderr_raw) eq 'ARRAY') {
+ @$stderr_raw = cat_($stderr);
+ } else {
+ $$stderr_raw = cat_($stderr);
+ }
+ unlink $stderr;
+ }
+ $ok;
+ }
+ } else {
+ if ($options->{setuid}) {
+ require POSIX;
+ my ($logname, $home) = (getpwuid($options->{setuid}))[0,7];
+ $ENV{LOGNAME} = $logname if $logname;
+
+ # if we were root and are going to drop privilege, keep a copy of the X11 cookie:
+ if (!$> && $home) {
+ # FIXME: it would be better to remove this but most callers are using 'detach => 1'...
+ my $xauth = chomp_(`mktemp $home/.Xauthority.XXXXX`);
+ system('cp', '-a', $ENV{XAUTHORITY}, $xauth);
+ system('chown', $logname, $xauth);
+ $ENV{XAUTHORITY} = $xauth;
+ }
+
+ # drop privileges:
+ POSIX::setuid($options->{setuid});
+ }
+
+ sub _die_exit {
+ Sys::Syslog::syslog('warning', $_[0]);
+ POSIX::_exit(128);
+ }
+ if ($stderr && $stderr eq 'STDERR') {
+ } elsif ($stderr) {
+ $stderr_mode =~ s/2//;
+ open STDERR, "$stderr_mode $stderr" or _die_exit("ManaTools::Shared::RunProgram cannot output in $stderr (mode `$stderr_mode')");
+ } elsif ($::isInstall) {
+ open STDERR, ">> /tmp/ddebug.log" or open STDOUT, ">> /dev/tty7" or _die_exit("ManaTools::Shared::RunProgram cannot log, give me access to /tmp/ddebug.log");
+ }
+ if ($stdout && $stdout eq 'STDOUT') {
+ } elsif ($stdout) {
+ open STDOUT, "$stdout_mode $stdout" or _die_exit("ManaTools::Shared::RunProgram cannot output in $stdout (mode `$stdout_mode')");
+ } elsif ($::isInstall) {
+ open STDOUT, ">> /tmp/ddebug.log" or open STDOUT, ">> /dev/tty7" or _die_exit("ManaTools::Shared::RunProgram cannot log, give me access to /tmp/ddebug.log");
+ }
+
+ $root and chroot $root;
+ chdir($options->{chdir} || "/");
+
+ my $ok = ref $name ? do {
+ exec { $name->[0] } $name->[1], @args;
+ } : do {
+ exec $name, @args;
+ };
+ if (!$ok) {
+ _die_exit("exec of $real_name failed: $!");
+ }
+ }
+
+}
+
+=item get_parent_uid()
+
+Returns UID of the parent process.
+
+=cut
+
+sub _get_parent_uid() {
+ cat_('/proc/' . getppid() . '/status') =~ /Uid:\s*(\d+)/ ? $1 : undef;
+}
+
+
+
+#- Local Variables:
+#- mode:cperl
+#- tab-width:8
+#- End:
diff --git a/lib/ManaTools/Shared/Services.pm b/lib/ManaTools/Shared/Services.pm
new file mode 100644
index 0000000..abef8c3
--- /dev/null
+++ b/lib/ManaTools/Shared/Services.pm
@@ -0,0 +1,955 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Shared::Services;
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ManaTools::Shared::Services - shares the API to manage services
+
+=head1 SYNOPSIS
+
+use ManaTools::Shared::Services;
+
+my $serv = ManaTools::Shared::Services->new();
+
+my ($l, $on_services) = $serv->services();
+
+=head1 DESCRIPTION
+
+ This module aims to share all the API to manage system services,
+ to be used from GUI applications or console.
+
+ From the original code drakx services.
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command:
+
+perldoc ManaTools::Shared::Services
+
+=head1 SEE ALSO
+
+ManaTools::Shared
+
+=head1 AUTHOR
+
+Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2013-2015, Angelo Naselli.
+
+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
+
+=head1 FUNCTIONS
+
+=cut
+
+
+use Moose;
+
+use Sys::Syslog;
+use Net::DBus;
+use Net::DBus::Annotation qw(:auth);
+use File::Basename;
+
+use ManaTools::Shared::Locales;
+use MDK::Common::Func qw(find);
+use MDK::Common::File;
+use MDK::Common::DataStructure qw(member);
+use ManaTools::Shared::RunProgram qw(rooted);
+
+has 'loc' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_localeInitialize'
+);
+
+
+sub _localeInitialize {
+ my $self = shift();
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'libDrakX') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+
+has 'dbus_systemd1_service' => (
+ is => 'rw',
+ init_arg => undef,
+ lazy => 1,
+ builder => '_dbusServiceInitialize'
+);
+
+sub _dbusServiceInitialize {
+ my $self = shift();
+
+ my $bus = Net::DBus->system;
+ $self->dbus_systemd1_service($bus->get_service("org.freedesktop.systemd1"));
+}
+
+
+has 'dbus_systemd1_object' => (
+ is => 'rw',
+ init_arg => undef,
+ lazy => 1,
+ builder => '_dbusObjectInitialize'
+);
+
+sub _dbusObjectInitialize {
+ my $self = shift();
+
+ $self->dbus_systemd1_object($self->dbus_systemd1_service->get_object("/org/freedesktop/systemd1"));
+}
+
+
+has 'service_info' => (
+ is => 'rw',
+ traits => ['Hash'],
+ isa => 'HashRef',
+ handles => {
+ set_service_info => 'set',
+ get_service_info => 'get',
+ service_info_pairs => 'kv',
+ },
+ init_arg => undef,
+ lazy => 1,
+ builder => '_serviceInfoInitialization'
+);
+
+sub _serviceInfoInitialization {
+ my $self = shift();
+
+ my %services = ();
+ if ($self->_running_systemd()) {
+ my $object = $self->dbus_systemd1_object;
+ my $properties = $object->ListUnits();
+
+ foreach my $s (@{$properties}) {
+ my $name = $s->[0];
+ if (index($name, ".service") != -1) {
+ my $st = eval{$object->GetUnitFileState($name)} if $name !~ /.*\@.*$/g;
+ $name =~ s|.service||;
+ if (!$st) {
+ if ($name !~ /.*\@$/g &&
+ (-e "/usr/lib/systemd/system/$name.service" or -e "/etc/rc.d/init.d/$name") &&
+ ! -l "/usr/lib/systemd/system/$name.service") {
+ $st = 'enabled';
+ }
+ }
+ if ($st && $st ne 'static') {
+ $services{$name} = {
+ 'name' => $s->[0],
+ 'description' => $s->[1],
+ 'load_state' => $s->[2],
+ 'active_state' => $s->[3],
+ 'sub_state' => $s->[4],
+ 'unit_path' => $s->[6],
+ 'enabled' => $st eq 'enabled',
+ };
+ }
+ }
+ }
+
+ my $unit_files = $object->ListUnitFiles();
+ foreach my $s (@{$unit_files}) {
+ my $name = $s->[0];
+ my $st = $s->[1];
+ if (index($name, ".service") != -1) {
+ $name = File::Basename::basename($name, ".service");
+ if (!$services{$name} &&
+ $name !~ /.*\@$/g &&
+ (-e $s->[0] or -e "/etc/rc.d/init.d/$name") &&
+ ! -l $s->[0] && ($st eq "disabled" || $st eq "enabled")) {
+ my $wantedby = $self->_WantedBy($s->[0]);
+ if ($wantedby) {
+ my $descr = $self->getUnitProperty($name, 'Description');
+
+ $services{$name} = {
+ 'name' => $name,
+ 'description' => $descr,
+ 'enabled' => $st eq "enabled",
+ };
+ }
+ }
+ }
+ }
+ }
+
+ return \%services;
+}
+
+#=============================================================
+
+=head2 description
+
+=head3 INPUT
+
+name: Service Name
+
+=head3 OUTPUT
+
+Description: Service description
+
+=head3 DESCRIPTION
+
+THis function return the description for the given service
+
+=cut
+
+#=============================================================
+sub description {
+ my ($self, $name) = @_;
+
+ my %services = (
+acpid => $self->loc->N_("Listen and dispatch ACPI events from the kernel"),
+alsa => $self->loc->N_("Launch the ALSA (Advanced Linux Sound Architecture) sound system"),
+anacron => $self->loc->N_("Anacron is a periodic command scheduler."),
+apmd => $self->loc->N_("apmd is used for monitoring battery status and logging it via syslog.
+It can also be used for shutting down the machine when the battery is low."),
+atd => $self->loc->N_("Runs commands scheduled by the at command at the time specified when
+at was run, and runs batch commands when the load average is low enough."),
+'avahi-deamon' => $self->loc->N_("Avahi is a ZeroConf daemon which implements an mDNS stack"),
+chronyd => $self->loc->N_("An NTP client/server"),
+cpufreq => $self->loc->N_("Set CPU frequency settings"),
+crond => $self->loc->N_("cron is a standard UNIX program that runs user-specified programs
+at periodic scheduled times. vixie cron adds a number of features to the basic
+UNIX cron, including better security and more powerful configuration options."),
+cups => $self->loc->N_("Common UNIX Printing System (CUPS) is an advanced printer spooling system"),
+dm => $self->loc->N_("Launches the graphical display manager"),
+fam => $self->loc->N_("FAM is a file monitoring daemon. It is used to get reports when files change.
+It is used by GNOME and KDE"),
+g15daemon => $self->loc->N_("G15Daemon allows users access to all extra keys by decoding them and
+pushing them back into the kernel via the linux UINPUT driver. This driver must be loaded
+before g15daemon can be used for keyboard access. The G15 LCD is also supported. By default,
+with no other clients active, g15daemon will display a clock. Client applications and
+scripts can access the LCD via a simple API."),
+gpm => $self->loc->N_("GPM adds mouse support to text-based Linux applications such the
+Midnight Commander. It also allows mouse-based console cut-and-paste operations,
+and includes support for pop-up menus on the console."),
+haldaemon => $self->loc->N_("HAL is a daemon that collects and maintains information about hardware"),
+harddrake => $self->loc->N_("HardDrake runs a hardware probe, and optionally configures
+new/changed hardware."),
+httpd => $self->loc->N_("Apache is a World Wide Web server. It is used to serve HTML files and CGI."),
+inet => $self->loc->N_("The internet superserver daemon (commonly called inetd) starts a
+variety of other internet services as needed. It is responsible for starting
+many services, including telnet, ftp, rsh, and rlogin. Disabling inetd disables
+all of the services it is responsible for."),
+ip6tables => $self->loc->N_("Automates a packet filtering firewall with ip6tables"),
+iptables => $self->loc->N_("Automates a packet filtering firewall with iptables"),
+irqbalance => $self->loc->N_("Evenly distributes IRQ load across multiple CPUs for enhanced performance"),
+keytable => $self->loc->N_("This package loads the selected keyboard map as set in
+/etc/sysconfig/keyboard. This can be selected using the kbdconfig utility.
+You should leave this enabled for most machines."),
+kheader => $self->loc->N_("Automatic regeneration of kernel header in /boot for
+/usr/include/linux/{autoconf,version}.h"),
+kudzu => $self->loc->N_("Automatic detection and configuration of hardware at boot."),
+'laptop-mode' => $self->loc->N_("Tweaks system behavior to extend battery life"),
+linuxconf => $self->loc->N_("Linuxconf will sometimes arrange to perform various tasks
+at boot-time to maintain the system configuration."),
+lpd => $self->loc->N_("lpd is the print daemon required for lpr to work properly. It is
+basically a server that arbitrates print jobs to printer(s)."),
+lvs => $self->loc->N_("Linux Virtual Server, used to build a high-performance and highly
+available server."),
+mandi => $self->loc->N_("Monitors the network (Interactive Firewall and wireless"),
+mdadm => $self->loc->N_("Software RAID monitoring and management"),
+messagebus => $self->loc->N_("DBUS is a daemon which broadcasts notifications of system events and other messages"),
+msec => $self->loc->N_("Enables MSEC security policy on system startup"),
+named => $self->loc->N_("named (BIND) is a Domain Name Server (DNS) that is used to resolve host names to IP addresses."),
+netconsole => $self->loc->N_("Initializes network console logging"),
+netfs => $self->loc->N_("Mounts and unmounts all Network File System (NFS), SMB (Lan
+Manager/Windows), and NCP (NetWare) mount points."),
+network => $self->loc->N_("Activates/Deactivates all network interfaces configured to start
+at boot time."),
+'network-auth' => $self->loc->N_("Requires network to be up if enabled"),
+'network-up' => $self->loc->N_("Wait for the hotplugged network to be up"),
+nfs => $self->loc->N_("NFS is a popular protocol for file sharing across TCP/IP networks.
+This service provides NFS server functionality, which is configured via the
+/etc/exports file."),
+nfslock => $self->loc->N_("NFS is a popular protocol for file sharing across TCP/IP
+networks. This service provides NFS file locking functionality."),
+ntpd => $self->loc->N_("Synchronizes system time using the Network Time Protocol (NTP)"),
+numlock => $self->loc->N_("Automatically switch on numlock key locker under console
+and Xorg at boot."),
+oki4daemon => $self->loc->N_("Support the OKI 4w and compatible winprinters."),
+partmon => $self->loc->N_("Checks if a partition is close to full up"),
+pcmcia => $self->loc->N_("PCMCIA support is usually to support things like ethernet and
+modems in laptops. It will not get started unless configured so it is safe to have
+it installed on machines that do not need it."),
+portmap => $self->loc->N_("The portmapper manages RPC connections, which are used by
+protocols such as NFS and NIS. The portmap server must be running on machines
+which act as servers for protocols which make use of the RPC mechanism."),
+portreserve => $self->loc->N_("Reserves some TCP ports"),
+postfix => $self->loc->N_("Postfix is a Mail Transport Agent, which is the program that moves mail from one machine to another."),
+random => $self->loc->N_("Saves and restores system entropy pool for higher quality random
+number generation."),
+rawdevices => $self->loc->N_("Assign raw devices to block devices (such as hard disk drive
+partitions), for the use of applications such as Oracle or DVD players"),
+resolvconf => $self->loc->N_("Nameserver information manager"),
+routed => $self->loc->N_("The routed daemon allows for automatic IP router table updated via
+the RIP protocol. While RIP is widely used on small networks, more complex
+routing protocols are needed for complex networks."),
+rstatd => $self->loc->N_("The rstat protocol allows users on a network to retrieve
+performance metrics for any machine on that network."),
+rsyslog => $self->loc->N_("Syslog is the facility by which many daemons use to log messages to various system log files. It is a good idea to always run rsyslog."),
+rusersd => $self->loc->N_("The rusers protocol allows users on a network to identify who is
+logged in on other responding machines."),
+rwhod => $self->loc->N_("The rwho protocol lets remote users get a list of all of the users
+logged into a machine running the rwho daemon (similar to finger)."),
+saned => $self->loc->N_("SANE (Scanner Access Now Easy) enables to access scanners, video cameras, ..."),
+shorewall => $self->loc->N_("Packet filtering firewall"),
+smb => $self->loc->N_("The SMB/CIFS protocol enables to share access to files & printers and also integrates with a Windows Server domain"),
+sound => $self->loc->N_("Launch the sound system on your machine"),
+'speech-dispatcherd' => $self->loc->N_("layer for speech analysis"),
+sshd => $self->loc->N_("Secure Shell is a network protocol that allows data to be exchanged over a secure channel between two computers"),
+syslog => $self->loc->N_("Syslog is the facility by which many daemons use to log messages
+to various system log files. It is a good idea to always run syslog."),
+'udev-post' => $self->loc->N_("Moves the generated persistent udev rules to /etc/udev/rules.d"),
+usb => $self->loc->N_("Load the drivers for your usb devices."),
+vnStat => $self->loc->N_("A lightweight network traffic monitor"),
+xfs => $self->loc->N_("Starts the X Font Server."),
+xinetd => $self->loc->N_("Starts other deamons on demand."),
+ );
+
+ my $s = $services{$name};
+ if ($s) {
+ $s = $self->loc->N($s);
+ }
+ elsif ($self->get_service_info($name)) {
+ $s = $self->get_service_info($name)->{description};
+ }
+ else {
+ my $file = "/usr/lib/systemd/system/$name.service";
+ if (-e $file) {
+ $s = MDK::Common::File::cat_($file);
+ $s = $s =~ /^Description=(.*)/mg ? $1 : '';
+ } else {
+ $file = MDK::Common::Func::find { -e $_ } map { "$_/$name" } '/etc/rc.d/init.d', '/etc/init.d', '/etc/xinetd.d';
+ $s = MDK::Common::File::cat_($file);
+ $s =~ s/\\\s*\n#\s*//mg;
+ $s =
+ $s =~ /^#\s+(?:Short-)?[dD]escription:\s+(.*?)^(?:[^#]|# {0,2}\S)/sm ? $1 :
+ $s =~ /^#\s*(.*?)^[^#]/sm ? $1 : '';
+
+ $s =~ s/#\s*//mg;
+ }
+ }
+ $s =~ s/\n/ /gm; $s =~ s/\s+$//;
+ $s;
+}
+
+
+#=============================================================
+
+=head2 set_service
+
+=head3 INPUT
+
+ $service: Service name
+ $enable: enable/disable service
+
+=head3 DESCRIPTION
+
+ This function enable/disable at boot the given service
+
+=cut
+
+#=============================================================
+sub set_service {
+ my ($self, $service, $enable) = @_;
+
+ my @xinetd_services = map { $_->[0] } $self->xinetd_services();
+
+ if (MDK::Common::DataStructure::member($service, @xinetd_services)) {
+ $ENV{PATH} = "/usr/bin:/usr/sbin";
+ ManaTools::Shared::RunProgram::rooted("", "/usr/sbin/chkconfig", $enable ? "--add" : "--del", $service);
+ } elsif ($self->_running_systemd() || $self->_has_systemd()) {
+ $service = $service . ".service";
+ my $dbus_object = $self->dbus_systemd1_object;
+ if ($enable) {
+ $dbus_object->EnableUnitFiles(dbus_auth_interactive, [$service], 0, 1);
+ }
+ else {
+ $dbus_object->DisableUnitFiles(dbus_auth_interactive, [$service], 0);
+ }
+ # reload local cache
+ $self->_systemd_services(1);
+ } else {
+ my $script = "/etc/rc.d/init.d/$service";
+ $ENV{PATH} = "/usr/bin:/usr/sbin";
+ ManaTools::Shared::RunProgram::rooted("", "/usr/sbin/chkconfig", $enable ? "--add" : "--del", $service);
+ #- FIXME: handle services with no chkconfig line and with no Default-Start levels in LSB header
+ if ($enable && MDK::Common::File::cat_("$script") =~ /^#\s+chkconfig:\s+-/m) {
+ $ENV{PATH} = "/usr/bin:/usr/sbin";
+ ManaTools::Shared::RunProgram::rooted("", "/usr/sbin/chkconfig", "--level", "35", $service, "on");
+ }
+ }
+}
+
+sub _run_action {
+ my ($self, $service, $action) = @_;
+ if ($self->_running_systemd()) {
+ my $object = $self->dbus_systemd1_object;
+ if ($action eq 'start') {
+ $object->StartUnit(dbus_auth_interactive, "$service.service", 'fail');
+ }
+ elsif ($action eq 'stop') {
+ $object->StopUnit(dbus_auth_interactive, "$service.service", 'fail');
+ }
+ else {
+ $object->RestartUnit(dbus_auth_interactive, "$service.service", 'fail');
+ }
+ # reload local cache
+ $self->_systemd_services(1);
+ } else {
+ $ENV{PATH} = "/usr/bin:/usr/sbin:/etc/rc.d/init.d/";
+ ManaTools::Shared::RunProgram::rooted("", "/etc/rc.d/init.d/$service", $action);
+ }
+}
+
+sub _running_systemd {
+ my $self = shift;
+
+ $ENV{PATH} = "/usr/bin:/usr/sbin";
+ ManaTools::Shared::RunProgram::rooted("", '/usr/bin/mountpoint', '-q', '/sys/fs/cgroup/systemd');
+}
+
+sub _has_systemd {
+ my $self = shift;
+
+ $ENV{PATH} = "/usr/bin:/usr/sbin";
+ ManaTools::Shared::RunProgram::rooted("", '/usr/bin/rpm', '-q', 'systemd');
+}
+
+#=============================================================
+
+=head2 xinetd_services
+
+=head3 OUTPUT
+
+ xinetd_services: All the xinetd services
+
+=head3 DESCRIPTION
+
+ This functions returns all the xinetd services in the system.
+ NOTE that xinetd *must* be enable at boot to get this info
+
+=cut
+
+#=============================================================
+sub xinetd_services {
+ my $self = shift;
+
+ my @xinetd_services = ();
+
+ #avoid warning if xinetd is not installed and either enabled
+ my $ser_info = $self->get_service_info('xinetd');
+ if ($ser_info && $ser_info->{enabled} eq "1") {
+ local $ENV{LANGUAGE} = 'C';
+ $ENV{PATH} = "/usr/bin:/usr/sbin";
+ foreach (ManaTools::Shared::RunProgram::rooted_get_stdout("", '/usr/sbin/chkconfig', '--list', '--type', 'xinetd')) {
+ if (my ($xinetd_name, $on_off) = m!^\t(\S+):\s*(on|off)!) {
+ push @xinetd_services, [ $xinetd_name, $on_off eq 'on' ];
+ }
+ }
+ }
+ return @xinetd_services;
+}
+
+sub _systemd_services {
+ my ($self, $reload) = @_;
+
+ if ($reload) {
+ $self->service_info($self->_serviceInfoInitialization());
+ }
+
+ my @services;
+ for my $pair ( $self->service_info_pairs) {
+ my $name = $pair->[0];
+ my $info = $pair->[1];
+ push @services, [$name, $info->{'enabled'}];
+ }
+
+ return @services;
+
+}
+
+sub _legacy_services {
+ my $self = shift;
+
+ local $ENV{LANGUAGE} = 'C';
+ my @services;
+ my $has_systemd = $self->_has_systemd();
+ if ($has_systemd) {
+ # The system not using systemd but will be at next boot. This is
+ # is typically the case in the installer. In this mode we must read
+ # as much as is practicable from the native systemd unit files and
+ # combine that with information from chkconfig regarding legacy sysvinit
+ # scripts (which systemd will parse and include when running)
+ Sys::Syslog::syslog('info|local1', "Detected systemd installed. Using fake service+chkconfig introspection.");
+ foreach (glob_("/usr/lib/systemd/system/*.service")) {
+ my ($name) = m!([^/]*).service$!;
+
+ # We only look at non-template, non-symlinked service files
+ if (!(/.*\@\.service$/g) && ! -l $_) {
+ # Limit ourselves to "standard" targets
+ my $wantedby = MDK::Common::File::cat_($_) =~ /^WantedBy=(graphical|multi-user).target$/sm ? $1 : '';
+ if ($wantedby) {
+ # Exclude if enabled statically
+ # Note DO NOT use -e when testing for files that could
+ # be symbolic links as this will fail under a chroot
+ # setup where -e will fail if the symlink target does
+ # exist which is typically the case when viewed outside
+ # of the chroot.
+ if (!-l "/usr/lib/systemd/system/$wantedby.target.wants/$name.service") {
+ push @services, [ $name, !!-l "/etc/systemd/system/$wantedby.target.wants/$name.service" ];
+ }
+ }
+ }
+ }
+ } else {
+ Sys::Syslog::syslog('info|local1', "Could not detect systemd. Using chkconfig service introspection.");
+ }
+
+ # Regardless of whether we expect to use systemd on next boot, we still
+ # need to instrospect information about non-systemd native services.
+ my $runlevel;
+ my $on_off;
+ if (!$::isInstall) {
+ $runlevel = (split " ", `/sbin/runlevel`)[1];
+ }
+ foreach (ManaTools::Shared::RunProgram::rooted_get_stdout("", '/sbin/chkconfig', '--list', '--type', 'sysv')) {
+ if (my ($name, $l) = m!^(\S+)\s+(0:(on|off).*)!) {
+ # If we expect to use systemd (i.e. installer) only show those
+ # sysvinit scripts which are not masked by a native systemd unit.
+ my $has_systemd_unit = $self->_systemd_unit_exists($name);
+ if (!$has_systemd || !$has_systemd_unit) {
+ if ($::isInstall) {
+ $on_off = $l =~ /\d+:on/g;
+ } else {
+ $on_off = $l =~ /$runlevel:on/g;
+ }
+ push @services, [ $name, $on_off ];
+ }
+ }
+ }
+ @services;
+}
+
+#- returns:
+#--- the listref of installed services
+#--- the listref of "on" services
+#=============================================================
+
+=head2 services
+
+=head3 INPUT
+
+ $reload: load service again
+
+=head3 OUTPUT
+
+ @l: all the system services
+ @on_services: all the services that start at boot
+
+=head3 DESCRIPTION
+
+ This function returns two lists, all the system service and
+ all the active ones.
+
+=cut
+
+#=============================================================
+
+
+sub services {
+ my ($self, $reload) = @_;
+
+ my @Services;
+ if ($self->_running_systemd()) {
+ @Services = $self->_systemd_services($reload);
+ } else {
+ @Services = $self->_legacy_services();
+ }
+
+ my @l = $self->xinetd_services();
+ push @l, @Services;
+ @l = sort { $a->[0] cmp $b->[0] } @l;
+ [ map { $_->[0] } @l ], [ map { $_->[0] } grep { $_->[1] } @l ];
+}
+
+
+# if we loaded service info, then exists
+sub _systemd_unit_exists {
+ my ($self, $name) = @_;
+
+ return defined ($self->get_service_info($name));
+}
+
+#=============================================================
+
+=head2 service_exists
+
+=head3 INPUT
+
+ $service: Service name
+
+=head3 OUTPUT
+
+ 0/1: if the service exists
+
+=head3 DESCRIPTION
+
+ This function checks if a service is installed by looking for
+ its unit or init.d service
+
+=cut
+
+#=============================================================
+
+sub service_exists {
+ my ($self, $service) = @_;
+ $self->_systemd_unit_exists($service) or -x "/etc/rc.d/init.d/$service";
+}
+
+#=============================================================
+
+=head2 restart
+
+=head3 INPUT
+
+ $service: Service to restart
+
+=head3 DESCRIPTION
+
+ This function restarts a given service
+
+=cut
+
+#=============================================================
+
+
+sub restart {
+ my ($self, $service) = @_;
+ # Exit silently if the service is not installed
+ $self->service_exists($service) or return 1;
+ $self->_run_action($service, "restart");
+}
+
+#=============================================================
+
+=head2 restart_or_start
+
+=head3 INPUT
+
+ $service: Service to restart or start
+
+=head3 DESCRIPTION
+
+ This function starts a given service if it is not running,
+ it restarts that otherwise
+
+=cut
+
+#=============================================================
+
+sub restart_or_start {
+ my ($self, $service) = @_;
+ # Exit silently if the service is not installed
+ $self->service_exists($service) or return 1;
+ $self->_run_action($service, $self->is_service_running($service) ? "restart" : "start");
+}
+
+
+#=============================================================
+
+=head2 startService
+
+=head3 INPUT
+
+ $service: Service to start
+
+=head3 DESCRIPTION
+
+ This function starts a given service
+
+=cut
+
+#=============================================================
+
+sub startService {
+ my ($self, $service) = @_;
+ # Exit silently if the service is not installed
+ $self->service_exists($service) or return 1;
+ $self->_run_action($service, "start");
+}
+
+#=============================================================
+
+=head2 start_not_running_service
+
+=head3 INPUT
+
+ $service: Service to start
+
+=head3 DESCRIPTION
+
+ This function starts a given service if not running
+
+=cut
+
+#=============================================================
+
+sub start_not_running_service {
+ my ($self, $service) = @_;
+ # Exit silently if the service is not installed
+ $self->service_exists($service) or return 1;
+ $self->is_service_running($service) || $self->_run_action($service, "start");
+}
+
+#=============================================================
+
+=head2 stopService
+
+=head3 INPUT
+
+ $service: Service to stop
+
+=head3 DESCRIPTION
+
+ This function stops a given service
+
+=cut
+
+#=============================================================
+sub stopService {
+ my ($self, $service) = @_;
+ # Exit silently if the service is not installed
+ $self->service_exists($service) or return 1;
+ $self->_run_action($service, "stop");
+}
+
+#=============================================================
+
+=head2 is_service_running
+
+=head3 INPUT
+
+ $service: Service to check
+
+=head3 DESCRIPTION
+
+ This function returns if the given service is running
+
+=cut
+
+#=============================================================
+
+sub is_service_running {
+ my ($self, $service) = @_;
+ # Exit silently if the service is not installed
+ $self->service_exists($service) or return 0;
+ my $out;
+ if ($self->_running_systemd()) {
+ my $ser_info = $self->get_service_info($service);
+ $out = $ser_info->{active_state} eq 'active' if $ser_info->{active_state};
+ } else {
+ $ENV{PATH} = "/usr/bin:/usr/sbin";
+ $out = ManaTools::Shared::RunProgram::rooted("", '/usr/sbin/service', $service, 'status');
+ }
+ return $out;
+}
+
+#=============================================================
+
+=head2 starts_on_boot
+
+=head3 INPUT
+
+ $service: Service name
+
+
+=head3 DESCRIPTION
+
+ This function returns if the given service starts at boot
+
+=cut
+
+#=============================================================
+sub starts_on_boot {
+ my ($self, $service) = @_;
+ my (undef, $on_services) = $self->services();
+ MDK::Common::DataStructure::member($service, @$on_services);
+}
+
+#=============================================================
+
+=head2 start_service_on_boot
+
+=head3 INPUT
+
+ $service: Service name
+
+
+=head3 DESCRIPTION
+
+ This function set the given service active at boot
+
+=cut
+
+#=============================================================
+sub start_service_on_boot {
+ my ($self, $service) = @_;
+ $self->set_service($service, 1);
+}
+
+#=============================================================
+
+=head2 do_not_start_service_on_boot
+
+=head3 INPUT
+
+ $service: Service name
+
+
+=head3 DESCRIPTION
+
+ This function set the given service disabled at boot
+
+=cut
+
+#=============================================================
+sub do_not_start_service_on_boot {
+ my ($self, $service) = @_;
+ $self->set_service($service, 0);
+}
+
+#=============================================================
+
+=head2 enable
+
+=head3 INPUT
+
+ $service: Service name
+ $o_dont_apply: do not start it now
+
+=head3 DESCRIPTION
+
+ This function set the given service active at boot
+ and restarts it if o_dont_apply is not given
+
+=cut
+
+#=============================================================
+sub enable {
+ my ($self, $service, $o_dont_apply) = @_;
+ $self->start_service_on_boot($service);
+ $self->restart_or_start($service) unless $o_dont_apply;
+}
+
+#=============================================================
+
+=head2 disable
+
+=head3 INPUT
+
+ $service: Service name
+ $o_dont_apply: do not stop it now
+
+=head3 DESCRIPTION
+
+ This function set the given service disabled at boot
+ and stops it if o_dont_apply is not given
+
+=cut
+
+#=============================================================
+sub disable {
+ my ($self, $service, $o_dont_apply) = @_;
+ $self->do_not_start_service_on_boot($service);
+ $self->stopService($service) unless $o_dont_apply;
+}
+
+#=============================================================
+
+=head2 set_status
+
+=head3 INPUT
+
+ $service: Service name
+ $enable: Enable/disable
+ $o_dont_apply: do not start it now
+
+=head3 DESCRIPTION
+
+ This function set the given service to enable/disable at boot
+ and restarts/stops it if o_dont_apply is not given
+
+=cut
+
+#=============================================================
+sub set_status {
+ my ($self, $service, $enable, $o_dont_apply) = @_;
+ if ($enable) {
+ $self->enable($service, $o_dont_apply);
+ } else {
+ $self->disable($service, $o_dont_apply);
+ }
+}
+
+# NOTE $service->get_object("/org/freedesktop/systemd1/unit/$name_2eservice");
+# has empty WantedBy property if disabled
+sub _WantedBy {
+ my ($self, $path_service) = @_;
+
+ my $wantedby = MDK::Common::File::cat_($path_service) =~ /^WantedBy=(graphical|multi-user).target$/sm ? $1 : '';
+
+ return $wantedby;
+}
+
+#=============================================================
+
+=head2 getUnitProperty
+
+=head3 INPUT
+
+ $unit: unit name
+ $property: property name
+
+=head3 OUTPUT
+
+ $property_value: property value
+
+=head3 DESCRIPTION
+
+ This method returns the requested property value
+
+=cut
+
+#=============================================================
+sub getUnitProperty {
+ my ($self, $unit, $property) = @_;
+
+ my $name = $unit . ".service";
+ $name =~ s|-|_2d|g;
+ $name =~ s|\.|_2e|g;
+ my $service = $self->dbus_systemd1_service;
+ my $unit_object = $service->get_object("/org/freedesktop/systemd1/unit/" . $name);
+ my $property_value = eval {$unit_object->Get("org.freedesktop.systemd1.Unit", $property)} || "";
+
+ return $property_value;
+}
+
+1;
diff --git a/lib/ManaTools/Shared/Shorewall.pm b/lib/ManaTools/Shared/Shorewall.pm
new file mode 100644
index 0000000..f82c542
--- /dev/null
+++ b/lib/ManaTools/Shared/Shorewall.pm
@@ -0,0 +1,271 @@
+package ManaTools::Shared::Shorewall; # $Id: shorewall.pm 254244 2009-03-18 22:54:32Z eugeni $
+
+use detect_devices;
+use network::network;
+use ManaTools::Shared::RunProgram;
+use ManaTools::Shared::Services;
+use MDK::Common::Func qw(if_ partition map_each);
+use MDK::Common::File qw(cat_ substInFile output_with_perm);
+use MDK::Common::Various qw(to_bool);
+use MDK::Common::DataStructure qw(is_empty_array_ref);
+use List::Util qw(any);
+use List::MoreUtils qw(uniq);
+use log;
+
+my $shorewall_root = "/etc/shorewall";
+
+sub check_iptables() {
+ -f "$::prefix/etc/sysconfig/iptables" ||
+ $::isStandalone && do {
+ system('modprobe iptable_nat');
+ -x '/sbin/iptables' && listlength(`/sbin/iptables -t nat -nL`) > 8;
+ };
+}
+
+sub set_config_file {
+ my ($file, $ver, @l) = @_;
+
+ my $done;
+ substInFile {
+ my $last_line = /^#LAST LINE/ && $_;
+ if (!$done && ($last_line || eof)) {
+ $_ = join('', map { join("\t", @$_) . "\n" } @l);
+ $_ .= $last_line if $last_line;
+ $done = 1;
+ } else {
+ $_ = '' unless
+ /^#/ || $file eq 'rules' && /^SECTION/;
+ }
+ } "$::prefix${shorewall_root}${ver}/$file";
+}
+
+sub get_config_file {
+ my ($file, $o_ver) = @_;
+ map { [ split ' ' ] } grep { !/^#/ } cat_("$::prefix${shorewall_root}${o_ver}/$file");
+}
+
+# Note: Called from drakguard and drakfirewall.pm...
+# Deliberately not adding shorewall6 support here for now
+sub set_in_file {
+ my ($file, $enabled, @list) = @_;
+ my $done;
+ substInFile {
+ my $last_line = /^#LAST LINE/ && $_;
+ foreach my $l (@list) { s|^$l\n|| }
+ if (!$done && $enabled && ($last_line || eof)) {
+ $_ = join('', map { "$_\n" } @list);
+ $_ .= $last_line if $last_line;
+ $done = 1;
+ }
+ } "$::prefix${shorewall_root}/$file";
+}
+
+sub dev_to_shorewall {
+ my ($dev) = @_;
+ $dev =~ /^ippp/ && "ippp+" ||
+ $dev =~ /^ppp/ && "ppp+" ||
+ $dev;
+}
+
+sub get_net_zone_interfaces {
+ my ($interfacesfile, $_net, $all_intf) = @_;
+ if(ref($interfacesfile) eq "ARRAY")
+ {
+ #- read shorewall configuration first
+ my @interfaces = map { $_->[1] } grep { $_->[0] eq 'net' } $interfacesfile;
+ }
+ else
+ {
+ my @interfaces = undef;
+ }
+ #- else try to find the best interface available
+ @interfaces ? @interfaces : @{$all_intf || []};
+}
+
+sub add_interface_to_net_zone {
+ my ($conf, $interface) = @_;
+ if (!member($interface, @{$conf->{net_zone}})) {
+ push @{$conf->{net_zone}}, $interface;
+ @{$conf->{loc_zone}} = grep { $_ ne $interface } @{$conf->{loc_zone}};
+ }
+}
+
+sub read_ {
+ my ($o_ver) = @_;
+ my $ver = '';
+ $ver = $o_ver if $o_ver;
+ #- read old rules file if config is not moved to rules.drakx yet
+ my @rules = get_config_file(-f "$::prefix${shorewall_root}${ver}/rules.drakx" ? 'rules.drakx' : 'rules', $ver);
+ my $services = ManaTools::Shared::Services->new();
+ my %conf = (disabled => !$services->starts_on_boot("shorewall${ver}"),
+ version => $ver,
+ ports => join(' ', map {
+ my $e = $_;
+ map { "$_/$e->[3]" } split(',', $e->[4]);
+ } grep { $_->[0] eq 'ACCEPT' && $_->[1] eq 'net' } @rules),
+ );
+ push @{$conf{accept_local_users}{$_->[4]}}, $_->[8] foreach grep { $_->[0] eq 'ACCEPT+' } @rules;
+ $conf{redirects}{$_->[3]}{$_->[4]} = $_->[2] foreach grep { $_->[0] eq 'REDIRECT' } @rules;
+
+ if (my ($e) = get_config_file('masq', $ver)) {
+ ($conf{masq}{net_interface}, $conf{masq}{subnet}) = @$e;
+ }
+
+ my @policy = get_config_file('policy', $ver);
+ $conf{log_net_drop} = @policy ? (any { $_->[0] eq 'net' && $_->[1] eq 'all' && $_->[2] eq 'DROP' && $_->[3] } @policy) : 1;
+
+ return \%conf;
+
+ # get_zones has been moved to ManaTools::Module::Firewall cause it requires
+ # user interaction thus it should be logically separated by shorewall
+ # get_zones(\%conf);
+ # get_config_file('zones', $ver) && \%conf;
+ # consequently, to read shorewall conf
+ # you have to do something like this now (within Module::Firewall)
+ # my $conf = ManaTools::Shared::Shorewall::read_();
+ # OPTIONAL: my $self->get_zones(\$conf)
+ # my $shorewall = ManaTools::Shared::Shorewall::get_config_file('zones', '') && $conf;
+}
+
+sub ports_by_proto {
+ my ($ports) = @_;
+ my %ports_by_proto;
+ foreach (split ' ', $ports) {
+ m!^(\d+(?::\d+)?)/(udp|tcp|icmp)$! or die "bad port $_\n";
+ push @{$ports_by_proto{$2}}, $1;
+ }
+ \%ports_by_proto;
+}
+
+#=============================================================
+
+=head2 write_
+
+=head3 INPUT
+
+ $conf: HASH, contains the configuration to write
+
+ $action: Str, possible values are "keep" or "drop"
+
+=head3 OUTPUT
+
+ 0: requires user interaction
+ 1: everything has been done
+
+=head3 DESCRIPTION
+
+This function stores the configuration for shorewall inside
+the proper files.
+
+=head3 NOTES
+
+if write_ is called without the $action parameter it can return 0
+(i.e. user interaction requested) when the firewall configuration
+has been manually changed.
+
+In that case the developer will have to handle this request by providing
+two choices within the domain (keep | drop) and then recall write_ with
+the choosen behaviour.
+
+=cut
+
+#=============================================================
+
+sub write_ {
+ my ($conf, $action) = @_;
+ my $ver = $conf->{version} || '';
+ my $use_pptp = any { /^ppp/ && cat_("$::prefix/etc/ppp/peers/$_") =~ /pptp/ } @{$conf->{net_zone}};
+ my $ports_by_proto = ports_by_proto($conf->{ports});
+ my $has_loc_zone = to_bool(@{$conf->{loc_zone} || []});
+
+ my ($include_drakx, $other_rules) = partition { $_ eq "INCLUDE\trules.drakx\n" } grep { !/^(#|SECTION)/ } cat_("$::prefix${shorewall_root}${ver}/rules");
+ #- warn if the config is already in rules.drakx and additionnal rules are configured
+ if (!is_empty_array_ref($include_drakx) && !is_empty_array_ref($other_rules)) {
+ if(!defined($action) || ManaTools::Shared::trim($action) eq "")
+ {
+ return 0; # user interaction requested
+ }
+ my %actions = (
+ keep => N("Keep custom rules"),
+ drop => N("Drop custom rules"),
+ );
+ #- reset the rules files if the user has chosen to drop modifications
+ undef $include_drakx if $action eq 'drop';
+ }
+
+ my $interface_settings = sub {
+ my ($zone, $interface) = @_;
+ [ $zone, $interface, 'detect', if_(detect_devices::is_bridge_interface($interface), 'bridge') ];
+ };
+
+ set_config_file('zones', $ver,
+ if_($has_loc_zone, [ 'loc', 'ipv' . ($ver || '4') ]),
+ [ 'net', 'ipv' . ($ver || '4') ],
+ [ 'fw', 'firewall' ],
+ );
+ set_config_file('interfaces', $ver,
+ (map { $interface_settings->('net', $_) } @{$conf->{net_zone}}),
+ (map { $interface_settings->('loc', $_) } @{$conf->{loc_zone} || []}),
+ );
+ set_config_file('policy', $ver,
+ if_($has_loc_zone, [ 'loc', 'net', 'ACCEPT' ], [ 'loc', 'fw', 'ACCEPT' ], [ 'fw', 'loc', 'ACCEPT' ]),
+ [ 'fw', 'net', 'ACCEPT' ],
+ [ 'net', 'all', 'DROP', if_($conf->{log_net_drop}, 'info') ],
+ [ 'all', 'all', 'REJECT', 'info' ],
+ );
+ if (is_empty_array_ref($include_drakx)) {
+ #- make sure the rules.drakx config is read, erasing user modifications
+ set_config_file('rules', $ver, [ 'INCLUDE', 'rules.drakx' ]);
+ }
+ output_with_perm("$::prefix${shorewall_root}${ver}/" . 'rules.drakx', 0600, map { join("\t", @$_) . "\n" } (
+ if_($use_pptp, [ 'ACCEPT', 'fw', 'loc:10.0.0.138', 'tcp', '1723' ]),
+ if_($use_pptp, [ 'ACCEPT', 'fw', 'loc:10.0.0.138', 'gre' ]),
+ (map_each { [ 'ACCEPT', 'net', 'fw', $::a, join(',', @$::b), '-' ] } %$ports_by_proto),
+ (map_each {
+ if_($::b, map { [ 'ACCEPT+', 'fw', 'net', 'tcp', $::a, '-', '-', '-', $_ ] } @$::b);
+ } %{$conf->{accept_local_users}}),
+ (map {
+ my $proto = $_;
+ #- WARNING: won't redirect ports from the firewall system if a local zone exists
+ #- set redirect_fw_only to workaround
+ map_each {
+ map { [ 'REDIRECT', $_, $::b, $proto, $::a, '-' ] } 'fw', if_($has_loc_zone, 'loc');
+ } %{$conf->{redirects}{$proto}};
+ } keys %{$conf->{redirects}}),
+ ));
+ set_config_file('masq', $ver, if_(exists $conf->{masq}, [ $conf->{masq}{net_interface}, $conf->{masq}{subnet} ]));
+
+ my $services = ManaTools::Shared::Services->new();
+ if ($conf->{disabled}) {
+ $services->disable('shorewall', $::isInstall);
+ run_program::rooted($::prefix, '/sbin/shorewall', 'clear') unless $::isInstall;
+ } else {
+ $services->enable('shorewall', $::isInstall);
+ }
+ return 1;
+}
+
+sub set_redirected_ports {
+ my ($conf, $proto, $dest, @ports) = @_;
+ if (@ports) {
+ $conf->{redirects}{$proto}{$_} = $dest foreach @ports;
+ } else {
+ my $r = $conf->{redirects}{$proto};
+ @ports = grep { $r->{$_} eq $dest } keys %$r;
+ delete $r->{$_} foreach @ports;
+ }
+}
+
+sub update_interfaces_list {
+ my ($o_intf) = @_;
+ if (!$o_intf || !member($o_intf, map { $_->[1] } get_config_file('interfaces'))) {
+ my $shorewall = ManaTools::Shared::Shorewall::read_();
+ $shorewall && !$shorewall->{disabled} and ManaTools::Shared::Shorewall::write_($shorewall);
+ }
+ if (!$o_intf || !member($o_intf, map { $_->[1] } get_config_file('interfaces', 6))) {
+ my $shorewall6 = ManaTools::Shared::Shorewall::read_(undef, 6);
+ $shorewall6 && !$shorewall6->{disabled} and ManaTools::Shared::Shorewall::write_($shorewall6);
+ }
+}
+
+1;
diff --git a/lib/ManaTools/Shared/TimeZone.pm b/lib/ManaTools/Shared/TimeZone.pm
new file mode 100644
index 0000000..efb49a2
--- /dev/null
+++ b/lib/ManaTools/Shared/TimeZone.pm
@@ -0,0 +1,799 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Shared::TimeZone;
+
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ManaTools::Shared::TimeZone - module to manage TimeZone settings
+
+=head1 SYNOPSIS
+
+ my $tz = ManaTools::Shared::TimeZone->new();
+
+
+=head1 DESCRIPTION
+
+This module allows to manage time zone settings.
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command:
+
+perldoc ManaTools::Shared::TimeZone
+
+
+=head1 AUTHOR
+
+Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2014-2015, Angelo Naselli.
+
+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.
+
+=head1 METHODS
+
+=cut
+
+
+use diagnostics;
+use strict;
+
+use Moose;
+
+use DateTime::TimeZone;
+use Net::DBus;
+
+use ManaTools::Shared::Locales;
+use ManaTools::Shared::Services;
+
+use MDK::Common::File qw(cat_ output_p substInFile);
+use MDK::Common::Func qw(find if_);
+
+
+#=============================================================
+
+=head2 new - optional parameters
+
+=head3 timezone_prefix
+
+ optional parameter to set the system timezone directory,
+ default value is /usr/share/zoneinfo
+
+=cut
+
+#=============================================================
+
+has 'timezone_prefix' => (
+ is => 'rw',
+ isa => 'Str',
+ default => "/usr/share/zoneinfo",
+);
+
+
+#=============================================================
+
+=head2 new - optional parameters
+
+=head3 ntp_configuration_file
+
+ optional parameter to set the ntp server configuration file,
+ default value is /etc/[chrony|ntp].conf
+
+=cut
+
+#=============================================================
+
+has 'ntp_configuration_file' => (
+ is => 'rw',
+ isa => 'Str',
+ builder => '_ntp_configuration_file_init',
+);
+
+sub _ntp_configuration_file_init {
+ my $self = shift;
+
+ if (-f "/etc/chrony.conf") {
+ return "/etc/chrony.conf";
+ }
+ return "/etc/ntp.conf";
+}
+
+#=============================================================
+
+=head2 new - optional parameters
+
+=head3 ntp_conf_dir
+
+ optional parameter to set ntp configuration directory,
+ default value is /etc/ntp
+
+=cut
+
+#=============================================================
+
+has 'ntp_conf_dir' => (
+ is => 'rw',
+ isa => 'Str',
+ lazy => 1,
+ default => "/etc/ntp",
+);
+
+#=============================================================
+
+=head2 new - optional parameters
+
+=head3 ntp_program
+
+ optional parameter to set the ntp program that runs into the
+ system, default value is [chrony|ntp]
+
+=cut
+
+#=============================================================
+has 'ntp_program' => (
+ is => 'rw',
+ isa => 'Str',
+ builder => '_ntp_program_init',
+);
+
+sub _ntp_program_init {
+ my $self = shift;
+
+ if (-f "/etc/chrony.conf") {
+ return "chrony";
+ }
+ return "ntp";
+}
+
+#=============================================================
+
+=head2 new - optional parameters
+
+=head3 installer_or_livecd
+
+ To inform the back-end that is working during installer or
+ livecd. Useful if Time zone setting and using fix_system
+ to use the real time clock (see setLocalRTC and
+ writeConfiguration).
+
+=cut
+
+#=============================================================
+has 'installer_or_livecd' => (
+ is => 'rw',
+ isa => 'Bool',
+ default => 0,
+);
+
+#=== globals ===
+
+has 'sh_services' => (
+ is => 'rw',
+ init_arg => undef,
+ lazy => 1,
+ builder => '_SharedServicesInitialize'
+);
+
+sub _SharedServicesInitialize {
+ my $self = shift();
+
+ $self->sh_services(ManaTools::Shared::Services->new() );
+}
+
+
+has 'dbus_timedate1_service' => (
+ is => 'rw',
+ init_arg => undef,
+ lazy => 1,
+ builder => '_dbusTimeDateInitialize'
+);
+
+sub _dbusTimeDateInitialize {
+ my $self = shift();
+
+ my $bus = Net::DBus->system;
+ $self->dbus_timedate1_service($bus->get_service("org.freedesktop.timedate1"));
+}
+
+
+has 'dbus_timedate1_object' => (
+ is => 'rw',
+ init_arg => undef,
+ lazy => 1,
+ builder => '_dbusObjectInitialize'
+);
+
+sub _dbusObjectInitialize {
+ my $self = shift();
+
+ $self->dbus_timedate1_object($self->dbus_timedate1_service->get_object("/org/freedesktop/timedate1"));
+}
+
+
+has 'servername_config_suffix' => (
+ is => 'ro',
+ isa => 'Str',
+ lazy => 1,
+ builder => '_servername_config_suffix_init',
+);
+
+sub _servername_config_suffix_init {
+ my $self = shift;
+
+ return " iburst" if ($self->ntp_program eq "chrony");
+
+ return "";
+}
+
+has 'loc' => (
+ is => 'rw',
+ lazy => 1,
+ init_arg => undef,
+ builder => '_localeInitialize'
+);
+
+sub _localeInitialize {
+ my $self = shift;
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'libDrakX') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+
+has 'ntp_servers' => (
+ traits => ['Hash'],
+ is => 'rw',
+ isa => 'HashRef',
+ lazy => 1,
+ handles => {
+ get_ntp_server => 'get',
+ ntp_server_pairs => 'kv',
+ },
+ init_arg => undef,
+ builder => '_buildNTPServers'
+);
+
+sub _buildNTPServers {
+ my $self = shift;
+
+ my %ntpServersHash;
+ $ntpServersHash{"-"} = {
+ $self->loc->N_("Global") => "pool.ntp.org",
+ };
+ $ntpServersHash{Global} = {
+ $self->loc->N_("Africa") => "africa.pool.ntp.org",
+ $self->loc->N_("Asia") => "asia.pool.ntp.org",
+ $self->loc->N_("Europe") => "europe.pool.ntp.org",
+ $self->loc->N_("North America") => "north-america.pool.ntp.org",
+ $self->loc->N_("Oceania") => "oceania.pool.ntp.org",
+ $self->loc->N_("South America") => "south-america.pool.ntp.org",
+ };
+ $ntpServersHash{Africa} = {
+ $self->loc->N_("South Africa") => "za.pool.ntp.org",
+ $self->loc->N_("Tanzania") => "tz.pool.ntp.org",
+ };
+ $ntpServersHash{Asia} = {
+ $self->loc->N_("Bangladesh") => "bd.pool.ntp.org",
+ $self->loc->N_("China") => "cn.pool.ntp.org",
+ $self->loc->N_("Hong Kong") => "hk.pool.ntp.org",
+ $self->loc->N_("India") => "in.pool.ntp.org",
+ $self->loc->N_("Indonesia") => "id.pool.ntp.org",
+ $self->loc->N_("Iran") => "ir.pool.ntp.org",
+ $self->loc->N_("Israel") => "il.pool.ntp.org",
+ $self->loc->N_("Japan") => "jp.pool.ntp.org",
+ $self->loc->N_("Korea") => "kr.pool.ntp.org",
+ $self->loc->N_("Malaysia") => "my.pool.ntp.org",
+ $self->loc->N_("Philippines") => "ph.pool.ntp.org",
+ $self->loc->N_("Singapore") => "sg.pool.ntp.org",
+ $self->loc->N_("Taiwan") => "tw.pool.ntp.org",
+ $self->loc->N_("Thailand") => "th.pool.ntp.org",
+ $self->loc->N_("Turkey") => "tr.pool.ntp.org",
+ $self->loc->N_("United Arab Emirates") => "ae.pool.ntp.org",
+ };
+ $ntpServersHash{Europe} = {
+ $self->loc->N_("Austria") => "at.pool.ntp.org",
+ $self->loc->N_("Belarus") => "by.pool.ntp.org",
+ $self->loc->N_("Belgium") => "be.pool.ntp.org",
+ $self->loc->N_("Bulgaria") => "bg.pool.ntp.org",
+ $self->loc->N_("Czech Republic") => "cz.pool.ntp.org",
+ $self->loc->N_("Denmark") => "dk.pool.ntp.org",
+ $self->loc->N_("Estonia") => "ee.pool.ntp.org",
+ $self->loc->N_("Finland") => "fi.pool.ntp.org",
+ $self->loc->N_("France") => "fr.pool.ntp.org",
+ $self->loc->N_("Germany") => "de.pool.ntp.org",
+ $self->loc->N_("Greece") => "gr.pool.ntp.org",
+ $self->loc->N_("Hungary") => "hu.pool.ntp.org",
+ $self->loc->N_("Ireland") => "ie.pool.ntp.org",
+ $self->loc->N_("Italy") => "it.pool.ntp.org",
+ $self->loc->N_("Lithuania") => "lt.pool.ntp.org",
+ $self->loc->N_("Luxembourg") => "lu.pool.ntp.org",
+ $self->loc->N_("Netherlands") => "nl.pool.ntp.org",
+ $self->loc->N_("Norway") => "no.pool.ntp.org",
+ $self->loc->N_("Poland") => "pl.pool.ntp.org",
+ $self->loc->N_("Portugal") => "pt.pool.ntp.org",
+ $self->loc->N_("Romania") => "ro.pool.ntp.org",
+ $self->loc->N_("Russian Federation") => "ru.pool.ntp.org",
+ $self->loc->N_("Slovakia") => "sk.pool.ntp.org",
+ $self->loc->N_("Slovenia") => "si.pool.ntp.org",
+ $self->loc->N_("Spain") => "es.pool.ntp.org",
+ $self->loc->N_("Sweden") => "se.pool.ntp.org",
+ $self->loc->N_("Switzerland") => "ch.pool.ntp.org",
+ $self->loc->N_("Ukraine") => "ua.pool.ntp.org",
+ $self->loc->N_("United Kingdom") => "uk.pool.ntp.org",
+ $self->loc->N_("Yugoslavia") => "yu.pool.ntp.org",
+ };
+ $ntpServersHash{"North America"} = {
+ $self->loc->N_("Canada") => "ca.pool.ntp.org",
+ $self->loc->N_("Guatemala") => "gt.pool.ntp.org",
+ $self->loc->N_("Mexico") => "mx.pool.ntp.org",
+ $self->loc->N_("United States") => "us.pool.ntp.org",
+ };
+ $ntpServersHash{Oceania} = {
+ $self->loc->N_("Australia") => "au.pool.ntp.org",
+ $self->loc->N_("New Zealand") => "nz.pool.ntp.org",
+ };
+ $ntpServersHash{"South America"} = {
+ $self->loc->N_("Argentina") => "ar.pool.ntp.org",
+ $self->loc->N_("Brazil") => "br.pool.ntp.org",
+ $self->loc->N_("Chile") => "cl.pool.ntp.org",
+ };
+
+ return \%ntpServersHash;
+}
+
+
+#=============================================================
+
+=head2 get_timezone_prefix
+
+=head3 OUTPUT
+
+timezone_prefix: directory in which time zone files are
+
+=head3 DESCRIPTION
+
+Return the timezone directory (defualt: /usr/share/zoneinfo)
+
+=cut
+
+#=============================================================
+sub get_timezone_prefix {
+ my $self = shift;
+
+ return $self->timezone_prefix;
+}
+
+#=============================================================
+
+=head2 getTimeZones
+
+=head3 INPUT
+
+ $from_system: if present and its value is not 0 checks into timezone_prefix
+ directory and gets the list from there
+
+=head3 OUTPUT
+
+ @l: ARRAY containing sorted time zones
+
+=head3 DESCRIPTION
+
+ This method returns the available timezones
+
+=cut
+
+#=============================================================
+sub getTimeZones {
+ my ($self, $from_system) = @_;
+
+ if ($from_system and $from_system != 0) {
+ require MDK::Common::DataStructure;
+ require MDK::Common::Various;
+ my $tz_prefix = $self->get_timezone_prefix();
+ open(my $F, "cd $tz_prefix && find [A-Z]* -noleaf -type f |");
+ my @l = MDK::Common::DataStructure::difference2([ MDK::Common::Various::chomp_(<$F>) ], [ 'ROC', 'PRC' ]);
+ close $F or die "cannot list the available zoneinfos";
+ return sort @l;
+ }
+
+ return DateTime::TimeZone->all_names;
+}
+
+#=============================================================
+
+=head2 setTimeZone
+
+=head3 INPUT
+
+ $new_time_zone: New time zone to be set
+
+=head3 DESCRIPTION
+
+ This method get the new time zone to set and performs
+ the setting
+
+=cut
+
+#=============================================================
+sub setTimeZone {
+ my ($self, $new_time_zone) = @_;
+
+ die "Time zone value required" if !defined($new_time_zone);
+
+ my $object = $self->dbus_timedate1_object;
+ $object->SetTimezone($new_time_zone, 1);
+}
+
+#=============================================================
+
+=head2 getTimeZone
+
+=head3 OUTPUT
+
+ $timezone: current time zone
+
+=head3 DESCRIPTION
+
+ This method returns the current timezone setting
+
+=cut
+
+#=============================================================
+sub getTimeZone {
+ my ($self) = @_;
+
+ my $object = $self->dbus_timedate1_object;
+
+ return $object->Get("org.freedesktop.timedate1", 'Timezone') || "";
+}
+
+
+#=============================================================
+
+=head2 setLocalRTC
+
+=head3 INPUT
+
+ $enable: bool value enable/disable real time clock as
+ localtime
+ $fix_system: bool read or not the real time clock
+
+=head3 DESCRIPTION
+
+ This method enables/disables the real time clock as
+ localtime (e.g. disable means set the rtc to UTC).
+ NOTE from dbus:
+ Use SetLocalRTC() to control whether the RTC is in
+ local time or UTC. It is strongly recommended to maintain
+ the RTC in UTC. Some OSes (Windows) however maintain the
+ RTC in local time which might make it necessary to enable
+ this feature. However, this creates various problems as
+ daylight changes might be missed. If fix_system is passed
+ "true" the time from the RTC is read again and the system
+ clock adjusted according to the new setting.
+ If fix_system is passed "false" the system time is written
+ to the RTC taking the new setting into account.
+ Use fix_system=true in installers and livecds where the
+ RTC is probably more reliable than the system time.
+ Use fix_system=false in configuration UIs that are run during
+ normal operation and where the system clock is probably more
+ reliable than the RTC.
+
+=cut
+
+#=============================================================
+sub setLocalRTC {
+ my ($self, $enable, $fix_system) = @_;
+
+ die "Localtime enable/disable value required" if !defined($enable);
+
+ $fix_system = 0 if !defined($fix_system);
+ my $object = $self->dbus_timedate1_object;
+ $object->SetLocalRTC($enable, $fix_system, 1) ;
+}
+
+#=============================================================
+
+=head2 getLocalRTC
+
+=head3 OUTPUT
+
+ $localRTC: 1 if RTC is localtime 0 for UTC
+
+=head3 DESCRIPTION
+
+ This method returns the RTC localtime setting
+
+=cut
+
+#=============================================================
+sub getLocalRTC {
+ my $self = shift;
+
+ my $object = $self->dbus_timedate1_object;
+
+ return $object->Get("org.freedesktop.timedate1", 'LocalRTC') ? 1 : 0;
+}
+
+
+#=============================================================
+
+=head2 setTime
+
+=head3 INPUT
+
+ $sec_since_epoch: Time in seconds since 1/1/1970
+
+=head3 DESCRIPTION
+
+ This method set the system time and sets the RTC also
+
+=cut
+
+#=============================================================
+sub setTime {
+ my ($self, $sec_since_epoch) = @_;
+
+ die "second since epoch required" if !defined($sec_since_epoch);
+
+ my $object = $self->dbus_timedate1_object;
+ my $usec = $sec_since_epoch* 1000000;
+
+ $object->SetTime($usec, 0, 1);
+}
+
+#=============================================================
+
+=head2 readConfiguration
+
+=head3 OUTPUT
+
+ hash reference containing:
+ UTC => HW clock is set as UTC
+ ZONE => Time Zone set
+
+=head3 DESCRIPTION
+
+ This method returns the time zone system settings as hash
+ reference
+
+=cut
+
+#=============================================================
+sub readConfiguration {
+ my $self = shift;
+
+ my $prefs = {};
+ $prefs->{'ZONE'} = $self->getTimeZone();
+ $prefs->{'UTC'} = $self->getLocalRTC() ? 0 : 1;
+
+ return $prefs;
+}
+
+
+#=============================================================
+
+=head2 writeConfiguration
+
+=head3 INPUT
+
+ $info: hash containing:
+ UTC => HW clock is set as UTC
+ ZONE => Time Zone
+
+=head3 DESCRIPTION
+
+ This method sets the passed Time Zone configuration.
+ If installer_or_livecd attribute is set fix_system is
+ passed to setLocalRTC
+
+=cut
+
+#=============================================================
+sub writeConfiguration {
+ my ($self, $info) = @_;
+
+ die "UTC field required" if !defined($info->{UTC});
+ die "ZONE field required" if !defined($info->{ZONE});
+
+ my $localRTC = $info->{UTC} ? 0 : 1;
+ $self->setLocalRTC(
+ $localRTC,
+ $self->installer_or_livecd
+ );
+
+ $self->setTimeZone(
+ $info->{ZONE}
+ );
+}
+
+
+#left for back compatibility
+sub _get_ntp_server_tree {
+ my ($self, $zone) = @_;
+ $zone = "-" if ! $zone;
+ my $ns = $self->get_ntp_server($zone);
+ return if !$ns;
+
+ map {
+ $ns->{$_} => (
+ $self->get_ntp_server($_) ?
+ $zone ?
+ $self->loc->N($_) . "|" . $self->loc->N("All servers") :
+ $self->loc->N("All servers") :
+ $self->loc->N($zone) . "|" . $self->loc->N($_)
+ ),
+ $self->_get_ntp_server_tree($_)
+ } keys %{$ns};
+}
+
+#=============================================================
+
+=head2 ntpServers
+
+=head3 OUTPUT
+
+ HASHREF containing ntp_server => zone info
+
+=head3 DESCRIPTION
+
+ This method returns an hash ref containing pairs ntp-server, zone
+
+=cut
+
+#=============================================================
+sub ntpServers {
+ my ($self) = @_;
+ # FIXME: missing parameter:
+ +{$self->_get_ntp_server_tree()};
+}
+
+
+#=============================================================
+
+=head2 ntpCurrentServer
+
+=head3 INPUT
+
+Input_Parameter: in_par_description
+
+=head3 DESCRIPTION
+
+Returns the current ntp server address read from configuration file
+
+=cut
+
+#=============================================================
+
+sub ntpCurrentServer {
+ my $self = shift;
+
+ MDK::Common::Func::find { $_ ne '127.127.1.0' } map { MDK::Common::Func::if_(/^\s*server\s+(\S*)/, $1) } MDK::Common::File::cat_($self->ntp_configuration_file);
+}
+
+#=============================================================
+
+=head2 isNTPRunning
+
+=head3 DESCRIPTION
+
+ This method just returns if the given ntp server is running
+
+=cut
+
+#=============================================================
+sub isNTPRunning {
+ my $self = shift;
+
+ # TODO is that valid for any ntp program? adding ntp_service_name parameter
+ my $ntpd = $self->ntp_program . 'd';
+
+ return $self->sh_services->is_service_running($ntpd);
+}
+
+#=============================================================
+
+=head2 setNTPServer
+
+=head3 INPUT
+
+$server: server address to be configured
+
+=head3 DESCRIPTION
+
+This method writes into NTP configuration file new server address
+settings
+
+=cut
+
+#=============================================================
+sub setNTPServer {
+ my ($self, $server) = @_;
+
+ my $f = $self->ntp_configuration_file;
+ -f $f or return;
+ return if (!$server);
+
+ # TODO is that valid for any ntp program? adding ntp_service_name parameter
+ my $ntpd = $self->ntp_program . 'd';
+
+ ManaTools::Shared::disable_x_screensaver();
+ if ($self->isNTPRunning()) {
+ $self->sh_services->stopService($ntpd);
+ }
+
+ my $pool_match = qr/\.pool\.ntp\.org$/;
+ my @servers = $server =~ $pool_match ? (map { "$_.$server" } 0 .. 2) : $server;
+
+ my $added = 0;
+ my $servername_config_suffix = $self->servername_config_suffix ? $self->servername_config_suffix : " ";
+ MDK::Common::File::substInFile {
+ if (/^#?\s*server\s+(\S*)/ && $1 ne '127.127.1.0') {
+ $_ = $added ? $_ =~ $pool_match ? undef : "#server $1\n" : join('', map { "server $_$servername_config_suffix\n" } @servers);
+ $added = 1;
+ }
+ } $f;
+ if ($self->ntp_program eq "ntp") {
+ my $ntp_prefix = $self->ntp_conf_dir;
+ MDK::Common::File::output_p("$ntp_prefix/step-tickers", join('', map { "$_\n" } @servers));
+ }
+
+ # enable but do not start the service
+ $self->sh_services->set_status($ntpd, 1, 1);
+ if ($ntpd eq "chronyd") {
+ $self->sh_services->startService($ntpd);
+ $ENV{PATH} = "/usr/bin:/usr/sbin";
+ # Wait up to 30s for sync
+ system('/usr/bin/chronyc', 'waitsync', '30', '0.1');
+ } else {
+ $ENV{PATH} = "/usr/bin:/usr/sbin";
+ system('/usr/sbin/ntpdate', $server);
+ $self->sh_services->startService($ntpd);
+ }
+
+ ManaTools::Shared::enable_x_screensaver();
+}
+
+#=============================================================
+
+=head2 disableAndStopNTP
+
+=head3 DESCRIPTION
+
+ Disable and stop the ntp server
+
+=cut
+
+#=============================================================
+sub disableAndStopNTP {
+ my $self = shift;
+
+ # TODO is that valid for any ntp program? adding ntp_service_name parameter
+ my $ntpd = $self->ntp_program . 'd';
+
+ # also stop the service without dont_apply parameter
+ $self->sh_services->set_status($ntpd, 0);
+}
+
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+
+1;
+
+
diff --git a/lib/ManaTools/Shared/Users.pm b/lib/ManaTools/Shared/Users.pm
new file mode 100644
index 0000000..83c6061
--- /dev/null
+++ b/lib/ManaTools/Shared/Users.pm
@@ -0,0 +1,1612 @@
+# vim: set et ts=4 sw=4:
+package ManaTools::Shared::Users;
+#============================================================= -*-perl-*-
+
+=head1 NAME
+
+ManaTools::Shared::Users - backend to manage users
+
+=head1 SYNOPSIS
+
+ my $userBackEnd = ManaTools::Shared::Users->new();
+ my $userInfo = $userManager->getUserInfo('username');
+
+=head1 DESCRIPTION
+
+This module gives a low level access to the system user management it uses libUSER module.
+
+
+=head1 SUPPORT
+
+You can find documentation for this module with the perldoc command:
+
+perldoc ManaTools::Shared::Users
+
+=head1 SEE ALSO
+
+libUSER
+
+=head1 AUTHOR
+
+Angelo Naselli <anaselli@linux.it>
+
+=head1 COPYRIGHT and LICENSE
+
+Copyright (C) 2014-2015, Angelo Naselli.
+
+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
+
+=head1 METHODS
+
+=cut
+
+use Moose;
+use diagnostics;
+
+use Config::Auto;
+use Data::Password::Meter;
+use IO::All;
+use File::Basename;
+use File::Copy;
+use File::Remove 'remove';
+
+## USER is from userdrake
+use USER;
+use English;
+use POSIX qw/ceil/;
+
+use ManaTools::Shared::Locales;
+use ManaTools::Shared;
+
+
+#=============================================================
+
+=head2 new - optional parameters
+
+=head3 face_dir
+
+ optional parameter to set the system face icon directory,
+ default value is /usr/share/mga/faces/
+
+=cut
+
+#=============================================================
+
+has 'face_dir' => (
+ is => 'rw',
+ isa => 'Str',
+ default => "/usr/share/mga/faces/",
+);
+
+#=============================================================
+
+=head2 new - optional parameters
+
+=head3 user_face_dir
+
+ optional parameter to set the user face icon directory,
+ default value is /usr/share/mga/faces/
+
+=cut
+
+#=============================================================
+has 'user_face_dir' => (
+ is => 'rw',
+ isa => 'Str',
+ default => "/usr/share/faces/",
+);
+
+
+has 'loc' => (
+ is => 'rw',
+ init_arg => undef,
+ builder => '_localeInitialize'
+);
+
+
+sub _localeInitialize {
+ my $self = shift();
+
+ # TODO fix domain binding for translation
+ $self->loc(ManaTools::Shared::Locales->new(domain_name => 'userdrake') );
+ # TODO if we want to give the opportunity to test locally add dir_name => 'path'
+}
+
+## Used by USER (for getting values? TODO need explanations, where?)
+has 'USER_GetValue' => (
+ default => -65533,
+ is => 'ro',
+ isa => 'Int',
+ init_arg => undef,
+);
+
+## Used by USER (for getting values? TODO need explanations, where?)
+has 'ctx' => (
+ is => 'ro',
+ init_arg => undef,
+ builder => '_USERInitialize',
+);
+
+sub _USERInitialize {
+ my $self = shift;
+
+ # $EUID: effective user identifier
+ if ($EUID == 0) {
+ return USER::ADMIN->new;
+ }
+
+ return undef;
+}
+
+## min (custom) UID was 500 now is 1000, let's change in a single point
+has 'min_UID' => (
+ default => 1000,
+ is => 'ro',
+ isa => 'Int',
+ init_arg => undef,
+);
+
+## min (custom) GID was 500 now should be 1000 as for users
+has 'min_GID' => (
+ default => 1000,
+ is => 'ro',
+ isa => 'Int',
+ init_arg => undef,
+);
+
+#=============================================================
+
+=head2 BUILD
+
+=head3 INPUT
+
+ $self: this object
+
+=head3 DESCRIPTION
+
+ The BUILD method is called after a Moose object is created,
+ Into this method new optional parameters are tested once,
+ instead of into any other methods.
+
+=cut
+
+#=============================================================
+sub BUILD {
+ my $self = shift;
+
+ die "Missing face directory" if (! -d $self->face_dir);
+ die "Missing user face directory" if (! -d $self->user_face_dir);
+
+ $self->face_dir($self->face_dir . "/") if (substr($self->face_dir, -1) ne "/");
+ $self->user_face_dir($self->user_face_dir . "/") if (substr($self->user_face_dir, -1) ne "/");
+
+}
+
+
+=head2 facedir
+
+=head3 OUTPUT
+
+ path to directory containing face icon
+
+=head3 DESCRIPTION
+
+ Return the directory containing face icons.
+
+=cut
+
+#=============================================================
+
+sub facedir {
+ my $self = shift;
+
+ return $self->face_dir;
+}
+
+
+#=============================================================
+
+=head2 userfacedir
+
+=head3 OUTPUT
+
+ path to directory containing user face icons
+
+=head3 DESCRIPTION
+
+ Return the directory containing user face icons.
+
+=cut
+
+#=============================================================
+
+sub userfacedir {
+ my $self = shift;
+
+ return $self->user_face_dir;
+}
+
+
+#=============================================================
+
+=head2 face2png
+
+=head3 INPUT
+
+ $face: face icon name (usually username)
+
+=head3 OUTPUT
+
+ pathname to $face named icon with png extension
+
+=head3 DESCRIPTION
+
+ This method returns the face icon pathname related to username
+
+=cut
+
+#=============================================================
+
+sub face2png {
+ my ($self, $face) = @_;
+
+ return $self->face_dir . $face . ".png" if $face;
+}
+
+#=============================================================
+
+=head2 facenames
+
+
+=head3 OUTPUT
+
+ $namelist: ARRAY reference containing the face name list
+
+=head3 DESCRIPTION
+
+ Retrieves the list of icon name from facesdir()
+
+=cut
+
+#=============================================================
+
+sub facenames {
+ my $self = shift;
+
+ my $dir = $self->face_dir;
+ my @files = io->dir($dir)->all_files;
+ my @l = grep { /^[A-Z]/ } @files;
+ my @namelist = map { my $f = fileparse($_->filename, qr/\Q.png\E/) } (@l ? @l : @files);
+
+ return \@namelist;
+}
+
+#=============================================================
+
+=head2 addKdmIcon
+
+=head3 INPUT
+
+ $user: username to add
+ $icon: chosen icon for username $user
+
+
+=head3 DESCRIPTION
+
+ Add a $user named icon to $self->user_face_dir. It just copies
+ $icon to $self->user_face_dir, naming it as $user
+
+=cut
+
+#=============================================================
+
+sub addKdmIcon {
+ my ($self, $user, $icon) = @_;
+
+ if ($icon && $user) {
+ my $icon_name = $self->face_dir . $icon . ".png";
+ my $dest = $self->user_face_dir . $user . ".png";
+
+ eval { copy($icon_name, $dest) } ;
+ }
+}
+
+#=============================================================
+
+=head2 removeKdmIcon
+
+=head3 INPUT
+
+ $user: username icon to remove
+
+=head3 DESCRIPTION
+
+ Remove a $user named icon from $self->user_face_dir
+
+=cut
+
+#=============================================================
+sub removeKdmIcon {
+ my ($self, $user) = @_;
+
+ if ($user) {
+ my $icon_name = $self->user_face_dir . $user . ".png";
+ eval { remove($icon_name) } ;
+ }
+}
+
+
+#=============================================================
+
+=head2 _valid
+
+=head3 INPUT
+
+ $name: User or Group name
+ $name_length: Max length of $name (default 32)
+
+=head3 OUTPUT
+
+ 1, locale "Ok" if valid
+ 0, and explanation string if not valid:
+ - Name field is empty please provide a name
+ - The name must contain only lower cased latin letters, numbers, '.', '-' and '_'
+ - Name is too long
+
+=head3 DESCRIPTION
+
+ this internal method return if a name is compliant to
+ a group or user name.
+
+=cut
+
+#=============================================================
+
+sub _valid {
+ my ($self, $name, $name_length) = @_;
+
+ return (0, $self->loc->N("Name field is empty please provide a name")) if (!$name );
+
+ $name_length = 32 if !$name_length;
+
+ $name =~ /^[a-z]+?[a-z0-9_\-\.]*?$/ or do {
+ return (0, $self->loc->N("The name must start with a letter and contain only lower cased latin letters, numbers, '.', '-' and '_'"));
+ };
+
+ return (0, $self->loc->N("Name is too long. Maximum length is %d", $name_length)) if (! (length($name) <= $name_length));
+
+ return (1, $self->loc->N("Ok"));
+}
+
+#=============================================================
+
+=head2 valid_username
+
+=head3 INPUT
+
+ $username: user name to check
+
+=head3 OUTPUT
+
+ 1 if valid, 0 if not (see _valid)
+
+=head3 DESCRIPTION
+
+ Checks the valididty of the string $username
+
+=cut
+
+#=============================================================
+
+sub valid_username {
+ my ($self, $username) = @_;
+
+ return $self->_valid($username, 32);
+}
+
+#=============================================================
+
+=head2 valid_groupname
+
+=head3 INPUT
+
+ $groupname: user name to check
+
+=head3 OUTPUT
+
+ 1 if valid, 0 if not (see _valid)
+
+=head3 DESCRIPTION
+
+ Checks the valididty of the string $groupname
+
+=cut
+
+#=============================================================
+sub valid_groupname {
+ my ($self, $groupname) = @_;
+
+ return $self->_valid($groupname, 16);
+}
+
+
+#=============================================================
+
+=head2 updateOrDelUsersInGroup
+
+=head3 INPUT
+
+ $name: username
+
+=head3 DESCRIPTION
+
+ Fixes user deletion into groups.
+
+=cut
+
+#=============================================================
+sub updateOrDelUserInGroup {
+ my ($self, $name) = @_;
+ my $groups = $self->ctx->GroupsEnumerateFull;
+ foreach my $g (@$groups) {
+ my $members = $g->MemberName(1, 0);
+ if (ManaTools::Shared::inArray($name, $members)) {
+ eval { $g->MemberName($name, 2) };
+ eval { $self->ctx->GroupModify($g) };
+ }
+ }
+}
+
+
+#=============================================================
+
+=head2 getGoups
+
+=head3 OUTPUT
+
+ $groups: ARRAY reference containing all the groups
+
+=head3 DESCRIPTION
+
+ This method return the configured groups
+
+=cut
+
+#=============================================================
+sub getGoups {
+ my $self = shift;
+
+ return $self->ctx->GroupsEnumerate;
+}
+
+
+#=============================================================
+
+=head2 groupNameExists
+
+=head3 INPUT
+
+ $groupname: the name of the group to check
+
+=head3 OUTPUT
+
+ if group exists
+
+=head3 DESCRIPTION
+
+ This method return if a given group exists
+
+=cut
+
+#=============================================================
+sub groupNameExists {
+ my ($self, $groupname) = @_;
+
+ return 0 if (!defined($groupname));
+
+ return (defined($self->ctx->LookupGroupByName($groupname)));
+}
+
+#=============================================================
+
+=head2 groupIDExists
+
+=head3 INPUT
+
+ $group: the id of the group to check
+
+=head3 OUTPUT
+
+ if group exists
+
+=head3 DESCRIPTION
+
+ This method return if a given group exists
+
+=cut
+
+#=============================================================
+sub groupIDExists {
+ my ($self, $group) = @_;
+
+ return 0 if (!defined($group));
+
+ return (defined($self->ctx->LookupGroupById($group)));
+}
+
+
+#=============================================================
+
+=head2 groupID
+
+=head3 INPUT
+
+ $groupname: group name
+
+=head3 OUTPUT
+
+ groupid or undef
+
+=head3 DESCRIPTION
+
+ This method returns the group id for the group name
+
+=cut
+
+#=============================================================
+sub groupID {
+ my ($self, $groupname) = @_;
+
+ my $gr = $self->ctx->LookupGroupByName($groupname);
+ return $gr->Gid($self->USER_GetValue) if ($gr);
+
+ return undef;
+}
+
+#=============================================================
+
+=head2 groupName
+
+=head3 INPUT
+
+ $gid group identifier
+
+=head3 OUTPUT
+
+ group name or undef
+
+=head3 DESCRIPTION
+
+ This method returns the group name for the given group
+ identifier
+
+=cut
+
+#=============================================================
+sub groupName {
+ my ($self, $gid) = @_;
+
+ my $gr = $self->ctx->LookupGroupById($gid);
+ return $gr->GroupName($self->USER_GetValue) if ($gr);
+
+ return undef;
+}
+
+
+#=============================================================
+
+=head2 addGroup
+
+=head3 INPUT
+
+ $params: HASH reference containing:
+ groupname => name of teh group to be added
+ gid => group id of the group to be added
+ is_system => is a system group?
+
+=head3 OUTPUT
+
+ $gid the actual group id
+
+=head3 DESCRIPTION
+
+ This method add a group to system
+
+=cut
+
+#=============================================================
+sub addGroup {
+ my ($self, $params) = @_;
+
+ my $is_system = defined($params->{is_system}) ?
+ $params->{is_system} :
+ 0;
+
+ return -1 if !defined($params->{groupname});
+
+ my $groupEnt = $self->ctx->InitGroup($params->{groupname}, $is_system);
+
+ return -1 if !defined($groupEnt);
+
+ $groupEnt->Gid($params->{gid}) if defined($params->{gid});
+
+ $self->ctx->GroupAdd($groupEnt);
+
+ return $groupEnt->Gid($self->USER_GetValue);
+}
+
+#=============================================================
+
+=head2 groupMembers
+
+=head3 INPUT
+
+ $groupname: The group name
+
+=head3 OUTPUT
+
+ $members: ARRAY reference containing all the user belonging
+ to the given $groupname
+
+=head3 DESCRIPTION
+
+ This method gets the group name and returns the users
+ belonging to it
+
+=cut
+
+#=============================================================
+sub groupMembers {
+ my ($self, $groupname) = @_;
+
+ return $groupname if !defined($groupname);
+
+ my $members = $self->ctx->EnumerateUsersByGroup($groupname);
+
+ return $members;
+}
+
+
+#=============================================================
+
+=head2 isPrimaryGroup
+
+=head3 INPUT
+
+ $groupname: the name of the group
+
+=head3 OUTPUT
+
+ $username: undef if it is primary group or the username for
+ which the group is the primary one.
+
+=head3 DESCRIPTION
+
+ This methods check if the given group name is primary group
+ for any users belonging to the group
+
+=cut
+
+#=============================================================
+sub isPrimaryGroup {
+ my ($self, $groupname) = @_;
+
+ return $groupname if !defined($groupname);
+
+ my $groupEnt = $self->ctx->LookupGroupByName($groupname);
+ my $members = $self->ctx->EnumerateUsersByGroup($groupname);
+ foreach my $username (@$members) {
+ my $userEnt = $self->ctx->LookupUserByName($username);
+ if ($userEnt && $userEnt->Gid($self->USER_GetValue) == $groupEnt->Gid($self->USER_GetValue)) {
+ return $username;
+ }
+ }
+ return undef;
+}
+
+
+#=============================================================
+
+=head2 deleteGroup
+
+=head3 INPUT
+
+ $groupname: in_par_description
+
+=head3 OUTPUT
+
+ 0: if error occurred
+ 1: if removed
+
+=head3 DESCRIPTION
+
+ This method remove the group from the system
+
+=cut
+
+#=============================================================
+sub deleteGroup {
+ my ($self, $groupname) = @_;
+
+ return 0 if !defined($groupname);
+
+ my $groupEnt = $self->ctx->LookupGroupByName($groupname);
+ eval { $self->ctx->GroupDel($groupEnt) };
+ return 0 if $@;
+
+ return 1;
+}
+
+
+
+#=============================================================
+
+=head2 modifyGroup
+
+=head3 INPUT
+
+ $groupInfo: HASH reference containing:
+ old_groupname => old name of the group (if renaming)
+ groupname => group name
+ members => users belonging to the group
+
+=head3 OUTPUT
+
+ $retval => HASH reference
+ status => 1 (ok) 0 (error)
+ error => error message if status is 0
+
+=head3 DESCRIPTION
+
+ This method modifies the group groupname
+
+=cut
+
+#=============================================================
+sub modifyGroup {
+ my ($self, $groupInfo) = @_;
+
+ die "group name is mandatory" if !defined($groupInfo->{groupname});
+
+ my $groupEnt = defined($groupInfo->{old_groupname}) ?
+ $self->ctx->LookupGroupByName($groupInfo->{old_groupname}) :
+ $self->ctx->LookupGroupByName($groupInfo->{groupname});
+
+ my $orig_groupname = $groupInfo->{groupname};
+ if (defined($groupInfo->{old_groupname}) &&
+ $groupInfo->{old_groupname} ne $groupInfo->{groupname}) {
+ $groupEnt->GroupName($groupInfo->{groupname});
+ $orig_groupname = $groupInfo->{old_groupname};
+ }
+
+ my $members = $groupInfo->{members};
+ my $gid = $groupEnt->Gid($self->USER_GetValue);
+ my $users = $self->getUsers();
+ my @susers = sort(@{$users});
+
+ foreach my $user (@susers) {
+ my $uEnt = $self->ctx->LookupGroupByName($user);
+ if ($uEnt) {
+ my $ugid = $uEnt->Gid($self->USER_GetValue);
+ my $m = $self->ctx->EnumerateUsersByGroup($orig_groupname);
+ if (MDK::Common::DataStructure::member($user, @{$members})) {
+ if (!ManaTools::Shared::inArray($user, $m)) {
+ if ($ugid != $gid) {
+ eval { $groupEnt->MemberName($user, 1) };
+ }
+ }
+ }
+ else {
+ if (ManaTools::Shared::inArray($user, $m)) {
+ if ($ugid == $gid) {
+ return {
+ status => 0,
+ error =>$self->loc->N("You cannot remove user <%s> from their primary group", $user)
+ };
+ }
+ else {
+ eval { $groupEnt->MemberName($user, 2) };
+ }
+ }
+ }
+ }
+ }
+
+ $self->ctx->GroupModify($groupEnt);
+
+ return {status => 1,};
+}
+
+#=============================================================
+
+=head2 getGroupsInfo
+
+ $options: HASH reference containing
+ groupname_filter => groupname search string
+ filter_system => hides system groups
+
+=head3 OUTPUT
+
+ $groupsInfo: HASH reference containing
+ groupname-1 => {
+ gid => group identifier
+ members => ARRAY of username
+ }
+ groupname-2 => {
+ ...
+ }
+
+=head3 DESCRIPTION
+
+ This method get group information (all groups or the
+ filtered ones)
+
+=cut
+
+#=============================================================
+sub getGroupsInfo {
+ my ($self, $options) = @_;
+
+ my $groupsInfo = {};
+ return $groupsInfo if !defined $self->ctx;
+
+ my $strfilt = $options->{groupname_filter} if exists($options->{groupname_filter});
+ my $filtergroups = $options->{filter_system} if exists($options->{filter_system});
+
+ my $groups = $self->ctx->GroupsEnumerateFull;
+
+ my @GroupReal;
+ LOOP: foreach my $g (@{$groups}) {
+ my $gid = $g->Gid($self->USER_GetValue);
+ next LOOP if $filtergroups && $gid <= 499 || $gid == 65534;
+ if ($filtergroups && $gid > 499 && $gid < $self->min_GID) {
+ my $groupname = $g->GroupName($self->USER_GetValue);
+ my $l = $self->ctx->LookupUserByName($groupname);
+ if (!defined($l)) {
+ my $members = $self->ctx->EnumerateUsersByGroup($groupname);
+ next LOOP if !scalar(@{$members});
+ foreach my $username (@$members) {
+ my $userEnt = $self->ctx->LookupUserByName($username);
+ next LOOP if $userEnt->HomeDir($self->USER_GetValue) =~ /^\/($|var\/|run\/)/ || $userEnt->LoginShell($self->USER_GetValue) =~ /(nologin|false)$/;
+ }
+ }
+ else {
+ next LOOP if $l->HomeDir($self->USER_GetValue) =~ /^\/($|var\/|run\/)/ || $l->LoginShell($self->USER_GetValue) =~ /(nologin|false)$/;
+ }
+ }
+ push @GroupReal, $g if $g->GroupName($self->USER_GetValue) =~ /^\Q$strfilt/;
+ }
+
+ foreach my $g (@GroupReal) {
+ my $groupname = $g->GroupName($self->USER_GetValue);
+ my $u_b_g = $self->ctx->EnumerateUsersByGroup($groupname);
+ my $group_id = $g->Gid($self->USER_GetValue);
+
+ $groupsInfo->{"$groupname"} = {
+ gid => $group_id,
+ members => $u_b_g,
+ };
+ }
+
+ return $groupsInfo;
+}
+
+#=============================================================
+
+=head2 getUsers
+
+=head3 OUTPUT
+
+ $users: ARRAY reference containing all the users
+
+=head3 DESCRIPTION
+
+ This method return the configured users
+
+=cut
+
+#=============================================================
+sub getUsers {
+ my $self = shift;
+
+ return $self->ctx->UsersEnumerate;
+}
+
+#=============================================================
+
+=head2 getUserInfo
+
+=head3 INPUT
+
+ $username: user name
+
+=head3 OUTPUT
+
+ $userInfo: HASH reference containing
+ {
+ uid => user identifier
+ gid => group identifier
+ fullname => user full name
+ home => home directory
+ shell => user shell
+ expire => shadow expire time
+ locked => is locked?
+ exp_min => shadow Min
+ exp_max => shadow Max
+ exp_warn => shadow Warn
+ exp_inact => shadow Inact
+ last_change => Shadow last change
+ members => groups the user belongs to
+ }
+
+=head3 DESCRIPTION
+
+ This method get all the information for the given user
+
+=cut
+
+#=============================================================
+sub getUserInfo {
+ my ($self, $username) = @_;
+
+ my $userInfo = {};
+ return $userInfo if !defined $self->ctx;
+
+ my $userEnt = $self->ctx->LookupUserByName($username);
+ return $userInfo if !defined($userEnt);
+
+ my $fullname = $userEnt->Gecos($self->USER_GetValue);
+ utf8::decode($fullname);
+ $userInfo->{fullname} = $fullname;
+ $userInfo->{shell} = $userEnt->LoginShell($self->USER_GetValue);
+ $userInfo->{home} = $userEnt->HomeDir($self->USER_GetValue);
+ $userInfo->{uid} = $userEnt->Uid($self->USER_GetValue);
+ $userInfo->{gid} = $userEnt->Gid($self->USER_GetValue);
+ $userInfo->{expire} = $userEnt->ShadowExpire($self->USER_GetValue);
+ $userInfo->{locked} = $self->ctx->IsLocked($userEnt);
+
+ $userInfo->{exp_min} = $userEnt->ShadowMin($self->USER_GetValue);
+ $userInfo->{exp_max} = $userEnt->ShadowMax($self->USER_GetValue);
+ $userInfo->{exp_warn} = $userEnt->ShadowWarn($self->USER_GetValue);
+ $userInfo->{exp_inact} = $userEnt->ShadowInact($self->USER_GetValue);
+ $userInfo->{last_change} = $userEnt->ShadowLastChange($self->USER_GetValue);
+ $userInfo->{members} = $self->ctx->EnumerateGroupsByUser($username);
+
+ return $userInfo;
+}
+
+#=============================================================
+
+=head2 getUsersInfo
+
+=head3 INPUT
+
+ $options: HASH reference containing
+ username_filter => username search string
+ filter_system => hides system users
+
+=head3 OUTPUT
+
+ $usersInfo: HASH reference containing
+ username-1 => {
+ uid => user identifier
+ group => primary group name
+ gid => group identifier
+ fullname => user full name
+ home => home directory
+ shell => user shell
+ status => login status (locked, expired, etc)
+ }
+ username-2 => {
+ ...
+ }
+
+=head3 DESCRIPTION
+
+ This method get user information (all users or filtered ones)
+
+=cut
+
+#=============================================================
+sub getUsersInfo {
+ my ($self, $options) = @_;
+
+ my $usersInfo = {};
+ return $usersInfo if !defined $self->ctx;
+
+ my $strfilt = $options->{username_filter} if exists($options->{username_filter});
+ my $filterusers = $options->{filter_system} if exists($options->{filter_system});
+
+ my ($users, $group, $groupnm, $expr);
+ $users = $self->ctx->UsersEnumerateFull;
+
+ my @UserReal;
+ LOOP: foreach my $l (@{$users}) {
+ my $uid = $l->Uid($self->USER_GetValue);
+ next LOOP if $filterusers && $uid <= 499 || $uid == 65534;
+ next LOOP if $filterusers && $uid > 499 && $uid < $self->min_UID &&
+ ($l->HomeDir($self->USER_GetValue) =~ /^\/($|var\/|run\/)/ || $l->LoginShell($self->USER_GetValue) =~ /(nologin|false)$/);
+ push @UserReal, $l if $l->UserName($self->USER_GetValue) =~ /^\Q$strfilt/;
+ }
+ my $i;
+ my $itemColl = new yui::YItemCollection;
+ foreach my $l (@UserReal) {
+ $i++;
+ my $uid = $l->Uid($self->USER_GetValue);
+ if (!defined $uid) {
+ warn "bogus user at line $i\n";
+ next;
+ }
+ my $gid = $l->Gid($self->USER_GetValue);
+ $group = $self->ctx->LookupGroupById($gid);
+ $groupnm = '';
+ $expr = $self->computeLockExpire($l);
+ $group and $groupnm = $group->GroupName($self->USER_GetValue);
+ my $fulln = $l->Gecos($self->USER_GetValue);
+ utf8::decode($fulln);
+ my $username = $l->UserName($self->USER_GetValue);
+ my $shell = $l->LoginShell($self->USER_GetValue);
+ my $homedir = $l->HomeDir($self->USER_GetValue);
+ $usersInfo->{"$username"} = {
+ uid => $uid,
+ group => $groupnm,
+ gid => $gid,
+ fullname => $fulln,
+ home => $homedir,
+ status => $expr,
+ shell => $shell,
+ };
+ }
+
+ return $usersInfo;
+}
+
+#=============================================================
+
+=head2 getUserHome
+
+=head3 INPUT
+
+ $username: given user name
+
+=head3 OUTPUT
+
+ $homedir: user home directory
+
+=head3 DESCRIPTION
+
+ This method return the home directory belonging to the given
+ username
+
+=cut
+
+#=============================================================
+sub getUserHome {
+ my ($self, $username) = @_;
+
+ return $username if !defined($username);
+
+ my $userEnt = $self->ctx->LookupUserByName($username);
+ my $homedir = $userEnt->HomeDir($self->USER_GetValue);
+
+ return $homedir;
+}
+
+#=============================================================
+
+=head2 userNameExists
+
+=head3 INPUT
+
+ $username: the name of the user to check
+
+=head3 OUTPUT
+
+ if user exists
+
+=head3 DESCRIPTION
+
+ This method return if a given user exists
+
+=cut
+
+#=============================================================
+sub userNameExists {
+ my ($self, $username) = @_;
+
+ return 0 if (!defined($username));
+
+ return (defined($self->ctx->LookupUserByName($username)));
+}
+
+#=============================================================
+
+=head2 computeLockExpire
+
+=head3 INPUT
+
+ $l: login user info
+
+=head3 OUTPUT
+
+ $status: Locked, Expired, or empty string
+
+=head3 DESCRIPTION
+
+ This method returns if the login is Locked, Expired or ok.
+ Note this function is meant for internal use only
+
+=cut
+
+#=============================================================
+sub computeLockExpire {
+ my ( $self, $l ) = @_;
+ my $ep = $l->ShadowExpire($self->USER_GetValue);
+ my $tm = ceil(time()/(24*60*60));
+ $ep = -1 if int($tm) <= $ep;
+ my $status = $self->ctx->IsLocked($l) ? $self->loc->N("Locked") : ($ep != -1 ? $self->loc->N("Expired") : '');
+ return $status;
+}
+
+#=============================================================
+
+=head2 addUser
+
+=head3 INPUT
+
+ $params: HASH reference containing:
+ username => name of teh user to be added
+ uid => user id of the username to be added
+ is_system => is a system user?
+ homedir => user home directory
+ donotcreatehome => do not create the home directory
+ shell => user shall
+ fullname => user full name
+ gid => group id for the user
+ shadowMin => min time password validity
+ shadowMax => max time password validity
+ shadowInact =>
+ shadowWarn =>
+ password => user password
+
+=head3 OUTPUT
+
+ 0 if errors 1 if ok
+
+=head3 DESCRIPTION
+
+ This method add a user to system
+
+=cut
+
+#=============================================================
+sub addUser {
+ my ($self, $params) = @_;
+
+ return 0 if !defined($params->{username});
+
+ my $is_system = defined($params->{is_system}) ?
+ $params->{is_system} :
+ 0;
+
+ my $userEnt = $self->ctx->InitUser($params->{username}, $is_system);
+ return 0 if !defined($userEnt);
+
+
+ $userEnt->HomeDir($params->{homedir}) if defined($params->{homedir});
+ $userEnt->Uid($params->{uid}) if defined($params->{uid});
+ $userEnt->Gecos($params->{fullname}) if defined($params->{fullname});
+ $userEnt->LoginShell($params->{shell}) if defined($params->{shell});
+ $userEnt->Gid($params->{gid}) if defined ($params->{gid});
+ my $shd = defined ($params->{shadowMin}) ? $params->{shadowMin} : -1;
+ $userEnt->ShadowMin($shd);
+ $shd = defined ($params->{shadowMax}) ? $params->{shadowMax} : 99999;
+ $userEnt->ShadowMax($shd);
+ $shd = defined ($params->{shadowWarn}) ? $params->{shadowWarn} : -1;
+ $userEnt->ShadowWarn($shd);
+ $shd = defined ($params->{shadowInact}) ? $params->{shadowInact} : -1;
+ $userEnt->ShadowInact($shd);
+ $self->ctx->UserAdd($userEnt, $is_system, $params->{donotcreatehome});
+ $self->ctx->UserSetPass($userEnt, $params->{password});
+
+ return 1;
+}
+
+
+#=============================================================
+
+=head2 modifyUser
+
+=head3 INPUT
+
+ $userInfo: HASH reference containing:
+ old_username => old name of the user (if renaming)
+ username => user name
+ fullname => full name of teh user
+ password => password
+ homedir => home directory
+ shell => user shell
+ members => groups the user belongs to
+ gid => primary group identifier
+ lockuser => lock user
+ acc_expires => account expire time - containing:
+ exp_y => year
+ exp_m => month
+ exp_d => day
+ password_expires => password expire time - containing:
+ exp_min => min
+ exp_max => max
+ exp_warn => when warn
+ exp_inact => when inactive
+
+=head3 DESCRIPTION
+
+ This method modifies the group groupname
+
+=cut
+
+#=============================================================
+sub modifyUser {
+ my ($self, $userInfo) = @_;
+
+ die "user name is mandatory" if !defined($userInfo->{username});
+ die "primary group identifier is mandatory" if !defined($userInfo->{gid});
+ die "a valid group identifier is mandatory" if $userInfo->{gid} < 0;
+
+ if (defined($userInfo->{acc_expires})) {
+ die "expiring year is mandatory" if !defined($userInfo->{acc_expires}->{exp_y});
+ die "expiring month is mandatory" if !defined($userInfo->{acc_expires}->{exp_m});
+ die "expiring day is mandatory" if !defined($userInfo->{acc_expires}->{exp_d});
+ }
+ if (defined($userInfo->{password_expires})) {
+ die "password expiring min is mandatory" if !($userInfo->{password_expires}->{exp_min});
+ die "password expiring max is mandatory" if !($userInfo->{password_expires}->{exp_max});
+ die "password expiring warn is mandatory" if !($userInfo->{password_expires}->{exp_warn});
+ die "password expiring inactive is mandatory" if !($userInfo->{password_expires}->{exp_inact});
+ }
+ my $userEnt = defined($userInfo->{old_username}) ?
+ $self->ctx->LookupUserByName($userInfo->{old_username}) :
+ $self->ctx->LookupUserByName($userInfo->{username});
+
+ my $orig_username = $userInfo->{username};
+ if (defined($userInfo->{old_username}) &&
+ $userInfo->{old_username} ne $userInfo->{username}) {
+ $userEnt->UserName($userInfo->{username});
+ $orig_username = $userInfo->{old_username};
+ }
+
+ # $userEnt->UserName($userInfo->{username});
+ $userEnt->Gecos($userInfo->{fullname}) if defined($userInfo->{fullname});
+ $userEnt->HomeDir($userInfo->{homedir}) if defined($userInfo->{homedir});
+ $userEnt->LoginShell($userInfo->{shell}) if defined($userInfo->{shell});
+
+
+ my $username = $userEnt->UserName($self->USER_GetValue);
+ my $grps = $self->getGoups();
+ my @sgroups = sort @{$grps};
+
+ my $members = $userInfo->{members};
+ foreach my $group (@sgroups) {
+ my $gEnt = $self->ctx->LookupGroupByName($group);
+ my $ugid = $gEnt->Gid($self->USER_GetValue);
+ my $m = $gEnt->MemberName(1,0);
+ if (MDK::Common::DataStructure::member($group, @$members)) {
+ if (!ManaTools::Shared::inArray($username, $m) && $userInfo->{gid} != $ugid) {
+ eval { $gEnt->MemberName($username, 1) };
+ $self->ctx->GroupModify($gEnt);
+ }
+ }
+ else {
+ if (ManaTools::Shared::inArray($username, $m)) {
+ eval { $gEnt->MemberName($username, 2) };
+ $self->ctx->GroupModify($gEnt);
+ }
+ }
+ }
+
+ $userEnt->Gid($userInfo->{gid}) if defined($userInfo->{gid});
+
+ if (defined($userInfo->{acc_expires})) {
+ my $yr = $userInfo->{acc_expires}->{exp_y};
+ my $mo = $userInfo->{acc_expires}->{exp_m};
+ my $dy = $userInfo->{acc_expires}->{exp_d};
+ my $Exp = _ConvTime($dy, $mo, $yr);
+ $userEnt->ShadowExpire($Exp);
+ }
+ else {
+ $userEnt->ShadowExpire(ceil(-1))
+ }
+ if (defined($userInfo->{password_expires})) {
+ my $allowed = $userInfo->{password_expires}->{exp_min};
+ my $required = $userInfo->{password_expires}->{exp_max};
+ my $warning = $userInfo->{password_expires}->{exp_warn};
+ my $inactive = $userInfo->{password_expires}->{exp_inact};
+ $userEnt->ShadowMin($allowed);
+ $userEnt->ShadowMax($required);
+ $userEnt->ShadowWarn($warning);
+ $userEnt->ShadowInact($inactive);
+ }
+ else {
+ $userEnt->ShadowMin(-1);
+ $userEnt->ShadowMax(99999);
+ $userEnt->ShadowWarn(-1);
+ $userEnt->ShadowInact(-1);
+ }
+
+ $self->ctx->UserSetPass($userEnt, $userInfo->{password}) if defined($userInfo->{password});
+ $self->ctx->UserModify($userEnt);
+
+ if ($userInfo->{lockuser}) {
+ !$self->ctx->IsLocked($userEnt) and $self->ctx->Lock($userEnt);
+ }
+ else {
+ $self->ctx->IsLocked($userEnt) and $self->ctx->UnLock($userEnt);
+ }
+
+ return 1;
+}
+
+
+#=============================================================
+
+=head2 deleteUser
+
+=head3 INPUT
+
+ $username: username to be deleted
+ $options: HASH reference containing
+ clean_home => if home has to be removed
+ clean_spool => if sppol has to be removed
+
+=head3 OUTPUT
+
+ error string or undef if no errors occurred
+
+=head3 DESCRIPTION
+
+ This method delete a user from the system.
+
+=cut
+
+#=============================================================
+sub deleteUser {
+ my ($self, $username, $options) = @_;
+
+ return $username if !defined($username);
+
+ my $userEnt = $self->ctx->LookupUserByName($username);
+
+ $self->ctx->UserDel($userEnt);
+ $self->updateOrDelUserInGroup($username);
+ #Let's check out the user's primary group
+ my $usergid = $userEnt->Gid($self->USER_GetValue);
+ my $groupEnt = $self->ctx->LookupGroupById($usergid);
+ if ($groupEnt) {
+ my $member = $groupEnt->MemberName(1, 0);
+ # TODO check if 499 is ok nowadays
+ if (scalar(@$member) == 0 && $groupEnt->Gid($self->USER_GetValue) > 499) {
+ $self->ctx->GroupDel($groupEnt);
+ }
+ }
+ if (defined($options)) {
+ ## testing jusr if exists also undef is allowed
+ ## as valid option
+ if (exists($options->{clean_home})) {
+ eval { $self->ctx->CleanHome($userEnt) };
+ return $@ if $@;
+ }
+ if (exists($options->{clean_spool})) {
+ eval { $self->ctx->CleanSpool($userEnt) };
+ return $@ if $@;
+ }
+ }
+ return undef;
+}
+
+#=============================================================
+
+=head2 getUserShells
+
+
+=head3 OUTPUT
+
+ GetUserShells: from libUSER
+
+=head3 DESCRIPTION
+
+ This method returns the available shell
+
+=cut
+
+#=============================================================
+
+sub getUserShells {
+ my $self = shift;
+
+ return $self->ctx->GetUserShells;
+}
+#=============================================================
+
+=head2 GetFaceIcon
+
+=head3 INPUT
+
+ $name: icon name for the given username
+ $next: if passed means getting next icon from the given $name
+
+=head3 OUTPUT
+
+ $user_icon: icon name
+
+=head3 DESCRIPTION
+
+ This method returns the icon for the given user ($name) or the
+ following one if $next is passed
+
+=cut
+
+#=============================================================
+sub GetFaceIcon {
+ my ($self, $name, $next) = @_;
+ my $icons = $self->facenames();
+ my $i;
+ my $current_icon;
+ # remove shortcut "&" from label
+ $name =~ s/&// if ($name);
+ my $user_icon = $self->user_face_dir . $name . ".png" if ($name);
+ if ($name) {
+ $user_icon = $self->face2png($name) unless(-e $user_icon);
+ }
+ if ($name && -e $user_icon) {
+ my $current_md5 = ManaTools::Shared::md5sum($user_icon);
+ my $found = 0;
+ for ($i = 0; $i < scalar(@$icons); $i++) {
+ if (ManaTools::Shared::md5sum($self->face2png($icons->[$i])) eq $current_md5) {
+ $found = 1;
+ last;
+ }
+ }
+ if ($found) { #- current icon found in @icons, select it
+ $current_icon = $icons->[$i];
+ } else { #- add and select current icon in @icons
+ push @$icons, $user_icon;
+ $current_icon = $user_icon;
+ $i = scalar(@$icons) - 1;
+ }
+ } else {
+ #- no icon yet, select a random one
+ $current_icon = $icons->[$i = rand(scalar(@$icons))];
+ }
+
+ if ($next) {
+ $current_icon = $icons->[$i = defined $icons->[$i+1] ? $i+1 : 0];
+ }
+ return $current_icon;
+}
+
+
+#=============================================================
+
+=head2 strongPassword
+
+=head3 INPUT
+
+ $passwd: password to be checked
+
+=head3 OUTPUT
+
+ 1: if password is strong
+ 0: if password is weak
+
+=head3 DESCRIPTION
+
+ Check for a strong password
+
+=cut
+
+#=============================================================
+sub strongPassword {
+ my ($self, $passwd, $threshold) = @_;
+
+ return 0 if !$passwd;
+
+ my $pwdm = $threshold ? Data::Password::Meter->new($threshold) : Data::Password::Meter->new();
+
+ # Check a password
+ return $pwdm->strong($passwd);
+}
+
+
+# TODO methods not tested in Users.t
+
+#=============================================================
+
+=head2 weakPasswordForSecurityLevel
+
+=head3 INPUT
+
+ $passwd: password to check
+
+=head3 OUTPUT
+
+ 1: if the password is too weak for security level
+
+=head3 DESCRIPTION
+
+ Check the security level set if /etc/security/msec/security.conf
+ exists and the level is not 'standard' and if the password
+ is not at least 6 characters return true
+
+=cut
+
+#=============================================================
+sub weakPasswordForSecurityLevel {
+ my ($self, $passwd) = @_;
+
+ my $sec_conf_file = "/etc/security/msec/security.conf";
+ if (-e $sec_conf_file) {
+ my $prefs = Config::Auto::parse($sec_conf_file);
+ my $level = $prefs->{BASE_LEVEL};
+ if ($level eq 'none' or $level eq 'standard') {
+ return 0;
+ }
+ elsif (length($passwd) < 6) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+#=============================================================
+
+=head2 Add2UsersGroup
+
+=head3 INPUT
+
+ $name: username
+
+=head3 OUTPUT
+
+ $gid: group id
+
+=head3 DESCRIPTION
+
+ Adds the given username $name to 'users' group
+
+=cut
+
+#=============================================================
+sub Add2UsersGroup {
+ my ($self, $name) = @_;
+
+ my $usersgroup = $self->ctx->LookupGroupByName('users');
+ $usersgroup->MemberName($name, 1);
+ return $usersgroup->Gid($self->USER_GetValue);
+}
+
+sub _ConvTime {
+ my ($day, $month, $year) = @_;
+ my ($tm, $days, $mon, $yr);
+ $mon = $month - 1; $yr = $year - 1900;
+ $tm = POSIX::mktime(0, 0, 0, $day, $mon, $yr);
+ $days = ceil($tm / (24 * 60 * 60));
+ return $days;
+}
+
+no Moose;
+__PACKAGE__->meta->make_immutable;
+
+1;
diff --git a/lib/ManaTools/rpmdragora.pm b/lib/ManaTools/rpmdragora.pm
new file mode 100644
index 0000000..ecdf06f
--- /dev/null
+++ b/lib/ManaTools/rpmdragora.pm
@@ -0,0 +1,1165 @@
+# vim: set et ts=4 sw=4:
+#*****************************************************************************
+#
+# Copyright (c) 2002 Guillaume Cottenceau
+# Copyright (c) 2002-2007 Thierry Vignaud <tvignaud@mandriva.com>
+# Copyright (c) 2003, 2004, 2005 MandrakeSoft SA
+# Copyright (c) 2005, 2007 Mandriva SA
+# Copyright (c) 2013 - 2015 Matteo Pasotti <matteo.pasotti@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2, as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+#
+#*****************************************************************************
+#
+# $Id: rpmdragora.pm 267936 2010-04-26 16:40:21Z jvictor $
+
+package ManaTools::rpmdragora;
+use warnings::register;
+
+use urpm;
+use urpm::cfg;
+use urpm::mirrors;
+use urpm::download ();
+use urpm::prompt;
+use urpm::media;
+
+# quick fix for mirror.pm
+use lib qw(/usr/lib/libDrakX);
+
+use MDK::Common;
+use MDK::Common::System;
+use MDK::Common::String;
+use MDK::Common::Func;
+use MDK::Common::File qw(basename cat_ output);
+use URPM;
+use URPM::Resolve;
+use strict;
+use POSIX qw(_exit);
+
+use feature 'state';
+
+use ManaTools::Shared;
+use ManaTools::Shared::Locales;
+use ManaTools::Shared::GUI;
+
+use Carp;
+
+our @ISA = qw(Exporter);
+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
+ locale
+ 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
+ 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;
+use Glib;
+#ugtk2::add_icon_path('/usr/share/rpmdragora/icons');
+
+# Locale::gettext::bind_textdomain_codeset('rpmdragora', 'UTF8');
+my $loc = ManaTools::Shared::Locales->new(domain_name => 'rpmdrake');;
+
+our $mageia_release = MDK::Common::File::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 ? $loc->N_("Software Update") : $loc->N_("Mageia Update");
+
+@rpmdragora::prompt::ISA = 'urpm::prompt';
+
+
+sub locale() {
+
+ if (!defined($loc)) {
+ $loc = ManaTools::Shared::Locales->new(domain_name => 'rpmdrake');
+ }
+
+ return $loc;
+}
+
+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($loc->N("Ok")), clicked => sub { Gtk2->main_quit }),
+ ),
+ );
+ $d->main;
+ map { $_->get_text } @answers;
+}
+
+$urpm::download::PROMPT_PROXY = new rpmdragora::prompt(
+ $loc->N_("Please enter your credentials for accessing proxy\n"),
+ [ $loc->N_("User name:"), $loc->N_("Password:") ],
+ undef,
+ [ 0, 1 ],
+);
+
+sub myexit {
+ writeconf();
+ destroy $::main_window if $::main_window;
+ exit @_;
+}
+
+my ($root) = grep { $_->[2] == 0 } list_passwd();
+$ENV{HOME} = $> == 0 ? $root->[7] : $ENV{HOME} || '/root';
+$ENV{HOME} = $::env if $::env = $ManaTools::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 (MDK::Common::File::cat_($configfile)) {
+ my ($key, @values) = split ' ', $l;
+ ${$config{$key}{var}} = \@values if scalar @values;
+ }
+ # special cases:
+ $::rpmdragora_options{'no-confirmation'} = $no_confirmation->[0] if !defined $::rpmdragora_options{'no-confirmation'};
+ $ManaTools::Rpmdragora::init::default_list_mode = $tree_mode->[0] if ref $tree_mode && !$ManaTools::Rpmdragora::init::overriding_config;
+}
+
+sub writeconf() {
+ return if $::env;
+ unlink $configfile;
+
+ # special case:
+ $no_confirmation->[0] = $::rpmdragora_options{'no-confirmation'};
+ my @config_content = map { "$_ " . (ref ${$config{$_}{var}} ? join(' ', @${$config{$_}{var}}) : '') . "\n" } sort keys %config;
+ MDK::Common::File::output($configfile, @config_content);
+ print "writeconf done!\n";
+}
+
+sub getbanner() {
+ $::MODE or return undef;
+ if (0) {
+ +{
+ remove => $loc->N("Software Packages Removal"),
+ update => $loc->N("Software Packages Update"),
+ install => $loc->N("Software Packages Installation"),
+ };
+ }
+# Gtk2::Banner->new($ugtk2::wm_icon, $::MODE eq 'update' ? $loc->N("Software Packages Update") : $loc->N("Software Management"));
+}
+
+
+#=============================================================
+
+=head2 interactive_msg
+
+=head3 INPUT
+
+ $title: dialog title
+ $contents: dialog text
+ %options: optional HASH containing {
+ scroll => Rich Text with scroll bar used
+ yesno => dialog with "yes" and "no" buttons (deafult yes)
+ dont_ask_again => add a checkbox with "dont ask again text"
+ main_dialog => create a main dialog instead of a popup one
+ min_size => {columns => X, lines => Y} for minimum dialog size,
+ }
+
+=head3 OUTPUT
+
+ retval: if dont_ask_again HASH reference containig {
+ value => 1 yes (or ok) pressed, 0 no pressed
+ dont_ask_again => 1 if checked
+ }
+ or if dont_ask_again is not passed:
+ 1 yes (or ok) pressed, 0 no pressed
+
+=head3 DESCRIPTION
+
+ This function shows a dialog with contents text and return the button
+ pressed (1 ok or yes), optionally returns the checkbox value if dont_ask_again is
+ passed.
+ If min_size is passed a minimum dialog size is set (default is 75x6) see libyui
+ createMinSize documenatation for explanation.
+
+=cut
+
+#=============================================================
+sub interactive_msg {
+ my ($title, $contents, %options) = @_;
+
+ my $retVal = 0;
+
+ my $info;
+
+ if ($options{scroll}) {
+ ## richtext needs <br> instead of '\n'
+ $contents =~ s/\n/<br>/g;
+ }
+
+ my $oldTitle = yui::YUI::app()->applicationTitle();
+ yui::YUI::app()->setApplicationTitle($title);
+
+ my $factory = yui::YUI::widgetFactory;
+ my $dlg = $options{main_dialog} ?
+ $factory->createMainDialog() :
+ $factory->createPopupDialog();
+ my $columns = $options{min_size}->{columns} || 75;
+ my $lines = $options{min_size}->{lines} || 6;
+ my $minSize = $factory->createMinSize( $dlg, $columns, $lines);
+ my $vbox = $factory->createVBox( $minSize );
+ my $midhbox = $factory->createHBox($vbox);
+ ## app description
+ my $toprightvbox = $factory->createVBox($midhbox);
+ $toprightvbox->setWeight($yui::YD_HORIZ, 5);
+ $factory->createSpacing($toprightvbox,$yui::YD_HORIZ, 0, 5.0);
+ $factory->createRichText($toprightvbox, $contents, !$options{scroll});
+ $factory->createSpacing($toprightvbox, $yui::YD_HORIZ, 0, 5.0);
+
+ if ($options{dont_ask_again}){
+ my $hbox = $factory->createHBox($vbox);
+ my $align = $factory->createRight($hbox);
+ $info->{checkbox} = $factory->createCheckBox($align, $loc->N("Do not ask me next time"));
+ }
+
+ my $bottomhbox = $factory->createHBox($vbox);
+ if ($options{yesno}) {
+ my $alignRight = $factory->createRight($bottomhbox);
+ my $buttonBox = $factory->createHBox($alignRight);
+
+ $info->{B1} = $factory->createPushButton($buttonBox, $options{text}{yes} || $loc->N("Yes"));
+ $info->{B2} = $factory->createPushButton($buttonBox, $options{text}{no} || $loc->N("No"));
+ }
+ else {
+ $info->{B1} = $factory->createPushButton($bottomhbox, $loc->N("Ok"));
+ }
+
+ $dlg->setDefaultButton($info->{B1});
+
+ while (1) {
+ my $event = $dlg->waitForEvent();
+ my $eventType = $event->eventType();
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ $retVal = 1; ##default value
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+
+ if ($info->{B1} && $widget == $info->{B1}) {
+ $retVal = 1;
+ last;
+ }
+ elsif ($info->{B2} && $widget == $info->{B2}) {
+ last;
+ }
+ }
+ }
+
+ if ($info->{checkbox}) {
+ my $value = $retVal;
+ $retVal = undef;
+ $retVal->{value} = $value;
+ $retVal->{dont_ask_again} = $info->{checkbox}->isChecked();
+ }
+
+ $dlg->destroy();
+ yui::YUI::app()->setApplicationTitle($oldTitle);
+
+ return $retVal;
+}
+
+sub interactive_list {
+ my ($title, $contents, $list, $callback, %options) = @_;
+
+ my $factory = yui::YUI::widgetFactory;
+ my $mainw = $factory->createPopupDialog();
+ my $vbox = $factory->createVBox($mainw);
+ my $lbltitle = $factory->createLabel($vbox, $loc->N("Dependencies"));
+ my $left = $factory->createLeft($factory->createHBox($vbox));
+ my $radiobuttongroup = $factory->createRadioButtonGroup($left);
+ my $rbbox = $factory->createVBox($radiobuttongroup);
+ foreach my $item (@$list) {
+ my $radiobutton = $factory->createRadioButton($rbbox,$item);
+ if ($item eq $list->[0]) {
+ # select first by default
+ $radiobutton->setValue(1);
+ }
+ $radiobutton->setNotify(0);
+ $radiobuttongroup->addRadioButton($radiobutton);
+ }
+ my $submitButton = $factory->createIconButton($vbox,"", $loc->N("OK"));
+ my $choice;
+
+ while(1) {
+ my $event = $mainw->waitForEvent();
+ my $eventType = $event->eventType();
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ $mainw->destroy();
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+
+ if($widget == $submitButton) {
+ # NOTE if for any reason radio button is not checked let's take the first package
+ $choice = $radiobuttongroup->currentButton() ? $radiobuttongroup->currentButton()->label() : $list->[0];
+ $choice =~s/\&//g;
+ last;
+ }
+ }
+ }
+ $mainw->destroy();
+ return $choice;
+}
+
+sub interactive_list_ { interactive_list(@_, if_($::main_window, transient => $::main_window)) }
+
+sub fatal_msg {
+ interactive_msg @_;
+ myexit -1;
+}
+
+sub wait_msg {
+ my $msg = shift;
+
+ my $label = $msg ? $msg : $loc->N("Please wait");
+
+ my $factory = yui::YUI::widgetFactory;
+ my $mainw = $factory->createPopupDialog();
+ my $vbox = $factory->createVBox($mainw);
+ my $title = $factory->createLabel($vbox, $label);
+ #$mainw->recalcLayout();
+ #$mainw->doneMultipleChanges();
+ $mainw->waitForEvent(10);
+ $mainw->pollEvent();
+ #$mainw->recalcLayout();
+ #$mainw->doneMultipleChanges();
+ yui::YUI::app()->busyCursor();
+
+ $mainw;
+}
+
+sub remove_wait_msg {
+ my $w = shift;
+ #gtkset_mousecursor_normal($w->{rwindow}->window);
+ $w->destroy;
+ yui::YUI::app()->normalCursor();
+}
+
+sub but { " $_[0] " }
+sub but_ { " $_[0] " }
+
+#=============================================================
+
+=head2 slow_func
+
+=head3 INPUT
+
+ $func: function to be executed with a busy cursor or waiting
+ dialog
+ $msg: message to be shown in ncurses waiting dialog (if any)
+
+=head3 DESCRIPTION
+
+ This function executes a given function with a busy cursor set
+ in graphical environment, or with a waiting dialog if in ncurses
+ text mode
+
+=cut
+
+#=============================================================
+sub slow_func (&) {
+ my ($func, $msg) = @_;
+
+ my $retval = 1;
+ # NOTE busy cursor is not implemented in yui-ncurses
+ # but we can avoid a waiting dialog in Gtk and QT
+ if (yui::YUI::app()->isTextMode()) {
+ my $w = wait_msg($msg);
+ $retval = $func->();
+ remove_wait_msg($w)
+ }
+ else {
+ yui::YUI::app()->busyCursor();
+ $retval = $func->();
+ yui::YUI::app()->normalCursor();
+ }
+ return $retval;
+}
+
+sub statusbar_msg {
+ my ($msg, $o_timeout) = @_;
+
+ unless ($::statusbar) { #- fallback if no status bar
+ return wait_msg($msg);
+ }
+
+ $::statusbar->setLabel($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;
+ $::statusbar->setTimeout ( 5000 );
+ Glib::Timeout->add(5000, sub { statusbar_msg_remove(); 0 }) if $o_timeout;
+ #$id;
+ return 1;
+}
+
+sub statusbar_msg_remove {
+ if (!$::statusbar) { #- fallback if no status bar
+ my $id = shift;
+ return remove_wait_msg($id);;
+ }
+ my ($msg_id) = @_;
+ #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->setLabel("");
+}
+
+sub slow_func_statusbar ($$&) {
+ my ($msg, $w, $func) = @_;
+ yui::YUI::app()->busyCursor();
+
+ my $msg_id = statusbar_msg($msg);
+ $func->();
+ statusbar_msg_remove($msg_id);
+
+ yui::YUI::app()->normalCursor();
+}
+
+my %u2l = (
+ ar => $loc->N_("Argentina"),
+ at => $loc->N_("Austria"),
+ au => $loc->N_("Australia"),
+ by => $loc->N_("Belarus"),
+ be => $loc->N_("Belgium"),
+ br => $loc->N_("Brazil"),
+ gb => $loc->N_("Britain"),
+ ca => $loc->N_("Canada"),
+ ch => $loc->N_("Switzerland"),
+ cr => $loc->N_("Costa Rica"),
+ cz => $loc->N_("Czech Republic"),
+ de => $loc->N_("Germany"),
+ dk => $loc->N_("Danmark"),
+ ec => $loc->N_("Ecuador"),
+ el => $loc->N_("Greece"),
+ es => $loc->N_("Spain"),
+ fi => $loc->N_("Finland"),
+ fr => $loc->N_("France"),
+ gr => $loc->N_("Greece"),
+ hu => $loc->N_("Hungary"),
+ id => $loc->N_("Indonesia"),
+ il => $loc->N_("Israel"),
+ it => $loc->N_("Italy"),
+ jp => $loc->N_("Japan"),
+ ko => $loc->N_("Korea"),
+ nl => $loc->N_("Netherlands"),
+ no => $loc->N_("Norway"),
+ pl => $loc->N_("Poland"),
+ pt => $loc->N_("Portugal"),
+ ru => $loc->N_("Russia"),
+ se => $loc->N_("Sweden"),
+ sg => $loc->N_("Singapore"),
+ sk => $loc->N_("Slovakia"),
+ za => $loc->N_("South Africa"),
+ tw => $loc->N_("Taiwan"),
+ th => $loc->N_("Thailand"),
+ tr => $loc->N_("Turkey"),
+ uk => $loc->N_("United Kingdom"),
+ cn => $loc->N_("China"),
+ us => $loc->N_("United States"),
+ com => $loc->N_("United States"),
+ org => $loc->N_("United States"),
+ net => $loc->N_("United States"),
+ edu => $loc->N_("United States"),
+);
+my $us = [ qw(us 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(gb 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(gb 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(gb 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 / /, MDK::Common::File::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 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(
+ $loc->N("Error"),
+ $loc->N("Unable to add medium, errors reported:\n\n%s",
+ join("\n", map { MDK::Common::String::formatAlaTeX($_) } @error_msgs)) . "\n\n" . $loc->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, $loc->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($loc->N("Error"), $loc->N("Unable to create medium."));
+ return 0;
+ }
+
+ fatal_error:
+ interactive_msg($loc->N("Failure when adding medium"),
+ $loc->N("There was a problem adding medium:\n\n%s", $fatal_msg));
+ return 0;
+}
+
+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($loc->N("Error"), $loc->N($error_msg, join("\n", map { formatAlaTeX($_) } @error_msgs)), scroll => 1);
+ return 0;
+ }
+ return 1;
+}
+
+sub update_sources {
+ my ($urpm, %options) = @_;
+ my $cancel = 0;
+
+
+ my $factory = yui::YUI::widgetFactory;
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($loc->N("rpmdragora"));
+
+ my $dlg = $factory->createPopupDialog();
+ my $minSize = $factory->createMinSize( $dlg, 80, 5 );
+ my $vbox = $factory->createVBox($minSize);
+ my $hbox = $factory->createHBox($factory->createLeft($vbox));
+ my $label = $factory->createRichText($hbox, $loc->N("Please wait, updating media..."), 1 );
+ $label->setWeight($yui::YD_HORIZ, 1);
+ $label->setWeight($yui::YD_VERT, 1);
+
+ my $pb = $factory->createProgressBar( $vbox, "");
+ $pb->setValue(0);
+ # NOTE urpm::media::update_those_media seems not to say anything
+ # when downloads and tests md5sum
+ # a fake event (timeout 10 msec) allow to draw the waiting
+ # dialog
+ $dlg->waitForEvent(10);
+ $dlg->pollEvent();
+
+ my @media; @media = @{$options{medialist}} if ref $options{medialist};
+ my $outerfatal = $urpm->{fatal};
+ local $urpm->{fatal} = sub { $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) = @_;
+ goto cancel_update if $type !~ /^(?:start|progress|end)$/ && @media && !member($media, @media);
+ if ($type eq 'failed') {
+ $urpm->{fatal}->($loc->N("Error retrieving packages"),
+$loc->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, @_);
+ $dlg->pollEvent();
+ }
+ },
+ );
+
+ $pb->setValue(100);
+ $dlg->waitForEvent(10);
+ $dlg->pollEvent();
+
+cancel_update:
+ $dlg->destroy();
+}
+
+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->setValue(0);
+ $label->setValue($loc->N("Copying file for medium `%s'...", $file));
+ } elsif ($mode eq 'parse') {
+ $pb->setValue(0);
+ $label->setValue($loc->N("Examining file of medium `%s'...", $file));
+ } elsif ($mode eq 'retrieve') {
+ $pb->setValue(0);
+ $label->setValue($loc->N("Examining remote file of medium `%s'...", $file));
+ $medium = $file;
+ } elsif ($mode eq 'done') {
+ $pb->setValue(100);
+ $label->setValue($label->value() . $loc->N(" done."));
+ $medium = undef;
+ } elsif ($mode eq 'failed') {
+ $pb->setValue(100);
+ $label->setValue($label->value() . $loc->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
+ $loc->N("%s from medium %s", basename($file), $medium)
+ : basename($file);
+ if ($mode eq 'start') {
+ $pb->setValue(0);
+ $label->setValue($loc->N("Starting download of `%s'...", $file));
+ } elsif ($mode eq 'progress') {
+ if (defined $total && defined $eta) {
+ $pb->setValue($percent);
+ $label->setValue($loc->N("Download of `%s'\ntime to go:%s, speed:%s", $file, $eta, $speed));
+ } else {
+ $pb->setValue($percent);
+ $label->setValue($loc->N("Download of `%s'\nspeed:%s", $file, $speed));
+ }
+ }
+ }
+}
+
+
+sub update_sources_interactive {
+ my ($urpm, %options) = @_;
+
+ my @media = grep { ! $_->{ignore} } @{$urpm->{media}};
+ unless (@media) {
+ interactive_msg($loc->N("Warning"), $loc->N("No active medium found. You must enable some media to be able to update them."));
+ return 0;
+ }
+
+ my $appTitle = yui::YUI::app()->applicationTitle();
+
+ ## set new title to get it in dialog
+ yui::YUI::app()->setApplicationTitle($loc->N("Update media"));
+
+ my $retVal = 0;
+ my $mageiaPlugin = "mga";
+ my $factory = yui::YUI::widgetFactory;
+ my $mgaFactory = yui::YExternalWidgets::externalWidgetFactory($mageiaPlugin);
+ $mgaFactory = yui::YMGAWidgetFactory::getYMGAWidgetFactory($mgaFactory);
+
+ my $dialog = $factory->createPopupDialog();
+ my $minSize = $factory->createMinSize( $dialog, 60, 15 );
+ my $vbox = $factory->createVBox($minSize);
+
+ my $yTableHeader = new yui::YTableHeader();
+ $yTableHeader->addColumn("", $yui::YAlignBegin);
+ $yTableHeader->addColumn($loc->N("Media"), $yui::YAlignBegin);
+
+ my $mediaTable = $mgaFactory->createCBTable($vbox, $yTableHeader, $yui::YCBTableCheckBoxOnFirstColumn);
+
+ my $itemCollection = new yui::YItemCollection;
+ foreach (@media) {
+ my $item = new yui::YCBTableItem($_->{name});
+ $item->setLabel($_->{name});
+ $itemCollection->push($item);
+ $item->DISOWN();
+ }
+ $mediaTable->addItems($itemCollection);
+
+ # dialog buttons
+ $factory->createVSpacing($vbox, 1.0);
+ ## Window push buttons
+ my $hbox = $factory->createHBox( $vbox );
+
+ my $cancelButton = $factory->createPushButton($hbox, $loc->N("Cancel") );
+ my $selectButton = $factory->createPushButton($hbox, $loc->N("Select all") );
+ my $updateButton = $factory->createPushButton($hbox, $loc->N("Update") );
+
+ while(1) {
+ my $event = $dialog->waitForEvent();
+ my $eventType = $event->eventType();
+
+ #event type checking
+ if ($eventType == $yui::YEvent::CancelEvent) {
+ last;
+ }
+ elsif ($eventType == $yui::YEvent::WidgetEvent) {
+ # widget selected
+ my $widget = $event->widget();
+ my $wEvent = yui::toYWidgetEvent($event);
+
+ if ($widget == $cancelButton) {
+ last;
+ }
+ elsif ($widget == $selectButton) {
+ yui::YUI::app()->busyCursor();
+ yui::YUI::ui()->blockEvents();
+ $dialog->startMultipleChanges();
+ for (my $it = $mediaTable->itemsBegin(); $it != $mediaTable->itemsEnd(); ) {
+ my $item = $mediaTable->YItemIteratorToYItem($it);
+ if ($item) {
+ $mediaTable->checkItem($item, 1);
+ # NOTE for some reasons it is never == $mediaTable->itemsEnd()
+ if ($item->index() == $mediaTable->itemsCount()-1) {
+ last;
+ }
+ }
+ $it = $mediaTable->nextItem($it);
+ }
+ $dialog->recalcLayout();
+ $dialog->doneMultipleChanges();
+ yui::YUI::ui()->unblockEvents();
+ yui::YUI::app()->normalCursor();
+
+ }
+ elsif ($widget == $updateButton) {
+ yui::YUI::app()->busyCursor();
+ my @checked_media;
+ for (my $it = $mediaTable->itemsBegin(); $it != $mediaTable->itemsEnd(); ) {
+ my $item = $mediaTable->YItemIteratorToYItem($it);
+ $item = $mediaTable->toCBYTableItem($item);
+ if ($item) {
+ if ($item->checked()) {
+ push @checked_media, $item->label();
+ }
+ # NOTE for some reasons it is never == $mediaTable->itemsEnd()
+ if ($item->index() == $mediaTable->itemsCount()-1) {
+ last;
+ }
+ }
+ $it = $mediaTable->nextItem($it);
+ }
+
+ $retVal = update_sources_noninteractive($urpm, \@checked_media, %options);
+ yui::YUI::app()->normalCursor();
+ last;
+ }
+ }
+ }
+
+ $dialog->destroy();
+
+ #restore old application title
+ yui::YUI::app()->setApplicationTitle($appTitle) if $appTitle;
+
+ return $retVal;
+}
+
+sub update_sources_noninteractive {
+ my ($urpm, $media, %options) = @_;
+
+ urpm::media::select_media_by_name($urpm, $media);
+ update_sources_check(
+ $urpm,
+ {},
+ $loc->N_("Unable to update medium; it will be automatically disabled.\n\nErrors:\n%s"),
+ @$media,
+ );
+ return 1;
+}
+
+sub add_distrib_update_media {
+ my ($urpm, $mirror, %options) = @_;
+ #- ensure a unique medium name
+ my $medium_name = $ManaTools::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 warn_for_network_need {
+ my ($message, %options) = @_;
+ $message ||=
+ $branded
+ ? $loc->N("I need to access internet to get the mirror list.
+Please check that your network is currently running.
+
+Is it ok to continue?")
+ : $loc->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($loc->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($loc->N("Error during download"),
+ ($branded
+ ? $loc->N("There was an error downloading the mirror list:\n%s\n
+The network, or the website, may be unavailable.
+Please try again later.", $error)
+ : $loc->N("There was an error downloading the mirror list:\n%s\n
+The network, or the Mageia website, may be unavailable.
+Please try again later.", $error)), %options
+ );
+ return '';
+ }
+
+ !@mirrors and interactive_msg($loc->N("No mirror"),
+ ($branded
+ ? $loc->N("I can't find any suitable mirror.")
+ : $loc->N("I can't find any suitable mirror.\n
+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 @mirrorlist = map {$_->{country} . "|" . $_->{url}} @mirrors;
+
+ my $sh_gui = ManaTools::Shared::GUI->new();
+ my $mirror = $sh_gui->ask_fromTreeList({title => $loc->N("Mirror choice"),
+ header => $loc->N("Please choose the desired mirror."),
+ default_button => 1,
+ item_separator => "|",
+ default_item => $mirrors[0]->{url},
+ skip_path => 1,
+ list => \@mirrorlist }
+ );
+
+ return $mirror ? { url => $mirror} : undef;
+
+}
+
+
+
+
+
+
+
+#- 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(
+ $loc->N("Warning"),
+ $branded
+ ? $loc->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})
+ : $loc->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 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
+ MDK::Common::Func::before_leaving(sub { unlink $file });
+
+ my ($gurpm, $id, $canceled);
+ # display a message in statusbar (if availlable):
+ $::statusbar and $id = statusbar_msg(
+ $branded
+ ? $loc->N("Please wait, downloading mirror addresses.")
+ : $loc->N("Please wait, downloading mirror addresses from the Mageia website."),
+ 0);
+ my $_clean_guard = MDK::Common::Func::before_leaving {
+ undef $gurpm;
+ $id and statusbar_msg_remove($id);
+ };
+
+ require ManaTools::Rpmdragora::gurpm;
+ require ManaTools::Rpmdragora::pkg;
+
+ my $res = urpm::download::sync_url($urpm, $url,
+ dir => $cachedir,
+ callback => sub {
+ $gurpm ||=
+ ManaTools::Rpmdragora::gurpm->new(
+ text => $loc->N("Please wait"),
+ );
+ $canceled ||=
+ !ManaTools::Rpmdragora::pkg::download_callback($gurpm, @_);
+ $gurpm->flush();
+ },
+ );
+ $res or die $loc->N("retrieval of [%s] failed", $file) . "\n";
+ return $canceled ? () : MDK::Common::File::cat_($file);
+ });
+ my @mirrors = @{ mirror::list(urpm::mirrors::parse_LDAP_namespace_structure(MDK::Common::File::cat_('/etc/product.id')), 'distrib') || [] };
+
+ require ManaTools::Shared::TimeZone;
+ my $tzo = ManaTools::Shared::TimeZone->new();
+ my $tz = $tzo->readConfiguration()->{ZONE};
+ foreach my $mirror (@mirrors) {
+ my $goodness = 0;
+ my $pri_mirr = defined ($t2l{$tz}) ? $t2l{$tz} : $us;
+ my $ind = 0;
+ foreach (@{$pri_mirr}) {
+ if (($_ eq lc($mirror->{zone})) || ($_ eq lc($mirror->{country}))) {
+ $goodness = scalar(@{$pri_mirr}) - $ind;
+ }
+ $ind ++;
+ }
+
+ $mirror->{goodness} = $goodness + rand();
+ $mirror->{country} = $u2l{lc($mirror->{country})} ? $loc->N($u2l{lc($mirror->{country})}) : $mirror->{country};
+ }
+ unless (-x '/usr/bin/rsync') {
+ @mirrors = grep { $_->{url} !~ /^rsync:/ } @mirrors;
+ }
+ return sort { $b->{goodness} <=> $a->{goodness} } @mirrors;
+}
+
+
+
+
+
+sub open_help {
+ my ($mode) = @_;
+ require ManaTools::Shared::RunProgram;
+ ManaTools::Shared::RunProgram::raw({ detach => 1, as_user => 1 }, 'drakhelp', '--id', $mode ? "software-management-$mode" : 'software-management');
+ my $_s = $loc->N("Help launched in background");
+ statusbar_msg($loc->N("The help window has been started, it should appear shortly on your desktop."), 1);
+}
+
+sub run_drakbug {
+ my ($id) = @_;
+ require ManaTools::Shared::RunProgram;
+ ManaTools::Shared::RunProgram::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;