From 7b56e13962dfa49dba1f064a1ba0ab5cb27a179c Mon Sep 17 00:00:00 2001 From: Dexter Morgan Date: Mon, 7 Feb 2011 11:58:29 +0000 Subject: tag 1.13.3 --- userdrake | 1329 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1329 insertions(+) create mode 100755 userdrake (limited to 'userdrake') diff --git a/userdrake b/userdrake new file mode 100755 index 0000000..1c1bc15 --- /dev/null +++ b/userdrake @@ -0,0 +1,1329 @@ +#!/usr/bin/perl +################################################################################ +# # +# # +# Copyright (C) 2003-2009 Mandriva # +# # +# Daouda Lo # +# Thierry Vignaud # +# # +# This program is free software; you can redistribute it and/or modify # +# it under the terms of the GNU General Public License Version 2 as # +# published by the Free Software Foundation. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program; if not, write to the Free Software # +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # +################################################################################ + +use strict; +use lib qw(/usr/lib/libDrakX); +# i18n: IMPORTANT: to get correct namespace (userdrake instead of libDrakX) +BEGIN { unshift @::textdomains, 'userdrake', 'libuser', 'drakconf' } +use standalone; +use transfugdrake; + +use common; +use run_program; +use any; +use mygtk2 qw(gtknew); #- do not import gtkadd which conflicts with ugtk2 version +use ugtk2 qw(:all); +use interactive; +use POSIX qw(mktime ceil); +use USER; +#Only for Debugging +#use Devel::Peek; +use Gtk2::Gdk::Keysyms; +use utf8; +use log; + +$ugtk2::wm_icon = "userdrake"; + +my $conffile = '/etc/sysconfig/userdrake'; +my $secfile = '/etc/sysconfig/msec'; +my $pixdir = '/usr/share/userdrake/pixmaps/'; +my @pix = ($pixdir . 'selected.png', $pixdir . 'unselected.png'); + +my $in = interactive->vnew('su'); + +my $us = {}; +$us->{VERSION} = '1.13.3'; + +my $window_splash = Gtk2::Window->new('popup'); +$window_splash->signal_connect(delete_event => \&QuitGlobal); +$window_splash->set_title(N("Userdrake") . $us->{VERSION}); +$window_splash->set_position('center_always'); +$window_splash->add(gtkadd(gtkset_shadow_type(Gtk2::Frame->new, 'etched_out'), + gtkpack(Gtk2::VBox->new(0, 0), + gtkcreate_img("$pixdir/userdrake.png"), + Gtk2::Label->new(N("Loading Users and Groups... Please wait")) + ) + ) + ); +$window_splash->show_all; +gtkflush(); + +#my $wait = $in->wait_message(N("Please wait"), N("Loading User and Groups")); +#gtkflush(); + +my $error = 0; +my $GetValue = -65533; +my $stringsearch = ''; +my %prefs = getVarsFromSh($conffile); +my %sec = getVarsFromSh($secfile); +my $sysfilter = text2bool($prefs{FILTER}); + +sub HelpSystem() { run_program::raw({ detach => 1 }, 'drakhelp', '--id', 'userdrake') } + +$us->{wnd} = ugtk2->new(N("Mageia Users Management Tool") . " " . $us->{VERSION}); +$::main_window = $us->{wnd}{real_window}; +gtkset_size_request($us->{wnd}{rwindow}, 660, 460); +$us->{wnd}{rwindow}->set_position('center') if !$::isEmbedded; +$us->{wnd}{window}->signal_connect(delete_event => \&QuitGlobal); +my $utree_model = Gtk2::ListStore->new("Glib::String", "Glib::Int", "Glib::String", + "Glib::String", "Glib::String", "Glib::String", "Glib::String"); +my $gtree_model = Gtk2::ListStore->new("Glib::String", "Glib::Int", "Glib::String"); +my ($usertree, $grouptree); +$usertree = CreateTree($utree_model); $grouptree = CreateTree($gtree_model); +# slightly verbatimed from control-center + +my %xguest_labels = ( + to_install => N("/_Install guest account"), + installed => N("/_Uninstall guest account"), +); + +my %options = ( + 'edit' => [ N("/_Actions"), N("/_Edit") ], + 'delete' => [ N("/_Actions"), N("/_Delete") ], + 'xguest' => [ N("/_Actions"), $xguest_labels{installed} ], + 'filter' => [ N("/_Options"), N("/_Filter system users") ] + ); +my %buttorcheck; +my ($menu, $factory) = create_factory_menu( + $::isEmbedded ? $::Plug : $us->{wnd}{rwindow}, + ([ N("/_File"), undef, undef, undef, '' ], + [ N("/_File") . N("/_Refresh"), undef, + sub { Refresh($sysfilter, $stringsearch) }, + undef, '' ], + [ N("/_File") . N("/_Quit"), N("Q"), \&QuitGlobal, undef, '' ], + [ N("/_Actions"), undef, undef, undef, '' ], + [ N("/_Actions") . N("/_Add User"), undef, \&AddUser, undef, '' ], + [ N("/_Actions") . N("/Add _Group"), undef, \&AddGroup, undef, '' ], + [ join('', @{$options{edit}}), undef, \&Edit, undef, '' ], + [ join('', @{$options{delete}}), undef, \&Delete, undef, '' ], + [ join('', @{$options{xguest}}), undef, \&Xguest, undef, '' ], + [ N("/_Options"), undef, undef, undef, '' ], + [ join('', @{$options{filter}}), undef, sub { + $sysfilter = $buttorcheck{filter}->get_active; + Refresh($sysfilter, $stringsearch); + }, undef, '' ], + [ N("/_Help"), undef, undef, undef, '' ], + [ N("/_Help") . N("/_Help"), undef, sub { HelpSystem() }, undef, '' ], + [ N("/_Help") . N("/_Report Bug"), undef, + sub { run_program::raw({ detach => 1 }, 'drakbug', '--report', 'userdrake') }, + undef, '' ], + [ N("/_Help") . N("/_About..."), undef, \&About, undef, '' ] + ) +); +%buttorcheck = map { + $_ => $factory->get_widget("
" . join '', + map { s/_//; $_ } @{$options{$_}}) } + ('edit', 'delete', 'filter', 'xguest'); + +if (defined $buttorcheck{filter}) { + $buttorcheck{filter}->set_active($sysfilter); +} else { + print STDERR "BUG with LANGUAGE $ENV{LANGUAGE}\n"; +} + +my $toolb = Gtk2::Toolbar->new; +my $filter; +my $searchBox = gtkpack_(Gtk2::HBox->new(0,5), + 1, Gtk2::Label->new(""), + 0, Gtk2::Label->new(N("Search:")), + 0, gtksignal_connect($filter = Gtk2::Entry->new, + key_press_event => sub { + $_[1]->keyval == $Gtk2::Gdk::Keysyms{Return} + and Refresh($sysfilter, $filter->get_text); + }), + 0, my $fbut = Gtk2::Button->new(N("Apply filter")), + ); +gtkappend_page(my $nb = Gtk2::Notebook->new, + gtkpack(create_scrolled_window($usertree)), + gtkshow(Gtk2::Label->new(N("Users")))); +#PO: list of users belonging to that group +gtkappend_page($nb, + gtkpack(create_scrolled_window($grouptree)), + gtkshow(Gtk2::Label->new(N("Groups")))); +$nb->set_show_border(0); +my @extra_widgets; +if ($::isEmbedded) { + push @extra_widgets, 0, Gtk2::Banner->new("/usr/share/mcc/themes/default/user-mdk.png", + #-PO: do not translate, this is already translated in mcc + N("Users and groups")); +} +$us->{wnd}{window}->add(gtkpack_(Gtk2::VBox->new(0, 0), + 0, $menu, + @extra_widgets, + 0, $toolb, + 0, $searchBox, + 0, Gtk2::HSeparator->new, + 1, $nb)); +my @ucolsize = (60, 45, 40, 120, 80, 120, 50, -1); +my @gcolsize = (100, 80, 160, -1); +each_index { + my $col = Gtk2::TreeViewColumn->new_with_attributes($_, + Gtk2::CellRendererText->new, + 'text' => $::i); + $col->set_sort_column_id($::i); + $col->set_min_width($ucolsize[$::i]); + $usertree->append_column($col); +} (N("User Name"), N("User ID"), N("Primary Group"), + N("Full Name"), N("Login Shell"), N("Home Directory"), N("Status")); + +each_index { + my $col = Gtk2::TreeViewColumn->new_with_attributes($_, Gtk2::CellRendererText->new, 'text' => $::i); + $col->set_sort_column_id($::i); + $col->set_min_width($gcolsize[$::i]); + $grouptree->append_column($col); +} (N("Group Name"), N("Group ID"), N("Group Members")); +my @toolbwg; +foreach ([ N("Add User"), N("Add a user to the system"), 'user_add', \&AddUser ], + [ N("Add Group"), N("Add a group to the system"), 'group_add', \&AddGroup ], + [ N("Edit"), N("Edit selected row"), 'user_conf', \&Edit ], + [ N("Delete"), N("Delete selected row"), 'user_del', \&Delete ], + [ N("Refresh"), N("Refresh the list"), 'refresh', + sub { Refresh($sysfilter, $stringsearch) } ]) { + my $t = $toolb->append_item($_->[0], $_->[1], $_->[2], + Gtk2::Image->new_from_file($pixdir . $_->[2] . '.png'), + $_->[3], $toolb); + push(@toolbwg, $t); + # $toolb->append_space; +} + +my ($_tbuser, $_tbgroup, $tbedit, $tbdel, $_tbref) = @toolbwg; +GrayDelEdit(); +RefreshXguest(); + +my $ctx = USER::ADMIN->new; +$fbut->signal_connect('clicked', sub { + $stringsearch = $filter->get_text; + Refresh($sysfilter, $stringsearch); + }); +Refresh($sysfilter, $stringsearch); +$nb->signal_connect('switch-page' => sub { NotebookSwitch() }); +$us->{wnd}{rwindow}->show_all; +#undef $wait; +$window_splash->destroy; +undef $window_splash; + +$us->{wnd}->main; +ugtk2->exit(0); + +sub is_xguest_installed() { + -e '/etc/security/namespace.d/guest.conf'; +} + +sub GrayDelEdit() { + foreach ($tbedit, $tbdel, $buttorcheck{edit}, $buttorcheck{delete}) { + defined $_ and $_->set_sensitive(0); + } +} + +sub RefreshXguest { + my ($o_is_already_refreshed) = @_; + my $label = $xguest_labels{is_xguest_installed() ? 'installed' : 'to_install'}; + $label =~ s!^/!!; + $buttorcheck{xguest}->get_child->set_label($label); + Refresh($sysfilter, $stringsearch) if !$o_is_already_refreshed; +} + +sub TreeUnselect { + my $treev = shift; + $treev->get_selection->unselect_all; + GrayDelEdit(); + RefreshXguest(); +} + +sub NotebookSwitch() { + #my $page = $nb->get_current_page; + TreeUnselect($usertree); TreeUnselect($grouptree); +} + +sub ComputeLockExpire { + my $l = shift; + my $ep = $l->ShadowExpire($GetValue); + my $tm = ceil(time()/(24*60*60)); + $ep = -1 if int($tm) <= $ep; + my $status = $ctx->IsLocked($l) ? N("Locked") : ($ep != -1 ? N("Expired") : ''); + $status; +} + +sub RefreshUsersFull { + my ($filterusers, $strfilt) = @_; + my ($users, $group, $groupnm, $expr); + defined $ctx and $users = $ctx->UsersEnumerateFull; + $utree_model->clear; + my @UserReal; + LOOP: foreach my $l (@$users) { + next LOOP if $filterusers && $l->Uid($GetValue) <= 499 || $l->Uid($GetValue) == 65534; + push @UserReal, $l if $l->UserName($GetValue) =~ /^\Q$strfilt/; + } + my $i; + foreach my $l (@UserReal) { + $i++; + my $uid = $l->Uid($GetValue); + if (!defined $uid) { + warn "bogus user at line $i\n"; + next; + } + my $a = $l->Gid($GetValue); + $group = $ctx->LookupGroupById($a); + $groupnm = ''; + $expr = ComputeLockExpire($l); + $group and $groupnm = $group->GroupName($GetValue); + my $s = $l->Gecos($GetValue); + c::set_tagged_utf8($s); + $utree_model->append_set([ 0 => $l->UserName($GetValue), + 1 => $l->Uid($GetValue), + 2 => $groupnm, + 3 => $s, + 4 => $l->LoginShell($GetValue), + 5 => $l->HomeDir($GetValue), + 6 => $expr ]); + } +} + +sub RefreshGroupsFull { + my ($filtergroups, $strfilt) = @_; + my $groups; + defined $ctx and $groups = $ctx->GroupsEnumerateFull; + $gtree_model->clear; + my @GroupReal; + LOOP: foreach my $g (@$groups) { + next LOOP if $filtergroups && $g->Gid($GetValue) <= 499 || $g->Gid($GetValue) == 65534; + push @GroupReal, $g if $g->GroupName($GetValue) =~ /^\Q$strfilt/; + } + foreach my $g (@GroupReal) { + my $a = $g->GroupName($GetValue); + #my $group = $ctx->LookupGroupById($a); + my $u_b_g = $a && $ctx->EnumerateUsersByGroup($a); + my $listUbyG = join(',', @$u_b_g); + my $group_id = $g->Gid($GetValue); + $gtree_model->append_set([ 0 => $g->GroupName($GetValue), + if_($group_id, 1 => $group_id), + if_($listUbyG, 2 => $listUbyG) ]); + } +} + +sub Refresh { + my ($filt, $strfilt) = @_; + RefreshUsersFull($filt, $strfilt); + RefreshGroupsFull($filt, $strfilt); + GrayDelEdit(); + RefreshXguest(1); +} + +sub GetFaceIcon { + my ($user) = @_; + my @icons = any::facesnames(); + my $i; + my $current_icon; + my $user_icon = "$::prefix/usr/share/faces/$user.png"; + gtkpack_(my $hb = Gtk2::HBox->new(0, 2), + 0, Gtk2::Label->new(N("Click on the icon to change it") . ' '), + 0, my $bt = Gtk2::Button->new + ); + my $set = sub { + my ($icon) = @_; + my $f = $icon =~ m,^/, ? $icon : any::face2png($icon); + $us->{o}{iconval} = $icon; + gtkdestroy($us->{o}{icon}); + # so that we do display "no icon" if either file doesn't exist or it's corrupted: + undef $us->{o}{icon}; + $us->{o}{icon} = eval { gtkcreate_img($f) } if -e $f; + $us->{o}{icon} ||= Gtk2::Label->new("No Icon"); + $bt->add($us->{o}{icon}); + $us->{o}{icon}->show; + }; + if (-e $user_icon) { + my $current_md5 = common::md5file($user_icon); + eval { $i = find_index { common::md5file(any::face2png($_)) eq $current_md5 } @icons }; + if (!$@) { #- 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 = @icons - 1; + } + } else { + #- no icon yet, select a random one + $current_icon = $icons[$i = rand(@icons)]; + } + $set->($current_icon); + my $leave = 0; + $bt->signal_connect(enter => sub { $leave = 0 }); + $bt->signal_connect(leave => sub { $leave = 1 }); + $bt->signal_connect(button_release_event => sub { + return if $leave; + if ($_[1]->button == 1) { + $i = defined $icons[$i+1] ? $i+1 : 0; + } else { + $i = defined $icons[$i-1] ? $i-1 : @icons-1; + } + $set->($icons[$i]); + }); + $hb; +} + +sub AddUser() { + my $w = NewWindow(N("Create New User")); + my $dontcreatehomedir = 0; my $is_system = 0; + my %u; + gtkpack_($w->vbox, + 0, BuildUui(), + 0, Gtk2::HSeparator->new, + 0, $us->{o}{createhomedir} = Gtk2::CheckButton->new(N("Create Home Directory")), + 0, gtkpack_(my $vv = Gtk2::HBox->new(0, 4), + 0, Gtk2::Label->new(N("Home Directory: ")), + 0, $us->{o}{homedir} = Gtk2::Entry->new + ), + 0, $us->{o}{privategroup} = Gtk2::CheckButton->new(N("Create a private group for the user")), + 0, $us->{o}{userid} = Gtk2::CheckButton->new(N("Specify user ID manually")), + 0, gtkset_sensitive(my $h = Gtk2::HBox->new(0, 4), 0), + 0, Gtk2::HSeparator->new, + 0, GetFaceIcon() + ); + gtkadd($w->action_area, + map { + my $r = $_->[1]; + gtksignal_connect( + Gtk2::Button->new($_->[0]), + clicked => sub { + if (!$r) { + $u{username} = $us->{o}{login}->get_text; $error = 0; + $u{gecos} = $us->{o}{fullname}->get_text; + if (!valid_username($u{username})) { RaiseError($us->{error}) } + my $nm = !$error && $ctx->LookupUserByName($u{username}); + if ($nm) { + RaiseError(N("User already exists, please choose another User Name")); + $us->{o}{login}->set_text(''); + } + $u{passwd} = $us->{o}{passwd}->get_text; + if ($u{passwd} ne $us->{o}{confpasswd}->get_text) { + RaiseError(N("Password Mismatch")); + } + if ($sec{SECURE_LEVEL} > 3 && length($u{passwd}) < 6) { + RaiseError(N("This password is too simple. \n Good passwords should be > 6 characters")); + } + my $userEnt = !$error && $ctx->InitUser($u{username}, $is_system); + if (!$error && $us->{o}{createhomedir}->get_active) { + $dontcreatehomedir = 0; + $u{homedir} = $us->{o}{homedir}->get_text; + $userEnt and $userEnt->HomeDir($u{homedir}); + } else { + $dontcreatehomedir = 1; + } + if (!$error && $us->{o}{userid}->get_active) { + if (($u{uid} = $us->{o}{uid}->get_value) < 500) { + my $uidchoice = GimmeChoice(N("User Uid is < 500"), + N("Creating a user with a UID less than 500 is not recommended.\n Are you sure you want to do this?\n\n")); + $uidchoice and $userEnt->Uid($u{uid}); + } else { $userEnt->Uid($u{uid}) } + } + if ($us->{o}{privategroup}->get_active) { + if (!$error) { + #Check if group exist + my $gr = $ctx->LookupGroupByName($u{username}); + if ($gr) { + my $groupchoice = ChooseGroup(); + if ($groupchoice == 0 && !$error) { + #You choose to put it in the existing group + $u{gid} = $gr->Gid($GetValue); + } elsif ($groupchoice == 1) { + # Put it in 'users' group + log::explanations(N("Putting %s to 'users' group", + $u{username})); + $u{gid} = Add2UsersGroup($u{username}); + } + } else { + #it's a new group: Add it + my $newgroup = $ctx->InitGroup($u{username},$is_system); + log::explanations(N("Creating new group: %s", $u{username})); + $u{gid} = $newgroup->Gid($GetValue); + $ctx->GroupAdd($newgroup); + } + } + } else { + !$error and $u{gid} = Add2UsersGroup($u{username}); + } + if (!$error) { + log::explanations(N("Adding user: %s", $u{username})); + $u{loginshell} = $us->{o}{shells}->entry->get_text; + $userEnt->Gecos($u{gecos}); $userEnt->LoginShell($u{loginshell}); + $userEnt->Gid($u{gid}); + $userEnt->ShadowMin(-1); $userEnt->ShadowMax(99999); + $userEnt->ShadowWarn(-1); $userEnt->ShadowInact(-1); + $ctx->UserAdd($userEnt, $is_system, $dontcreatehomedir); + $ctx->UserSetPass($userEnt, $u{passwd}); + defined $us->{o}{iconval} and + any::addKdmIcon($u{username}, $us->{o}{iconval}); + Refresh($sysfilter, $stringsearch); + transfugdrake::get_windows_disk() + and $in->ask_yesorno(N("Migration wizard"), + 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'); + } + } + !$error and $w->destroy; $error = 0 }); + } ([ N("Cancel"), 1 ], [ N("Ok"), 0 ]), + ); + foreach (qw(privategroup createhomedir)) { $us->{o}{$_}->set_active(1) } + GrayBox($us->{o}{createhomedir}, $vv, 1); + $us->{o}{login}->signal_connect( + 'focus_out_event' => sub { + my $fullname = $us->{o}{fullname}->get_text; + $us->{o}{homedir}->set_text("/home/" . $us->{o}{login}->get_text); + $fullname or $us->{o}{fullname}->set_text($us->{o}{login}->get_text); + 0; # Gdk expect focus event handlers to return false + }); + $us->{o}{uid} = Gtk2::SpinButton->new(Gtk2::Adjustment->new(500, 1, 65000, 1, 10, 10), 1, 0); + $h->pack_end($us->{o}{uid}, 0, 0, 4); + $h->pack_end(Gtk2::Label->new(N("UID: ")), 0, 0, 4); + GrayBox($us->{o}{userid}, $h, 0); + $w->show_all; +} + +sub Add2UsersGroup { + my $name = shift; + my $usersgroup = $ctx->LookupGroupByName('users'); + $usersgroup->MemberName($name, 1); + return $usersgroup->Gid($GetValue); +} + +sub ChooseGroup() { + my $w = NewWindow(N("Choose group")); + my $choice; + my @radio = gtkradio(N("Add to the existing group"), + (N("Add to the existing group"), + N("Add to the 'users' group"))); + gtkadd($w->vbox, + Gtk2::Label->new(N("A group with this name already exists. What would you like to do?")), + gtkpack(Gtk2::VBox->new(0,0), @radio), + ); + gtkadd($w->action_area, + gtksignal_connect( + Gtk2::Button->new(N("Ok")), + clicked => sub { + each_index { $_->get_active and $choice = $::i } @radio; + $w->destroy; + Gtk2->main_quit; + }), + gtksignal_connect( + Gtk2::Button->new(N("Cancel")), + clicked => sub { + $error = 1; + $w->destroy; + Gtk2->main_quit; + })); + $w->show_all; + Gtk2->main; + $choice; +} +sub GimmeChoice { + my ($title, $text) = @_; + my $choice = $in->ask_yesorno($title, $text) or $error = 1; + gtkset_mousecursor_normal(); + $choice; +} + +sub AddGroup() { + my $w = NewWindow(N("Create New Group")); + my $mode = 0; my %g; my $is_system = 0; + gtkpack_($w->vbox, + 0, BuildGui(), + 0, Gtk2::HSeparator->new, + 0, $us->{o}{groupid} = Gtk2::CheckButton->new(N("Specify group ID manually")), + 0, gtkset_sensitive(my $h = Gtk2::HBox->new(0, 4), 0), + ); + gtkadd($w->action_area, + map { + my $r = $_->[1]; + gtksignal_connect( + Gtk2::Button->new($_->[0]), + clicked => sub { + if (!$r) { + $g{groupname} = $us->{o}{groupname}->get_text; $error = 0; + if (!valid_groupname($g{groupname})) { + RaiseError($us->{error}); + } + my $nm = $ctx->LookupGroupByName($g{groupname}); + if ($nm) { + RaiseError(N("Group already exists, please choose another Group Name")); + $us->{o}{groupname}->set_text(''); + } + my $groupEnt = $ctx->InitGroup($g{groupname}, $is_system); + if ($us->{o}{groupid}->get_active) { + if (($g{gid} = $us->{o}{gid}->get_value) < 500) { + my $gidchoice = GimmeChoice(N(" Group Gid is < 500"), + N("Creating a group with a GID less than 500 is not recommended.\n Are you sure you want to do this?\n\n")); + $gidchoice and $groupEnt->Gid($g{gid}); + } else { + $groupEnt->Gid($g{gid}); + } + } + if (!$error) { + log::explanations(N("Adding group: %s ", $g{groupname})); + $ctx->GroupAdd($groupEnt); + Refresh($sysfilter, $stringsearch); + } + } + !$error and $w->destroy; $error = 0 }); + } ([ N("Cancel"), 1 ], [ N("Ok"), 0 ]) + ); + $us->{o}{gid} = Gtk2::SpinButton->new(Gtk2::Adjustment->new(500, 1, 65000, 1, 10, 10), 1, 0); + $h->pack_end($us->{o}{gid}, 0, 0, 4); + $h->pack_end(Gtk2::Label->new(N("GID: ")), 0, 0, 4); + $us->{o}{groupid}->signal_connect('clicked' => sub { $mode = !$mode; $h->set_sensitive($mode) }); + $w->show_all; +} + +sub UpdateOrDelUsersInGroup { + my ($name, $action) = @_; + my $groups = $ctx->GroupsEnumerateFull; + if ($action) { + foreach my $g (@$groups) { + my $members = $g->MemberName(1, 0); + if (InArray($name, $members)) { + eval { $g->MemberName($name, 2) }; + eval { $ctx->GroupModify($g) }; + } + } + } +} + +sub GetNameEntFromIter { + my ($tree, $model, $rank) = @_; + my (undef, $iter) = $tree->get_selection->get_selected; + my $name = $model->get($iter, $rank); + $name; +} + +sub FillUserInfo { + my $ent = shift; + my $s = $ent->Gecos($GetValue); + c::set_tagged_utf8($s); + $us->{o}{fullname}->set_text($s); + $us->{o}{passwd}->set_text(' '); + $us->{o}{confpasswd}->set_text(' '); + $us->{o}{shells}->entry->set_text($ent->LoginShell($GetValue)); + $us->{o}{homedir}->set_text($ent->HomeDir($GetValue)); +} + +sub UserDelete() { + my ($checkhome, $checkspool); + my $username = GetNameEntFromIter($usertree, $utree_model, 0); + my $userEnt = $ctx->LookupUserByName($username); + # Old Delete version + #my $removehome = GimmeChoice(N(" Remove Home Directory"), N("Do you want to delete the user's home directory and mail spool?")); + #$removehome and $ctx->Clean($userEnt); + # New version + my $w = NewWindow(N("Delete files or not?")); + my $hd = $userEnt->HomeDir($GetValue); + gtkpack_($w->vbox, + 0, Gtk2::Label->new(N("Deleting user %s\n Also perform the following actions\n", + $username)), + 0, $checkhome = Gtk2::CheckButton->new(N("Delete Home Directory: %s", $hd)), + 0, $checkspool = Gtk2::CheckButton->new(N("Delete Mailbox: /var/spool/mail/%s", + $username)), + ); + gtkadd($w->action_area, + map { + my $r = $_->[1]; + gtksignal_connect(Gtk2::Button->new($_->[0]), clicked => + sub { + if (!$r) { + log::explanations(N("Removing user: %s", $username)); + $ctx->UserDel($userEnt); + UpdateOrDelUsersInGroup($username, 1); + #Let's check out the user's primary group + my $usergid = $userEnt->Gid($GetValue); + my $groupEnt = $ctx->LookupGroupById($usergid); + if ($groupEnt) { + my $member = $groupEnt->MemberName(1, 0); + if (scalar(@$member) == 0 && $groupEnt->Gid($GetValue) > 499) { + $ctx->GroupDel($groupEnt); + } + } + if ($checkhome->get_active) { + eval { $ctx->CleanHome($userEnt) }; + $@ and RaiseError($@); + } + if ($checkspool->get_active) { + eval { $ctx->CleanSpool($userEnt) }; + $@ and RaiseError($@); + } + Refresh($sysfilter, $stringsearch); + } + !$error and $w->destroy; $error = 0 }); + } ([ N("Cancel"), 1 ], [ N("Delete"), 0 ]), + ); + if ($hd !~ m!(?:/home|/var/spool)!) { $checkhome->set_sensitive(0); $checkspool->set_sensitive(0) } + $w->show_all; +} + +sub GroupDelete() { + my $groupname = GetNameEntFromIter($grouptree, $gtree_model, 0); + my $wg = NewWindow(translate("Warning")); + gtkadd($wg->vbox, + Gtk2::Label->new(N("Do you really want to delete the group %s?", $groupname)), + ); + gtkadd($wg->action_area, + map { + my $r = $_->[1]; + gtksignal_connect(Gtk2::Button->new($_->[0]), clicked => + sub { + if (!$r) { + my $groupEnt = $ctx->LookupGroupByName($groupname); + my $members = $ctx->EnumerateUsersByGroup($groupname); + GLOOP: foreach my $username (@$members) { + my $userEnt = $ctx->LookupUserByName($username); + if ($userEnt && $userEnt->Gid($GetValue) == $groupEnt->Gid($GetValue)) { + RaiseError(N("%s is a primary group for user %s\n Remove the user first", $groupname, $username)); + last GLOOP; + } + } + if (!$error) { + log::explanations(N("Removing group: %s", $groupname)); + eval { $ctx->GroupDel($groupEnt) }; Refresh($sysfilter, $stringsearch); + } + } + !$error and $wg->destroy; $error = 0 }); + } ([ N("Cancel"), 1 ], [ N("Delete"), 0 ]), + ); + $wg->show_all; +} + +sub Delete() { + my $page = $nb->get_current_page; + $us->{wnd}{rwindow}->set_sensitive(0); + gtkset_mousecursor_wait(); $error = 0; + if ($page <= 0) { + UserDelete(); + } elsif ($page == 1) { + GroupDelete(); + } + $us->{wnd}{rwindow}->set_sensitive(1); + gtkset_mousecursor_normal(); +} + +# Gtk Facilities +sub CreateTree { + my ($tree_model) = @_; + my $tree = Gtk2::TreeView->new_with_model($tree_model); + $tree->get_selection->set_mode('browse'); + $tree->set_headers_visible(1); + $tree->set_rules_hint(1); + $tree->get_selection->signal_connect( + 'changed' => sub { + foreach ($tbedit, $tbdel, $buttorcheck{edit}, $buttorcheck{delete}) { + $_->set_sensitive(1); + } + }); + my $menu_treeview = Gtk2::Menu->new; + my @menu_treeview_actions = ([ 'edit', N("Edit") ], [ 'delete', N("Delete") ]); + foreach (@menu_treeview_actions) { + my ($action, $text) = @$_; + my %actions; %actions = ( + edit => sub { + my (undef, $iter) = $tree->get_selection->get_selected; + $iter and Edit(); + }, + delete => sub { + my (undef, $iter) = $tree->get_selection->get_selected; + $iter and Delete(); + } + ); + $menu_treeview->append( + gtksignal_connect( + gtkshow(Gtk2::MenuItem->new_with_label($text)), + activate => sub { $actions{$action}->() })); + } + $tree->signal_connect( + button_press_event => sub { + my (undef, $event) = @_; + my (undef, $iter) = $tree->get_selection->get_selected; + return unless $iter; + foreach ($tbedit, $tbdel, $buttorcheck{edit}, $buttorcheck{delete}) { + $_->set_sensitive(1); + } + Edit() if $event->type eq '2button-press'; + $_[1]->button == 3 + and $menu_treeview->popup(undef, undef, undef, undef, $_[1]->button, $_[1]->time); + }); + $tree->signal_connect( + key_press_event => sub { + my (undef, $event) = @_; + my (undef, $iter) = $tree->get_selection->get_selected; + return unless $iter; + Edit() if $event->keyval == $Gtk2::Gdk::Keysyms{Return}; + }); + $tree; +} + +sub GtkEntryHidePass { + my ($o_text) = @_; + my $e = gtknew('WeaknessCheckEntry'); + $o_text and $e->set_text($o_text); + $e->set_visibility(0); + $e; +} + +sub GtkEntrySized { + my ($i, $spac) = @_; + my $e = Gtk2::Entry->new_with_max_length($i); + $e->set_size_request($spac, 20); + $e; +} + +sub BuildUui { + my ($o_extra_widget) = @_; + gtkpack_(my $vbox = Gtk2::VBox->new(0, 2), + 1, create_packtable({ homogeneous => 1, col_spacings => 5, row_spacings => 5 }, + [ gtknew('Label_Left', text => N("Full Name:")), + $us->{o}{fullname} = Gtk2::Entry->new ], + [ gtknew('Label_Left', text => N("Login:")), + $us->{o}{login} = Gtk2::Entry->new ], + [ gtknew('Label_Left', text => N("Password:")), + $us->{o}{passwd} = GtkEntryHidePass() ], + [ gtknew('Label_Left', text => N("Confirm Password:")), + $us->{o}{confpasswd} = GtkEntryHidePass() ], + [ gtknew('Label_Left', text => N("Login Shell:")), + $us->{o}{shells} = Gtk2::Combo->new ], + $o_extra_widget, + ) + ); + $us->{o}{shells}->set_popdown_strings(@{$ctx->GetUserShells}); + $us->{o}{shells}->entry->set_text("/bin/bash"); + $vbox; +} + +sub BuildGui() { + gtkpack_(my $vbox = Gtk2::VBox->new(0, 2), + 1, create_packtable({ homogeneous => 1, col_spacings => 5, row_spacings =>5 }, + [ N("Group Name:"), $us->{o}{groupname} = Gtk2::Entry->new ] + ) + ); + $vbox; +} + +sub NewWindow { + my ($title) = @_; + my $dialog = gtkset_border_width( + _create_dialog($title, { transient_for => $us->{wnd}{real_window} }), + 5); + $dialog->signal_connect(delete_event => sub { $dialog->destroy }); + my $prev_main_window = $::main_window; + $::main_window = $dialog; + $dialog->signal_connect(destroy => sub { $::main_window = $prev_main_window }); + $dialog; +} + +sub Xguest() { + if (is_xguest_installed()) { + run_program::run('rpm', '-e', 'xguest'); + } else { + run_program::run('gurpmi2', 'xguest'); + } + RefreshXguest(); +} + +sub UserEdit_valid { + my ($userEnt, $model, $primgid) = @_; + my ($Exp, $gEnt, $ugid); + my $error = 0; + my %u = ( + username => $us->{o}{login}->get_text, gecos => $us->{o}{fullname}->get_text, + homedir => $us->{o}{homedir}->get_text, pw => $us->{o}{passwd}->get_text, + confm => $us->{o}{confpasswd}->get_text, shell => $us->{o}{shells}->entry->get_text + ); + if (!valid_username($u{username})) { + RaiseError($us->{error}); + } + if ($u{pw} ne $u{confm}) { + RaiseError(N("Password Mismatch")); + } elsif ($u{pw} eq $u{confm} && $u{pw} ne ' ') { + if ($sec{SECURE_LEVEL} > 3 && length($u{pw}) < 6) { + RaiseError(N("This password is too simple. \n Good passwords should be > 6 characters")); + } + !$error and $ctx->UserSetPass($userEnt, $u{pw}); + } + if (!$error) { + $userEnt->UserName($u{username}); + $userEnt->Gecos($u{gecos}); + $userEnt->HomeDir($u{homedir}); + $userEnt->LoginShell($u{shell}); + my $username = $userEnt->UserName($GetValue); + $model->foreach(sub { + my ($mod, $_path, $iter) = @_; + my $ch = $mod->get($iter, 2); + my $name = $mod->get($iter, 1); + $gEnt = $ctx->LookupGroupByName($name); + $ugid = $gEnt->Gid($GetValue); + my $m = $gEnt->MemberName(1,0); + if ($ch == 1) { + if (!InArray($username, $m) && $primgid != $ugid) { + eval { $gEnt->MemberName($username, 1) }; + $ctx->GroupModify($gEnt); + } + } else { + if (InArray($username, $m)) { + eval { $gEnt->MemberName($username, 2) }; + $ctx->GroupModify($gEnt); + } + } + return 0; + }, undef); + if ($us->{o}{primgroup}->entry->get_text eq '') { + RaiseError(N("Please select at least one group for the user")); + } elsif (!$error) { + my $ent = $ctx->LookupGroupByName($us->{o}{primgroup}->entry->get_text); + $ugid = $ent->Gid($GetValue); + $userEnt->Gid($ugid); + if ($us->{o}{acheckexpire}->get_active) { + my $yr = $us->{o}{expy}->get_value; + my $mo = $us->{o}{expm}->get_value; + my $dy = $us->{o}{expd}->get_value; + ValidInt($yr, $dy, $mo) + or RaiseError(N("Please specify Year, Month and Day \n for Account Expiration ")); + if (!$error) { $Exp = ConvTime($dy, $mo, $yr); + $userEnt->ShadowExpire($Exp) } + } else { $userEnt->ShadowExpire(ceil(-1)) } + if ($us->{o}{pcheckexpire}->get_active) { + my $allowed = int($us->{o}{dbca}->get_text); + my $required = int($us->{o}{dbcr}->get_text); + my $warning = int($us->{o}{bwbc}->get_text); + my $inactive = int($us->{o}{dbai}->get_text); + $allowed && $required && $warning && $inactive + or RaiseError(N("Please fill up all fields in password aging\n")); + if (!$error) { + $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); + } + !$error and $ctx->UserModify($userEnt); + if ($us->{o}{lockuser}->get_active) { + !$ctx->IsLocked($userEnt) and $ctx->Lock($userEnt); + } else { $ctx->IsLocked($userEnt) and $ctx->UnLock($userEnt) } + defined $us->{o}{iconval} and any::addKdmIcon($u{username}, $us->{o}{iconval}); + !$error and Refresh($sysfilter, $stringsearch); + } + } +} + +sub GroupEdit_valid { + my ($groupEnt, $model, $groupname) = @_; + my %g; + my ($uEnt); + $g{groupname} = $us->{o}{groupname}->get_text; $error = 0; + if (!valid_groupname($g{groupname})) { RaiseError($us->{error}) } + if (!$error && $groupname ne $g{groupname}) { $groupEnt->GroupName($g{groupname}) } + $groupname = $groupEnt->GroupName($GetValue); + my $members = $ctx->EnumerateUsersByGroup($groupname); + my $gid = $groupEnt->Gid($GetValue); + !$error and $model->foreach( + sub { + my ($mod, $_path, $iter) = @_; + my $ch = $mod->get($iter, 2); + my $name = $mod->get($iter, 1); + if ($ch == 1) { + if (!InArray($name, $members)) { + $uEnt = $ctx->LookupUserByName($name); + my $ugid = $uEnt->Gid($GetValue); + if ($ugid != $gid) { + eval { $groupEnt->MemberName($name,1) }; + } + } + } else { + if (InArray($name, $members)) { + $uEnt = $ctx->LookupUserByName($name); + if ($uEnt->Gid($GetValue) == $groupEnt->Gid($GetValue)) { + $model->set($iter, 0 => gtkcreate_pixbuf($pix[0])); + $model->set($iter, 2 => 1); + RaiseError(N("You cannot remove user '%s' from their primary group", $name)); + } + if (!$error) { + eval { $groupEnt->MemberName($name,2) }; + } + } + } + return 0; + }, undef); + if (!$error) { + $ctx->GroupModify($groupEnt); + Refresh($sysfilter, $stringsearch); + } +} + +sub UserEdit_widget { + my ($nbU, $tree, $model, $Gent) = @_; + my ($vald, $mo, $ye) = (localtime())[3, 4, 5]; + my $valy = $ye+1900; + my $valm = $mo+1; + gtkappend_page($nbU, + BuildUui([ gtknew('Label_Left', text => N("Home:")), + $us->{o}{homedir} = Gtk2::Entry->new ]), + gtkshow(Gtk2::Label->new(N("User Data")))); + gtkappend_page( + $nbU, + gtkpack_(Gtk2::VBox->new(0, 2), + 0, $us->{o}{acheckexpire} = Gtk2::CheckButton->new(N("Enable account expiration")), + 0, gtkpack__(my $h = Gtk2::HBox->new(0, 10), + Gtk2::Label->new(N("Account expires (YYYY-MM-DD):")), + map { my ($s, $value, $minv, $maxv) = @$_; #my $spc = $_->[4]; + $us->{o}{$s} = Gtk2::SpinButton->new(Gtk2::Adjustment->new($value, $minv , $maxv, 1, 10, 10), 1, 0); + } (['expy', $valy, 1970, 10000], + ['expm', $valm, 1, 12], + ['expd', $vald, 1, 31]), + ), + 0, Gtk2::HSeparator->new, + 0, $us->{o}{lockuser} = Gtk2::CheckButton->new(N("Lock User Account")), + 0, Gtk2::HSeparator->new, + 0, GetFaceIcon(GetNameEntFromIter($usertree, $utree_model,0)) + ), gtkshow(Gtk2::Label->new(N("Account Info")))); + GrayBox($us->{o}{acheckexpire}, $h, 0); + gtkappend_page($nbU, + gtkpack_(Gtk2::VBox->new(0, 2), + 0, gtkpack_(Gtk2::HBox->new(0,5), + 0, Gtk2::Label->new(N("User last changed password on: ")), + 0, my $dayStr = Gtk2::Label->new(""), + 0, my $month = Gtk2::Label->new(""), + 0, my $dayInt = Gtk2::Label->new(""), + 0, my $year = Gtk2::Label->new("") + ), + 0, Gtk2::HSeparator->new, + 0, $us->{o}{pcheckexpire} = Gtk2::CheckButton->new(N("Enable Password Expiration")), + 1, gtkpack_( + my $v = Gtk2::VBox->new(0,1), + 1, create_packtable( + { homogeneous => 1, col_spacings => 5, row_spacings => 5 }, + [ N("Days before change allowed:"), + $us->{o}{dbca} = Gtk2::Entry->new_with_text(0) ], + [ N("Days before change required:"), + $us->{o}{dbcr} = Gtk2::Entry->new_with_text(0) ], + [ N("Days warning before change:"), + $us->{o}{bwbc} = Gtk2::Entry->new_with_text(0) ], + [ N("Days before account inactive:"), + $us->{o}{dbai} = Gtk2::Entry->new_with_text(0) ] + ) + )), gtkshow(Gtk2::Label->new(N("Password Info")))); + GrayBox($us->{o}{pcheckexpire}, $v, 0); + gtkappend_page($nbU, + gtkpack_(Gtk2::VBox->new(0, 2), + 0, Gtk2::Label->new(N("Select the groups that the user will be a member of:")), + 1, create_scrolled_window($tree), + 0, gtkpack_(Gtk2::HBox->new(0, 1), + 0, Gtk2::Label->new(N("Primary Group")), + 1, $us->{o}{primgroup} = Gtk2::OptionMenu->new, + ) + ), gtkshow(Gtk2::Label->new(N("Groups")))); + my $username = GetNameEntFromIter($usertree, $utree_model,0); + $us->{o}{login}->set_text($username); + my $userEnt = $ctx->LookupUserByName($username); FillUserInfo($userEnt); + my $Uid = $userEnt->Uid($GetValue); + my $expire = $userEnt->ShadowExpire($GetValue); + if ($expire && $expire != -1) { + $us->{o}{acheckexpire}->set_active(1); + $h->set_sensitive(1); + my $times = TimeOfArray($expire, 1); + $us->{o}{expd}->set_value($times->{dayint}); + $us->{o}{expm}->set_value($times->{month}); + $us->{o}{expy}->set_value($times->{year}); + } + #root account should never be locked + !$Uid and $us->{o}{lockuser}->set_sensitive(0); + # Check if user account is locked + $ctx->IsLocked($userEnt) and $us->{o}{lockuser}->set_active(1); + my $lastchg = $userEnt->ShadowLastChange($GetValue); + if ($lastchg) { + my $times = TimeOfArray($lastchg, 0); + $dayStr->set_text($times->{daystr}); + $month->set_text($times->{month}); + $dayInt->set_text($times->{dayint}); + $year->set_text($times->{year}); + } + my $min = $userEnt->ShadowMin($GetValue); + my $max = $userEnt->ShadowMax($GetValue); + my $warn = $userEnt->ShadowWarn($GetValue); + my $inact = $userEnt->ShadowInact($GetValue); + if ($min && $min != -1 || $max && $max != 99999 || $warn && $warn != 7 && $warn != -1 || $inact && $inact != -1) { + $us->{o}{pcheckexpire}->set_active(1); + $v->set_sensitive(1); + } + $min && $min != -1 and $us->{o}{dbca}->set_text($min); + $max && $max != -1 and $us->{o}{dbcr}->set_text($max); + $warn && $warn != -1 and $us->{o}{bwbc}->set_text($warn); + $inact && $inact != -1 and $us->{o}{dbai}->set_text($inact); + my $grps = $ctx->GroupsEnumerate; + my @sgroups = sort @$grps; + my $members = $ctx->EnumerateGroupsByUser($username); + my $primgid = $userEnt->Gid($GetValue); + $Gent = $ctx->LookupGroupById($primgid); + my @primgroup; + foreach my $group (@sgroups) { + if (member($group, @$members)) { + $model->append_set([ 0 => gtkcreate_pixbuf($pix[0]), 1 => $group, 2 => 1 ]); + push @primgroup, $group; + } else { $model->append_set([ 0 => gtkcreate_pixbuf($pix[1]), 1 => $group, 2 => 0 ]) } + } + $us->{o}{primgroup}->set_popdown_strings(@primgroup); + $Gent and $us->{o}{primgroup}->entry->set_text($Gent->GroupName($GetValue)); + ($userEnt, $primgid, @primgroup); +} + +sub GroupEdit_widget { + my ($nbG, $tree, $model, $groupname) = @_; + $nbG->set_size_request(300, 200); + gtkappend_page($nbG, + gtkpack_(Gtk2::VBox->new(0, 2), + 1, BuildGui()), + gtkshow(Gtk2::Label->new(N("Group Data")))); + gtkappend_page($nbG, + gtkpack_(Gtk2::VBox->new(0, 1), + 0, Gtk2::Label->new(N("Select the users to join this group:")), + 1, create_scrolled_window($tree)), + gtkshow(Gtk2::Label->new(N("Group Users")))); + $groupname = GetNameEntFromIter($grouptree, $gtree_model, 0); + $us->{o}{groupname}->set_text($groupname); + # Don't allow change on group name since there is a bug in lu_user_modify group + $us->{o}{groupname}->set_editable(0); + my $groupEnt = $ctx->LookupGroupByName($groupname); + my $users = $ctx->UsersEnumerate; + my @susers = sort(@$users); + my $members = $ctx->EnumerateUsersByGroup($groupname); + foreach my $user (@susers) { + if (member($user, @$members)) { + $model->append_set([ 0 => gtkcreate_pixbuf($pix[0]), 1 => $user, 2 => 1 ]); + } else { + $model->append_set([ 0 => gtkcreate_pixbuf($pix[1]), 1 => $user, 2 => 0 ]); + } + } + $groupEnt; +} + +sub Edit() { + my $w = NewWindow(N("Edit Groups / Users")); + my $model = Gtk2::ListStore->new("Gtk2::Gdk::Pixbuf", "Glib::String", "Glib::Int"); + my $tree = Gtk2::TreeView->new_with_model($model); + my ($groupname, $groupEnt, $userEnt, $primgid, $Gent); + my @primgroup; + $tree->get_selection->set_mode('browse'); + my $check = Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererPixbuf->new, + 'pixbuf' => 0); + $tree->append_column($check); + $check->{is_pixbuf_column} = 1; + $tree->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, + 'text' => 1)); + $tree->set_headers_visible(0); + my $p = $nb->get_current_page; + my $nbU = Gtk2::Notebook->new; + my $nbG = Gtk2::Notebook->new; + if ($p <= 0) { + ($userEnt, $primgid, @primgroup) = UserEdit_widget($nbU, $tree, $model, $Gent); + } elsif ($p == 1) { + $groupEnt = GroupEdit_widget($nbG, $tree, $model, $groupname); + } + gtkpack_($w->vbox, + 1, !$p ? $nbU : $nbG); + gtkadd($w->action_area, + map { + my $retv = $_->[1]; + gtksignal_connect( + Gtk2::Button->new($_->[0]), clicked => sub { + if (!$retv) { + if ($p <= 0) { + eval { UserEdit_valid($userEnt, $model, $primgid) }; + if (my $err = $@) { + c::set_tagged_utf8($err); + RaiseError(N("An error occurred:") . "\n" . $err); + } + } elsif ($p == 1) { + GroupEdit_valid($groupEnt, $model, $groupname); + } + } + !$error and $w->destroy; $error = 0 }); + } ([ N("Cancel"), 1 ], [ N("Ok"), 0 ]), + ); + $tree->show; + $w->show_all; + $tree->signal_connect( + button_press_event => sub { + my ($path, $column) = $tree->get_path_at_pos($_[1]->x, $_[1]->y); + if ($path && $column) { + if ($column->{is_pixbuf_column}) { + my $iter = $model->get_iter($path); + if ($iter) { + my $cp = $model->get($iter, 2); + my $item = $model->get($iter, 1); + $model->set($iter, 0 => gtkcreate_pixbuf($pix[$cp])); + $model->set($iter, 2 => !$cp); + if ($p <= 0) { + if (!$cp) { + !InArray($item, \@primgroup) and push(@primgroup, $item); + } else { + InArray($item, \@primgroup) and @primgroup = RemoveFromArray($item, \@primgroup); + } + $us->{o}{primgroup}->set_popdown_strings(@primgroup); + $Gent and $us->{o}{primgroup}->entry->set_text($Gent->GroupName($GetValue)); + } + } + } + } + }); + $tree->signal_connect( + key_press_event => sub { + my $c = chr($_[1]->keyval & 0xff); + if ($_[1]->keyval >= 0x100 ? $c eq "\r" || $c eq "\x8d" : $c eq ' ') { + my (undef, $iter) = $tree->get_selection->get_selected; + return unless $iter; + my $cp = $model->get($iter, 2); + my $item = $model->get($iter, 1); + $model->set($iter, 0 => gtkcreate_pixbuf($pix[$cp])); + $model->set($iter, 2 => !$cp); + if ($p <= 0) { + if (!$cp) { + !InArray($item, \@primgroup) and push(@primgroup, $item); + } else { + InArray($item, \@primgroup) and @primgroup = RemoveFromArray($item, \@primgroup); + } + $us->{o}{primgroup}->set_popdown_strings(@primgroup); + $Gent and $us->{o}{primgroup}->entry->set_text($Gent->GroupName($GetValue)); + } + } + 0; + }); +} + +sub ValidInt { + foreach my $i (@_) { $i =~ /\d+/ or return 0 } + return 1; +} + +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; +} + +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; +} + +sub InArray { + my ($item, $arr) = @_; + return any { $item eq $_ } @$arr; +} + +sub RemoveFromArray { + my ($item, $arr) = @_; + my ($t, $_s) = partition { $item ne $_ } @$arr; + return @$t; +} + +sub GrayBox { + my ($o, $v, $m) = @_; + $v->set_sensitive($m); + $o->signal_connect('clicked' => sub { $m = !$m; $v->set_sensitive($m) }); +} + +sub NewDialog { + my ($title) = @_; + my $dialog = gtkset_border_width( + _create_dialog($title, { transient_for => $us->{wnd}{real_window} }), + 5); + $dialog->action_area->pack_start( + gtksignal_connect(Gtk2::Button->new(N("Close")), clicked => sub { $dialog->destroy }), + 0,0,0); + $dialog; +} + +sub About() { + my $license = formatAlaTeX(translate($::license)); + $license =~ s/\n/\n\n/sg; # nicer formatting + my $w = gtknew('AboutDialog', name => N("Userdrake"), + version => $us->{VERSION}, + copyright => N("Copyright (C) %s by Mandriva", '2001-2009') . "\n" . N("Copyright (C) %s by Mageia", '2011'), + if_(-r "$pixdir/userdrake.png", logo => "$pixdir/userdrake.png"), + license => $license, wrap_license => 1, + comments => N("Users Management"), + website => 'http://www.mageia.org', + website_label => N("Mageia"), + authors => 'Daouda Lo +Thierry Vignaud ', + artists => 'Hélène Durosini ', + translator_credits => + #-PO: put here name(s) and email(s) of translator(s) (eg: "John Smith ") + N("_: Translator(s) name(s) & email(s)\n"), + ); + $w->show_all; + $w->run; +} + +sub valid { + $_[0] or $us->{error} = N("Name field is empty please provide a name"), return 0; + $_[0] =~ /^[a-z]+?[a-z0-9_-]*?$/ or do { + $us->{error} = N("The name must contain only lower cased latin letters, numbers, `-' and `_'"); + return 0; + }; + length($_[0]) <= $_[1] or $us->{error} = N("Name is too long"), return 0; + return 1; +} + +sub valid_username { + return valid($_[0], 32); +} + +sub valid_groupname { + return valid($_[0], 16); +} + +sub RaiseError { + my $w = NewWindow(N("Error")); + $error = 1; + gtkpack($w->vbox, Gtk2::Label->new($_[0])); + gtkadd($w->action_area, + gtksignal_connect(Gtk2::Button->new(N("Ok")), + clicked => sub { $w->destroy }) + ); + $w->show_all; +} + +sub QuitGlobal() { + setVarsInSh($conffile, { + FILTER => bool2text($sysfilter), + }); + Gtk2->main_quit; +} -- cgit v1.2.1