#!/usr/bin/perl use lib qw(/usr/lib/libDrakX); # i18n: IMPORTANT: to get correct namespace (drakguard instead of libDrakX) BEGIN { unshift @::textdomains, 'drakguard' } use strict; use diagnostics; 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; my $dansguardian_main_file = "/etc/dansguardian/dansguardian.conf"; my $dansguardian_filter_file = "/etc/dansguardian/dansguardianf1.conf"; 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, $allow_time_change); my $shorewall = network::shorewall::read(); 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 $users_list = Gtk2::SimpleList->new(N("Allowed users") => 'text'); $users_list->get_selection->set_mode('multiple'); 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, } ); sub update_time_change() { gtkval_modify(\$allow_time_change, $enable && $time_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 => [ 1, gtknew('WrappedLabel', text => N("This tool allows to configure parental control. It can block access to web sites and restrict connection during a specified timeframe.")), 1, gtknew('Label'), 0, gtknew('Title2', label => N("Main options")), 0, gtknew('CheckButton', text => N("Enable parental control"), active_ref => \$enable, toggled => \&update_time_change), 0, gtknew('HBox', children_tight => [ gtknew('Label_Left', text_markup => N("Control level"), 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 => 90, child => $allusers_list), gtknew('VBox', spacing => 5, children_tight => [ gtknew('Button', stock => "gtk-add", sensitive => 0), gtknew('Button', stock => "gtk-remove", sensitive => 0), ]), gtknew('ScrolledWindow', width => 220, height => 90, 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 => 0, #, 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 => 24, step_increment => 1, value => 18), gtknew('Label', text => ':'), gtknew('SpinButton', lower => 0, upper => 60, step_increment => 1), ]), gtknew('Label'), gtknew('Label', text => N("End:")), gtknew('HBox', spacing => 2, children_tight => [ gtknew('SpinButton', lower => 0, upper => 24, step_increment => 1, value => 21), gtknew('Label', text => ':'), gtknew('SpinButton', lower => 0, upper => 60, step_increment => 1), ]), ]), ]), (map { my $url_list = $_; $url_list->{list} = Gtk2::SimpleList->new($url_list->{list_title} => 'text'); $url_list->{list}->get_selection->set_mode('multiple'); @{$url_list->{list}{data}} = read_url_list($url_list->{file}); my $entry; ( gtknew('Label', text => $url_list->{tab_title}), gtknew('VBox', spacing => 5, children => [ 0, gtknew('HBox', border_width => 5, spacing => 5, children_loose => [ $entry = gtknew('Entry'), gtknew('Button', text => N("Add"), clicked => sub { my $text = $entry->get_text; $text =~ s,^[^:]+://,,g; #- strip protocol list_add_addr($url_list->{list}, $text); $entry->set_text(""); }), ]), 1, gtknew('ScrolledWindow', width => 500, height => 300, child => $url_list->{list}), 0, gtknew('HButtonBox', border_width => 5, layout => 'edge', children_loose => [ gtknew('Button', text => $url_list->{remove_text}, clicked => sub { list_remove_selected($url_list->{list}); }), ]), ]), ); } @url_lists), ]), 0, $w->create_okcancel( undef, undef, undef, [ N("Help"), sub { run_program::raw({ detach => 1 }, 'drakhelp', '--id', $toolname) } ]), ]), ); $w->show; Gtk2->main; $w->exit(0); sub list_add_addr { my ($list, @addr) = @_; foreach my $a (@addr) { push @{$list->{data}}, $a unless any { $_->[0] eq $a } @{$list->{data}}; } } sub list_remove_addr { 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_addr($list, list_get_selected($list)); } sub quit_gui { my ($code) = @_; $w->exit($code); } sub load() { my $guardian = read_dansguardian(); $level = { reverse %dansguardian_levels }->{$guardian->{naughtynesslimit}}; $level ||= { reverse %dansguardian_levels }->{High}; $enable = services::starts_on_boot('dansguardian'); } sub save() { my $proxy_port = 3128; my $proxy_user = 'squid'; my $guardian_port = 8080; my $guardian_user = 'dansguardian'; if ($enable) { $in->do_pkgs->ensure_are_installed([ qw(shorewall squid dansguardian) ]) or quit_gui(1); $_->{apply}(map { $_->[0] } @{$_->{list}{data}}) foreach @url_lists; write_dansguardian(); enable_transparent_proxy($proxy_port); #- reload shorewall config if it has just been installed $shorewall ||= network::shorewall::read(); } services::set_status($_, $enable) foreach qw(squid dansguardian); if ($shorewall) { $shorewall->{disabled} = 0 if $enable; $shorewall->{accept_local_user}{http} = $enable && $proxy_user; $shorewall->{accept_local_user}{$proxy_port} = $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 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 enable_transparent_proxy { my ($port) = @_; #- FIXME: use network::squid once it is rewritten to be more gentle with the config file subst_config_line($network::squid::squid_conf_file, "http_port $port transparent\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"); } sub include_guardian_file { my ($guardian_file, $external_file) = @_; my $to_add = ".Include<$external_file>\n"; my @all = cat_($guardian_file); if (!member($to_add, @all)) { output_p($guardian_file, @all, $to_add); } } sub read_url_list { my ($file) = @_; grep { $_ && !/^\s*#/ } chomp_(cat_($file)); } sub apply_blacklist { my @addr = @_; my $blacklist_top = "/etc/dansguardian/lists/bannedsitelist"; my $blacklist_category = "blocked by Mandriva 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/exceptionurllist"; output_p($whitelist_url_file, map { $_ . "\n" } @addr); include_guardian_file($whitelist_top, $whitelist_url_file); }