#!/usr/bin/perl 

use strict;
use diagnostics;
use lib qw(/usr/lib/libDrakX);
use standalone;

use common;
use ugtk2 qw(:helpers :wrappers :create);
use interactive;

my $in = 'interactive'->vnew('su');
local $_ = join '', @ARGV;

#- vars declaration
my ($default_perm_level) = "level ".chomp_(`cat /etc/sysconfig/msec | grep SECURE_LEVEL= |cut -d= -f2`);
my $perm_path       = '/usr/share/msec/';
my $local_path = '/etc/security/msec/';
my %perm = ( 
	     'level 1' => $perm_path.'perm.1',
             'level 2' => $perm_path.'perm.2',
             'level 3' => $perm_path.'perm.3',
             'level 4' => $perm_path.'perm.4',
             'level 5' => $perm_path.'perm.5',
             'editable' => $local_path.'perm.local',
           );
my %perm_l10n = ('level 1' => N("Level 1"),
                 'level 2' => N("Level 2"),
                 'level 3' => N("Level 3"),
                 'level 4' => N("Level 4"),
                 'level 5' => N("Level 5"),
                 'editable' => N("Custom settings"),
                );
my %rev_perm_l10n = reverse %perm_l10n;
my $editable = 0;
my $modified = 0;
my $prec_txt = $default_perm_level;

#- Widget declaration
my $w = ugtk2->new('drakperm');
$w->{rwindow}->set_size_request(550, 400) unless $::isEmbedded;
my $W = $w->{window};
$W->signal_connect(delete_event => sub { ugtk2->exit });
my $treeModel = Gtk2::TreeStore->new(("Glib::String") x 4);
my $permList = Gtk2::TreeView->new_with_model($treeModel);

my @column_sizes = (150, 100, 100, 15, -1);

each_index {
    my $col = Gtk2::TreeViewColumn->new_with_attributes($_, Gtk2::CellRendererText->new, 'text' => $::i);
    $col->set_min_width($column_sizes[$::i]);
#    $col->set_sort_column_id($::i);
    $permList->append_column($col);
} (N("Path"), N("User"), N("Group"), N("Permissions"));

#- widgets settings
my $combo_perm = new Gtk2::OptionMenu;
$combo_perm->set_popdown_strings(sort(values %perm_l10n));

sub add_callback() {
    row_setting_dialog();
    $modified++;
}

sub edit_callback() {
    my (undef, $iter) = $permList->get_selection->get_selected;
    return unless $iter;
    row_setting_dialog($iter);
}

my @buttons;

sub del_callback() {
    my ($tree, $iter) = $permList->get_selection->get_selected;
    $tree->remove($iter);
    sensitive_buttons(0);
    $modified++;
}

sub down_callback() {
    my ($model, $iter) = $permList->get_selection->get_selected;
    return unless $iter;
    my $new_iter = $model->iter_next($iter);
    return if !$new_iter;

    $model->move_after($iter, $new_iter);
    $model->move_after($iter, $new_iter);
    $permList->get_selection->select_iter($iter);
    $permList->queue_draw;
}

sub up_callback() {
    my ($model, $iter) = $permList->get_selection->get_selected;
    return if !$iter;
    my $path = $model->get_path($iter);
    return if !$path;
    $path->prev;
    my $prev_iter = $model->get_iter($path);
    return if !$prev_iter;
    $model->move_before($iter, $prev_iter);
    $model->move_before($iter, $prev_iter);
    $permList->get_selection->select_iter($iter);
    $permList->queue_draw;
}

my $combo_sig = $combo_perm->entry->signal_connect(changed => sub { display_perm($rev_perm_l10n{$combo_perm->entry->get_text} , @_) });
$permList->signal_connect(button_press_event => sub { 
                     return unless $editable;
                     my (undef, $event) = @_;
                     my (undef, $iter) = $permList->get_selection->get_selected;
                     return unless $iter;
                     row_setting_dialog($iter) if $event->type eq '2button-press';
                 });


my $tips = new Gtk2::Tooltips;

