#!/usr/bin/perl

use lib qw(/usr/lib/libDrakX);
# i18n: IMPORTANT: to get correct namespace (drakguard instead of libDrakX)
BEGIN { unshift @::textdomains, 'drakguard' }


use common;
use standalone;
use mygtk2;
use ugtk2 qw(:create :helpers :wrappers);
use Gtk2::SimpleList;
use interactive;
use network::shorewall;
use network::squid;
use services;
use Encode;

my $dansguardian_main_file = "/etc/dansguardian/dansguardian.conf";
my $dansguardian_filter_file = "/etc/dansguardian/dansguardianf1.conf";
my $time_control_file = "/etc/shorewall/time_control";
my $dansguardian_bannedsitelist = "/etc/dansguardian/lists/bannedsitelist";
my $dansguardian_exceptionsitelist = "/etc/dansguardian/lists/exceptionsitelist";
my $dansguardian_protected_program_list = "/etc/dansguardian/lists/blacklists/drakguard/protected_list";
my $msec_conf = "/etc/security/msec/perms.conf";
my $perms_orig = "/etc/security/msec/perms.orig";
my $fstab_file = "/etc/fstab";
my %dansguardian_levels = (
    160 => N_("Low"),
    100 => N_("Normal"),
    50  => N_("High"),
);
my %dansguardian_langs = (
    arspanish => 'es_AR',
    bulgarian => 'bg',
    chinesebig5 => 'zh_TW',
    chinesegb2312 => 'zh_CN',
    czech => 'cs',
    danish => 'da',
    dutch => 'nl',
    french => 'fr',
    german => 'de',
    hebrew => 'he',
    hungarian => 'hu',
    indonesian => 'id',
    italian => 'it',
    japanese => 'ja',
    lithuanian => 'lt',
    malay => 'ms',
    mxspanish => 'es_MX',
    polish => 'pl',
    portuguese => 'pt',
    ptbrazilian => 'pt_BR',
    #russian-1251
    'russian-koi8-r' => 'ru',
    slovak => 'sk',
    spanish => 'es',
    swedish => 'sv',
    turkish => 'tr',
    ukenglish => 'en',
);

my $blacklist_url_file = "/etc/dansguardian/lists/blacklists/drakguard/urls";
my $whitelist_url_file = "/etc/dansguardian/lists/whitelists/drakguard/urls";
my ($enable, $level, $time_control, $time_start_h, $time_start_m, $time_stop_h, $time_stop_m, $allow_time_change, $net_control, $not_net_control, $program_control, $net_control_state, $program_control_state, $acl_state);
my ($acl_active);
my $shorewall = network::shorewall::read();
my $proxy_port = 3128;
my $proxy_user = 'squid';
my $guardian_port = 8080;
my $guardian_user = 'dansguardian';
my $root_user = 'root';
load();


my $toolname = 'drakguard';
my $title = N("Parental Control");
my $icon = 'drakguard';

$ugtk2::wm_icon = $icon;
my $w = ugtk2->new($title);
#- so that transient_for is defined, for wait messages and popups to be centered
$::main_window = $w->{real_window};
my $in = interactive->vnew('su');

my $allusers_list = Gtk2::SimpleList->new(N("All users") => 'text');
$allusers_list->get_selection->set_mode('multiple');
@{$allusers_list->{data}} = sort(list_users());

my $allusers_program_list;
my $users_program_list;

my $users_list = Gtk2::SimpleList->new(N("Allowed users") => 'text');
$users_list->get_selection->set_mode('multiple');
@{$users_list->{data}} = difference2($shorewall->{accept_local_users}{http}, [ $root_user, $proxy_user ]);

my @url_lists = (
    {
        tab_title => N("Blacklist"),
        list_title => N("Forbidden addresses"),
        remove_text => N("Remove from blacklist"),
        file => $blacklist_url_file,
        apply => \&apply_blacklist,
    },
    {
        tab_title => N("Whitelist"),
        list_title => N("Allowed addresses"),
        remove_text => N("Remove from whitelist"),
        file => $whitelist_url_file,
        apply => \&apply_whitelist,
    },
    {
        tab_title => N("Block programs"),
        list_title => N("Programs with blocked execution"),
        remove_text => N("Remove from blocked execution list"),
        file => $dansguardian_protected_program_list,
        apply => \&apply_block_program,
    }
);

