# shorewall-lib.pl
# Common functions for the shorewall configuration files

do '../web-lib.pl';
&init_config();
do '../ui-lib.pl';

# Get the version
open(VERSION, "$module_config_directory/version");
chop($shorewall_version = <VERSION>);
close(VERSION);

# get access permissions
%access = &get_module_acl();

@shorewall_files = ( 'zones', 'interfaces', 'policy', 'rules', 'tos',
	   	     'masq', 'nat', 'proxyarp', 'routestopped',
	   	     'tunnels', 'hosts', 'blacklist' );

# version_atleast(v1, v2, v3)
sub version_atleast
{
local @vsp = split(/\./, $shorewall_version);
local $i;
for($i=0; $i<@vsp || $i<@_; $i++) {
	return 0 if ($vsp[$i] < $_[$i]);
	return 1 if ($vsp[$i] > $_[$i]);
	}
return 1;	# same!
}

# read_table_file(table, &parserfunc)
sub read_table_file
{
local @rv;
local $func = $_[1];
open(FILE, "$config{'config_dir'}/$_[0]");
while(<FILE>) {
	s/\r|\n//g;
	local $l = &$func($_);
	push(@rv, $l) if ($l);
	}
close(FILE);
return @rv;
}

# read_table_struct(table, &parserfunc)
sub read_table_struct
{
if (!defined($read_table_cache{$_[0]})) {
	local @rv;
	local $func = $_[1];
	open(FILE, "$config{'config_dir'}/$_[0]");
	local $lnum = 0;
	while(<FILE>) {
		s/\r|\n//g;
		local $cmt;
		if (s/#\s*(.*)$//) {
			$cmt = $1;
			}
		local $l = &$func($_);
		if ($l) {
			push(@rv, { 'line' => $lnum,
				    'file' => "$config{'config_dir'}/$_[0]",
				    'table' => $_[0],
				    'index' => scalar(@rv),
				    'values' => $l,
				    'comment' => $cmt });
			}
		$lnum++;
		}
	close(FILE);
	$read_table_cache{$_[0]} = \@rv;
	}
return $read_table_cache{$_[0]};
}

# find_line_num(&lref, &parserfunc, index)
sub find_line_num
{
local $lref = $_[0];
local $func = $_[1];
local $idx = 0;
for($i=0; $i<@$lref; $i++) {
	if (&$func($lref->[$i])) {
		if ($idx++ == $_[2]) {
			return $i;
			}
		}
	}
return undef;
}

# delete_table_row(table, &parserfunc, index)
sub delete_table_row
{
local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
local $lnum = &find_line_num($lref, $_[1], $_[2]);
splice(@$lref, $lnum, 1) if (defined($lnum));
&flush_file_lines();
}

# delete_table_struct(&struct)
sub delete_table_struct
{
local $lref = &read_file_lines($_[0]->{'file'});
splice(@$lref, $_[0]->{'line'}, 1);
&flush_file_lines();
local $cache = $read_table_cache{$_[0]->{'table'}};
local $idx = &indexof($_[0], @$cache);
if ($idx >= 0) {
	splice(@$cache, $idx, 1);
	}
local $c;
foreach $c (@$cache) {
	$c->{'line'}-- if ($c->{'line'} > $_[0]->{'line'});
	$c->{'index'}-- if ($c->{'index'} > $_[0]->{'index'});
	}
}

# create_table_row(table, &parserfunc, line, [insert-index])
sub create_table_row
{
local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
local ($i, $idx);
for($i=0; $i<@$lref; $i++) {
	if ($lref->[$i] =~ /^#+\s*LAST\s+LINE/) {
		$idx = $i;
		last;
		}
	}
if (defined($_[3])) {
	local $lnum = &find_line_num($lref, $_[1], $_[3]);
	$lnum = $idx if (!defined($lnum));
	splice(@$lref, $lnum, 0, &simplify_line($_[2]));
	}
else {
	splice(@$lref, $idx, 0, &simplify_line($_[2]));
	}
&flush_file_lines();
}

# create_table_struct(&struct, parserfunc, [&insert-before])
sub create_table_struct
{
local $lref = &read_file_lines("$config{'config_dir'}/$_[0]->{'table'}");
local ($i, $idx);
for($i=0; $i<@$lref; $i++) {
	if ($lref->[$i] =~ /^#+\s*LAST\s+LINE/) {
		$idx = $i;
		last;
		}
	}
if (!defined($idx)) {
	$idx = @$lref;
	}
local $cache = &read_table_struct($_[0]->{'table'}, $_[1]);
if ($_[2]) {
	# Insert into file
	splice(@$lref, $_[2]->{'line'}, 0, &make_struct($_[0]));
	$_[0]->{'file'} = "$config{'config_dir'}/$_[0]->{'table'}";
	$_[0]->{'line'} = $_[2]->{'line'};
	$_[0]->{'index'} = $_[2]->{'index'};
	local $c;
	foreach $c (@$cache) {
		$_[0]->{'line'}++ if ($c->{'line'} >= $_[2]->{'line'});
		$_[0]->{'index'}++ if ($c->{'index'} >= $_[2]->{'index'});
		}
	local $iidx = &indexof($_[2], @$cache);
	splice(@$cache, $iidx, 0, $_[0]);
	}
else {
	# Append to file
	splice(@$lref, $idx, 0, &make_struct($_[0]));
	$_[0]->{'file'} = "$config{'config_dir'}/$_[0]->{'table'}";
	$_[0]->{'line'} = $idx;
	$_[0]->{'index'} = @$cache;
	push(@$cache, $_[0]->{'index'});
	}
&flush_file_lines();
}

# modify_table_row(table, &parserfunc, index, line)
sub modify_table_row
{
local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
local $lnum = &find_line_num($lref, $_[1], $_[2]);
$lref->[$lnum] = &simplify_line($_[3]) if (defined($lnum));
&flush_file_lines();
}

# modify_table_struct(&newstruct, &oldstruct)
sub modify_table_struct
{
local $lref = &read_file_lines("$config{'config_dir'}/$_[1]->{'table'}");
$lref->[$_[1]->{'line'}] = &make_struct($_[0]);
if ($_[0] ne $_[1]) {
	$_[0]->{'line'} = $_[1]->{'line'};
	$_[0]->{'index'} = $_[1]->{'index'};
	local $cache = $read_table_cache{$_[1]->{'table'}};
	local $idx = &indexof($_[1], @$cache);
	$cache->[$idx] = $_[0];
	}
&flush_file_lines();
}

# swap_table_rows(table, &parserfunc, index1, index2)
sub swap_table_rows
{
local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
local $lnum1 = &find_line_num($lref, $_[1], $_[2]);
local $lnum2 = &find_line_num($lref, $_[1], $_[3]);
($lref->[$lnum1], $lref->[$lnum2]) = ($lref->[$lnum2], $lref->[$lnum1]);
&flush_file_lines();
}

