#!/usr/local/bin/perl
# acl.cgi
# Display a form for editing or creating a new ACL

use strict;
use warnings;
no warnings 'redefine';
no warnings 'uninitialized';
our (%text, %in, %access, $squid_version, %config, %acl_types,
     @caseless_acl_types, @nodns_acl_types);
require './squid-lib.pl';
$access{'actrl'} || &error($text{'eacl_ecannot'});
&ReadParse();
my $conf = &get_config();

my $type;
my (@acl, @aclopts, @deny, @vals, $file, $acloptfound);
if ($in{'type'}) {
	&ui_print_header(undef, $text{'acl_header1'}, "", undef, 0, 0, 0,
			 &restart_button());
	$type = $in{'type'};
	@vals = ( );
	}
else {
	&ui_print_header(undef, $text{'acl_header2'}, "", undef, 0, 0, 0,
			 &restart_button());
	@acl = @{$conf->[$in{'index'}]->{'values'}};
	$type = $acl[1];
	$acloptfound = 0;
	if ($type eq "external" ||
	     &indexof($type, @caseless_acl_types) >= 0 ||
	     &indexof($type, @nodns_acl_types) >= 0) {
	    if ($acl[3] =~ /^"(.*)"$/) {
		# Extra parameters come from file
		@vals = ( $acl[2] );
		$file = $1;
		$acloptfound = 1;
		}
	    elsif ($acl[4] =~ /^"(.*)"$/) {
		# Extra parameters come from file
		@vals = @acl[2..3];
		$file = $1;
		$acloptfound = 1;
		}
	    if ($type ne "external") {
		@aclopts = @vals;
		@vals = ( );
		}
	}
	if ($acloptfound) {
		# do nothing
		}
	elsif ($acl[2] =~ /^"(.*)"$/) {
		# All values come from a file
		$file = $1;
		}
	else {
		# All values come from acl parameters
		@vals = ( );
		for(my $i=2; $i<=$#acl; $i++) {
		    if ($acl[$i] eq "--") {
			# end of options push everything remaining
			push (@vals, @acl[$i..$#acl]);
			last;
			}
		    elsif (($acl[$i] eq "-i" && &indexof($type, @caseless_acl_types) >= 0) ||
			   ($acl[$i] eq "-n" && &indexof($type, @nodns_acl_types) >= 0)) {
			push (@aclopts, $acl[$i]);
			$acloptfound = 1;
			}
		    else {
			push (@vals, $acl[$i]);
			}
		    }
		}
	if ($file) {
		my @newvals = split(/\r?\n/, &read_file_contents($file));
		push(@vals, @newvals);
		}
	if ($type =~ /^(src|dst|srcdomain|dstdomain|user|myip)$/) {
		@vals = sort { $a cmp $b } @vals;
		}
	elsif ($type eq "port") {
		@vals = sort { $a <=> $b } @vals;
		}
	@deny = grep { $_->{'values'}->[1] eq $acl[0] }
			&find_config("deny_info", $conf);
	}

print &ui_form_start("acl_save.cgi", "form-data");
if (@acl) {
	print &ui_hidden("index", $in{'index'});
	}
if (@deny) {
	print &ui_hidden("dindex", $deny[0]->{'index'});
	}
print &ui_hidden("type", $type);
print &ui_table_start("$acl_types{$type} ACL", undef, 2);

# ACL name
print &ui_table_row($text{'acl_name'},
	&ui_textbox("name", $acl[0], 30));

if ($type eq "src" || $type eq "dst") {
	# By source or dest address/network
	my $nodns;
	$nodns = 1 if ($type eq 'dst' && grep (/^\-n$/, @aclopts));
	my $table = &ui_columns_start([ $text{'acl_fromip'},
					$text{'acl_toip'},
					$text{'acl_nmask'} ]);
	for(my $i=0; $i<=@vals; $i++) {
		my ($from, $to, $mask) = @_;
		if ($vals[$i] =~ /^([a-z0-9\.\:]+)-([a-z0-9\.\:]+)\/([\d\.]+)$/) {
			$from = $1; $to = $2; $mask = $3;
			}
		elsif ($vals[$i] =~ /^([a-z0-9\.\:]+)-([a-z0-9\.\:]+)$/) {
			$from = $1; $to = $2; $mask = "";
			}
		elsif ($vals[$i] =~ /^([a-z0-9\.\:]+)\/([\d\.]+)$/) {
			$from = $1; $to = ""; $mask = $2;
			}
		elsif ($vals[$i] =~ /^([a-z0-9\.\:]+)$/) {
			$from = $1; $to = ""; $mask = "";
			}
		else { $from = $to = $mask = ""; }
		$table .= &ui_columns_row([
			&ui_textbox("from_$i", $from, 20),
			&ui_textbox("to_$i", $to, 20),
			&ui_textbox("mask_$i", $mask, 20),
			]);
		}
	$table .= &ui_columns_end();
	print &ui_table_row(undef, $table, 2);
	print &ui_table_row(undef,
		&ui_checkbox("nodns", 1, $text{'acl_nodns'}, $nodns)) if ($type eq "dst");
	}
elsif ($type eq "myip") {
	# By local IP address
	my $table = &ui_columns_start([ $text{'acl_ipaddr'},
					$text{'acl_nmask'} ]);
	for(my $i=0; $i<=@vals; $i++) {
		my ($ip, $mask);
		if ($vals[$i] =~ /^([a-z0-9\.\:]+)\/([\d\.]+)$/) {
			$ip = $1; $mask = $2;
			}
		else { $ip = $mask = ""; }
		$table .= &ui_columns_row([
			&ui_textbox("ip_$i", $ip, 20),
			&ui_textbox("mask_$i", $mask, 20),
			]);
		}
	$table .= &ui_columns_end();
	print &ui_table_row(undef, $table, 2);
	}
elsif ($type eq "srcdomain" || $type eq "dstdomain") {
	# Source or destination domain
	my $nodns;
	$nodns = 1 if ($type eq 'dstdomain' && grep (/^\-n$/, @aclopts));
	print &ui_table_row($text{'acl_domains'},
		&ui_textarea("vals", join("\n", @vals), 6, 60));
	print &ui_table_row(undef,
		&ui_checkbox("nodns", 1, $text{'acl_nodns'}, $nodns)) if ($type eq "dstdomain");
	}
elsif ($type eq "time") {
	# Day or week and time of day
	my $vals = join(' ', @vals);
	my %day;
	if ($vals =~ /[A-Z]+/) {
		foreach my $d (split(//, $vals)) {
			$day{$d}++;
			}
		}
	my ($h1, $h2, $m1, $m2, $hour);
	if ($vals =~ /(\d+):(\d+)-(\d+):(\d+)/) {
		$h1 = $1; $m1 = $2;
		$h2 = $3; $m2 = $4;
		$hour++;
		}
	my @day_name = ( [ 'S', $text{'acl_dsun'} ], 
                         [ 'M', $text{'acl_dmon'} ], 
                         [ 'T', $text{'acl_dtue'} ],
		         [ 'W', $text{'acl_dwed'} ], 
                         [ 'H', $text{'acl_dthu'} ], 
                         [ 'F', $text{'acl_dfri'} ],
		         [ 'A', $text{'acl_dsat'} ] );
	print &ui_table_row($text{'acl_dofw'},
		&ui_radio("day_def", %day ? 0 : 1,
			  [ [ 1, $text{'acl_all'} ],
			    [ 0, $text{'acl_sel'} ] ])."<br>\n".
		&ui_select("day", [ keys %day ], \@day_name,
			   7, 1));

	print &ui_table_row($text{'acl_hofd'},
		&ui_radio("hour_def", $hour ? 0 : 1,
			  [ [ 1, $text{'acl_all'} ],
			    [ 0, &ui_textbox("h1", $h1, 2).":".
				 &ui_textbox("m1", $m1, 2)." $text{'acl_to'} ".
				 &ui_textbox("h2", $h2, 2).":".
				 &ui_textbox("m2", $m2, 2) ] ]));
	}
elsif ($type eq "url_regex" || $type eq "urlpath_regex") {
	# URL regular expression
	my $caseless;
	$caseless = 1 if (grep (/^\-i$/, @aclopts));
	print &ui_table_row($text{'acl_regexp'},
		&ui_checkbox("caseless", 1, $text{'acl_case'}, $caseless).
		"<br>\n".
		&ui_textarea("vals", join("\n", @vals), 6, 60));
	}
elsif ($type eq "port") {
	# Request port number
	print &ui_table_row($text{'acl_tcpports'},
		&ui_textbox("vals", join(" ", @vals), 60));
	}
elsif ($type eq "proto") {
	# Request protocol
	my %proto = map { $_, 1 } @vals;
	print &ui_table_row($text{'acl_urlproto'},
		join(" ", map { &ui_checkbox("vals", $_, $_, $proto{$_}) }
		      ('http', 'ftp', 'gopher', 'wais', 'cache_object')));
	}
elsif ($type eq "method") {
	# HTTP method
	my %meth =  map { $_, 1 } @vals;
	print &ui_table_row($text{'acl_reqmethods'},
		join(" ", map { &ui_checkbox("vals", $_, $_, $meth{$_}) }
                      ('GET', 'POST', 'HEAD', 'CONNECT', 'PUT', 'DELETE')));
	}
elsif ($type eq "browser") {
	# Browser user agent
	print &ui_table_row($text{'acl_bregexp'},
		&ui_textbox("vals", join(" ", @vals), 60));
	}
elsif ($type eq "user") {
	# Proxy usernames
	print &ui_table_row($text{'acl_pusers'},
		&ui_textarea("vals", join("\n", @vals), 6, 60));
	}
elsif ($type eq "src_as" || $type eq "dst_as") {
	# Source or destination AS number
	print &ui_table_row($text{'acl_asnum'},
		&ui_textbox("vals", join(" ", @vals), 20));
	}
elsif ($type eq "proxy_auth" && $squid_version < 2.3) {
	# Refresh time
	print &ui_table_row($text{'acl_rtime'},
		&ui_textbox("vals", $vals[0], 8));
	}
elsif ($type eq "proxy_auth" && $squid_version >= 2.3) {
	# Proxy username
	print &ui_table_row($text{'acl_eusers'},
		&ui_radio("authall",
			  $vals[0] eq 'REQUIRED' || $in{'type'} ? 1 : 0,
			  [ [ 1, $text{'acl_eusersall'} ],
			    [ 0, $text{'acl_euserssel'} ] ])."<br>\n".
		&ui_textarea("vals", $vals[0] eq 'REQUIRED' || $in{'type'} ?
					"" : join("\n", @vals), 6, 60));
	}
elsif ($type eq "proxy_auth_regex") {
	# Username regexp
	my $caseless;
	$caseless = 1 if (grep (/^\-i$/, @aclopts));
	print &ui_table_row($text{'acl_eusers'},
		&ui_checkbox("caseless", 1, $text{'acl_case'}, $caseless).
		"<br>\n".
		&ui_textarea("vals", join("\n", @vals), 6, 60));
	}
elsif ($type eq "srcdom_regex" || $type eq "dstdom_regex") {
	# Source or destination domain regexp
	my ($caseless, $nodns, $dnshtml);
	$caseless = 1 if (grep (/^\-i$/, @aclopts));
	$nodns = 1 if ($type eq "dstdom_regex" && grep (/^\-n$/, @aclopts));
	$dnshtml = "";
        $dnshtml = &ui_checkbox("nodns", 1, $text{'acl_nodns'}, $nodns) . "<br>\n" if ($type eq "dstdom_regex");
	print &ui_table_row($text{'acl_regexp'},
		&ui_checkbox("caseless", 1, $text{'acl_case'}, $caseless).
		"<br>\n" . $dnshtml .
		&ui_textarea("vals", join("\n", @vals), 6, 60));
	}
elsif ($type eq "ident") {
	# IDENT protocol user
	print &ui_table_row($text{'acl_rfcusers'},
		&ui_textarea("vals", join("\n", @vals), 6, 60));
	}
elsif ($type eq "ident_regex") {
	# IDENT protocol username regexp
	my $caseless;
	$caseless = 1 if (grep (/^\-i$/, @aclopts));
	print &ui_table_row($text{'acl_rfcusersr'},
		&ui_checkbox("caseless", 1, $text{'acl_case'}, $caseless).
                "<br>\n".
                &ui_textarea("vals", join("\n", @vals), 6, 60));
	}
elsif ($type eq "maxconn") {
	# Max concurrent connections
	print &ui_table_row($text{'acl_mcr'},
		&ui_textbox("vals", $vals[0], 8));
	}
elsif ($type eq "max_user_ip") {
	# Max connections per IP
	my $mipstrict;
	if ($vals[0] eq '-s') {
		$mipstrict++;
		shift(@vals);
		}
	print &ui_table_row($text{'acl_mai'},
		&ui_textbox("vals", $vals[0], 8));
	print &ui_table_row($text{'acl_extargs'},
		&ui_checkbox("strict", 1, $text{'acl_maistrict'}, $mipstrict).
		"<br>\n".
		&ui_textbox("args", join(" ", @vals[1..$#vals]), 60).
		"<br>\n".$text{'acl_mairemind'});
	}
elsif ($type eq "myport") {
	# Local port number
	print &ui_table_row($text{'acl_psp'},
		&ui_textbox("vals", $vals[0], 8));
	}
elsif ($type eq "snmp_community") {
	# SNMP community
	print &ui_table_row($text{'acl_scs'},
		&ui_textbox("vals", $vals[0], 15));
	}
elsif ($type eq "req_mime_type") {
	# Request MIME type
	print &ui_table_row($text{'acl_rmt'},
		&ui_textbox("vals", $vals[0], 15));
	}
elsif ($type eq "rep_mime_type") {
	# Reply MIME type
	print &ui_table_row($text{'acl_rpmt'},
		&ui_textbox("vals", $vals[0], 15));
	}
elsif ($type eq "arp") {
	# Client MAC address
	print &ui_table_row($text{'acl_arp'},
		&ui_textarea("vals", join("\n", @vals), 6, 60));
	}
elsif ($type eq "external") {
	# External program
	print &ui_table_row($text{'acl_extclass'},
		&ui_select("class", $vals[0],
			[ map { $_->{'values'}->[0] }
			      &find_config("external_acl_type", $conf) ]));
	print &ui_table_row($text{'acl_extargs'},
		&ui_textbox("args", join(" ", @vals[1..$#vals]), 60));
	}

# Show URL to redirect on failure
print &ui_table_row($text{'acl_failurl'},
	&ui_textbox("deny", @deny ? $deny[0]->{'values'}->[0] : "", 40));

# Show file in which ACL is stored
print &ui_table_row($text{'acl_file'},
	&ui_opt_textbox("file", $file, 40, $text{'acl_nofile'},
			$text{'acl_infile'})." ".
	&file_chooser_button("file")."<br>\n".
	($in{'type'} ? &ui_checkbox("keep", 1, $text{'acl_keep'}, 0) : ""));

print &ui_table_end();
print &ui_form_end([ [ undef, $text{'acl_buttsave'} ],
		     $in{'type'} ? ( ) : ( [ 'delete', $text{'acl_buttdel'} ] ),
		   ]); 

&ui_print_footer("edit_acl.cgi?mode=acls", $text{'acl_return'},
		 "", $text{'index_return'});