$W->add(gtkpack_(Gtk2::VBox->new(0,5),
                 0, gtkset_property(Gtk2::Label->new(N("Drakperm is used to see files to use in order to fix permissions, owners, and groups via msec.\nYou can also edit your own rules which will owerwrite the default rules.")), 'wrap', 1),
                 1, gtkadd(Gtk2::Frame->new,
                           gtkpack_(Gtk2::VBox->new(0,5),
                                    0, gtkadd(Gtk2::HBox->new(0,5),
                                              Gtk2::Label->new(N("The current security level is %s
Select permissions to see/edit", $default_perm_level)),
                                              $combo_perm
                              ),
                                    1, create_scrolled_window($permList),
                                    0, my $up_down_box = gtkadd(Gtk2::HBox->new(0, 5), @buttons =
                                                                map {
                                                                    gtkset_tip($tips, 
                                                                               gtksignal_connect(Gtk2::Button->new($_->[0]), clicked => $_->[2]),
                                                                               $_->[1]);
                                                                } ([ N("Up"), N("Move selected rule up one level"), \&up_callback ], 
                                                                   [ N("Down"), N("Move selected rule down one level"), \&down_callback ], 
                                                                   [ N("Add a rule"), N("Add a new rule at the end"), \&add_callback ],
                                                                   [ N("Delete"), N("Delete selected rule"), \&del_callback ], 
                                                                   [ N("Edit"), N("Edit current rule"), \&edit_callback ])),
                                    0, gtkpack(Gtk2::HButtonBox->new,
                                               gtksignal_connect(Gtk2::Button->new(N("Cancel")), clicked => sub { ugtk2->exit }),
                                               gtksignal_connect(Gtk2::Button->new(N("Ok")), clicked => \&save_perm),
                              )
                       )
                 )
          )
       );
$W->show_all;
$w->{rwindow}->set_position('center') unless $::isEmbedded;

display_perm($default_perm_level);
$combo_perm->entry->set_text($perm_l10n{$default_perm_level});

$permList->get_selection->signal_connect('changed' => sub {
                                               my ($select) = @_;
                                               my (undef, $iter) = $select->get_selected;
                                               sensitive_buttons($iter ? $editable : 0);
                                           });

$w->main;
ugtk2->exit;


sub check_save() {
    $modified or return 0;
    my $sav_ = $in->ask_okcancel('Warning', 'your changed will be lost do you wish to continue?');
    $sav_ 
      and $modified = 0;
    return $sav_;   
}

sub display_perm {
    my $perm_level = shift @_;
    return unless $perm_level;
    my $file = $perm{$perm_level};
    my $sav_ = &check_save;
    if ($modified && ! $sav_) {
     $combo_perm->entry->signal_handler_block($combo_sig);
     $combo_perm->entry->set_text($prec_txt);
     $combo_perm->entry->signal_handler_unblock($combo_sig);
     return 0;
    }

    $editable = $perm_level =~ /^level \d/ ? 0 : 1;
     
    $treeModel->clear;
    local *F;
    open F, $file;
    local $_;
    while (<F>) {
        if (m/^([^#]\S+)\s+([^.\s]+)\.(\S+)?\s+(\d+)/) {
            $treeModel->append_set(undef, 0 => $1, 1 => $2, 2 => $3, 3 => $4);
        } elsif (m/^([^#]\S+)\s+current?\s+(\d+)/) {
            $treeModel->append_set(undef, 0 => $1, 1 => 'current', 2 => '', 3 => $2);
        }
    }
    close F;
    $up_down_box->set_sensitive($editable);
    sensitive_buttons(0) if $editable;
     
    $prec_txt = $perm_level;
}

sub save_perm() {
    $modified or return 0;
    my $val;
    local *F;
    open F, '>' . $perm{editable} or die("Impssible to process \"", $perm{editable}, "\"");
    $treeModel->foreach(sub {
                             my ($model, $_path, $iter) = @_;
                             my $line = $model->get($iter, 0) . "\t" . $model->get($iter,1) . ($model->get($iter,2) ? "." . $model->get($iter,2) : "") . "\t" . $model->get($iter,3) . "\n";
                             print F $line;
                             return 0;
                         }, $val);
    close F;
    $modified = 0;
    ugtk2->exit;
}

sub row_setting_dialog {
    my ($iter) = @_;
    
    my $dlg         = new Gtk2::Dialog();
    $dlg->set_transient_for($w->{rwindow}) unless $::isEmbedded;
    $dlg->set_modal(1);
#    $dlg->set_resizable(0);
    my $ok          = Gtk2::Button->new(N("Ok"));
    my $cancel      = Gtk2::Button->new(N("Cancel"));
    my $browse = new Gtk2::Button(N("browse"));
    my $file        = new Gtk2::Entry;
    my $usr_check  = new Gtk2::CheckButton(N("Current user"));
    my $sticky = new Gtk2::CheckButton(N("Sticky-bit"));
    my $suid   = new Gtk2::CheckButton(N("Set-UID"));
    my $gid    = new Gtk2::CheckButton(N("Set-GID"));
    my $rght = $treeModel->get($iter, 3) if $iter;
    my $s = length($rght) == 4 ? substr($rght,0,1) : 0;
    my $user  = $s ? substr($rght,1,1) : substr($rght,0,1);
    my $group = $s ? substr($rght,2,1) : substr($rght,1,1);
    my $other = $s ? substr($rght,3,1) : substr($rght,2,1);
    
    my %rights = (user => $user, group => $group, other => $other);
    my %rights_labels = (user => N("User"), group => N("Group"), other => N("Other"));
    my @check  = ('', 'read', 'write', 'execute');
    
    my %checks = ('read' => {
                             label => N("Read"),
                             tip => { map { $_ => N("Enable \"%s\" to read the file", $_) } keys %rights },
                            },
                  'write' => {
                              label => N("Write"),
                              tip => { map { $_ => N("Enable \"%s\" to write the file", $_) } keys %rights },
                             },
                  'execute' => {
                                label => N("Execute"),
                                tip => { map { $_ => N("Enable \"%s\" to execute the file", $_) } keys %rights },
                               },
                  '' => { label => '', tip => '' },
                 );
    $tips->set_tip($sticky, N("Used for directory:\n only owner of directory or file in this directory can delete it"));
    $tips->set_tip($suid, N("Use owner id for execution"));
    $tips->set_tip($gid, N("Use group id for execution"));
    $tips->set_tip($usr_check, N("When checked, owner and group won't be changed"));

    #- dlg widgets settings
    my %s_right = get_right($s);
    $sticky->set_active($s_right{execute});
    $gid->set_active($s_right{write});
    $suid->set_active($s_right{read});
     
    $file->set_text($treeModel->get($iter, 0)) if $iter;

    my $users  = Gtk2::OptionMenu->new;
    $users->set_popdown_strings(&get_user_or_group('users'));
    $users->entry->set_text($treeModel->get($iter, 1)) if $iter;
     
    my $groups = Gtk2::OptionMenu->new;
    $groups->set_popdown_strings(&get_user_or_group);
    $groups->entry->set_text($treeModel->get($iter, 2)) if $iter;
     
    if ($iter && $treeModel->get($iter, 1) eq 'current') {
     $usr_check->set_active(1);
     $groups->set_sensitive(0);
     $users->set_sensitive(0);
    }
     
    $tips->set_tip($sticky, N("Used for directory:\n only owner of directory or file in this directory can delete it"));
    $tips->set_tip($suid, N("Use owner id for execution"));
    $tips->set_tip($gid, N("Use group id for execution"));
    $tips->set_tip($usr_check, N("when checked, owner and group won't be changed"));
     
    $cancel->signal_connect(clicked => sub { $dlg->destroy });
    $browse->signal_connect(clicked => sub {
                     my $file_dlg = new Gtk2::FileSelection(N("Path selection"));
                     $file_dlg->set_modal(1);
                     $file_dlg->set_transient_for($dlg);
                     $file_dlg->show;
                     $file_dlg->set_filename($file->get_text);
                     $file_dlg->cancel_button->signal_connect(clicked => sub { $file_dlg->destroy });
                     $file_dlg->ok_button->signal_connect(clicked => sub {
                                                $file->set_text($file_dlg->get_filename);
                                                $file_dlg->destroy;
                                               });
                    });
    my %perms;
    $ok->signal_connect(clicked => sub {
                            unless ($iter) {
                                $iter = $treeModel->append(undef);
                            }
                            $treeModel->set($iter, 0 => $file->get_text);
                            if ($usr_check->get_active) {
                                $treeModel->set($iter, 1 => 'current');
                                $treeModel->set($iter, 2 => '');
                            } else {
                                $treeModel->set($iter, 1 => $users->entry->get_text);
                                $treeModel->set($iter, 2 => $groups->entry->get_text);
                            }
                            $user = ($perms{user}{read}->get_active ? 4 : 0)+($perms{user}{write}->get_active ? 2 : 0)+($perms{user}{execute}->get_active ? 1 : 0);
                            $group = ($perms{group}{read}->get_active ? 4 : 0)+($perms{group}{write}->get_active ? 2 : 0)+($perms{group}{execute}->get_active ? 1 : 0);
                            $other = ($perms{other}{read}->get_active ? 4 : 0)+($perms{other}{write}->get_active ? 2 : 0)+($perms{other}{execute}->get_active ? 1 : 0);
                            my $s = ($sticky->get_active ? 1 : 0) + ($suid->get_active ? 4 : 0) + ($gid->get_active ? 2 : 0);
                            $treeModel->set($iter, 3 => ($s || '') . $user . $group . $other);
                            $dlg->destroy;
                            $modified++;
                        });
    $usr_check->signal_connect(clicked => sub {
                        my $bool = $usr_check->get_active; 
                        $groups->set_sensitive(!$bool);
                        $users->set_sensitive(!$bool);
                    });

    gtkpack_($dlg->vbox,
             0, gtkadd(Gtk2::Frame->new(N("Path")),
                       gtkpack_(Gtk2::HBox->new(0,5),
                                1, $file,
                                0, $browse
                               )
                      ),
             0, gtkadd(Gtk2::Frame->new(N("Property")),
                       gtkadd(Gtk2::VBox->new(0,5),
                              $usr_check,
                              gtkadd(Gtk2::HBox->new(0,5),
                                     Gtk2::Label->new(N("User :")),
                                     $users,
                                     Gtk2::Label->new(N("Group :")),
                                     $groups,
                                    ),
                             ),
                      ),
             1, gtkadd(Gtk2::Frame->new(N("Permissions")),
                       gtkpack(Gtk2::HBox->new(0,15),
                               gtkadd(Gtk2::VBox->new(0,15),
                                      map { gtkset_tip($tips, Gtk2::Label->new($checks{$_}{label}), $checks{$_}{tip}) } @check,
                                     ),
                               (map {
                                   my $owner = $_;
                                   $perms{$owner} = { get_right($rights{$owner}) };
                                   my $vbox = gtkadd(Gtk2::VBox->new(0,5), 
                                                     Gtk2::Label->new($rights_labels{$owner}),
                                                     map {
                                                         my $c = $_;
                                                         my $active = $perms{$owner}{$c};
                                                         $perms{$owner}{$c} = Gtk2::CheckButton->new;
                                                         $tips->set_tip($perms{$owner}{$c},
                                                                        $checks{$c}{tip}{$owner},
                                                                       );
                                                         gtkset_active($perms{$owner}{$c}, $active);
                                                     } grep { $_ } @check,
                                                    );
                                   
                                   $vbox;
                               } keys %rights),
                               gtkpack(Gtk2::VBox->new(0,5),
                                       Gtk2::Label->new(' '),
                                       $suid,
                                       $gid,
                                       $sticky,
                                      ),
                              ),
                      ),
            );

    gtkadd($dlg->action_area,
           $cancel,
           $ok
          );
     
    $dlg->show_all;

}

sub get_user_or_group {
    my $what = @_;
    my @users;
    local *F;
    open F, $what eq 'users' ? '/etc/passwd' : '/etc/group';
     
    local $_;
    while (<F>) {
     m/^([^#:]+):[^:]+:[^:]+:/ or next;
     push @users, $1;
    }
    close F;
    return sort(@users);
}

sub get_right {
    my ($right) = @_;
    my %rght   = ('read' => 0, 'write' => 0, 'execute' => 0);
    $right - 4 >= 0 and $rght{read}=1 and $right = $right-4;
    $right - 2 >= 0 and $rght{write}=1 and $right = $right-2;
    $right - 1 >= 0 and $rght{execute}=1 and $right = $right-1;
    return %rght;
}

sub sensitive_buttons {
    foreach my $i (0, 1, 3, 4) {
        $buttons[$i]->set_sensitive($_[0]);
    }
}