# make_struct(&struct)
sub make_struct
{
local $line = join("\t", @{$_[0]->{'values'}});
if ($_[0]->{'comment'}) {
	$line .= "\t# $_[0]->{'comment'}";
	}
return &simplify_line($line);
}

# simplify_line(line)
# Removes blank fields from the end of a line
sub simplify_line
{
local $rv = $_[0];
while($rv =~ s/\s+$// || $rv =~ s/\-$//) { }
return $rv;
}

sub lock_table
{
&lock_file("$config{'config_dir'}/$_[0]");
}

sub unlock_table
{
&unlock_file("$config{'config_dir'}/$_[0]");
}

sub standard_parser
{
local $l = $_[0];
$l =~ s/#.*$//;
local @sp = split(/\s+/, $l);
return @sp ? \@sp : undef;
}

# zone_field(name, value, othermode, simplemode)
sub zone_field
{
local @ztable = &read_table_file("zones", \&zones_parser);
local $found = 0;

print "<select name=$_[0]>\n";
if ($_[3] == 2) {
	$found = !$_[1];
	}
elsif ($_[3] == 1) {
	printf "<option value=- %s>%s\n",
		$_[1] eq '-' ? "selected" : "", "&lt;$text{'list_any'}&gt;";
	$found = !$_[1] || $_[1] eq '-';
	}
elsif ($_[3] == 0) {
	printf "<option value=all %s>%s\n",
		$_[1] eq 'all' ? "selected" : "", "&lt;$text{'list_any'}&gt;";
	printf "<option value=\$FW %s>%s\n",
		&is_fw($_[1]) ? "selected" : "", "&lt;$text{'list_fw'}&gt;";
	$found = !$_[1] || $_[1] eq 'all' || &is_fw($_[1]);
	}
foreach $z (@ztable) {
	printf "<option value=%s %s>%s\n",
		$z->[0], $_[1] eq $z->[0] ? "selected" : "", $z->[1];
	$found++ if ($_[1] eq $z->[0]);
	}
if ($_[2]) {
	printf "<option value='' %s>%s\n",
		$found ? "" : "selected", $text{'list_other'};
	}
else {
	print "<option value=$_[1] selected>$_[1]\n" if (!$found);
	}
print "</select>\n";
return $found;
}

# iface_field(name, value)
sub iface_field
{
local @itable = &read_table_file("interfaces", \&standard_parser);
print "<select name=$_[0]>\n";
local $found = !$_[1];
foreach $i (@itable) {
	printf "<option value=%s %s>%s\n",
		$i->[1], $_[1] eq $i->[1] ? "selected" : "", $i->[1];
	$found++ if ($_[1] eq $i->[1]);
	}
print "<option value=$_[1] selected>$_[1]\n" if (!$found);
print "</select>\n";
}

sub convert_zone
{
local @ztable = &read_table_file("zones", \&zones_parser);
foreach $z (@ztable) {
	if ($_[0] eq $z->[0]) {
	     $ret = $z->[1];
	     }
	}
if (&is_fw($_[0])) {
	$ret = $text{'list_fw'};
	}
return $ret || $_[0];
}

# is_fw(zone)
sub is_fw
{
return $_[0] eq '$FW' || $_[0] eq 'fw';
}

################################# zones #######################################

sub zones_parser
{
local $l = $_[0];
$l =~ s/#.*$//;
if ($l =~ /^(\S+)\s+(\S+)\s*(.*)/) {
	return [ $1, $2, $3 ];
	}
else {
	return undef;
	}
}

sub zones_form
{
print "<tr> <td><b>$text{'zones_0'}</b></td>\n";
print "<td><input name=id size=8 value='$_[0]'></td> </tr>\n";

print "<tr> <td><b>$text{'zones_1'}</b></td>\n";
print "<td><input name=name size=15 value='$_[1]'></td> </tr>\n";

print "<tr> <td><b>$text{'zones_2'}</b></td>\n";
print "<td><input name=desc size=70 value='$_[2]'></td> </tr>\n";
}

sub zones_validate
{
$in{'id'} =~ /^\S+$/ || &error($text{'zones_eid'});
&is_fw($in{'id'}) && &error($text{'zones_efwid'});
$in{'name'} =~ /^\S+$/ || &error($text{'zones_ename'});
$in{'desc'} =~ /\S/ || &error($text{'zones_edesc'});
return ( $in{'id'}, $in{'name'}, $in{'desc'} );
}

################################# interfaces ###################################

sub interfaces_row
{
return ( $_[1],
	 $_[0] eq '-' ? $text{'list_any'} : $_[0],
	 $_[2] eq 'detect' ? $text{'list_auto'} :
	  $_[2] eq '-' || $_[2] eq '' ? $text{'list_none'} : $_[2],
	 $_[3] ? $_[3] : $text{'list_none'} );
}

@interfaces_opts = ( 'dhcp', 'noping', 'filterping', 'routestopped', 'norfc1918',
		     'multi', 'routefilter', 'dropunclean', 'logunclean',
		     'blacklist', 'maclist', 'tcpflags', 'proxyarp' );

sub interfaces_form
{
print "<tr> <td><b>$text{'interfaces_0'}</b></td>\n";
print "<td><input name=iface size=6 value='$_[1]'></td>\n";

local @ztable = &read_table_file("zones", \&zones_parser);
print "<td><b>$text{'interfaces_1'}</b></td>\n";
print "<td>\n";
&zone_field("zone", $_[0], 0, 1);
print "</td> </tr>\n";

local $bmode = $_[2] eq 'detect' ? 2 :
	       $_[2] eq '-' || $_[2] eq '' ? 1 : 0;
print "<tr> <td><b>$text{'interfaces_2'}</b></td> <td colspan=3>\n";
printf "<input type=radio name=broad_mode value=1 %s> %s\n",
	$bmode == 1 ? "checked" : "", $text{'list_none'};
printf "<input type=radio name=broad_mode value=2 %s> %s\n",
	$bmode == 2 ? "checked" : "", $text{'list_auto'};
printf "<input type=radio name=broad_mode value=0 %s>\n",
	$bmode == 0 ? "checked" : "";
printf "<input name=broad size=50 value='%s'></td> </tr>\n",
	$bmode == 0 ? $_[2] : "";

# options
local %opts = map { $_, 1 } split(/,/, $_[3]);
print "<tr> <td valign=top><b>$text{'interfaces_3'}</b></td> <td colspan=3>\n";
print "<table width=100%>\n";
local $i = 0;
foreach $o (@interfaces_opts) {
	print "<tr>\n" if ($i%3 == 0);
	printf "<td><input type=checkbox name=opts value=%s %s> %s</td>\n",
		$o, $opts{$o} ? "checked" : "", $o;
	print "</tr>\n" if ($i%3 == 2);
	delete($opts{$o});
	$i++;
	}
foreach $o (keys %opts) {
	print "<input type=hidden name=opts value=$o>\n";
	}
print "</table></td> </tr>\n";
}

sub interfaces_validate
{
$in{'iface'} =~ /^[a-z]+\d*(\.\d+)?$/ ||
	$in{'iface'} =~ /^[a-z]+\+$/ || &error($text{'interfaces_eiface'});
$in{'broad_mode'} || $in{'broad'} =~ /^[0-9\.,]+$/ ||
	&error($text{'interfaces_ebroad'});
return ( $in{'zone'}, $in{'iface'},
	 $in{'broad_mode'} == 2 ? 'detect' :
	 $in{'broad_mode'} == 1 ? '-' : $in{'broad'},
	 join(",", split(/\0/, $in{'opts'})) );
}

################################# policy #######################################

sub policy_row
{
return ( $_[0] eq 'all' ? $text{'list_any'} :
	  &is_fw($_[0]) ? $text{'list_fw'} : $_[0],
	 $_[1] eq 'all' ? $text{'list_any'} :
	  &is_fw($_[1]) ? $text{'list_fw'} : $_[1],
	 $_[2], $_[3] eq '-' || $_[3] eq '' ? $text{'list_none'} : $_[3],
	 $_[4] =~ /(\d+):(\d+)/ ? &text('policy_limit', "$1", "$2")
				: $text{'list_none'} );
}

@policy_list = ( "ACCEPT", "DROP", "REJECT", "CONTINUE" );

sub policy_form
{
local $found;

print "<tr> <td><b>$text{'policy_0'}</b></td>\n";
print "<td>\n";
&zone_field("source", $_[0], 0);
print "</td>\n";

print "<td><b>$text{'policy_1'}</b></td>\n";
print "<td>\n";
&zone_field("dest", $_[1], 0);
print "</td> </tr>\n";

print "<tr> <td><b>$text{'policy_2'}</b></td>\n";
print "<td><select name=policy>\n";
$found = !$_[2];
foreach $p (@policy_list) {
	printf "<option value=%s %s>%s\n",
		$p, lc($p) eq lc($_[2]) ? "selected" : "", $p;
	$found++ if (lc($p) eq lc($_[2]));
	}
print "<option value=$_[2] selected>$_[2]\n" if (!$found);
print "</select></td>\n";

print "<td><b>$text{'policy_3'}</b></td>\n";
print "<td><select name=log>\n";
printf "<option value=- %s>%s\n",
	$_[3] eq '-' || !$_[3] ? "selected" : "", "&lt;$text{'policy_nolog'}&gt;";
printf "<option value=ULOG %s>%s\n",
	$_[3] eq 'ULOG' ? "selected" : "", "&lt;$text{'policy_ulog'}&gt;";
$found = !$_[3] || $_[3] eq '-' || $_[3] eq 'ULOG';
&foreign_require("syslog", "syslog-lib.pl");
foreach $l (&syslog::list_priorities()) {
	printf "<option value=%s %s>%s\n",
		$l, $_[3] eq $l ? "selected" : "", $l;
	$found++ if ($_[3] eq $l);
	}
print "<option value=$_[3] selected>$_[3]\n" if (!$found);
print "</select></td> </tr>\n";

local ($l, $b) = $_[4] =~ /(\d+):(\d+)/ ? ($1, $2) : ( );
print "<tr> <td><b>$text{'policy_4'}</b></td> <td colspan=3>\n";
printf "<input type=radio name=limit_def value=1 %s> %s\n",
	$l eq '' ? "checked" : "", $text{'list_none'};
printf "<input type=radio name=limit_def value=0 %s>\n",
	$l eq '' ? "" : "checked";
print &text('policy_limit',
	    "<input name=limit size=5 value='$l'>",
	    "<input name=burst size=5 value='$b'>"),"</td> </tr>\n";
}

sub policy_validate
{
&is_fw($in{'source'}) && &is_fw($in{'dest'}) && &error($text{'policy_efw'});
if (!$in{'limit_def'}) {
	$in{'limit'} =~ /^\d+$/ || &error($text{'policy_elimit'});
	$in{'burst'} =~ /^\d+$/ || &error($text{'policy_eburst'});
	}
return ( $in{'source'}, $in{'dest'}, $in{'policy'}, $in{'log'}, 
	 $in{'limit_def'} ? ( ) : ( "$in{'limit'}:$in{'burst'}" ) );
}

################################# rules #######################################

sub rules_row
{
return ( $_[0] =~ /^(\S+):/ ? "$1" : $_[0],
	 &is_fw($_[1]) ? $text{'list_fw'} :
	  $_[1] eq 'all' ? $text{'list_any'} :
	  $_[1] =~ /^([^:]+):(\S+)$/ ?
	  &text('rules_hosts', &convert_zone("$1"), "$2") :
	  &text('rules_zone', &convert_zone($_[1])),
	 &is_fw($_[2]) ? $text{'list_fw'} :
	  $_[2] eq 'all' ? $text{'list_any'} :
	  $_[2] =~ /^\d+$/ ? &text('rules_rport', $_[2]) :
	  $_[2] =~ /^([^:]+):(\S+)$/ ?
	  &text('rules_hosts', &convert_zone("$1"), "$2") :
	  &text('rules_zone', &convert_zone($_[2])),
	 $_[3] eq 'all' ? $text{'list_any'} :
	  $_[3] eq 'related' ? $text{'rules_related'} : uc($_[3]),
	 $_[3] eq 'all' || $_[3] eq 'related' ? "" :
	  $_[5] eq '-' || $_[5] eq '' ? $text{'list_any'} : $_[5],
	 $_[4] eq '-' || $_[4] eq '' ? "" : $_[4],
	 &version_atleast(1, 4, 7) ? (
		$_[7] eq "-" ? "" : $_[7],
		$_[8] eq "-" ? "" : $_[8] ) :
		( )
	);
}

@rules_actions = ( 'ACCEPT', 'DROP', 'REJECT', 'DNAT', 'DNAT-', 'REDIRECT' );
if (&version_atleast(2, 0, 0)) {
	push(@rules_actions, 'CONTINUE');
	push(@rules_actions, 'ACCEPT+');
	push(@rules_actions, 'NONAT');
	push(@rules_actions, 'REDIRECT-');
	push(@rules_actions, 'LOG');
	}
@rules_protos = ( 'all', 'related', 'tcp', 'udp', 'icmp' );

sub rules_form
{
local $found;
local @ztable = &read_table_file("zones", \&zones_parser);

local ($action, $log) = split(/:/, $_[0]);
print "<tr> <td><b>$text{'rules_0'}</b></td>\n";
print "<td colspan=3><select name=action>\n";
$found = !$_[0];
foreach $a ((sort { $a cmp $b } @rules_actions),
	    "--------",
	    &list_standard_actions()) {
	printf "<option value=%s %s>%s\n",
		$a, $action eq $a ? "selected" : "", $a;
	$found++ if ($action eq $a);
	}
print "<option value=$action selected>$action\n" if (!$found);
print "</select>\n";

print "<b>$text{'rules_log'}</b> <select name=log>\n";
printf "<option value='' %s>%s\n",
	!$log ? "selected" : "", "&lt;$text{'rules_nolog'}&gt;";
printf "<option value=ULOG %s>%s\n",
	$log eq 'ULOG' ? "selected" : "", "&lt;$text{'policy_ulog'}&gt;";
$found = !$log || $log eq '-' || $log eq 'ULOG';
&foreign_require("syslog", "syslog-lib.pl");
foreach $l (&syslog::list_priorities()) {
	printf "<option value=%s %s>%s\n",
		$l, $log eq $l ? "selected" : "", $l;
	$found++ if ($log eq $l);
	}
print "<option value=$log selected>$log\n" if (!$found);
print "</select></td> </tr>\n";

local ($zone, $host) = split(/:/, $_[1], 2);
print "<tr> <td valign=top><b>$text{'rules_1z'}</b></td>\n";
print "<td colspan=3 nowrap>\n";
$found = &zone_field("source", $zone, 1);
printf "<input name=sother size=10 value='%s'>\n",
	$found ? "" : $zone;

print "<br><b>$text{'rules_inzone'}</b>\n";
printf "<input type=checkbox name=sinzone_def value=1 %s> %s\n",
	$host ? "checked" : "", $text{'rules_addr'};
printf "<input name=sinzone size=50 value='%s'></td> </tr>\n",
	join(" ", split(/,/, $host));

($zone, $host) = split(/:/, $_[2], 2);
print "<tr> <td valign=top><b>$text{'rules_2z'}</b></td>\n";
print "<td colspan=3 nowrap>\n";
$found = &zone_field("dest", $zone, 1);
printf "<input name=dother size=10 value='%s'>\n",
	$found ? "" : $zone;

print "<br><b>$text{'rules_inzone'}</b>\n";
printf "<input type=checkbox name=dinzone_def value=1 %s> %s\n",
	$host ? "checked" : "", $text{'rules_addr'};
printf "<input name=dinzone size=50 value='%s'>\n",
	join(" ", split(/,/, $host));
print "<br>$text{'rules_dnat_dest'}</td> </tr>\n";

print "<tr> <td><b>$text{'rules_3'}</b></td>\n";
print "<td colspan=3><select name=proto>\n";
$found = !$_[3];
foreach $p (@rules_protos) {
	printf "<option value=%s %s>%s\n",
		$p, $p eq $_[3] ? "selected" : "",
		$p eq 'all' ? "&lt;$text{'list_any'}&gt;" :
		 $p eq 'related' ? "&lt;$text{'rules_related'}&gt;" : uc($p);
	$found++ if ($p eq $_[3]);
	}
printf "<option value='' %s>%s\n",
	$found ? "" : "selected", $text{'list_other'};
print "</select>\n";
printf "<input name=pother size=5 value='%s'></td> </tr>\n",
	$found ? "" : $_[3];

print "<tr> <td><b>$text{'rules_4'}</b></td> <td colspan=3>\n";
printf "<input type=radio name=sport_def value=1 %s> %s\n",
	$_[5] eq '' || $_[5] eq '-' ? "checked" : "", $text{'list_any'};
printf "<input type=radio name=sport_def value=0 %s> %s\n",
	$_[5] eq '' || $_[5] eq '-' ? "" : "checked", $text{'rules_ranges'};
printf "<input name=sport size=30 value='%s'></td> </tr>\n",
	$_[5] eq '' || $_[5] eq '-' ? "" : join(" ", split(/,/, $_[5]));

print "<tr> <td><b>$text{'rules_5'}</b></td> <td colspan=3>\n";
printf "<input type=radio name=dport_def value=1 %s> %s\n",
	$_[4] eq '' || $_[4] eq '-' ? "checked" : "", $text{'list_any'};
printf "<input type=radio name=dport_def value=0 %s> %s\n",
	$_[4] eq '' || $_[4] eq '-' ? "" : "checked", $text{'rules_ranges'};
printf "<input name=dport size=30 value='%s'>\n",
	$_[4] eq '' || $_[4] eq '-' ? "" : join(" ", split(/,/, $_[4]));
print "<br>$text{'rules_dnat_port'}</td> </tr>\n";

print "<tr> <td><b>$text{'rules_dnat'}</b></td> <td colspan=3>\n";
printf "<input type=radio name=dnat_def value=1 %s> %s\n",
	$_[6] eq '' || $_[6] eq '-' ? "checked" : "", $text{'list_none'};
printf "<input type=radio name=dnat_def value=0 %s>\n",
	$_[6] eq '' || $_[6] eq '-' ? "" : "checked";
printf "<input name=dnat size=30 value='%s'></td> </tr>\n",
	$_[6] eq '' || $_[6] eq '-' ? "" : $_[6];

if (&version_atleast(1, 4, 7)) {
	print "<tr> <td><b>$text{'rules_rate'}</b></td> <td colspan=3>\n";
	printf "<input type=radio name=rate_def value=1 %s> %s\n",
		$_[7] eq "-" || !$_[7] ? "checked" : "", $text{'rules_norate'};
	printf "<input type=radio name=rate_def value=0 %s>\n",
		$_[7] eq "-" || !$_[7] ? "" : "checked";
	printf "<input name=rate size=15 value='%s'></td> </tr>\n",
		$_[7] eq "-" ? "" : $_[7];

	print "<tr> <td><b>$text{'rules_set'}</b></td> <td colspan=3>\n";
	printf "<input type=radio name=set_def value=1 %s> %s\n",
		$_[8] eq "-" || !$_[8] ? "checked" : "", $text{'rules_noset'};
	printf "<input type=radio name=set_def value=0 %s>\n",
		$_[8] eq "-" || !$_[8] ? "" : "checked";
	printf "<input name=set size=15 value='%s'></td> </tr>\n",
		$_[8] eq "-" ? "" : $_[8];
	}
}

sub rules_validate
{
$in{'action'} !~ /----/ || &error($text{'rules_eaction'});
$in{'source'} || $in{'sother'} =~ /^\S+$/ || &error($text{'rules_esother'});
!$in{'sinzone_def'} || $in{'sinzone'} =~ /\S/ || &error($text{'rules_esinzone'});
$in{'dest'} || $in{'dother'} =~ /^\S+$/ || &error($text{'rules_edother'});
!$in{'dinzone_def'} || $in{'dinzone'} =~ /\S/ || &error($text{'rules_edinzone'});
$in{'proto'} || $in{'pother'} =~ /^\S+$/ || &error($text{'rules_epother'});
$in{'sport_def'} || $in{'sport'} =~ /\S/ || &error($text{'rules_esport'});
$in{'dport_def'} || $in{'dport'} =~ /\S/ || &error($text{'rules_edport'});
$in{'dnat_def'} || &check_ipaddress($in{'dnat'}) ||
	($in{'dnat'} =~ /^([0-9\.]+):([0-9\.]+)$/ &&
	 &check_ipaddress("$1") && &check_ipaddress("$2")) ||
	($in{'dnat'} =~ /^\!([0-9\.]+)$/ && &check_ipaddress("$1")) ||
	($in{'dnat'} =~ /^\!([0-9\.]+),([0-9\.]+)(\/\d+)?$/ &&
	 &check_ipaddress("$1") && &check_ipaddress("$2")) ||
	&error($text{'rules_ednat'});
$in{'action'} ne 'DNAT' && $in{'action'} ne 'REDIRECT' && $in{'action'} ne 'DNAT-' && 
	!$in{'dnat_def'} && &error($text{'rules_ednat2'});

$in{'sinzone'} =~ s/\s+/,/g;
$in{'dinzone'} =~ s/\s+/,/g;
$in{'sport'} =~ s/\s+/,/g;
$in{'dport'} =~ s/\s+/,/g;
if (&version_atleast(1, 4, 7)) {
	$in{'rate_def'} || $in{'rate'} =~ /^\S+$/ ||
		&error($text{'rules_erate'});
	$in{'set_def'} || $in{'set'} =~ /^\S+$/ ||
		&error($text{'rules_eset'});
	}
return ( $in{'log'} ? "$in{'action'}:$in{'log'}" : $in{'action'},
	 ($in{'source'} || $in{'sother'}).
	  ($in{'sinzone_def'} ? ":$in{'sinzone'}" : ""),
	 ($in{'dest'} || $in{'dother'}).
	  ($in{'dinzone_def'} ? ":$in{'dinzone'}" : ""),
	 $in{'proto'} || $in{'pother'},
	 $in{'dport_def'} ? "-" : $in{'dport'},
	 $in{'sport_def'} ? "-" : $in{'sport'},
	 $in{'dnat_def'} ? ( "-" ) : ( $in{'dnat'} ),
	 &version_atleast(1, 4, 7) ? (
		( $in{'rate_def'} ? "-" : $in{'rate'} ),
		( $in{'set_def'} ? "-" : $in{'set'} )
		) : ( )
	);
}

sub rules_columns
{
return &version_atleast(1, 4, 7) ? 6 : 8;
}

################################# tos #########################################

%tos_map = ( 0, 'Normal-Service',
	     2, 'Minimize-Cost',
	     4, 'Maximize-Reliability',
	     8, 'Maximize-Throughput',
	     16, 'Minimize-Delay' );
@tos_protos = ( 'tcp', 'udp', 'icmp' );

sub tos_row
{
return ( &is_fw($_[0]) ? $text{'list_fw'} :
	  $_[0] eq 'all' ? $text{'list_any'} :
	  $_[0] =~ /^([^:]+):(\S+)$/ ? &text('rules_hosts', "$1", "$2") :
	  &text('rules_zone', $_[0]),
	 &is_fw($_[1]) ? $text{'list_fw'} :
	  $_[1] eq 'all' ? $text{'list_any'} :
	  $_[1] =~ /^([^:]+):(\S+)$/ ? &text('rules_hosts', "$1", "$2") :
	  &text('rules_zone', $_[1]),
	 uc($_[2]),
	 $_[3] eq '-' || $_[3] eq '' ? $text{'list_any'} : $_[3],
	 $_[4] eq '-' || $_[4] eq '' ? $text{'list_any'} : $_[4],
	 $tos_map{$_[5]} || $_[5] );
}

sub tos_form
{
local ($zone, $host) = split(/:/, $_[0], 2);
print "<tr> <td valign=top><b>$text{'tos_0z'}</b></td>\n";
print "<td colspan=3 nowrap>\n";
$found = &zone_field("source", $zone, 1);
printf "<input name=sother size=10 value='%s'>\n",
	$found ? "" : $zone;

print "<br><b>$text{'rules_inzone'}</b>\n";
printf "<input type=checkbox name=sinzone_def value=1 %s> %s\n",
	$host ? "checked" : "", $text{'rules_addr'};
printf "<input name=sinzone size=50 value='%s'></td> </tr>\n",
	join(" ", split(/,/, $host));

($zone, $host) = split(/:/, $_[1], 2);
print "<tr> <td valign=top><b>$text{'tos_1z'}</b></td>\n";
print "<td colspan=3 nowrap>\n";
$found = &zone_field("dest", $zone, 1);
printf "<input name=dother size=10 value='%s'>\n",
	$found ? "" : $zone;

print "<br><b>$text{'rules_inzone'}</b>\n";
printf "<input type=checkbox name=dinzone_def value=1 %s> %s\n",
	$host ? "checked" : "", $text{'rules_addr'};
printf "<input name=dinzone size=50 value='%s'></td> </tr>\n",
	join(" ", split(/,/, $host));

print "<tr> <td><b>$text{'tos_2'}</b></td>\n";
print "<td><select name=proto>\n";
$found = !$_[2];
foreach $p (@tos_protos) {
	printf "<option value=%s %s>%s\n",
		$p, $p eq $_[2] ? "selected" : "", uc($p);
	$found++ if ($p eq $_[2]);
	}
printf "<option value='' %s>%s\n",
	$found ? "" : "selected", $text{'list_other'};
print "</select>\n";
printf "<input name=pother size=5 value='%s'></td> </tr>\n",
	$found ? "" : $_[2];

print "<tr> <td><b>$text{'tos_3'}</b></td> <td colspan=3>\n";
printf "<input type=radio name=sport_def value=1 %s> %s\n",
	$_[3] eq '' || $_[3] eq '-' ? "checked" : "", $text{'list_any'};
printf "<input type=radio name=sport_def value=0 %s> %s\n",
	$_[3] eq '' || $_[3] eq '-' ? "" : "checked", $text{'rules_ranges'};
printf "<input name=sport size=30 value='%s'></td> </tr>\n",
	$_[3] eq '' || $_[3] eq '-' ? "" : join(" ", split(/,/, $_[3]));

print "<tr> <td><b>$text{'tos_4'}</b></td> <td colspan=3>\n";
printf "<input type=radio name=dport_def value=1 %s> %s\n",
	$_[4] eq '' || $_[4] eq '-' ? "checked" : "", $text{'list_any'};
printf "<input type=radio name=dport_def value=0 %s> %s\n",
	$_[4] eq '' || $_[4] eq '-' ? "" : "checked", $text{'rules_ranges'};
printf "<input name=dport size=30 value='%s'></td> </tr>\n",
	$_[4] eq '' || $_[4] eq '-' ? "" : join(" ", split(/,/, $_[4]));

print "<tr> <td><b>$text{'tos_5'}</b></td>\n";
print "<td><select name=tos>\n";
$found = !$_[5];
foreach $t (sort { $a <=> $b } keys %tos_map) {
	printf "<option value=%s %s>%s\n",
		$t, $_[5] == $t ? "selected" : "", $tos_map{$t};
	$found++ if ($_[5] == $t);
	}
print "<option value=$_[5] selected>$_[5]\n" if (!$found);
print "</select></td> </tr>\n";
}

sub tos_validate
{
$in{'source'} || $in{'sother'} =~ /^\S+$/ || &error($text{'rules_esother'});
!$in{'sinzone_def'} || $in{'sinzone'} =~ /\S/ || &error($text{'rules_esinzone'});
$in{'dest'} || $in{'dother'} =~ /^\S+$/ || &error($text{'rules_edother'});
!$in{'dinzone_def'} || $in{'dinzone'} =~ /\S/ || &error($text{'rules_edinzone'});
$in{'proto'} || $in{'pother'} =~ /^\S+$/ || &error($text{'rules_epother'});
$in{'sport_def'} || $in{'sport'} =~ /\S/ || &error($text{'rules_esport'});
$in{'dport_def'} || $in{'dport'} =~ /\S/ || &error($text{'rules_edport'});
return ( ($in{'source'} || $in{'sother'}).
	  ($in{'sinzone_def'} ? ":$in{'sinzone'}" : ""),
	 ($in{'dest'} || $in{'dother'}).
	  ($in{'dinzone_def'} ? ":$in{'dinzone'}" : ""),
	 $in{'proto'} || $in{'pother'},
	 $in{'sport_def'} ? "-" : $in{'sport'},
	 $in{'dport_def'} ? "-" : $in{'dport'},
	 $in{'tos'} );
}

################################# masq #########################################

sub masq_row
{
return ( $_[0] =~ /^(\S+):(\S+)$/ ? &text('masq_in', "$1", "$2") : $_[0],
	 $_[1] =~ /^[0-9\.\/]+$/ ? $_[1] :
	  $_[1] =~ /^([a-z]+\d*)\!(\S+)$/ ? &text('masq_ex', "$1", "$2") :
	  &text('masq_iface', $_[1]),
	 $_[2] eq "" ? "" : $_[2] );
}

sub masq_form
{
local ($iface, $net) = split(/:/, $_[0], 2);
print "<tr> <td><b>$text{'masq_0'}</b></td> <td colspan=3>\n";
&iface_field("iface", $iface);

printf "<input type=checkbox name=net_def value=1 %s> %s\n",
	$net ? "checked" : "", $text{'masq_net'};
print "<input name=net size=20 value='$net'></td> </tr>\n";

local ($mnet, $miface, $mode);
if ($_[1] =~ /^[0-9\.\/]+$/) {
	$mnet = $_[1];
	$mode = 0;
	}
elsif ($_[1] =~ /^([a-z]+\d*)\!(\S+)$/) {
	$miface = $1;
	$mnet = $2;
	$mode = 1;
	}
else {
	$miface = $_[1];
	$mode = 1;
	}
print "<tr> <td valign=top><b>$text{'masq_1'}</b></td> <td colspan=3>\n";
printf "<input type=radio name=mode value=0 %s> %s\n",
	$mode == 0 ? "checked" : "", $text{'masq_mode0'};
printf "<input name=mnet size=20 value='%s'><br>\n",
	$mode == 0 ? $mnet : "";
printf "<input type=radio name=mode value=1 %s> %s\n",
	$mode == 1 ? "checked" : "", $text{'masq_mode1'};
&iface_field("miface", $mode == 1 ? $miface : undef);
printf "<input type=checkbox name=mnet_def value=1 %s> %s\n",
	$mode == 1 && $mnet ? "checked" : "", $text{'masq_except'};
printf "<input name=mnete size=20 value='%s'>\n",
	$mode == 1 ? join(" ", split(/,/, $mnet)) : "";
print "</td> </tr>\n";

print "<tr> <td><b>$text{'masq_2'}</b></td> <td colspan=3>\n";
printf "<input type=radio name=snat_def value=1 %s> %s\n",
	$_[2] eq '' || $_[2] eq '-' ? "checked" : "", $text{'list_none'};
printf "<input type=radio name=snat_def value=0 %s>\n",
	$_[2] eq '' || $_[2] eq '-' ? "" : "checked";
printf "<input name=snat size=15 value='%s'></td> </tr>\n",
	$_[2] eq '' || $_[2] eq '-' ? "" : $_[2];
}

sub masq_validate
{
!$in{'net_def'} || $in{'net'} =~ /^\S+$/ || &error($text{'masq_enet'});
if ($in{'mode'} == 0) {
	$in{'mnet'} =~ /^\S+$/ || &error($text{'masq_emnet'});
	}
else {
	!$in{'mnet_def'} || $in{'mnete'} =~ /\S/ ||&error($text{'masq_emnete'});
	}
$in{'snat_def'} || &check_ipaddress($in{'snat'}) || &error($text{'masq_esnat'});
return ( $in{'iface'}.(!$in{'net_def'} ? "" : ":$in{'net'}"),
	 $in{'mode'} == 0 ? $in{'mnet'} :
	  $in{'miface'}.(!$in{'mnet_def'} ? "" :
			 "!".join(",", split(/\s+/, $in{'mnete'}))),
	 $in{'snat_def'} ? ( ) : ( $in{'snat'} ) );
}

################################# nat #########################################

sub nat_form
{
print "<tr> <td><b>$text{'nat_0'}</b></td>\n";
print "<td><input name=ext size=15 value='$_[0]'></td>\n";

print "<td><b>$text{'nat_1'}</b></td>\n";
print "<td>";
if (&version_atleast(1, 3, 14)) {
	local ($iface, $virt) = split(/:/, $_[1]);
	&iface_field("iface", $iface);
	print "<b>$text{'nat_virt'}</b>\n";
	print "<input name=virt size=3 value='$virt'>\n";
	print "</td> </tr>\n";
	}
else {
	&iface_field("iface", $_[1]);
	print "</td> </tr>\n";
	}

print "<tr> <td><b>$text{'nat_2'}</b></td>\n";
print "<td><input name=int size=15 value='$_[2]'></td> </tr>\n";

local $all = $_[3] eq '-' || $_[3] eq '' || $_[3] =~ /yes/i;
print "<tr> <td><b>$text{'nat_all'}</b></td>\n";
printf "<td><input type=radio name=all value=1 %s> %s\n",
	$all ? "checked" : "", $text{'yes'};
printf "<input type=radio name=all value=0 %s> %s</td>\n",
	$all ? "" : "checked", $text{'no'};

local $local = $_[4] =~ /yes/i;
print "<td><b>$text{'nat_local'}</b></td>\n";
printf "<td><input type=radio name=local value=1 %s> %s\n",
	$local ? "checked" : "", $text{'yes'};
printf "<input type=radio name=local value=0 %s> %s</td> </tr>\n",
	$local ? "" : "checked", $text{'no'};
}

sub nat_validate
{
&check_ipaddress($in{'ext'}) || &error($text{'nat_eext'});
&check_ipaddress($in{'int'}) || &error($text{'nat_eint'});
$in{'virt'} =~ /^\d*$/ || &error($text{'nat_evirt'});
return ( $in{'ext'},
	 $in{'virt'} ne '' ? $in{'iface'}.":".$in{'virt'} : $in{'iface'},
	 $in{'int'},
	 $in{'all'} ? "yes" : "no",
	 $in{'local'} ? "yes" : "no" );
}

################################# proxyarp #######################################

sub proxyarp_row
{
return ( $_[0],
	 $_[1] eq '-' || $_[1] eq '' ? $text{'list_auto'} : $_[1],
	 $_[2],
	 &version_atleast(2, 0, 0) ?
		( $_[4] =~ /yes/i ? $text{'yes'} : $text{'no'} ) : ( ) );
}

sub proxyarp_form
{
print "<tr> <td><b>$text{'proxyarp_0'}</b></td>\n";
print "<td><input name=addr size=15 value='$_[0]'></td>\n";

print "<td><b>$text{'proxyarp_1'}</b></td>\n";
printf "<td><input type=radio name=int_def value=1 %s> %s\n",
	$_[1] eq '-' || $_[1] eq '' ? "checked" : "", $text{'list_auto'};
printf "<input type=radio name=int_def value=0 %s>\n",
	$_[1] eq '-' || $_[1] eq '' ? "" : "checked";
&iface_field("int", $_[1] eq '-' ? undef : $_[1]);
print "</td> </tr>";

local $have = $_[3] =~ /yes/i;
print "<tr> <td><b>$text{'proxyarp_have'}</b></td>\n";
printf "<td><input type=radio name=have value=1 %s> %s\n",
	$have ? "checked" : "", $text{'yes'};
printf "<input type=radio name=have value=0 %s> %s</td>\n",
	$have ? "" : "checked", $text{'no'};

print "<td><b>$text{'proxyarp_2'}</b></td>\n";
print "<td>";
&iface_field("ext", $_[2]);
print "</td> </tr>";

if (&version_atleast(2, 0, 0)) {
	local $pers = $_[4] =~ /yes/i;
	print "<tr> <td><b>$text{'proxyarp_pers'}</b></td>\n";
	printf "<td><input type=radio name=pers value=1 %s> %s\n",
		$pers ? "checked" : "", $text{'yes'};
	printf "<input type=radio name=pers value=0 %s> %s</td>\n",
		$pers ? "" : "checked", $text{'no'};
	}
}

sub proxyarp_validate
{
&check_ipaddress($in{'addr'}) || &error($text{'proxyarp_eaddr'});
return ( $in{'addr'},
	 $in{'int_def'} ? "-" : $in{'int'},
	 $in{'ext'},
	 $in{'have'} ? "yes" : "no",
	 &version_atleast(2, 0, 0) ? ( $in{'pers'} ? "yes" : "no" ) : ( )
	); 
	 
}

sub proxyarp_columns
{
return &version_atleast(2, 0, 0) ? 4 : 3;
}

################################ routestopped ##################################

sub routestopped_row
{
return ( $_[0], $_[1],
	 $_[2] eq '-' || $_[2] eq '' ? $text{'default'} : $_[2],
	 $_[3] eq '-' || $_[3] eq '' ? $text{'tunnels_gnone'} : $_[3] );
}

sub routestopped_form
{
print "<tr> <td valign=top><b>$text{'routestopped_0'}</b></td>\n";
print "<td valign=top>";
&iface_field("iface", $_[0]);
print "</td>\n";

local $none = $_[1] eq '' || $_[1] eq '-' || $_[1] eq '0.0.0.0/0';
print "<td valign=top><b>$text{'routestopped_1'}</b></td>\n";
printf "<td><input type=radio name=addr_def value=1 %s> %s<br>\n",
	$none ? "checked" : "", $text{'routestopped_all'};
printf "<input type=radio name=addr_def value=0 %s> %s<br>\n",
	$none ? "" : "checked", $text{'routestopped_list'};
print "<textarea name=addr rows=5 cols=20>",
	$none ? "" : join("\n", split(/,/, $_[1])),
	"</textarea></td> </tr>\n";
}

sub routestopped_validate
{
$in{'addr_def'} || $in{'addr'} =~ /\S/ || &error($text{'routestopped_eaddr'});
return ( $in{'iface'},
	 $in{'addr_def'} ? "-" : join(",", split(/\s+/, $in{'addr'})) );
}

################################ tunnels ##################################

sub tunnels_row
{
local $tt = $_[0];
$tt =~ s/^(openvpn|generic):.*$/$1/;
return ( $text{'tunnels_'.$tt} || $tt,
	 $_[1] eq '-' || $_[1] eq '' ? $text{'routestopped_all'} : $_[1],
	 $_[2], $_[3] );
}

sub tunnels_form
{
print "<tr> <td><b>$text{'tunnels_0'}</b></td>\n";
print "<td><select name=type>\n";
local $tt;
local $found = !$_[0];
local $ttype = $_[0];
local $tport;
if ($ttype =~ s/^(openvpn|generic):(.*)$/$1/) {
	$tport = $2;
	}
foreach $tt ('ipsec', 'ipsecnat',
	     (&version_atleast(2, 0, 0) ? ( 'ipsec:noah', 'ipsecnat:noah' )
				       : ( )),
	     'ip', 'gre', 'pptpclient', 'pptpserver', 'generic',
	     (&version_atleast(1, 3, 14) ? ( 'openvpn' ) : ( )) ) {
	printf "<option value=%s %s>%s\n",
		$tt, $ttype eq $tt ? "selected" : "",
		$text{'ltunnels_'.$tt} || $text{'tunnels_'.$tt};
	$found++ if ($ttype eq $tt);
	}
print "<option value=$ttype selected>",uc($ttype),"\n" if (!$found);
print "</select>\n";
print "<input name=tport size=10 value='$tport'>\n";
print "</td>\n";

print "<tr> <td><b>$text{'tunnels_1'}</b></td>\n";
print "<td>";
&zone_field("zone", $_[1], 0, 0);
print "</td> </tr>\n";

local $none = $_[2] eq '' || $_[2] eq '-';
print "<tr> <td><b>$text{'tunnels_2'}</b></td> <td valign=top>\n";
printf "<input type=radio name=gateway_def value=1 %s> %s\n",
	$none ? "checked" : "", $text{'default'};
printf "<input type=radio name=gateway_def value=0 %s> %s\n",
	$none ? "" : "checked", $text{'tunnels_sel'};
printf "<input name=gateway size=20 value='%s'></td> </tr>\n", $_[2];

local $none = $_[2] eq '' || $_[2] eq '-';
print "<tr> <td><b>$text{'tunnels_3'}</b></td> <td valign=top>\n";
printf "<input type=radio name=gzones_def value=1 %s> %s\n",
	$none ? "checked" : "", $text{'tunnels_gnone'};
printf "<input type=radio name=gzones_def value=0 %s> %s\n",
	$none ? "" : "checked", $text{'tunnels_gsel'};
printf "<input name=gzones size=50 value='%s'></td> </tr>\n",
	join(" ", split(/,/, $_[3]));
}

sub tunnels_validate
{
$in{'gateway_def'} || &check_ipaddress($in{'gateway'}) ||
	($in{'gateway'} =~ /^(\S+)\/(\d+)$/ && &check_ipaddress($1)) ||
		&error($text{'tunnels_egateway'});
if ($in{'type'} eq "openvpn") {
	$in{'tport'} =~ /^\d*$/ || &error($text{'tunnels_eopenvpn'});
	$in{'type'} .= ":".$in{'tport'} if ($in{'tport'});
	}
elsif ($in{'type'} eq 'generic') {
	$in{'tport'} =~ /^\S+$/ || &error($text{'tunnels_egeneric'});
	$in{'type'} .= ":".$in{'tport'};
	}
return ( $in{'type'}, $in{'zone'},
	 $in{'gateway_def'} ? '-' : $in{'gateway'},
	 $in{'gzones_def'} ? '-' : join(",", split(/\s+/, $in{'gzones'})) );
}

################################ hosts ##################################

sub hosts_row
{
return ( $_[0], $_[1] =~ /^(\S+):(\S+)$/ ? ( $1, $2 ) : ( undef, undef ) );
}

sub hosts_form
{
print "<tr> <td><b>$text{'hosts_0'}</b></td>\n";
print "<td>";
&zone_field("zone", $_[0], 0, 2);
print "</td> </tr>\n";

local ($iface, $net) = split(/:/, $_[1]);
print "<tr> <td><b>$text{'hosts_1'}</b></td>\n";
print "<td>";
&iface_field("iface", $iface);
print "</td> </tr>\n";

print "<tr> <td><b>$text{'hosts_2'}</b></td>\n";
print "<td><input name=net size=50 value='$net'></td> </tr>\n";

local %opts = map { $_, 1 } split(/,/, $_[2]);
print "<tr> <td valign=top><b>$text{'hosts_opts'}</b></td> <td>\n";
printf "<input type=checkbox name=opts value=routestopped %s> %s<br>\n",
	$opts{'routestopped'} ? "checked" : "", $text{'hosts_routestopped'};
delete($opts{'routestopped'});
printf "<input type=checkbox name=opts value=maclist %s> %s\n",
	$opts{'maclist'} ? "checked" : "", $text{'hosts_maclist'};
delete($opts{'maclist'});
foreach $o (keys %opts) {
	print "<input type=hidden name=opts value=$o>\n";
	}
print "</td> </tr>\n";
}

sub hosts_validate
{
&check_ipaddress($in{'net'}) ||
	$in{'net'} =~ /^(\S+)\/(\d+)$/ && &check_ipaddress($1) ||
	&error($text{'hosts_enet'});
return ( $in{'zone'}, $in{'iface'}.":".$in{'net'},
	 join(",", split(/\0/, $in{'opts'})) );
}

################################ blacklist ##################################

sub blacklist_row
{
return ( $_[0], uc($_[1]) || $text{'blacklist_any'},
		$_[2] || $text{'blacklist_any'} );
}

@blacklist_protos = ( undef, 'tcp', 'udp', 'icmp' );

sub blacklist_form
{
print "<tr> <td><b>$text{'blacklist_host'}</b></td>\n";
print "<td colspan=3><input name=host size=50 value='$_[0]'></td> </tr>\n";

print "<tr> <td><b>$text{'blacklist_proto'}</b></td>\n";
print "<td colspan=3><select name=proto>\n";
$found = !$_[1];
foreach $p (@blacklist_protos) {
	printf "<option value='%s' %s>%s\n",
		$p, $p eq $_[1] ? "selected" : "",
		$p eq '' ? "&lt;$text{'list_any'}&gt;" : uc($p);
	$found++ if ($p eq $_[1]);
	}
printf "<option value='*' %s>%s\n",
	$found ? "" : "selected", $text{'list_other'};
print "</select>\n";
printf "<input name=pother size=5 value='%s'></td> </tr>\n",
	$found ? "" : $_[1];

print "<tr> <td><b>$text{'blacklist_ports'}</b></td>\n";
print "<td colspan=3><input name=ports size=20 value='$_[2]'></td> </tr>\n";
}

sub blacklist_validate
{
&check_ipaddress($in{'host'}) ||
	$in{'host'} =~ /^(\S+)\/(\d+)$/ && &check_ipaddress($1) ||
	$in{'host'} =~ /^\~([0-9a-f]{2}\-){5}[0-9a-f]{2}$/i ||
	&error($text{'blacklist_ehost'});
local $proto;
if ($in{'proto'} eq '*') {
	$in{'pother'} =~ /^\d+$/ ||
	    defined(getprotobyname($in{'pother'})) ||
		&error($text{'blacklist_eproto'});
	$proto = lc($in{'pother'});
	}
else {
	$proto = lc($in{'proto'});
	}
if ($proto eq "tcp" || $proto eq "udp") {
	$in{'ports'} =~ /^\S+$/ || &error($text{'blacklist_eports'});
	}
elsif ($in{'ports'}) {
	&error($text{'blacklist_eports2'});
	}
return ( $in{'host'}, $proto, $in{'ports'} );
}

# can_access(file)
sub can_access
{
if ($access{'files'} eq '*') {
	return 1;
	}
else {
	local @acc = split(/\s+/, $access{'files'});
	return &indexof($_[0], @acc) >= 0;
	}
}

# run_before_apply_command()
# Runs the before-applying command, if any. If it failes, returns the error
# message output
sub run_before_apply_command
{
if ($config{'before_apply_cmd'}) {
	local $out = &backquote_logged("($config{'before_apply_cmd'}) </dev/null 2>&1");
	return $out if ($?);
	}
return undef;
}

# run_after_apply_command()
# Runs the after-applying command, if any
sub run_after_apply_command
{
if ($config{'after_apply_cmd'}) {
	&system_logged("($config{'after_apply_cmd'}) </dev/null >/dev/null 2>&1");
	}
}

# list_standard_actions()
# Returns a list of standard Shorewall actions
sub list_standard_actions
{
local @rv;
open(ACTIONS, $config{'actions'});
while(<ACTIONS>) {
	s/\r|\n//g;
	s/#.*$//;
	s/\s+$//;
	if (/\S/) {
		push(@rv, $_);
		}
	}
close(ACTIONS);
return @rv;
}

1;

