#!/usr/bin/perl
#*****************************************************************************
#
# Copyright (c) 2002-2008 Christian Belisle
#                         Thierry Vignaud <tvignaud@mandriva.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.
#
#*****************************************************************************

use strict;
use lib qw(/usr/lib/libDrakX);
use common;
use standalone;
use vars qw($MODE %options);
use interactive;
use mygtk2 qw(gtknew gtkset);
use ugtk2 qw(:helpers :wrappers :ask :create);
use run_program;
use security::level;
use security::msec;
use security::help;
use security::l10n;

#$MODE = 'basic';
#$0 =~ /draksec-firewall$/ and $MODE = 'firewall';
#$0 =~ /draksec-perms$/ and $MODE = 'perms';

#/^-?-(\S+)$/ and $options{$1} = 1 foreach @ARGV;


my $in = 'interactive'->vnew('su');

$ugtk2::wm_icon = "/usr/share/mcc/themes/default/draksec-mdk.png";
my ($w, %fields);

############################    I18N     ###################################

my @help;

my %translations = (
	'ALL'     => N("ALL"),
	'LOCAL'   => N("LOCAL"),
	'NONE'    => N("NONE"),
	'default' => N("Default"),
	'ignore'  => N("Ignore"),
 	'no'      => N("No"),
	'yes'     => N("Yes"),
);
my %inv_translations = reverse %translations;

sub to_i18n { map { $translations{$_} || $_ } @_ }
sub from_i18n { $inv_translations{$_[0]} || $_[0] }
sub resize { gtkset($_[0], width => 50) }

%fields = security::l10n::fields();
my %inv_fields = reverse %fields;

# factorize this with rpmdrake and harddrake2
sub wait_msg {
    my $mainw = ugtk2->new(N("Please wait"), (modal => 1, transient => $w->{real_window}));
    $mainw->{window}->add(gtknew('WrappedLabel', text => $_[0]));
    $mainw->{rwindow}->show_all;
    gtkset_mousecursor_wait($mainw->{rwindow}->window);
    # ugly workaround for gtk+ not displaying subdialog contents:
    Glib::Timeout->add(300, sub { Gtk2->main_quit; 0 });
    Gtk2->main;
    $mainw;
}

sub remove_wait_msg { $_[0]->destroy }