sub update_time_change() {
    gtkval_modify(\$allow_time_change, $enable && $time_control);
    gtkval_modify(\$net_control_state, $enable && $net_control); 
    gtkval_modify(\$not_net_control, $enable && !$net_control); 
    gtkval_modify(\$program_control_state, $enable && $program_control);
    gtkval_modify(\$acl_state, $enable && $acl_active);
}

sub update_network_change() {
    gtkval_modify(\$not_net_control, $enable && !$net_control);
    gtkval_modify(\$net_control_state, $enable && $net_control); 
}

sub update_program_state() {
    gtkval_modify(\$program_control_state, $enable && $program_control);
}

$w->{ok_clicked} = \&save;
$w->{cancel_clicked} = \&quit_gui;

gtkadd($w->{window},
       gtknew('VBox', spacing => 5, children => [
           $::isEmbedded ? () : (0, Gtk2::Banner->new($icon, $title)),
           1, gtknew('Notebook', children => [
               gtknew('Label', text => N("Configuration")),
               gtknew('VBox', spacing => 5, border_width => 5, children => [
                   0, gtknew('Label', text => N("This tool allows to configure parental control.\nIt can block access to web sites and restrict connection during a specified timeframe.")),
                   0, gtknew('CheckButton', text => N("Enable parental control"),
                             active_ref => \$enable, toggled => \&update_time_change),
                   1, gtknew('Label'),
                   0, gtknew('Title2', label => N("Main options")),
                   0, gtknew('HBox', children_tight => [
		       gtknew('CheckButton', text => N("Block all network traffic"),
		                 sensitive_ref => \$enable, active_ref => \$net_control, toggled => \&update_network_change),
                   ]),
                   0, gtknew('HBox', children_tight => [
                       gtknew('Label', sensitive_ref => \$enable, text_markup => N("Obscenity sensibility"), #Sensitivity to bad words
                                 alignment => [ 0, 0.5 ]),
                       gtknew('ComboBox',
                                 list => [ keys %dansguardian_levels ],
                                 text_ref => \$level,
                                 sensitive_ref => \$enable,
                                 format => sub { translate($dansguardian_levels{$_[0]}) }),
                   ]),
                   1, gtknew('Label'),
                   0, gtknew('Title2', label => N("User access")),
                   0, gtknew('HBox', spacing => 5, children_tight => [
                       gtknew('ScrolledWindow', width => 220, height => 120, child => $allusers_list),
                       gtknew('VBox', spacing => 5, children_tight => [
                           gtknew('Button', stock => "gtk-add", clicked => \&add_user),
                           gtknew('Button', stock => "gtk-remove", clicked => \&remove_user),
                       ]),
                       gtknew('ScrolledWindow', width => 220, height => 120, child => $users_list),
                   ]),
                   1, gtknew('Label'),
                   0, gtknew('Title2', label => N("Time control")),
                   0, gtknew('CheckButton', text => N("Allow connections only between these times:"),
                             active_ref => \$time_control, sensitive_ref => \$enable,
                             toggled => \&update_time_change),
                   0, gtknew('HBox', sensitive_ref => \$allow_time_change, spacing => 20, children_tight => [
                       gtknew('Label', text => N("Start:")),
                       gtknew('HBox', spacing => 2, children_tight => [
                           gtknew('SpinButton', lower => 0, upper => 23, step_increment => 1, value => $time_start_h,
                                 value_changed => sub { $time_start_h = $_[0]->get_value }),
                           gtknew('Label', text => ':'),
                           gtknew('SpinButton', lower => 0, upper => 59, step_increment => 1, value => $time_start_m,
                                 value_changed => sub { $time_start_m = $_[0]->get_value }),
                       ]),
                       gtknew('Label'),
                       gtknew('Label', text => N("End:")),
                       gtknew('HBox', spacing => 2, children_tight => [
                           gtknew('SpinButton', lower => 0, upper => 23, step_increment => 1, value => $time_stop_h,
                                 value_changed => sub { $time_stop_h = $_[0]->get_value }),
                           gtknew('Label', text => ':'),
                           gtknew('SpinButton', lower => 0, upper => 59, step_increment => 1, value => $time_stop_m,
                                 value_changed => sub { $time_stop_m = $_[0]->get_value }),
                       ]),
                   ]),
               ]),
               (map {
                   my $url_list = $_;
                   $url_list->{list} = Gtk2::SimpleList->new($url_list->{list_title} => 'text');
                   $url_list->{list}->get_selection->set_mode('multiple');
                   if ($url_list->{tab_title} eq N("Block programs")) { @{$url_list->{list}{data}} = read_program_list($url_list->{file}) }
		   else { @{$url_list->{list}{data}} = read_url_list($url_list->{file}) }
		   
		   $allusers_program_list = Gtk2::SimpleList->new(N("Blocked users") => 'text'); 
                   $allusers_program_list->get_selection->set_mode('multiple');
                   @{$allusers_program_list->{data}} = sort(list_users());

                   $users_program_list = Gtk2::SimpleList->new(N("Allowed users") => 'text');
                   $users_program_list->get_selection->set_mode('multiple');
                   @{$users_program_list->{data}} = sort(read_program_user_list($url_list->{file}));
                   my $entry;
		   my $original_entry;

                   (
                       gtknew('Label', if_($url_list->{tab_title} eq N("Whitelist"), sensitive_ref => \$net_control_state),
			   if_($url_list->{tab_title} eq N("Blacklist"), sensitive_ref => \$not_net_control),
			   if_($url_list->{tab_title} eq N("Block programs"), sensitive_ref => \$acl_state), text => $url_list->{tab_title}),
                       gtknew('VBox', if_($url_list->{tab_title} eq N("Whitelist"), sensitive_ref => \$net_control_state),
			   if_($url_list->{tab_title} eq N("Blacklist"), sensitive_ref => \$not_net_control), spacing => 5, children => [
                           0, gtknew('HBox', children_tight => [
		               if_($url_list->{tab_title} eq N("Block programs"), gtknew('CheckButton', text => N("Block defined applications"),
		                   sensitive_ref => \$acl_state, active_ref => \$program_control, toggled => \&update_program_state)),
                           ]),
                           0, gtknew('HBox', if_($url_list->{tab_title} eq N("Block programs"), sensitive_ref => \$program_control_state), border_width => 5, spacing => 5, children_loose => [
                               $entry = gtknew('Entry'),
                               if_($url_list->{tab_title} eq N("Block programs"), gtknew('Button', text => N("..."), clicked => sub {
					  $entry->set_text($in->ask_file(N("Please select the program you want to control"), "/usr/bin"));
                                      })),
                               0, gtknew('Button', text => N("Add"), clicked => sub {
                                          my $text = $entry->get_text;
					  $text =~ s,^[^:]+://,,g; #- strip protocol
					  if ($url_list->{tab_title} eq N("Block programs")) {
					      $text =~ s,(\W?)$,,g; #- remove unknown caracters
					      $original_entry = "";
					      $text = resolve_symlinks($text);
					      if ((grep { $_ && m/^\[Desktop Entry\]\s*/g } chomp_(cat_($text))) || $text =~ m/\.desktop/g) {# make this only for application block
					          my @exec_line = grep { $_ && m/^Exec=(.*)\s*/g } chomp_(cat_($text));
    						  require lang;
    						  my $locale = lang::read();
						  $locale = lc($locale->{country});
						  my @system_name = grep { $_ && m/^GenericName(\[$locale\])?=(.*)\s*/g } chomp_(cat_($text));
					          foreach (@exec_line) {
						      ($text) = /^Exec=(\w+)\s*/;
						      if ($text eq "") {
						          ($text) = /^Exec=(.*+)\s*/ }
					          }
						  foreach my $b (@system_name) {
						     $original_entry = $b;
						     $original_entry =~ s/^GenericName(\[$locale\])?=//g;
						     $original_entry = Encode::decode("utf8", $original_entry); 
						  }
				              }
					      $text = `which $text 2> /dev/null` if $text ne "";
					      $text =~ s/\n//g;
					  }
					  if ($text eq "" && $url_list->{tab_title} eq N("Block programs")) { $in->ask_warn(N("Error"), N("Invalid Binary Name")) and return }
					  else { if ($url_list->{tab_title} eq N("Block programs")) { 
						     list_add_entry_program($url_list->{list}, $text . " ( $original_entry )") if $original_entry ne "";
						     list_add_entry_program($url_list->{list}, $text) if $original_entry eq "";
					         }
						 else { list_add_entry($url_list->{list}, $text) }
					  }
					  $entry->set_text("");
                                      }),
                           ]),
                           1, gtknew('ScrolledWindow', if_($url_list->{tab_title} eq N("Block programs"), sensitive_ref => \$program_control_state), width => 500, height => 150, child => $url_list->{list}),
                           0, gtknew('HButtonBox', if_($url_list->{tab_title} eq N("Block programs"), sensitive_ref => \$program_control_state), border_width => 5, layout => 'edge', children_loose => [
                               gtknew('Button', text => $url_list->{remove_text}, clicked => sub {
                                          list_remove_selected($url_list->{list});
                                      }),
                           ]),
                           if_($url_list->{tab_title} eq N("Block programs"), 1, gtknew('Label'),
                           0, gtknew('Title2', label => N("Unblock users")),
                           0, gtknew('HButtonBox', sensitive_ref => \$program_control_state, spacing => 5, children_tight => [
                               gtknew('ScrolledWindow', width => 220, height => 120, child => $allusers_program_list),
                               gtknew('VBox', spacing => 5, children_tight => [
                                   gtknew('Button', stock => "gtk-add", clicked => \&add_user_program),
                                   gtknew('Button', stock => "gtk-remove", clicked => \&remove_user_program),
                               ]),
                               gtknew('ScrolledWindow', width => 220, height => 120, child => $users_program_list),
                           ]),
		   	  0, gtknew('HBox'),
	   		  ),
                       ]),
                   );
               } @url_lists),
           ]),
           0, $w->create_okcancel(
               undef, undef, undef,
               [ N("Help"), sub { run_program::raw({ detach => 1 }, 'drakhelp', '--id', $toolname) } ]),
       ]),
);