sub basic_seclevel_explanations() {
    my $text = gtknew('TextView');
    use Gtk2::Pango;
    my %common_opts = ('left-margin' => '10', 'right-margin' => '10');

    gtktext_insert($text, ugtk2::markup_to_TextView_format(formatAlaTeX(
#-PO: Do not alter the <span ..> and </span> tags.
#-PO: Translate the security levels (Poor, Standard, High, Higher and Paranoid) in the same way, you translated these individuals words.
#-PO: keep the double empty lines between sections, this is formatted a la LaTeX.
					  N("Here, you can setup the security level and administrator of your machine.


The '<span weight=\"bold\">Security Administrator</span>' is the one who will receive security alerts if the
'<span weight=\"bold\">Security Alerts</span>' option is set. It can be a username or an email.


The '<span weight=\"bold\">Security Level</span>' menu allows you to select one of the six preconfigured security levels
provided with msec. These levels range from '<span weight=\"bold\">poor</span>' security and ease of use, to
'<span weight=\"bold\">paranoid</span>' config, suitable for very sensitive server applications:


<span foreground=\"royalblue3\">Poor</span>: This is a totally unsafe but very
easy to use security level. It should only be used for machines not connected to
any network and that are not accessible to everybody.


<span foreground=\"royalblue3\">Standard</span>: This is the standard security
recommended for a computer that will be used to connect to the Internet as a
client.


<span foreground=\"royalblue3\">High</span>: There are already some
restrictions, and more automatic checks are run every night.


<span foreground=\"royalblue3\">Higher</span>: The security is now high enough
to use the system as a server which can accept connections from many clients. If
your machine is only a client on the Internet, you should choose a lower level.


<span foreground=\"royalblue3\">Paranoid</span>: This is similar to the previous
level, but the system is entirely closed and security features are at their
maximum")), \%common_opts));

    gtknew('ScrolledWindow', h_policy => 'never', child => $text);
}

sub new_nonedit_combo {
    my ($string_list, $o_default_value) = @_;
    gtknew('ComboBox', text => to_i18n($o_default_value), list => [ to_i18n(@$string_list) ]);
}

sub register_help_page {
     my ($domain) = @_;
     push @help,
       ([ 
         [ $domain . "\n\n", 
           {
            justification => 'center',
            scale => Gtk2::Pango->PANGO_SCALE_LARGE,
            weight => Gtk2::Pango->PANGO_WEIGHT_BOLD, 
           },
         ],
         [ N("Description of the fields:\n\n"), ],
        ],
       );
}


sub register_help_entry {
     my ($label, $default, $opt) = @_;
     my $help = $security::help::help{$opt};
     push @{$help[-1]},
       ([
         formatAlaTeX($label) . ":\n",
         {  foreground => "royalblue3" },
        ],
        [ join("\n", formatAlaTeX($help), N("(default value: %s)", map { s/ /, /g; $_ } to_i18n($default)), "\n") ]
       );
}

my $msec = new security::msec;
$w = ugtk2->new(N("Security Level and Checks"));
my $window = $w->{window};


############################ MAIN WINDOW ###################################
# Set different options to Gtk2::Window
unless ($::isEmbedded) {
    $w->{rwindow}->set_position('center');
    $w->{rwindow}->set_title("DrakSec");
    $window->set_size_request(598, 520);
}

# Connect the signals
$window->signal_connect('delete_event', sub { $window->destroy });
$window->signal_connect('destroy', sub { ugtk2->exit });

$window->add(my $vbox = gtkshow(gtknew('VBox')));

# Create the notebook (for bookmarks at the top)
my $notebook = create_notebook();

my $common_opts = { col_spacings => 10, row_spacings => 5, mcc => 1 };

######################## BASIC OPTIONS PAGE ################################
my ($seclevel_entry, $secadmin_entry);

$notebook->append_page(gtkshow(gtknew('VBox', children => [
                                       1, basic_seclevel_explanations(),
                                       0, create_packtable($common_opts,
                                                        [ 
                                                          do {
                                                              my @sec_levels = security::level::get_common_list();
                                                              my $current_level = security::level::get_string();
                                                              
                                                              push(@sec_levels, $current_level) unless member($current_level, @sec_levels);
                                                              $seclevel_entry = new_nonedit_combo(\@sec_levels, $current_level);
                                                              
                                                              gtknew('Label_Left', text => N("Security Level:")), $seclevel_entry;
                                                          }
                                                          ],
                                                        [ gtknew('Label_Left', text => N("Security Alerts:")), 
                                                          my $secadmin_check = gtksignal_connect(gtknew('CheckButton'), toggled => sub {
                                                                                                     $secadmin_entry->set_sensitive($_[0]->get_active);
                                                                                                 }) ],
                                                        [ gtknew('Label_Left', text => N("Security Administrator:")),
                                                          $secadmin_entry = Gtk2::Entry->new_with_text($msec->get_check_value("MAIL_USER")) ]) ])),
                               gtknew('Label', text => N("Basic options")));
                       
if ($msec->get_check_value("MAIL_WARN") eq "yes") {
    $secadmin_check->set_active(1);
} else {
    $secadmin_entry->set_sensitive(0);
 }
                       
######################### NETWORK & SYSTEM OPTIONS #########################
my @yesno_choices    = qw(yes no default ignore);
my @alllocal_choices = qw(ALL LOCAL NONE default);
my @all_choices = (@yesno_choices, @alllocal_choices);
my %options_values;

foreach ([ 'network', N("Network Options") ], [ 'system', N("System Options") ]) {
    my ($domain, $label) = @$_;
    register_help_page($label);
    my %values;
    gtkappend_page($notebook, gtkshow(gtknew('VBox', children => [
                                            1, create_scrolled_window(create_packtable($common_opts,
                                                                                       map {
                                                                                           my $i = $_;
                                                                                           
                                                                                           my $entry;
                                                                                           my $opt = $inv_fields{$i} || $i;
                                                                                           my $default = $msec->get_function_default($opt);
                                                                                           if (member($default, @all_choices)) {
                                                                                               $values{$i} = new_nonedit_combo(member($default, @yesno_choices) ? \@yesno_choices : if_(member($default, @alllocal_choices), \@alllocal_choices));
                                                                                               $entry = $values{$i}->entry;
                                                                                           } else {
                                                                                               $values{$i} = gtknew('Entry');
                                                                                               $entry = $values{$i};
                                                                                           }
                                                                                           $entry->set_text(to_i18n($msec->get_function_value($opt)));
                                                                                           register_help_entry($i, $default, $opt);
                                                                                           [ gtknew('Label_Left', line_wrap => 1, text => $i), resize($values{$i}) ];
                                                                                       } sort map { $fields{$_} || $_ } $msec->list_functions($domain),
                                                                                      ),
                                                                      [ 'never', 'automatic' ],
                                                                     ),
                                           ])
                                  ),
                           gtknew('Label_Left', text => $label));
    $options_values{$domain} = \%values;
}

######################## PERIODIC CHECKS ###################################
my %security_checks_value;

my $check_string = N("Periodic Checks");

register_help_page($check_string);
gtkappend_page($notebook, gtkshow(gtknew('VBox', children => [
                                        1, create_scrolled_window(create_packtable($common_opts,
                                                                                   map {
                                                                                       my $i = $_;
                                                                                       my $opt = $inv_fields{$i} || $i;
                                                                                       $security_checks_value{$i} = new_nonedit_combo([ 'yes', 'no', 'default' ], $msec->get_check_value($opt));
                                                                                       register_help_entry($i, $msec->get_check_default($opt), $opt);
                                                                                       [ gtkshow(gtknew('Label_Left', line_wrap => 1, text => $i)), resize($security_checks_value{$i}) ];
                                                                                   } sort map { $fields{$_} || $_ } $msec->list_checks)) ])),
                       gtknew('Label', text => $check_string));


######################## AUTH ###################################
my %progs;

my $auth_string = N("Authentication");

my %auth = (
      no_passwd => N("No password"),
      root_passwd => N("Root password"),
      user_passwd => N("User password"),
);

sub default_auth_value {
    my ($prog) = @_;
    my $link = readlink("/etc/pam.d/$prog");
    if ($link =~ /mandriva-console-auth/) {
        return $auth{no_passwd};
    } elsif ($link =~ /mandriva-simple-auth/) {
        my ($user) = cat_("/etc/security/console.apps/$prog") =~ /USER=(.*)/;
        return $auth{root_passwd} if $user eq 'root';
        return $auth{user_passwd} if $user eq '<user>';
    }
}

sub set_auth_value {
    my ($prog, $auth) = @_;
    if ($auth eq 'no_passwd') {
        symlinkf('../../etc/pam.d/mandriva-console-auth', "/etc/pam.d/$prog");
    } else {
        symlinkf('../../etc/pam.d/mandriva-simple-auth', "/etc/pam.d/$prog");
        my $value = $auth eq 'user_passwd' ? '<user>' : 'root';
        substInFile {
            s/^USER=.*/USER=$value/;
        } "/etc/security/console.apps/$prog";
    }
}

my %descr = (
    
    rpmdrake => N("Software Management"),
    mandrivaupdate => N("Mandriva Update"),
    'drakrpm-edit-media' => N("Software Media Manager"),
    drak3d => N("Configure 3D Desktop effects"),
    xfdrake => N("Graphical Server Configuration"),
    drakmouse => N("Mouse Configuration"),
    drakkeyboard => N("Keyboard Configuration"),
    drakups => N("UPS Configuration"),
    drakconnect => N("Network Configuration"),
    drakhosts => N("Hosts definitions"),
    draknetcenter => N("Network Center"),
    drakvpn => N("VPN"),
    drakproxy => N("Proxy Configuration"),
    drakgw => N("Connection Sharing"),
    drakauth => N("Authentication"),
    drakbackup => N("Backups"),
    drakfont => N("Import fonts"),
    draklog => N("Logs"),
    drakxservices => N("Services"),
    userdrake => N("Users"),
    drakclock => N("Date, Clock & Time Zone Settings"),
    drakboot => N("Boot Configuration"),
);

gtkappend_page(
    $notebook,
    gtkshow(create_scrolled_window(
        gtknew('VBox', children => [
            1, create_packtable(
                $common_opts,
            map {
                my ($title, $progs) = @$_;
                ([ gtknew('Title2', label => $title), '' ],
                     map {
                         [
                             gtkshow(gtknew('Label_Left', line_wrap => 1, text => $descr{$_} || $_)),
                                 $progs{$_} = new_nonedit_combo([
                                     @auth{qw(user_passwd root_passwd no_passwd)}
                                 ],
                                                                default_auth_value($_)
                                                                #$msec->get_check_value($opt)
                                                            )
                         ];
                     } split(' ', $progs)
                );
            } (
                [ N("Software Management"), 'rpmdrake mandrivaupdate drakrpm-edit-media' ],
                [ N("Hardware"), 'drak3d xfdrake drakmouse drakkeyboard drakups' ],
                [ N("Network"), 'drakconnect drakhosts draknetcenter drakvpn drakproxy drakgw' ],
                [ N("System"), 'drakauth drakbackup drakfont draklog drakxservices userdrake drakclock' ],
                [ N("Boot"), 'drakboot' ],
            )
        )
        ]))),
    gtknew('Label', text => $auth_string));





####################### OK CANCEL BUTTONS ##################################
gtkpack_($vbox,
         1, gtkshow($notebook),
         0, create_okcancel(my $oc =
                            {
                             cancel_clicked => sub { ugtk2->exit(0) },
                             ok_clicked => sub {
                                my $seclevel_value = $seclevel_entry->entry->get_text;
                                my $secadmin_check_value = $secadmin_check->get_active;
                                my $secadmin_value = $secadmin_entry->get_text;
                                my $w;

                                log::explanations("Configuring msec");

                                if ($seclevel_value ne security::level::get_string()) {
                                    $w = wait_msg(N("Please wait, setting security level..."));
                                    log::explanations(qq(Setting security level to "$seclevel_value"));
                                    security::level::set(security::level::from_string($seclevel_value));
                                    remove_wait_msg($w);
                                }

                                $w = wait_msg(N("Please wait, setting security options..."));
                                log::explanations(qq(Setting security administrator option to ") . bool2yesno($secadmin_check_value) . '"');
                                $msec->set_check('MAIL_WARN', bool2yesno($secadmin_check_value));

                                if ($secadmin_value ne $msec->get_check_value('MAIL_USER') && $secadmin_check_value) {
                                    log::explanations(qq(Setting security administrator contact to "$secadmin_value"));
                                    $msec->set_check('MAIL_USER', $secadmin_value);
                                  }

                                log::explanations("Setting security periodic checks");
                                foreach my $key (keys %security_checks_value) {
                                    $msec->set_check($inv_fields{$key} || $key, from_i18n($security_checks_value{$key}->entry->get_text));
                                }
                                $msec->apply_checks;

                                foreach my $domain (keys %options_values) {
                                    log::explanations("Setting msec functions related to $domain");
                                    foreach my $key (keys %{$options_values{$domain}}) {
                                        my $opt = $options_values{$domain}{$key};
                                        $msec->set_function($inv_fields{$key} || $key, from_i18n($opt->get_text));
                                    }
                                }
                                $msec->apply_functions;
                                log::explanations("Applying msec changes");
                                run_program::run("/usr/sbin/msec");

                                log::explanations("Setting up right delegation");
                                my %rev_auth = reverse %auth;
                                foreach my $key (keys %progs) {
                                    my $value = $progs{$key}->get_text;
                                    set_auth_value($key, $rev_auth{$value});
                                }

                                remove_wait_msg($w);

                                if ($secadmin_value ne $msec->get_check_value('CHKROOTKIT_CHECK')) {
                                    $in->do_pkgs->ensure_is_installed_if_available('chkrootkit', '/usr/sbin/chkrootkit');
                                }

                                ugtk2->exit(0);
                            }
                            },
                            undef, undef, '',
                            [ N("Help"), sub {
                                  my $text = gtknew('TextView');
                                  create_dialog(N("Help"), 
                                                gtktext_insert($text,
                                                               [
                                                                # -1 b/c of main page:
                                                                @{$help[$notebook->get_current_page-1]}
                                                               ]
                                                              ),
                                                { use_markup => 1,
                                                  transient => $w->{real_window},
                                                  height => 400,
                                                  width => 600,
                                                  scroll => 1,
                                                },
                                               );
                              } ],
                           ),
        );

$notebook->set_scrollable(1);
$notebook->signal_connect("switch-page" => sub { $oc->{buttons}{N("Help")}->set_sensitive(!member($_[2], qw(0 4))) });

$oc->{buttons}{N("Help")}->set_sensitive(0);
$oc->{cancel}->can_default(1);
$oc->{cancel}->grab_default;

exit(1) if !$in->do_pkgs->ensure_is_installed('msec', '/usr/sbin/msec');

$w->main;
ugtk2->exit(0);