$w->show;

# FIXME: bogus check: if ACLs are disabled everywhere but in /unused/path, it's OK...
if (cat_('/boot/config') =~ /_FS_POSIX_ACL=y/ &&  grep { $_ && !/acl/ && !/^#/ && m/ext[23]|reiserfs/ } chomp_(cat_($fstab_file))) {
    $acl_active = 1;
	$in->ask_yesorno(N("Warning"), N("The support for Access Control Lists must be enabled in order to use the Block programs feature.\nDo you want to enable it now?", $acl_active = 1)) or $acl_active = 0;
    if ($acl_active) { 
	my @ext_lines = grep { $_ && m/ext[23]|reiserfs/ && !/^#/ } chomp_(cat_($fstab_file));
    	foreach my $line (@ext_lines) {
            my ($flag) = $line =~ /^.+\s.+\s(ext(\w)\s(\w*))\s/ or next;
            $line =~ s/ext(\w)\s(\w*)\s/$flag,acl /g;
            subst_config_line($fstab_file, "$line\n"); #adds ,acl in the fstab file in the first run
            # remount filesystem with acl parameter
    	}
	$acl_active = 0;
    $in->ask_warn(N("Warning"), N("The support for Access Control Lists was enabled.") . N("It is necessary to restart your computer to activate it."));
    }
}
else {
    my @mount = `mount`;
    if (grep { !/,acl/ && m/ext[23]|reiserfs/ } @mount) {
        $in->ask_warn(N("Warning"), N("The support for Access Control Lists, required by the Block programs feature, is enabled, but not yet activated.") . N("It is necessary to restart your computer to activate it."), $acl_active = 0);
    } else {
        $acl_active = 1 }
}
update_time_change();
Gtk2->main;

$w->exit(0);

sub list_add_entry {
    my ($list, @addr) = @_;
    foreach my $a (@addr) {
        push @{$list->{data}}, $a
          unless any { $_->[0] eq $a } @{$list->{data}};
    }
}

sub list_add_entry_program {
    my ($list, @addr) = @_;
    foreach my $a (@addr) {
	my $b = $a;
	$a =~ s/\s(.*)//g;
	my @abc = map { $_->[0] } @{$list->{data}};
        push @{$list->{data}}, $b
          unless (any { $_->[0] eq $b } @{$list->{data}}) || (grep { $_ && m/$a(.*)/g } @abc);
    }
}

sub list_remove_entry {
    my ($list, @addr) = @_;
    #- workaround buggy Gtk2::SimpleList array abstraction, it destroys references
    @{$list->{data}} = map { member($_->[0], @addr) ? () : [ @$_ ] } @{$list->{data}};
}

sub list_get_selected {
    my ($list) = @_;
    uniq(map { $list->{data}[$_][0] } $list->get_selected_indices);
}

sub list_remove_selected {
    my ($list) = @_;
    list_remove_entry($list, list_get_selected($list));
}

sub list_get_entries {
    my ($list) = @_;
    map { $_->[0] } @{$list->{data}};
}

sub add_user() {
    list_add_entry($users_list, list_get_selected($allusers_list));
}

sub remove_user() {
    list_remove_selected($users_list);
}

sub add_user_program() {
    list_add_entry($users_program_list, list_get_selected($allusers_program_list));
}

sub remove_user_program() {
    list_remove_selected($users_program_list);
}

sub quit_gui {
    my ($o_code) = @_;
    $w->exit($o_code);
}

sub load() {
    my $guardian = read_dansguardian();
    my $levelname = { %dansguardian_levels }->{$guardian->{naughtynesslimit}};
    $net_control = $guardian->{netblock};
    $program_control = $guardian->{programblock};
    gtkval_modify(\$not_net_control, $enable && !$net_control);
    $level = { reverse %dansguardian_levels }->{$levelname} if $levelname;
    $level ||= { reverse %dansguardian_levels }->{High};
    $enable = services::starts_on_boot('dansguardian');

    $time_control = cat_($::prefix . "/etc/shorewall/start") =~ /^INCLUDE $time_control_file$/m;
    my @time_control_settings = grep { /\bnet2fw\b/ } cat_($::prefix . $time_control_file);
    my ($drop_start, $drop_stop);
    if (my ($drop_start_h, $drop_start_m) = top(@time_control_settings) =~ /\B--timestart\s(\d+):(\d+)\b/) {
	$drop_start = $drop_start_h*60 + $drop_start_m - 1;
    }
    if (my ($drop_stop_h, $drop_stop_m) = first(@time_control_settings) =~ /\B--timestop\s(\d+):(\d+)\b/) {
	$drop_stop = $drop_stop_h*60 + $drop_stop_m + 1;
    }
    if (defined($drop_start) && defined($drop_stop)) {
	my $day_time = 24*60;
	$drop_start = ($drop_start + $day_time) % $day_time;
	$drop_stop = ($drop_stop + $day_time) % $day_time;

	$time_start_h = int($drop_stop/60);
	$time_start_m = $drop_stop%60;
	$time_stop_h = int($drop_start/60);
	$time_stop_m = $drop_start%60;
    }

    $time_start_h //= 18;
    $time_start_m //= 0;
    $time_stop_h //= 21;
    $time_stop_m //= 0;
}

sub save() {
    my $_wait = $in->wait_message(N("Please wait"), N("Please wait"));

    network::shorewall::set_in_file('start', $enable && $time_control, "INCLUDE $time_control_file");
    if ($enable && $time_control) {
	my $day_time = 24*60;
	#- start/stop dropping the minute after/before traffic is allowed
	#- and make sure times are positive and in the 00:00 <-> 23:59 interval
	my $drop_start = ($time_stop_h*60 + $time_stop_m + 1 + $day_time) % $day_time;
	my $drop_stop = ($time_start_h*60 + $time_start_m - 1 + $day_time) % $day_time;
	output_p($::prefix . $time_control_file,
		 join('', map {
		     my $chain = $_;
		     map {
			 sprintf("iptables -I $chain -j DROP -m time --timestart %02d:%02d --timestop %02d:%02d --kerneltz\n",
				 int($_->[0]/60), $_->[0]%60,
				 int($_->[1]/60), $_->[1]%60,
			     );
		     } ($drop_stop >= $drop_start ? [ $drop_start, $drop_stop ] : ([ 0, $drop_stop ], [ $drop_start, $day_time-1 ]));
		     #- if allowing start time is before allowing stop time,
		     #- we have to use two intervals to cover the completary parts of the day
		 } qw(net2fw fw2net)),
	    );
	#- allowing from 00:00 to 23:59 is a special case that does not need rules
	$time_control = 0 if $drop_stop == $day_time - 1 && $drop_start == 0;
    }
    network::shorewall::set_in_file('start', $enable && $time_control, "INCLUDE $time_control_file");

    if ($enable) {
        $in->do_pkgs->ensure_are_installed([ qw(shorewall squid dansguardian) ])
          or quit_gui(1);

        $_->{apply}(list_get_entries($_->{list})) foreach @url_lists;
        write_dansguardian();
        enable_transparent_proxy($proxy_port);
        #- reload shorewall config if it has just been installed
        $shorewall ||= network::shorewall::read();
    }
    else {
	$program_control = 0;
	$url_lists[2]{apply}(list_get_entries($url_lists[2]{list}));
    }
    services::set_status($_, $enable) foreach qw(squid dansguardian);

    if ($shorewall) {
        $shorewall->{disabled} = 0 if $enable;
        @{$shorewall->{accept_local_users}{http}} = if_($enable, uniq($proxy_user, $root_user, list_get_entries($users_list)));
        @{$shorewall->{accept_local_users}{$proxy_port}} = if_($enable, $guardian_user);
        network::shorewall::set_redirected_ports($shorewall, 'tcp', $guardian_port, if_($enable, 'http', $proxy_port));
        network::shorewall::write($shorewall, $in);
    }
    
    quit_gui();
}

sub resolve_symlinks {

    # Check if a given file (either the pure filename or in a SANE device
    # string as "<prefix>:<file>") is a symlink, if so expand the link.
    # If the new file name is a link, expand again, until finding the
    # physical file.
    my ($file) = @_;
    my $prefix = "";
    if ($file =~ m!^([^/]*)(/.*)$!) {
        $prefix = $1;
        $file = $2;
    } else {
        return $file;
    }
    while (1) {
    #my $ls = `ls -l $file 2> /dev/null`;
        my $ls =  readlink $file;
        if ($ls =~ m!\s($file)\s*\->\s*(\S+)\s*$!) {
            my $target = $2;
            if ($target !~ m!^/! && $file =~ m!^(.*)/[^/]+$!) {
                $target = "$1/$target";
            }
            $file = $target;
        } else {
            last;
        }
    }
    return $prefix . $file;
}

sub remove_acl {
    my ($file, $binary) = @_;
    `setfacl -b $binary`;
    clean_config_line($file, $binary);
}

sub set_permissions {
    my ($line, $line_info) = @_;
    #`protect $line --add`;
    chmod 0700, $line;
    chown "root", "root", $line;
    #remove msec lines
    #clean_config_line($msec_conf,"$line");	
    subst_config_line($dansguardian_protected_program_list, "$line_info \n"); #adds the file to dansguardian_protected_program_list
    subst_config_line($msec_conf, "$line\troot.root\t700\tforce\t\n"); #adds the file to msec
    #`protect $line $_` foreach list_get_entries($users_program_list);
    #add msec line or lines...
    foreach my $user_msec (list_get_entries($users_program_list)) {
        my @msec_line = grep { $_ && m/$line/g } chomp_(cat_($msec_conf));
        my @program_list_line = grep { $_ && m/^$line/g } chomp_(cat_($dansguardian_protected_program_list));
	`setfacl -m u:$user_msec:r-x $line`;
	subst_config_line($dansguardian_protected_program_list, "$_$user_msec,\n") foreach @program_list_line; #adds the users to the programs blocked in msec
	subst_config_line($msec_conf, "$_$user_msec:r-x,\n") foreach @msec_line; #adds the users to the programs blocked in msec
    }
}

sub restore_permissions {
    my ($line) = @_;
    	my ($fich, $usrname, $grpname, $mode) = $line =~ /^(.*)\t(\w+)\.(\w+)\t(\d+)\s*/ or return;
        $mode = sprintf "%04d", $mode;#make number with 4 digits
        my $guid = getgrnam($grpname);
        my $uid = getpwnam($usrname);
        chown $uid, $guid, $fich;
        chmod oct($mode), $fich;
}

sub subst_config_line {
    my ($file, $line) = @_;
    my $key = first(split(' ', $line));
    my $done;
    substInFile {
        $done = 1 if s|^\s*$key\b.*\n|$line|;
        $_ .= $line if eof && !$done;
    } $file;
}

sub clean_config_line {
    my ($file, $line) = @_;
    my $key = first(split(' ', $line));
    substInFile {
        s|^\s*$key.*\n||;
    } $file;
}

sub block_internet_dansguardian {
    my ($file) = @_;
    my $key = "#";
    substInFile {
	s|^$key\*\*\s|**\n|;
    } $file;
}

sub unblock_internet_dansguardian {
    my ($file) = @_;
    my $key = "#";
    substInFile {
	s|^\*\*\s|$key**\n|;
    } $file;
}

sub enable_transparent_proxy {
    my ($port) = @_;
    #- FIXME: use network::squid once it is rewritten to be more gentle with the config file
    # Only use transparent proxy if squid version < 3.2. Handle upgrades nicely
    substr(`rpm -q squid`, 6, 3) < 3.2 ?  subst_config_line($network::squid::squid_conf_file, "http_port $port transparent\n") :
       subst_config_line($network::squid::squid_conf_file, "http_port $port\n");
}

#- mostly duplicated for MDK::Common::System::getVarsFromSh
sub read_dansguardian() {
    my $guardian = {};
    foreach (cat_($dansguardian_filter_file)) {
	s/#.*//;
        s/^\s*//;
	my ($v, $val) = /^(\w+)\s*=\s*(.*)/ or next;
	$val = $1 if $val =~ /^"(.*)"$/ || $val =~ /^'(.*)'$/;
	$guardian->{$v} = $val;
    }
    $guardian;
}

sub write_dansguardian() {
    require lang;
    my $locale = lang::read();
    my $locale_lang = lang::getlocale_for_lang($locale->{lang}, $locale->{country});
    my %lang_to_dansguardian = reverse %dansguardian_langs;
    my $dansguardian_lang = $lang_to_dansguardian{$locale_lang} || $lang_to_dansguardian{$locale->{lang}};

    subst_config_line($dansguardian_main_file, "language = '$dansguardian_lang'\n") if $dansguardian_lang;
    subst_config_line($dansguardian_filter_file, "naughtynesslimit = '$level'\n");
    subst_config_line($dansguardian_filter_file, "netblock = '$net_control'\n");
    subst_config_line($dansguardian_filter_file, "programblock = '$program_control'\n");
    if ($net_control) {
	    
	block_internet_dansguardian($dansguardian_bannedsitelist);

    }
    else {

	unblock_internet_dansguardian($dansguardian_bannedsitelist);
    }
}

sub include_guardian_file {
    my ($guardian_file, $external_file) = @_;
    my $to_add = ".Include<$external_file>\n";
    clean_config_line($guardian_file, $to_add);
    my @all = cat_($guardian_file);
    if ($dansguardian_bannedsitelist eq $guardian_file && !$net_control || $dansguardian_exceptionsitelist eq $guardian_file && $net_control) {
	if (!member($to_add, @all)) {
               output_p($guardian_file, @all, $to_add);
       }
    }
}

sub read_url_list {
    my ($file) = @_;
    grep { $_ && !/^\s*#/ } chomp_(cat_($file));
}

sub read_program_list {
    my ($file) = @_;
    grep { $_ && s/(\s*(\w+),)*//g } chomp_(cat_($file));
}

sub read_program_user_list {
    my ($file) = @_;
    my @user_list = grep { $_ && s/(.*?)\s//g } chomp_(cat_($file));
    uniq(map { my $user = $_; 
	      $user =~ m/(\w+),/g } @user_list);
}

sub apply_blacklist {
    my @addr = @_;
    my $blacklist_top = "/etc/dansguardian/lists/bannedsitelist";
    my $blacklist_category = "blocked by Mageia parental control tool";
    output_p($blacklist_url_file, map { $_ . "\n" }
               qq(#listcategory: "$blacklist_category"), @addr);
    include_guardian_file($blacklist_top, $blacklist_url_file);
}

sub apply_whitelist {
    my @addr = @_;
    my $whitelist_top = "/etc/dansguardian/lists/exceptionsitelist";
    output_p($whitelist_url_file, map { $_ . "\n" } @addr);
    include_guardian_file($whitelist_top, $whitelist_url_file);
}

sub apply_block_program() {
    if ($program_control) {
        my @original_list = read_program_list($url_lists[2]{file});
	my @orig_list = read_program_list($url_lists[2]{file});
	foreach (@original_list) {
	    s/\s(.*)//;
	}
        my @actual_list = list_get_entries($url_lists[2]{list});
	my @act_list = list_get_entries($url_lists[2]{list});
	foreach (@actual_list) {
	    s/\s(.*)//;
	}
	#code to make diff bettween vectors
    my (@intersection, @difference);
	@intersection = @difference = ();
	my %count;
	foreach my $element (@original_list, @actual_list) { $count{$element}++ }
    foreach my $element (keys %count) {
	    push @{ $count{$element} > 1 ? \@intersection : \@difference }, $element;
	}
	if (scalar(@difference)) {
	    #keep the permissons save, make the way to do that
	    foreach my $diff (@difference) {
		my @orig_file = cat_($perms_orig);
		my @progs_orig_file = grep { $_ && m/$diff/g } chomp_(cat_($perms_orig));
		#test if program is in msec
		my @msec_program = grep { $_ && m/$diff/g } chomp_(cat_($msec_conf));
	        if (scalar(@progs_orig_file)) {#test if is to remove or to add acl
		    #clean config file of the removed programs
		    foreach my $element (@progs_orig_file) {
			my $val = $element;
		    	substInFile {
			    s|$element.*\n||;
		        } $perms_orig;
			$val =~ s/\t(.*)\s*//;
			$element =~ s/^(\d+)\t//;
			if ($val) {
			    #write back in the msec file and set original file permissions
			    #`unprotect $diff --remove`; 
			    remove_acl($dansguardian_protected_program_list, $diff);
	                    subst_config_line($msec_conf, "$element\n");
			    restore_permissions($element);
			} 
			else { 
			    #only make the permissions wright
			    #`unprotect $diff --remove`; 
			    remove_acl($dansguardian_protected_program_list, $diff);
       			    clean_config_line($msec_conf, $diff);	
			    restore_permissions($element);
			}
	    	    }
	        }
		else {
		    if (scalar(@msec_program)) {
			output_p($perms_orig, @orig_file, "1\t" . $_ . "\n") foreach @msec_program;
			#add file to msec and set acls
		    }
		    else {
			my $mode = (stat($diff))[2] & 07777;
			#"%04o" important to make 0755 to chmod ou chown
			$mode = sprintf "%lo", $mode;
			my $uid = (stat($diff))[4];
			my $gid = (stat($diff))[5];
			my $usrname = getpwuid($uid);
			my $gname = getgrgid($gid);
			my $to_add = "0\t$diff\t$usrname.$gname\t$mode\n";
			#keep the permissons safe make the way to do that
			output_p($perms_orig, @orig_file, $to_add);
		    }
		    set_permissions($diff, grep { $_ && m/$diff/g } @act_list);
	    	}
	    }
	}
	#this is just the case to reactivate the parental control
	#`unprotect $_ --remove` foreach @intersection;
	remove_acl($dansguardian_protected_program_list, $_) foreach @intersection;
        foreach my $intersec (@intersection) {	
	    set_permissions($intersec, grep { $_ && m/$intersec/g } @orig_list);
	}
    } 
    else {
	my @original_list = list_get_entries($url_lists[2]{list});
	foreach (@original_list) {
	    s/\s(.*)//;
	}
	#restore the original file permissions
	`setfacl -b $_` foreach @original_list; 
	foreach my $list (@original_list) {
	    my @progs_orig_file = grep { $_ && m/$list/g } chomp_(cat_($perms_orig));
	    #test if program is in msec
	    if (scalar(@progs_orig_file)) {
	        #clean config file of the removed programs
	        foreach my $element (@progs_orig_file) {
	            my $val = $element;
	            $val =~ s/\t(.*)\s*//;
	            $element =~ s/^(\d+)\t//;
	            if ($val) {
	                #write back in the msec file and set original file permissions
	                subst_config_line($msec_conf, "$element\n");
		        restore_permissions($element);
	            } 
	            else { 
	                #only make the permissions wright
       		        clean_config_line($msec_conf, $list);	
		        restore_permissions($element);
	            }
	        }
	    }
	}
    }
}
