Handle hostnames with upper-case letters
[webmin.git] / shorewall / shorewall-lib.pl
1 # shorewall-lib.pl
2 # Common functions for the shorewall configuration files
3 # FIXME:
4 # - rule sections
5 # - read_shorewall_config & standard_parser do not allow quoted comment characters
6
7 BEGIN { push(@INC, ".."); };
8 use WebminCore;
9 &init_config();
10
11 # Get the version
12 $shorewall_version = &get_shorewall_version(0);
13 %shorewall_config = &read_shorewall_config();
14 #&dump_shorewall_config();
15
16 # get access permissions
17 %access = &get_module_acl();
18
19 @shorewall_files = ( 'zones', 'interfaces', 'policy', 'rules', 'tos',
20                      'masq', 'nat', 'proxyarp', 'routestopped',
21                      'tunnels', 'hosts', 'blacklist',
22                      ( &version_atleast(2, 3) ? ( 'providers' ) : ( ) ),
23                      'params', 'shorewall.conf',
24 );
25 @comment_tables = ( 'masq', 'nat', 'rules', 'tcrules' );
26
27 sub debug_message
28 {
29         print STDERR scalar(localtime).": shorewall-lib: @_\n";
30 }
31
32 # version_atleast(v1, v2, v3, ...)
33 # - Check if the Shorewall version is greater than or equal to the one supplied.
34 sub version_atleast
35 {
36 local @vsp = split(/\./, $shorewall_version);
37 local $i;
38 for($i=0; $i<@vsp || $i<@_; $i++) {
39         return 0 if ($vsp[$i] < $_[$i]);
40         return 1 if ($vsp[$i] > $_[$i]);
41         }
42 return 1;       # same!
43 }
44
45 sub read_shorewall_config
46 {
47         local @ret;
48         open(SHOREWALL_CONF, "$config{'config_dir'}/shorewall.conf");
49         while (<SHOREWALL_CONF>) {
50                 chomp;
51                 s/\r//;
52                 s/#.*$//;
53                 @F = split( /=/, $_, 2 );
54                 next if $#F != 1;
55                 push @ret, ( $F[0], $F[1] );
56         }
57         close(SHOREWALL_CONF);
58         return @ret;
59 }
60
61 # dump_shorewall_config()
62 # - Debugging code
63 sub dump_shorewall_config
64 {
65         for (sort keys %shorewall_config) {
66                 print STDERR "$_=$shorewall_config{$_}\n";
67         }
68 }
69
70 # shorewall_config(var)
71 sub shorewall_config
72 {
73         if (exists $shorewall_config{$_[0]}  &&  defined $shorewall_config{$_[0]}) {
74                 return $shorewall_config{$_[0]};
75         }
76         return '';
77 }
78
79 # return true if new zones format is in use
80 sub new_zones_format
81 {
82         # Shorewall 3.4.0 - 3.4.4 have a bug that prevents the old format from being used.
83         if (&version_atleast(3, 4)  &&  !&version_atleast(3, 4, 5)) {
84                 return 1;
85         }
86         # Zones table is in new format in Shorewall 3, unless shorewall.conf has IPSECFILE=ipsec
87         if (!&version_atleast(3)  ||  &shorewall_config('IPSECFILE') eq 'ipsec') {
88                 return 0;
89         }
90         return 1;
91 }
92
93 # read_table_file(table, &parserfunc)
94 sub read_table_file
95 {
96 local @rv;
97 local $func = $_[1];
98 open(FILE, "$config{'config_dir'}/$_[0]");
99 while(<FILE>) {
100         s/\r|\n//g;
101         local $l = &$func($_);
102         push(@rv, $l) if ($l);
103         }
104 close(FILE);
105 return @rv;
106 }
107
108 # read_table_struct(table, &parserfunc)
109 sub read_table_struct
110 {
111 if (!defined($read_table_cache{$_[0]})) {
112         local @rv;
113         local $func = $_[1];
114         open(FILE, "$config{'config_dir'}/$_[0]");
115         local $lnum = 0;
116         while(<FILE>) {
117                 s/\r|\n//g;
118                 local $cmt;
119                 if (s/#\s*(.*)$//) {
120                         $cmt = $1;
121                         }
122                 local $l = &$func($_);
123                 if ($l) {
124                         push(@rv, { 'line' => $lnum,
125                                     'file' => "$config{'config_dir'}/$_[0]",
126                                     'table' => $_[0],
127                                     'index' => scalar(@rv),
128                                     'values' => $l,
129                                     'comment' => $cmt });
130                         }
131                 $lnum++;
132                 }
133         close(FILE);
134         $read_table_cache{$_[0]} = \@rv;
135         }
136 return $read_table_cache{$_[0]};
137 }
138
139 # find_line_num(&lref, &parserfunc, index)
140 sub find_line_num
141 {
142 local $lref = $_[0];
143 local $func = $_[1];
144 local $idx = 0;
145 for($i=0; $i<@$lref; $i++) {
146         if (&$func($lref->[$i])) {
147                 if ($idx++ == $_[2]) {
148                         return $i;
149                         }
150                 }
151         }
152 return undef;
153 }
154
155 # delete_table_row(table, &parserfunc, index)
156 sub delete_table_row
157 {
158 local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
159 local $lnum = &find_line_num($lref, $_[1], $_[2]);
160 splice(@$lref, $lnum, 1) if (defined($lnum));
161 &flush_file_lines();
162 }
163
164 # delete_table_struct(&struct)
165 sub delete_table_struct
166 {
167 local $lref = &read_file_lines($_[0]->{'file'});
168 splice(@$lref, $_[0]->{'line'}, 1);
169 &flush_file_lines();
170 local $cache = $read_table_cache{$_[0]->{'table'}};
171 local $idx = &indexof($_[0], @$cache);
172 if ($idx >= 0) {
173         splice(@$cache, $idx, 1);
174         }
175 local $c;
176 foreach $c (@$cache) {
177         $c->{'line'}-- if ($c->{'line'} > $_[0]->{'line'});
178         $c->{'index'}-- if ($c->{'index'} > $_[0]->{'index'});
179         }
180 }
181
182 # create_table_row(table, &parserfunc, line, [insert-index])
183 sub create_table_row
184 {
185 local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
186 local ($i, $idx);
187 for($i=0; $i<@$lref; $i++) {
188         if ($lref->[$i] =~ /^#+\s*LAST\s+LINE/) {
189                 $idx = $i;
190                 last;
191                 }
192         elsif ($lref->[$i] =~ /^SECTION\s+NEW/) {
193                 $idx = $i+1;
194                 last;
195                 }
196         }
197 if (defined($_[3])) {
198         local $lnum = &find_line_num($lref, $_[1], $_[3]);
199         $lnum = $idx if (!defined($lnum));
200         splice(@$lref, $lnum, 0, &simplify_line($_[2]));
201         }
202 else {
203         splice(@$lref, $idx, 0, &simplify_line($_[2]));
204         }
205 &flush_file_lines();
206 }
207
208 # create_table_struct(&struct, parserfunc, [&insert-before])
209 sub create_table_struct
210 {
211 local $lref = &read_file_lines("$config{'config_dir'}/$_[0]->{'table'}");
212 local ($i, $idx);
213 for($i=0; $i<@$lref; $i++) {
214         if ($lref->[$i] =~ /^#+\s*LAST\s+LINE/) {
215                 $idx = $i;
216                 last;
217                 }
218         }
219 if (!defined($idx)) {
220         $idx = @$lref;
221         }
222 local $cache = &read_table_struct($_[0]->{'table'}, $_[1]);
223 if ($_[2]) {
224         # Insert into file
225         splice(@$lref, $_[2]->{'line'}, 0, &make_struct($_[0]));
226         $_[0]->{'file'} = "$config{'config_dir'}/$_[0]->{'table'}";
227         $_[0]->{'line'} = $_[2]->{'line'};
228         $_[0]->{'index'} = $_[2]->{'index'};
229         local $c;
230         foreach $c (@$cache) {
231                 $_[0]->{'line'}++ if ($c->{'line'} >= $_[2]->{'line'});
232                 $_[0]->{'index'}++ if ($c->{'index'} >= $_[2]->{'index'});
233                 }
234         local $iidx = &indexof($_[2], @$cache);
235         splice(@$cache, $iidx, 0, $_[0]);
236         }
237 else {
238         # Append to file
239         splice(@$lref, $idx, 0, &make_struct($_[0]));
240         $_[0]->{'file'} = "$config{'config_dir'}/$_[0]->{'table'}";
241         $_[0]->{'line'} = $idx;
242         $_[0]->{'index'} = @$cache;
243         push(@$cache, $_[0]->{'index'});
244         }
245 &flush_file_lines();
246 }
247
248 # modify_table_row(table, &parserfunc, index, line)
249 sub modify_table_row
250 {
251 local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
252 local $lnum = &find_line_num($lref, $_[1], $_[2]);
253 $lref->[$lnum] = &simplify_line($_[3]) if (defined($lnum));
254 &flush_file_lines();
255 }
256
257 # modify_table_struct(&newstruct, &oldstruct)
258 sub modify_table_struct
259 {
260 local $lref = &read_file_lines("$config{'config_dir'}/$_[1]->{'table'}");
261 $lref->[$_[1]->{'line'}] = &make_struct($_[0]);
262 if ($_[0] ne $_[1]) {
263         $_[0]->{'line'} = $_[1]->{'line'};
264         $_[0]->{'index'} = $_[1]->{'index'};
265         local $cache = $read_table_cache{$_[1]->{'table'}};
266         local $idx = &indexof($_[1], @$cache);
267         $cache->[$idx] = $_[0];
268         }
269 &flush_file_lines();
270 }
271
272 # swap_table_rows(table, &parserfunc, index1, index2)
273 sub swap_table_rows
274 {
275 local $lref = &read_file_lines("$config{'config_dir'}/$_[0]");
276 local $lnum1 = &find_line_num($lref, $_[1], $_[2]);
277 local $lnum2 = &find_line_num($lref, $_[1], $_[3]);
278 ($lref->[$lnum1], $lref->[$lnum2]) = ($lref->[$lnum2], $lref->[$lnum1]);
279 &flush_file_lines();
280 }
281
282 # make_struct(&struct)
283 sub make_struct
284 {
285 local $line = join("\t", @{$_[0]->{'values'}});
286 if ($_[0]->{'comment'}) {
287         $line .= "\t# $_[0]->{'comment'}";
288         }
289 return &simplify_line($line);
290 }
291
292 # simplify_line(line)
293 # Removes blank fields from the end of a line
294 sub simplify_line
295 {
296 local $rv = $_[0];
297 while($rv =~ s/\s+$// || $rv =~ s/\-$//) { }
298 return $rv;
299 }
300
301 sub lock_table
302 {
303 &lock_file("$config{'config_dir'}/$_[0]");
304 }
305
306 sub unlock_table
307 {
308 &unlock_file("$config{'config_dir'}/$_[0]");
309 }
310
311 # parser for whitespace-separated config files
312 sub standard_parser
313 {
314 local $l = $_[0];
315 $l =~ s/#.*$//;
316 local @sp = split(/\s+/, $l);
317 return undef if ($sp[0] eq "SECTION");
318 return @sp ? \@sp : undef;
319 }
320
321 # parser for shell-style config files
322 sub config_parser
323 {
324     local $l = $_[0];
325     $l =~ s/#\s*(.*?)\s*$//;            # save the comment we strip
326     local @sp = split(/=/, $l, 2);
327     if ($#sp > -1 && defined $1) {
328         push @sp, $1;                   # add back the saved comment, if present
329         }
330     return @sp ? \@sp : undef;
331 }
332
333 # determine which parser function to use
334 sub get_parser_func
335 {
336     local $hashref = $_[0];
337     &get_clean_table_name($hashref);
338     local $pfunc = $hashref->{'tableclean'}."_parser";
339     if (!defined(&$pfunc)) {
340         if ($hashref->{'tableclean'} =~ /^(params|shorewall_conf)$/) {
341             $pfunc = "config_parser";
342         }
343         else {
344             $pfunc = "standard_parser";
345         }
346     }
347     return $pfunc;
348 }
349
350 # ensure that the passed string contains only characters valid in shell variable identifiers
351 sub clean_name
352 {
353     local $str = $_[0];
354     $str =~ s/\W/_/g;
355     return $str;
356 }
357
358 # get a table name that is clean enough to use as a function prefix
359 sub get_clean_table_name
360 {
361     local $hashref = $_[0];
362     if (!exists hashref->{'tableclean'}) {
363         $hashref->{'tableclean'} = &clean_name($in{'table'});
364     }
365 }
366
367 # zone_field(name, value, othermode, simplemode)
368 sub zone_field
369 {
370 local @ztable = &read_table_file("zones", \&zones_parser);
371 local $found = 0;
372
373 print "<select name=$_[0]>\n";
374 if ($_[3] == 2) {
375         $found = !$_[1];
376         }
377 elsif ($_[3] == 1) {
378         printf "<option value=- %s>%s\n",
379                 $_[1] eq '-' ? "selected" : "", "&lt;$text{'list_any'}&gt;";
380         $found = !$_[1] || $_[1] eq '-';
381         }
382 elsif ($_[3] == 0) {
383         printf "<option value=all %s>%s\n",
384                 $_[1] eq 'all' ? "selected" : "", "&lt;$text{'list_any'}&gt;";
385         printf "<option value=\$FW %s>%s\n",
386                 &is_fw($_[1]) ? "selected" : "", "&lt;$text{'list_fw'}&gt;";
387         $found = !$_[1] || $_[1] eq 'all' || &is_fw($_[1]);
388         }
389 foreach $z (@ztable) {
390         printf "<option value=%s %s>%s\n",
391                 $z->[0], $_[1] eq $z->[0] ? "selected" : "", &convert_zone($z->[0]);
392         $found++ if ($_[1] eq $z->[0]);
393         }
394 if ($_[2]) {
395         printf "<option value='' %s>%s\n",
396                 $found ? "" : "selected", $text{'list_other'};
397         }
398 else {
399         print "<option value=$_[1] selected>$_[1]\n" if (!$found);
400         }
401 print "</select>\n";
402 return $found;
403 }
404
405 # iface_field(name, value)
406 sub iface_field
407 {
408 local @itable = &read_table_file("interfaces", \&standard_parser);
409 print "<select name=$_[0]>\n";
410 local $found = !$_[1];
411 foreach $i (@itable) {
412         printf "<option value=%s %s>%s\n",
413                 $i->[1], $_[1] eq $i->[1] ? "selected" : "", $i->[1];
414         $found++ if ($_[1] eq $i->[1]);
415         }
416 print "<option value=$_[1] selected>$_[1]\n" if (!$found);
417 print "</select>\n";
418 }
419
420 # convert_zone(name)
421 # Given a zone name, returns a description
422 # FIXME: inefficient - should be able to pass ztable into this function
423 sub convert_zone
424 {
425 local @ztable = &read_table_file("zones", \&zones_parser);
426 foreach $z (@ztable) {
427         if ($_[0] eq $z->[0]) {
428                 if (&new_zones_format()) {
429                         # No descriptions in new format - use comment field if present
430                         if (defined $z->[6]  &&  $z->[6] ne "") {
431                                 $ret = $_[0]." - ".$z->[6];
432                                 }
433                         else {
434                                 $ret = $_[0];
435                                 }
436                         }
437                 else {
438                         $ret = $z->[1];
439                         }
440                 }
441         }
442 if (&is_fw($_[0])) {
443         $ret = $text{'list_fw'};
444         }
445 return $ret || $_[0];
446 }
447
448 # nice_host_list(list)
449 # Convert a comma-separate host string to space-separated
450 sub nice_host_list
451 {
452 local @hosts = split(/,/, $_[0]);
453 if (@host > 5) {
454         return join(", ", @hosts[0..5]).", ...";
455         }
456 else {
457         return join(", ", @hosts);
458         }
459 }
460
461 # is_fw(zone)
462 # - Checks if the supplied zone is the firewall zone.
463 #   Now handles renaming of firewall zone in shorewall.conf.
464 sub is_fw
465 {
466         local $fw = &shorewall_config('FW');
467         $fw = 'fw' if ($fw eq '');
468         return $_[0] eq '$FW' || $_[0] eq $fw;
469 }
470
471 ################################# zones #######################################
472
473 sub zones_parser
474 {
475 if (&new_zones_format()) {
476         # New format
477         local $l = $_[0];
478         $l =~ s/#\s*(.*?)\s*$//;        # save the stripped comment
479         local $comment = $1 if defined $1;
480         local @r = split(/\s+/, $l, 6);
481         if ($#r > -1) {
482             local $zone = shift @r;
483
484             # split out parent if it is present in the zone field
485             local $parent;
486             $zone =~ m/(.*?):(.*)/;
487             if (defined $2) {
488                 $zone = $1;
489                 $parent = $2;
490                 }
491             else {
492                 $parent = "";
493                 }
494             unshift @r, $zone, $parent;
495
496             # put the saved comment back
497             if (defined $comment) {
498                 # ensure option fields are present
499                 while ($#r < 5) {
500                     push @r, "";
501                 }
502
503                 # add the comment field
504                 push @r, $comment;
505                 }
506             }
507         return scalar(@r) ? \@r : undef;
508         }
509 else {
510         # Old format
511         local $l = $_[0];
512         $l =~ s/#.*$//;
513         if ($l =~ /^(\S+)\s+(\S+)\s*(.*)/) {
514                 return [ $1, $2, $3 ];
515                 }
516         else {
517                 return undef;
518                 }
519         }
520 }
521
522 sub zones_columns
523 {
524 return &new_zones_format() ? 4 : 3;
525 }
526
527 # format a parsed row for display in list form
528 sub zones_row
529 {
530 if (&new_zones_format()) {
531         return ( $_[0], $_[1], $text{'zones_'.$_[2]} || $_[2], $_[6] );
532         }
533 else {
534         return @_;
535         }
536 }
537
538 sub zones_colnames
539 {
540 if (&new_zones_format()) {
541         return ( $text{'zones_0'}, $text{'zones_1new'}, $text{'zones_2new'},
542 # The option fields are not displayed in the main list.
543 #               $text{'zones_3new'}, $text{'zones_4new'}, $text{'zones_5new'},
544                 $text{'zones_6new'} );
545         }
546 else {
547         return ( $text{'zones_0'}, $text{'zones_1'}, $text{'zones_2'} );
548         }
549 }
550
551 sub zones_form
552 {
553 if (&new_zones_format()) {
554         # Shorewall 3 zones format
555         print "<tr> <td><b>$text{'zones_0'}</b></td>\n";
556         print "<td>",&ui_textbox("id", $_[0], 8),"</td>\n";
557
558         print "<td><b>$text{'zones_1new'}</b></td>\n";
559         print "<td>\n";
560         &zone_field("parent", $_[1], 0, 1);
561         print "</td> </tr>\n";
562
563         print "<td><b>$text{'zones_2new'}</b></td>\n";
564         print "<td>",&ui_select("type", $_[2],
565                 [ [ "ipv4", $text{'zones_ipv4'} ],
566                   [ "ipsec", $text{'zones_ipsec'} ],
567                   [ "firewall", $text{'zones_firewall'} ] ]),"</td> </tr>\n";
568
569         print "<tr> <td><b>$text{'zones_3new'}</b></td>\n";
570         print "<td>",&ui_textbox("opts", $_[3], 50),"</td> </tr>\n";
571
572         print "<tr> <td><b>$text{'zones_4new'}</b></td>\n";
573         print "<td>",&ui_textbox("opts_in", $_[4], 50),"</td> </tr>\n";
574
575         print "<tr> <td><b>$text{'zones_5new'}</b></td>\n";
576         print "<td>",&ui_textbox("opts_out", $_[5], 50),"</td> </tr>\n";
577
578         print "<tr> <td><b>$text{'zones_6new'}</b></td>\n";
579         print "<td>",&ui_textbox("comment", $_[6], 50),"</td> </tr>\n";
580
581         }
582 else {
583         # Shorewall 2 zones format
584         print "<tr> <td><b>$text{'zones_0'}</b></td>\n";
585         print "<td><input name=id size=8 value='$_[0]'></td> </tr>\n";
586
587         print "<tr> <td><b>$text{'zones_1'}</b></td>\n";
588         print "<td><input name=name size=15 value='$_[1]'></td> </tr>\n";
589
590         print "<tr> <td><b>$text{'zones_2'}</b></td>\n";
591         print "<td><input name=desc size=70 value='$_[2]'></td> </tr>\n";
592         }
593 }
594
595 sub zones_validate
596 {
597 $in{'id'} =~ /^\S+$/ || &error($text{'zones_eid'});
598 &is_fw($in{'id'}) && &error($text{'zones_efwid'});
599 if (&new_zones_format()) {
600         # Parse new format
601         $in{'opts'} =~ /^\S*$/ || &error($text{'zones_eopts'});
602         $in{'opts_in'} =~ /^\S*$/ || &error($text{'zones_eopts_in'});
603         $in{'opts_out'} =~ /^\S*$/ || &error($text{'zones_eopts_out'});
604         if (!defined $in{'parent'} || $in{'parent'} eq "-") {
605             return ( $in{'id'}, $in{'type'}, $in{'opts'},
606                      $in{'opts_in'}, $in{'opts_out'}, "# $in{'comment'}" );
607             }
608         else {
609             return ( $in{'id'}.":".$in{'parent'}, $in{'type'}, $in{'opts'},
610                      $in{'opts_in'}, $in{'opts_out'}, "# $in{'comment'}" );
611             }
612         }
613 else {
614         # Parse old format
615         $in{'name'} =~ /^\S+$/ || &error($text{'zones_ename'});
616         $in{'desc'} =~ /\S/ || &error($text{'zones_edesc'});
617         return ( $in{'id'}, $in{'name'}, $in{'desc'} );
618         }
619 }
620
621 ################################# interfaces ###################################
622
623 sub interfaces_row
624 {
625 return ( $_[1],
626          $_[0] eq '-' ? $text{'list_any'} : $_[0],
627          $_[2] eq 'detect' ? $text{'list_auto'} :
628           $_[2] eq '-' || $_[2] eq '' ? $text{'list_none'} : $_[2],
629          $_[3] ? $_[3] : $text{'list_none'} );
630 }
631
632 @interfaces_opts = ( 'dhcp', 'noping', 'filterping', 'routestopped', 'norfc1918',
633                      'multi', 'routefilter', 'dropunclean', 'logunclean',
634                      'blacklist', 'maclist', 'tcpflags', 'proxyarp' );
635 if (&version_atleast(3)) {
636         push(@interfaces_opts, "logmartians", "routeback", "arp_filter",
637                                "arp_ignore", "nosmurfs", "detectnets", "upnp");
638         }
639
640 sub interfaces_form
641 {
642 print "<tr> <td><b>$text{'interfaces_0'}</b></td>\n";
643 print "<td><input name=iface size=6 value='$_[1]'></td>\n";
644
645 local @ztable = &read_table_file("zones", \&zones_parser);
646 print "<td><b>$text{'interfaces_1'}</b></td>\n";
647 print "<td>\n";
648 &zone_field("zone", $_[0], 0, 1);
649 print "</td> </tr>\n";
650
651 local $bmode = $_[2] eq 'detect' ? 2 :
652                $_[2] eq '-' || $_[2] eq '' ? 1 : 0;
653 print "<tr> <td><b>$text{'interfaces_2'}</b></td> <td colspan=3>\n";
654 printf "<input type=radio name=broad_mode value=1 %s> %s\n",
655         $bmode == 1 ? "checked" : "", $text{'list_none'};
656 printf "<input type=radio name=broad_mode value=2 %s> %s\n",
657         $bmode == 2 ? "checked" : "", $text{'list_auto'};
658 printf "<input type=radio name=broad_mode value=0 %s>\n",
659         $bmode == 0 ? "checked" : "";
660 printf "<input name=broad size=50 value='%s'></td> </tr>\n",
661         $bmode == 0 ? $_[2] : "";
662
663 # options
664 local %opts = map { $_, 1 } split(/,/, $_[3]);
665 print "<tr> <td valign=top><b>$text{'interfaces_3'}</b></td> <td colspan=3>\n";
666 &options_input("opts", $_[3], \@interfaces_opts);
667 print "</td> </tr>\n";
668 }
669
670 sub interfaces_validate
671 {
672 $in{'iface'} =~ /^[a-z]+\d*(\.\d+)?$/ ||
673         $in{'iface'} =~ /^[a-z]+\+$/ || &error($text{'interfaces_eiface'});
674 $in{'broad_mode'} || $in{'broad'} =~ /^[0-9\.,]+$/ ||
675         &error($text{'interfaces_ebroad'});
676 return ( $in{'zone'}, $in{'iface'},
677          $in{'broad_mode'} == 2 ? 'detect' :
678          $in{'broad_mode'} == 1 ? '-' : $in{'broad'},
679          join(",", split(/\0/, $in{'opts'})) );
680 }
681
682 ################################# policy #######################################
683
684 sub policy_row
685 {
686 return ( $_[0] eq 'all' ? $text{'list_any'} :
687           &is_fw($_[0]) ? $text{'list_fw'} : $_[0],
688          $_[1] eq 'all' ? $text{'list_any'} :
689           &is_fw($_[1]) ? $text{'list_fw'} : $_[1],
690          $_[2], $_[3] eq '-' || $_[3] eq '' ? $text{'list_none'} : $_[3],
691          $_[4] =~ /(\d+):(\d+)/ ? &text('policy_limit', "$1", "$2")
692                                 : $text{'list_none'} );
693 }
694
695 @policy_list = ( "ACCEPT", "DROP", "REJECT", "CONTINUE" );
696
697 sub policy_form
698 {
699 local $found;
700
701 print "<tr> <td><b>$text{'policy_0'}</b></td>\n";
702 print "<td>\n";
703 &zone_field("source", $_[0], 0);
704 print "</td>\n";
705
706 print "<td><b>$text{'policy_1'}</b></td>\n";
707 print "<td>\n";
708 &zone_field("dest", $_[1], 0);
709 print "</td> </tr>\n";
710
711 print "<tr> <td><b>$text{'policy_2'}</b></td>\n";
712 print "<td><select name=policy>\n";
713 $found = !$_[2];
714 foreach $p (@policy_list) {
715         printf "<option value=%s %s>%s\n",
716                 $p, lc($p) eq lc($_[2]) ? "selected" : "", $p;
717         $found++ if (lc($p) eq lc($_[2]));
718         }
719 print "<option value=$_[2] selected>$_[2]\n" if (!$found);
720 print "</select></td>\n";
721
722 print "<td><b>$text{'policy_3'}</b></td>\n";
723 print "<td><select name=log>\n";
724 printf "<option value=- %s>%s\n",
725         $_[3] eq '-' || !$_[3] ? "selected" : "", "&lt;$text{'policy_nolog'}&gt;";
726 printf "<option value=ULOG %s>%s\n",
727         $_[3] eq 'ULOG' ? "selected" : "", "&lt;$text{'policy_ulog'}&gt;";
728 $found = !$_[3] || $_[3] eq '-' || $_[3] eq 'ULOG';
729 &foreign_require("syslog", "syslog-lib.pl");
730 foreach $l (&syslog::list_priorities()) {
731         printf "<option value=%s %s>%s\n",
732                 $l, $_[3] eq $l ? "selected" : "", $l;
733         $found++ if ($_[3] eq $l);
734         }
735 print "<option value=$_[3] selected>$_[3]\n" if (!$found);
736 print "</select></td> </tr>\n";
737
738 local ($l, $b) = $_[4] =~ /(\d+):(\d+)/ ? ($1, $2) : ( );
739 print "<tr> <td><b>$text{'policy_4'}</b></td> <td colspan=3>\n";
740 printf "<input type=radio name=limit_def value=1 %s> %s\n",
741         $l eq '' ? "checked" : "", $text{'list_none'};
742 printf "<input type=radio name=limit_def value=0 %s>\n",
743         $l eq '' ? "" : "checked";
744 print &text('policy_limit',
745             "<input name=limit size=5 value='$l'>",
746             "<input name=burst size=5 value='$b'>"),"</td> </tr>\n";
747 }
748
749 sub policy_validate
750 {
751 &is_fw($in{'source'}) && &is_fw($in{'dest'}) && &error($text{'policy_efw'});
752 if (!$in{'limit_def'}) {
753         $in{'limit'} =~ /^\d+$/ || &error($text{'policy_elimit'});
754         $in{'burst'} =~ /^\d+$/ || &error($text{'policy_eburst'});
755         }
756 return ( $in{'source'}, $in{'dest'}, $in{'policy'}, $in{'log'}, 
757          $in{'limit_def'} ? ( ) : ( "$in{'limit'}:$in{'burst'}" ) );
758 }
759
760 ################################# rules #######################################
761
762 sub rules_row
763 {
764 return ( $_[0] =~ /^(\S+):/ ? "$1" : $_[0],
765          &is_fw($_[1]) ? $text{'list_fw'} :
766           $_[1] eq 'all' ? $text{'list_any'} :
767           $config{'display_zone_descriptions'} == 0 ? $_[1] :
768           $_[1] =~ /^([^:]+):(\S+)$/ ?
769           &text('rules_hosts', &convert_zone("$1"), &nice_host_list("$2")) :
770           &text('rules_zone', &convert_zone($_[1])),
771          &is_fw($_[2]) ? $text{'list_fw'} :
772           $_[2] eq 'all' ? $text{'list_any'} :
773           $_[2] =~ /^\d+$/ ? &text('rules_rport', $_[2]) :
774           $config{'display_zone_descriptions'} == 0 ? $_[2] :
775           $_[2] =~ /^([^:]+):(\S+)$/ ?
776           &text('rules_hosts', &convert_zone("$1"), &nice_host_list("$2")) :
777           &text('rules_zone', &convert_zone($_[2])),
778          $_[3] eq 'all' ? $text{'list_any'} :
779           $_[3] eq 'related' ? $text{'rules_related'} : uc($_[3]),
780          $_[3] eq 'all' || $_[3] eq 'related' ? "" :
781           $_[5] eq '-' || $_[5] eq '' ? $text{'list_any'} : $_[5],
782          $_[4] eq '-' || $_[4] eq '' ? "" : $_[4],
783          &version_atleast(1, 4, 7) ? (
784                 $_[7] eq "-" ? "" : $_[7],
785                 $_[8] eq "-" ? "" : $_[8] ) :
786                 ( )
787         );
788 }
789
790 @rules_actions = ( 'ACCEPT', 'DROP', 'REJECT', 'DNAT', 'DNAT-', 'REDIRECT' );
791 if (&version_atleast(2, 0, 0)) {
792         push(@rules_actions, 'CONTINUE');
793         push(@rules_actions, 'ACCEPT+');
794         push(@rules_actions, 'NONAT');
795         push(@rules_actions, 'REDIRECT-');
796         push(@rules_actions, 'LOG');
797         }
798 if (&version_atleast(3)) {
799         push(@rules_actions, 'DNAT-');
800         push(@rules_actions, 'SAME');
801         push(@rules_actions, 'SAME-');
802         push(@rules_actions, 'QUEUE');
803         }
804 @rules_protos = ( 'all', 'related', 'tcp', 'udp', 'icmp' );
805
806 sub rules_form
807 {
808 local $found;
809 local @ztable = &read_table_file("zones", \&zones_parser);
810
811 local ($action, $log) = split(/:/, $_[0]);
812 local $macroarg;
813 if ($action =~ /^(.*)\/(.*)$/) {
814         $action = $1;
815         $macroarg = $2;
816         }
817
818 # Rule action
819 print "<tr> <td><b>$text{'rules_0'}</b></td>\n";
820 print "<td colspan=3><select name=action>\n";
821 $found = !$_[0];
822 foreach $a ((sort { $a cmp $b } @rules_actions),
823             "-------- Actions --------",
824             &list_standard_actions(),
825             (&version_atleast(3) ? ( "-------- Macros --------",
826                                      &list_standard_macros() ) : ( ) )) {
827         printf "<option value=%s %s>%s\n",
828                 $a, $action eq $a ? "selected" : "", $a;
829         $found++ if ($action eq $a);
830         }
831 print "<option value=$action selected>$action\n" if (!$found);
832 print "</select>\n";
833
834 # Logging level
835 print "<b>$text{'rules_log'}</b> <select name=log>\n";
836 printf "<option value='' %s>%s\n",
837         !$log ? "selected" : "", "&lt;$text{'rules_nolog'}&gt;";
838 printf "<option value=ULOG %s>%s\n",
839         $log eq 'ULOG' ? "selected" : "", "&lt;$text{'policy_ulog'}&gt;";
840 $found = !$log || $log eq '-' || $log eq 'ULOG';
841 &foreign_require("syslog", "syslog-lib.pl");
842 foreach $l (&syslog::list_priorities()) {
843         printf "<option value=%s %s>%s\n",
844                 $l, $log eq $l ? "selected" : "", $l;
845         $found++ if ($log eq $l);
846         }
847 print "<option value=$log selected>$log\n" if (!$found);
848 print "</select></td> </tr>\n";
849
850 if (&version_atleast(3)) {
851         print "<tr> <td valign=top><b>$text{'rules_macro'}</b></td>\n";
852         print "<td colspan=3 nowrap>\n";
853         print &ui_select("macro", $macroarg,
854                 [ [ "", "&lt;$text{'rules_none2'}&gt;" ],
855                   map { [ $_ ] } (sort { $a cmp $b } @rules_actions) ],
856                 1, 0, $macroarg);
857         print "</td> </tr>\n";
858         }
859
860 # Source zone and hosts
861 local ($zone, $host) = split(/:/, $_[1], 2);
862 print "<tr> <td valign=top><b>$text{'rules_1z'}</b></td>\n";
863 print "<td colspan=3 nowrap>\n";
864 $found = &zone_field("source", $zone, 1);
865 printf "<input name=sother size=10 value='%s'>\n",
866         $found ? "" : $zone;
867
868 print "<br><b>$text{'rules_inzone'}</b>\n";
869 printf "<input type=checkbox name=sinzone_def value=1 %s> %s\n",
870         $host ? "checked" : "", $text{'rules_addr'};
871 printf "<input name=sinzone size=50 value='%s'></td> </tr>\n",
872         join(" ", split(/,/, $host));
873
874 ($zone, $host) = split(/:/, $_[2], 2);
875 print "<tr> <td valign=top><b>$text{'rules_2z'}</b></td>\n";
876 print "<td colspan=3 nowrap>\n";
877 $found = &zone_field("dest", $zone, 1);
878 printf "<input name=dother size=10 value='%s'>\n",
879         $found ? "" : $zone;
880
881 print "<br><b>$text{'rules_inzone'}</b>\n";
882 printf "<input type=checkbox name=dinzone_def value=1 %s> %s\n",
883         $host ? "checked" : "", $text{'rules_addr'};
884 printf "<input name=dinzone size=50 value='%s'>\n",
885         join(" ", split(/,/, $host));
886 print "<br>$text{'rules_dnat_dest'}</td> </tr>\n";
887
888 print "<tr> <td><b>$text{'rules_3'}</b></td>\n";
889 print "<td colspan=3><select name=proto>\n";
890 $found = !$_[3];
891 foreach $p (@rules_protos) {
892         printf "<option value=%s %s>%s\n",
893                 $p, $p eq $_[3] ? "selected" : "",
894                 $p eq 'all' ? "&lt;$text{'list_any'}&gt;" :
895                  $p eq 'related' ? "&lt;$text{'rules_related'}&gt;" : uc($p);
896         $found++ if ($p eq $_[3]);
897         }
898 printf "<option value='' %s>%s\n",
899         $found ? "" : "selected", $text{'list_other'};
900 print "</select>\n";
901 printf "<input name=pother size=5 value='%s'></td> </tr>\n",
902         $found ? "" : $_[3];
903
904 print "<tr> <td><b>$text{'rules_4'}</b></td> <td colspan=3>\n";
905 printf "<input type=radio name=sport_def value=1 %s> %s\n",
906         $_[5] eq '' || $_[5] eq '-' ? "checked" : "", $text{'list_any'};
907 printf "<input type=radio name=sport_def value=0 %s> %s\n",
908         $_[5] eq '' || $_[5] eq '-' ? "" : "checked", $text{'rules_ranges'};
909 printf "<input name=sport size=30 value='%s'></td> </tr>\n",
910         $_[5] eq '' || $_[5] eq '-' ? "" : join(" ", split(/,/, $_[5]));
911
912 print "<tr> <td><b>$text{'rules_5'}</b></td> <td colspan=3>\n";
913 printf "<input type=radio name=dport_def value=1 %s> %s\n",
914         $_[4] eq '' || $_[4] eq '-' ? "checked" : "", $text{'list_any'};
915 printf "<input type=radio name=dport_def value=0 %s> %s\n",
916         $_[4] eq '' || $_[4] eq '-' ? "" : "checked", $text{'rules_ranges'};
917 printf "<input name=dport size=30 value='%s'>\n",
918         $_[4] eq '' || $_[4] eq '-' ? "" : join(" ", split(/,/, $_[4]));
919 print "<br>$text{'rules_dnat_port'}</td> </tr>\n";
920
921 print "<tr> <td><b>$text{'rules_dnat'}</b></td> <td colspan=3>\n";
922 printf "<input type=radio name=dnat_def value=1 %s> %s\n",
923         $_[6] eq '' || $_[6] eq '-' ? "checked" : "", $text{'list_none'};
924 printf "<input type=radio name=dnat_def value=0 %s>\n",
925         $_[6] eq '' || $_[6] eq '-' ? "" : "checked";
926 printf "<input name=dnat size=30 value='%s'></td> </tr>\n",
927         $_[6] eq '' || $_[6] eq '-' ? "" : $_[6];
928
929 if (&version_atleast(1, 4, 7)) {
930         print "<tr> <td><b>$text{'rules_rate'}</b></td> <td colspan=3>\n";
931         printf "<input type=radio name=rate_def value=1 %s> %s\n",
932                 $_[7] eq "-" || !$_[7] ? "checked" : "", $text{'rules_norate'};
933         printf "<input type=radio name=rate_def value=0 %s>\n",
934                 $_[7] eq "-" || !$_[7] ? "" : "checked";
935         printf "<input name=rate size=15 value='%s'></td> </tr>\n",
936                 $_[7] eq "-" ? "" : $_[7];
937
938         print "<tr> <td><b>$text{'rules_set'}</b></td> <td colspan=3>\n";
939         printf "<input type=radio name=set_def value=1 %s> %s\n",
940                 $_[8] eq "-" || !$_[8] ? "checked" : "", $text{'rules_noset'};
941         printf "<input type=radio name=set_def value=0 %s>\n",
942                 $_[8] eq "-" || !$_[8] ? "" : "checked";
943         printf "<input name=set size=15 value='%s'></td> </tr>\n",
944                 $_[8] eq "-" ? "" : $_[8];
945         }
946 }
947
948 sub rules_validate
949 {
950 $in{'action'} !~ /----/ || &error($text{'rules_eaction'});
951 $in{'source'} || $in{'sother'} =~ /^\S+$/ || &error($text{'rules_esother'});
952 !$in{'sinzone_def'} || $in{'sinzone'} =~ /\S/ || &error($text{'rules_esinzone'});
953 $in{'dest'} || $in{'dother'} =~ /^\S+$/ || &error($text{'rules_edother'});
954 !$in{'dinzone_def'} || $in{'dinzone'} =~ /\S/ || &error($text{'rules_edinzone'});
955 $in{'proto'} || $in{'pother'} =~ /^\S+$/ || &error($text{'rules_epother'});
956 $in{'sport_def'} || $in{'sport'} =~ /\S/ || &error($text{'rules_esport'});
957 $in{'dport_def'} || $in{'dport'} =~ /\S/ || &error($text{'rules_edport'});
958 $in{'dnat_def'} || &check_ipaddress($in{'dnat'}) ||
959         ($in{'dnat'} =~ /^([0-9\.]+):([0-9\.]+)$/ &&
960          &check_ipaddress("$1") && &check_ipaddress("$2")) ||
961         ($in{'dnat'} =~ /^\!([0-9\.]+)$/ && &check_ipaddress("$1")) ||
962         ($in{'dnat'} =~ /^\!([0-9\.]+),([0-9\.]+)(\/\d+)?$/ &&
963          &check_ipaddress("$1") && &check_ipaddress("$2")) ||
964         ($in{'dnat'} =~ /^\!([0-9\.,]+)$/ &&
965          scalar(grep { &check_ipaddress($_) } split(/,/, $1))) ||
966         &error($text{'rules_ednat'});
967 $in{'action'} ne 'DNAT' && $in{'action'} ne 'REDIRECT' && $in{'action'} ne 'DNAT-' && 
968         !$in{'dnat_def'} && &error($text{'rules_ednat2'});
969
970 $in{'sinzone'} =~ s/\s+/,/g;
971 $in{'dinzone'} =~ s/\s+/,/g;
972 $in{'sport'} =~ s/\s+/,/g;
973 $in{'dport'} =~ s/\s+/,/g;
974 if (&version_atleast(1, 4, 7)) {
975         $in{'rate_def'} || $in{'rate'} =~ /^\S+$/ ||
976                 &error($text{'rules_erate'});
977         $in{'set_def'} || $in{'set'} =~ /^\S+$/ ||
978                 &error($text{'rules_eset'});
979         }
980 if ($in{'macro'} && &indexof($in{'action'}, &list_standard_macros()) >= 0) {
981         $in{'action'} .= "/".$in{'macro'};
982         $in{'proto'} = $in{'pother'} = undef;
983         }
984 return ( $in{'log'} ? "$in{'action'}:$in{'log'}" : $in{'action'},
985          ($in{'source'} || $in{'sother'}).
986           ($in{'sinzone_def'} ? ":$in{'sinzone'}" : ""),
987          ($in{'dest'} || $in{'dother'}).
988           ($in{'dinzone_def'} ? ":$in{'dinzone'}" : ""),
989          $in{'proto'} || $in{'pother'} || '-',
990          $in{'dport_def'} ? "-" : $in{'dport'},
991          $in{'sport_def'} ? "-" : $in{'sport'},
992          $in{'dnat_def'} ? "-" : $in{'dnat'},
993          &version_atleast(1, 4, 7) ? (
994                 ( $in{'rate_def'} ? "-" : $in{'rate'} ),
995                 ( $in{'set_def'} ? "-" : $in{'set'} )
996                 ) : ( )
997         );
998 }
999
1000 sub rules_columns
1001 {
1002 return &version_atleast(1, 4, 7) ? 6 : 8;
1003 }
1004
1005 ################################# tos #########################################
1006
1007 %tos_map = ( 0, 'Normal-Service',
1008              2, 'Minimize-Cost',
1009              4, 'Maximize-Reliability',
1010              8, 'Maximize-Throughput',
1011              16, 'Minimize-Delay' );
1012 @tos_protos = ( 'tcp', 'udp', 'icmp' );
1013
1014 sub tos_row
1015 {
1016 return ( &is_fw($_[0]) ? $text{'list_fw'} :
1017           $_[0] eq 'all' ? $text{'list_any'} :
1018           $_[0] =~ /^([^:]+):(\S+)$/ ? &text('rules_hosts', "$1", "$2") :
1019           &text('rules_zone', $_[0]),
1020          &is_fw($_[1]) ? $text{'list_fw'} :
1021           $_[1] eq 'all' ? $text{'list_any'} :
1022           $_[1] =~ /^([^:]+):(\S+)$/ ? &text('rules_hosts', "$1", "$2") :
1023           &text('rules_zone', $_[1]),
1024          uc($_[2]),
1025          $_[3] eq '-' || $_[3] eq '' ? $text{'list_any'} : $_[3],
1026          $_[4] eq '-' || $_[4] eq '' ? $text{'list_any'} : $_[4],
1027          $tos_map{$_[5]} || $_[5],
1028          $_[6] eq '-' ? $text{'list_none'} : $_[6] );
1029 }
1030
1031 sub tos_form
1032 {
1033 local ($zone, $host) = split(/:/, $_[0], 2);
1034 print "<tr> <td valign=top><b>$text{'tos_0z'}</b></td>\n";
1035 print "<td colspan=3 nowrap>\n";
1036 $found = &zone_field("source", $zone, 1);
1037 printf "<input name=sother size=10 value='%s'>\n",
1038         $found ? "" : $zone;
1039
1040 print "<br><b>$text{'rules_inzone'}</b>\n";
1041 printf "<input type=checkbox name=sinzone_def value=1 %s> %s\n",
1042         $host ? "checked" : "", $text{'rules_addr'};
1043 printf "<input name=sinzone size=50 value='%s'></td> </tr>\n",
1044         join(" ", split(/,/, $host));
1045
1046 ($zone, $host) = split(/:/, $_[1], 2);
1047 print "<tr> <td valign=top><b>$text{'tos_1z'}</b></td>\n";
1048 print "<td colspan=3 nowrap>\n";
1049 $found = &zone_field("dest", $zone, 1);
1050 printf "<input name=dother size=10 value='%s'>\n",
1051         $found ? "" : $zone;
1052
1053 print "<br><b>$text{'rules_inzone'}</b>\n";
1054 printf "<input type=checkbox name=dinzone_def value=1 %s> %s\n",
1055         $host ? "checked" : "", $text{'rules_addr'};
1056 printf "<input name=dinzone size=50 value='%s'></td> </tr>\n",
1057         join(" ", split(/,/, $host));
1058
1059 print "<tr> <td><b>$text{'tos_2'}</b></td>\n";
1060 print "<td><select name=proto>\n";
1061 $found = !$_[2];
1062 foreach $p (@tos_protos) {
1063         printf "<option value=%s %s>%s\n",
1064                 $p, $p eq $_[2] ? "selected" : "", uc($p);
1065         $found++ if ($p eq $_[2]);
1066         }
1067 printf "<option value='' %s>%s\n",
1068         $found ? "" : "selected", $text{'list_other'};
1069 print "</select>\n";
1070 printf "<input name=pother size=5 value='%s'></td> </tr>\n",
1071         $found ? "" : $_[2];
1072
1073 print "<tr> <td><b>$text{'tos_3'}</b></td> <td colspan=3>\n";
1074 printf "<input type=radio name=sport_def value=1 %s> %s\n",
1075         $_[3] eq '' || $_[3] eq '-' ? "checked" : "", $text{'list_any'};
1076 printf "<input type=radio name=sport_def value=0 %s> %s\n",
1077         $_[3] eq '' || $_[3] eq '-' ? "" : "checked", $text{'rules_ranges'};
1078 printf "<input name=sport size=30 value='%s'></td> </tr>\n",
1079         $_[3] eq '' || $_[3] eq '-' ? "" : join(" ", split(/,/, $_[3]));
1080
1081 print "<tr> <td><b>$text{'tos_4'}</b></td> <td colspan=3>\n";
1082 printf "<input type=radio name=dport_def value=1 %s> %s\n",
1083         $_[4] eq '' || $_[4] eq '-' ? "checked" : "", $text{'list_any'};
1084 printf "<input type=radio name=dport_def value=0 %s> %s\n",
1085         $_[4] eq '' || $_[4] eq '-' ? "" : "checked", $text{'rules_ranges'};
1086 printf "<input name=dport size=30 value='%s'></td> </tr>\n",
1087         $_[4] eq '' || $_[4] eq '-' ? "" : join(" ", split(/,/, $_[4]));
1088
1089 print "<tr> <td><b>$text{'tos_5'}</b></td>\n";
1090 print "<td><select name=tos>\n";
1091 $found = !$_[5];
1092 foreach $t (sort { $a <=> $b } keys %tos_map) {
1093         printf "<option value=%s %s>%s\n",
1094                 $t, $_[5] == $t ? "selected" : "", $tos_map{$t};
1095         $found++ if ($_[5] == $t);
1096         }
1097 print "<option value=$_[5] selected>$_[5]\n" if (!$found);
1098 print "</select></td> </tr>\n";
1099
1100 print "<tr> <td><b>$text{'tos_6'}</b></td>\n";
1101 printf "<td><input name=mark size=50 value='%s'></td> </tr>\n",
1102         $_[6] eq "-" ? "" : $_[6];
1103 }
1104
1105 sub tos_validate
1106 {
1107 $in{'source'} || $in{'sother'} =~ /^\S+$/ || &error($text{'rules_esother'});
1108 !$in{'sinzone_def'} || $in{'sinzone'} =~ /\S/ || &error($text{'rules_esinzone'});
1109 $in{'dest'} || $in{'dother'} =~ /^\S+$/ || &error($text{'rules_edother'});
1110 !$in{'dinzone_def'} || $in{'dinzone'} =~ /\S/ || &error($text{'rules_edinzone'});
1111 $in{'proto'} || $in{'pother'} =~ /^\S+$/ || &error($text{'rules_epother'});
1112 $in{'sport_def'} || $in{'sport'} =~ /\S/ || &error($text{'rules_esport'});
1113 $in{'dport_def'} || $in{'dport'} =~ /\S/ || &error($text{'rules_edport'});
1114 return ( ($in{'source'} || $in{'sother'}).
1115           ($in{'sinzone_def'} ? ":$in{'sinzone'}" : ""),
1116          ($in{'dest'} || $in{'dother'}).
1117           ($in{'dinzone_def'} ? ":$in{'dinzone'}" : ""),
1118          $in{'proto'} || $in{'pother'},
1119          $in{'sport_def'} ? "-" : join(",", split(/\s+/, $in{'sport'})),
1120          $in{'dport_def'} ? "-" : join(",", split(/\s+/, $in{'dport'})),
1121          $in{'tos'},
1122          $in{'mark'} || "-" );
1123 }
1124
1125 ################################# masq #########################################
1126
1127 sub masq_row
1128 {
1129 return ( $_[0] =~ /^(\S+):(\S+)$/ ? &text('masq_in', "$1", "$2") : $_[0],
1130          $_[1] =~ /^[0-9\.\/]+$/ ? $_[1] :
1131           $_[1] =~ /^([a-z]+\d*)\!(\S+)$/ ? &text('masq_ex', "$1", "$2") :
1132           &text('masq_iface', $_[1]),
1133          $_[2] eq "" ? "" : $_[2] );
1134 }
1135
1136 sub masq_columns
1137 {
1138 return 3;
1139 }
1140
1141 sub masq_form
1142 {
1143 local ($iface, $net) = split(/:/, $_[0], 2);
1144 print "<tr> <td><b>$text{'masq_0'}</b></td> <td colspan=3>\n";
1145 &iface_field("iface", $iface);
1146
1147 printf "<input type=checkbox name=net_def value=1 %s> %s\n",
1148         $net ? "checked" : "", $text{'masq_net'};
1149 print "<input name=net size=20 value='$net'></td> </tr>\n";
1150
1151 local ($mnet, $miface, $mode);
1152 if ($_[1] =~ /^[0-9\.\/]+$/) {
1153         $mnet = $_[1];
1154         $mode = 0;
1155         }
1156 elsif ($_[1] =~ /^([a-z]+\d*)\!(\S+)$/) {
1157         $miface = $1;
1158         $mnet = $2;
1159         $mode = 1;
1160         }
1161 else {
1162         $miface = $_[1];
1163         $mode = 1;
1164         }
1165 print "<tr> <td valign=top><b>$text{'masq_1'}</b></td> <td colspan=3>\n";
1166 printf "<input type=radio name=mode value=0 %s> %s\n",
1167         $mode == 0 ? "checked" : "", $text{'masq_mode0'};
1168 printf "<input name=mnet size=20 value='%s'><br>\n",
1169         $mode == 0 ? $mnet : "";
1170 printf "<input type=radio name=mode value=1 %s> %s\n",
1171         $mode == 1 ? "checked" : "", $text{'masq_mode1'};
1172 &iface_field("miface", $mode == 1 ? $miface : undef);
1173 printf "<input type=checkbox name=mnet_def value=1 %s> %s\n",
1174         $mode == 1 && $mnet ? "checked" : "", $text{'masq_except'};
1175 printf "<input name=mnete size=20 value='%s'>\n",
1176         $mode == 1 ? join(" ", split(/,/, $mnet)) : "";
1177 print "</td> </tr>\n";
1178
1179 print "<tr> <td><b>$text{'masq_2'}</b></td> <td colspan=3>\n";
1180 printf "<input type=radio name=snat_def value=1 %s> %s\n",
1181         $_[2] eq '' || $_[2] eq '-' ? "checked" : "", $text{'list_none'};
1182 printf "<input type=radio name=snat_def value=0 %s>\n",
1183         $_[2] eq '' || $_[2] eq '-' ? "" : "checked";
1184 printf "<input name=snat size=15 value='%s'></td> </tr>\n",
1185         $_[2] eq '' || $_[2] eq '-' ? "" : $_[2];
1186
1187 if (&version_atleast(3)) {
1188         print "<tr> <td><b>$text{'masq_3'}</b></td> <td colspan=3>\n";
1189         print &ui_radio("proto_def", $_[3] ? 0 : 1,
1190                         [ [ 1, $text{'masq_any'} ],
1191                           [ 0, " " ] ]),"\n",
1192               &ui_select("proto", $_[3],
1193                          [ map { [ $_, uc($_) ] } &list_protocols() ],
1194                          1, 0, $_[3] ? 1 : 0),"</td> </tr>\n";
1195
1196         print "<tr> <td><b>$text{'masq_4'}</b></td> <td colspan=3>\n";
1197         print &ui_opt_textbox("ports", $_[4], 40, $text{'masq_all'}),
1198               "</td> </tr>\n";
1199
1200         print "<tr> <td><b>$text{'masq_5'}</b></td> <td colspan=3>\n";
1201         print &ui_opt_textbox("ipsec", $_[5], 40, $text{'default'}),
1202               "</td> </tr>\n";
1203         }
1204 }
1205
1206 sub masq_validate
1207 {
1208 !$in{'net_def'} || $in{'net'} =~ /^\S+$/ || &error($text{'masq_enet'});
1209 if ($in{'mode'} == 0) {
1210         $in{'mnet'} =~ /^\S+$/ || &error($text{'masq_emnet'});
1211         }
1212 else {
1213         !$in{'mnet_def'} || $in{'mnete'} =~ /\S/ ||&error($text{'masq_emnete'});
1214         }
1215 $in{'snat_def'} || &check_ipaddress($in{'snat'}) || &error($text{'masq_esnat'});
1216
1217 local @rv = ( $in{'iface'}.(!$in{'net_def'} ? "" : ":$in{'net'}"),
1218          $in{'mode'} == 0 ? $in{'mnet'} :
1219           $in{'miface'}.(!$in{'mnet_def'} ? "" :
1220                          "!".join(",", split(/\s+/, $in{'mnete'}))),
1221          $in{'snat_def'} ? ( "" ) : ( $in{'snat'} ) );
1222 if (&version_atleast(3)) {
1223         push(@rv, $in{'proto_def'} ? "" : $in{'proto'});
1224         if ($in{'ports_def'}) {
1225                 push(@rv, "");
1226                 }
1227         else {
1228                 $in{'ports'} =~ /^\S+$/ || &error($text{'masq_eports'});
1229                 push(@rv, $in{'ports'});
1230                 }
1231         if ($in{'ipsec_def'}) {
1232                 push(@rv, "");
1233                 }
1234         else {
1235                 $in{'ipsec'} =~ /^\S+$/ || &error($text{'masq_eipsec'});
1236                 push(@rv, $in{'ipsec'});
1237                 }
1238         }
1239 return @rv;
1240 }
1241
1242 ################################# nat #########################################
1243
1244 sub nat_form
1245 {
1246 print "<tr> <td><b>$text{'nat_0'}</b></td>\n";
1247 print "<td><input name=ext size=15 value='$_[0]'></td>\n";
1248
1249 print "<td><b>$text{'nat_1'}</b></td>\n";
1250 print "<td>";
1251 if (&version_atleast(1, 3, 14)) {
1252         local ($iface, $virt) = split(/:/, $_[1]);
1253         &iface_field("iface", $iface);
1254         print "<b>$text{'nat_virt'}</b>\n";
1255         print "<input name=virt size=3 value='$virt'>\n";
1256         print "</td> </tr>\n";
1257         }
1258 else {
1259         &iface_field("iface", $_[1]);
1260         print "</td> </tr>\n";
1261         }
1262
1263 print "<tr> <td><b>$text{'nat_2'}</b></td>\n";
1264 print "<td><input name=int size=15 value='$_[2]'></td> </tr>\n";
1265
1266 local $all = $_[3] eq '-' || $_[3] eq '' || $_[3] =~ /yes/i;
1267 print "<tr> <td><b>$text{'nat_all'}</b></td>\n";
1268 printf "<td><input type=radio name=all value=1 %s> %s\n",
1269         $all ? "checked" : "", $text{'yes'};
1270 printf "<input type=radio name=all value=0 %s> %s</td>\n",
1271         $all ? "" : "checked", $text{'no'};
1272
1273 local $local = $_[4] =~ /yes/i;
1274 print "<td><b>$text{'nat_local'}</b></td>\n";
1275 printf "<td><input type=radio name=local value=1 %s> %s\n",
1276         $local ? "checked" : "", $text{'yes'};
1277 printf "<input type=radio name=local value=0 %s> %s</td> </tr>\n",
1278         $local ? "" : "checked", $text{'no'};
1279 }
1280
1281 sub nat_validate
1282 {
1283 &check_ipaddress($in{'ext'}) || &error($text{'nat_eext'});
1284 &check_ipaddress($in{'int'}) || &error($text{'nat_eint'});
1285 $in{'virt'} =~ /^\d*$/ || &error($text{'nat_evirt'});
1286 return ( $in{'ext'},
1287          $in{'virt'} ne '' ? $in{'iface'}.":".$in{'virt'} : $in{'iface'},
1288          $in{'int'},
1289          $in{'all'} ? "yes" : "no",
1290          $in{'local'} ? "yes" : "no" );
1291 }
1292
1293 ################################# proxyarp #######################################
1294
1295 sub proxyarp_row
1296 {
1297 return ( $_[0],
1298          $_[1] eq '-' || $_[1] eq '' ? $text{'list_auto'} : $_[1],
1299          $_[2],
1300          &version_atleast(2, 0, 0) ?
1301                 ( $_[4] =~ /yes/i ? $text{'yes'} : $text{'no'} ) : ( ) );
1302 }
1303
1304 sub proxyarp_form
1305 {
1306 print "<tr> <td><b>$text{'proxyarp_0'}</b></td>\n";
1307 print "<td><input name=addr size=15 value='$_[0]'></td>\n";
1308
1309 print "<td><b>$text{'proxyarp_1'}</b></td>\n";
1310 printf "<td><input type=radio name=int_def value=1 %s> %s\n",
1311         $_[1] eq '-' || $_[1] eq '' ? "checked" : "", $text{'list_auto'};
1312 printf "<input type=radio name=int_def value=0 %s>\n",
1313         $_[1] eq '-' || $_[1] eq '' ? "" : "checked";
1314 &iface_field("int", $_[1] eq '-' ? undef : $_[1]);
1315 print "</td> </tr>";
1316
1317 local $have = $_[3] =~ /yes/i;
1318 print "<tr> <td><b>$text{'proxyarp_have'}</b></td>\n";
1319 printf "<td><input type=radio name=have value=1 %s> %s\n",
1320         $have ? "checked" : "", $text{'yes'};
1321 printf "<input type=radio name=have value=0 %s> %s</td>\n",
1322         $have ? "" : "checked", $text{'no'};
1323
1324 print "<td><b>$text{'proxyarp_2'}</b></td>\n";
1325 print "<td>";
1326 &iface_field("ext", $_[2]);
1327 print "</td> </tr>";
1328
1329 if (&version_atleast(2, 0, 0)) {
1330         local $pers = $_[4] =~ /yes/i;
1331         print "<tr> <td><b>$text{'proxyarp_pers'}</b></td>\n";
1332         printf "<td><input type=radio name=pers value=1 %s> %s\n",
1333                 $pers ? "checked" : "", $text{'yes'};
1334         printf "<input type=radio name=pers value=0 %s> %s</td>\n",
1335                 $pers ? "" : "checked", $text{'no'};
1336         }
1337 }
1338
1339 sub proxyarp_validate
1340 {
1341 &check_ipaddress($in{'addr'}) || &error($text{'proxyarp_eaddr'});
1342 return ( $in{'addr'},
1343          $in{'int_def'} ? "-" : $in{'int'},
1344          $in{'ext'},
1345          $in{'have'} ? "yes" : "no",
1346          &version_atleast(2, 0, 0) ? ( $in{'pers'} ? "yes" : "no" ) : ( )
1347         ); 
1348          
1349 }
1350
1351 sub proxyarp_columns
1352 {
1353 return &version_atleast(2, 0, 0) ? 4 : 3;
1354 }
1355
1356 ################################ routestopped ##################################
1357
1358 sub routestopped_row
1359 {
1360 return ( $_[0], $_[1],
1361          $_[2] eq '-' || $_[2] eq '' ? $text{'default'} : $_[2],
1362          $_[3] eq '-' || $_[3] eq '' ? $text{'tunnels_gnone'} : $_[3] );
1363 }
1364
1365 sub routestopped_columns
1366 {
1367 return 2;
1368 }
1369
1370 @routestopped_options = ( "routeback", "source", "dest", "critical" );
1371
1372 sub routestopped_form
1373 {
1374 print "<tr> <td valign=top><b>$text{'routestopped_0'}</b></td>\n";
1375 print "<td valign=top>";
1376 &iface_field("iface", $_[0]);
1377 print "</td>\n";
1378
1379 local $none = $_[1] eq '' || $_[1] eq '-' || $_[1] eq '0.0.0.0/0';
1380 print "<td valign=top><b>$text{'routestopped_1'}</b></td>\n";
1381 printf "<td><input type=radio name=addr_def value=1 %s> %s<br>\n",
1382         $none ? "checked" : "", $text{'routestopped_all'};
1383 printf "<input type=radio name=addr_def value=0 %s> %s<br>\n",
1384         $none ? "" : "checked", $text{'routestopped_list'};
1385 print "<textarea name=addr rows=5 cols=20>",
1386         $none ? "" : join("\n", split(/,/, $_[1])),
1387         "</textarea></td> </tr>\n";
1388
1389 if (&version_atleast(3)) {
1390         print "<tr> <td valign=top><b>$text{'routestopped_2'}</b></td>\n";
1391         print "<td colspan=3>\n";
1392         &options_input("opts", $_[2], \@routestopped_options);
1393         print "</td> </tr>\n";
1394         }
1395 }
1396
1397 sub routestopped_validate
1398 {
1399 $in{'addr_def'} || $in{'addr'} =~ /\S/ || &error($text{'routestopped_eaddr'});
1400 return ( $in{'iface'},
1401          $in{'addr_def'} ? "-" : join(",", split(/\s+/, $in{'addr'})),
1402          join(",", split(/\0/, $in{'opts'})) );
1403 }
1404
1405 ################################ tunnels ##################################
1406
1407 sub tunnels_row
1408 {
1409 local $tt = $_[0];
1410 $tt =~ s/^(openvpn|generic):.*$/$1/;
1411 return ( $text{'tunnels_'.$tt} || $tt,
1412          $_[1] eq '-' || $_[1] eq '' ? $text{'routestopped_all'} : $_[1],
1413          $_[2], $_[3] );
1414 }
1415
1416 sub tunnels_form
1417 {
1418 print "<tr> <td><b>$text{'tunnels_0'}</b></td>\n";
1419 print "<td><select name=type>\n";
1420 local $tt;
1421 local $found = !$_[0];
1422 local $ttype = $_[0];
1423 local $tport;
1424 if ($ttype =~ s/^(openvpn|generic):(.*)$/$1/) {
1425         $tport = $2;
1426         }
1427 foreach $tt ('ipsec', 'ipsecnat',
1428              (&version_atleast(2, 0, 0) ? ( 'ipsec:noah', 'ipsecnat:noah' )
1429                                        : ( )),
1430              'ip', 'gre', 'pptpclient', 'pptpserver', 'generic',
1431              (&version_atleast(1, 3, 14) ? ( 'openvpn' ) : ( )) ) {
1432         printf "<option value=%s %s>%s\n",
1433                 $tt, $ttype eq $tt ? "selected" : "",
1434                 $text{'tunnels_'.$tt.'_l'} || $text{'tunnels_'.$tt};
1435         $found++ if ($ttype eq $tt);
1436         }
1437 print "<option value=$ttype selected>",uc($ttype),"\n" if (!$found);
1438 print "</select>\n";
1439 print "<input name=tport size=10 value='$tport'>\n";
1440 print "</td>\n";
1441
1442 print "<tr> <td><b>$text{'tunnels_1'}</b></td>\n";
1443 print "<td>";
1444 &zone_field("zone", $_[1], 0, 0);
1445 print "</td> </tr>\n";
1446
1447 local $none = $_[2] eq '' || $_[2] eq '-';
1448 print "<tr> <td><b>$text{'tunnels_2'}</b></td> <td valign=top>\n";
1449 printf "<input type=radio name=gateway_def value=1 %s> %s\n",
1450         $none ? "checked" : "", $text{'default'};
1451 printf "<input type=radio name=gateway_def value=0 %s> %s\n",
1452         $none ? "" : "checked", $text{'tunnels_sel'};
1453 printf "<input name=gateway size=20 value='%s'></td> </tr>\n", $_[2];
1454
1455 local $none = $_[2] eq '' || $_[2] eq '-';
1456 print "<tr> <td><b>$text{'tunnels_3'}</b></td> <td valign=top>\n";
1457 printf "<input type=radio name=gzones_def value=1 %s> %s\n",
1458         $none ? "checked" : "", $text{'tunnels_gnone'};
1459 printf "<input type=radio name=gzones_def value=0 %s> %s\n",
1460         $none ? "" : "checked", $text{'tunnels_gsel'};
1461 printf "<input name=gzones size=50 value='%s'></td> </tr>\n",
1462         join(" ", split(/,/, $_[3]));
1463 }
1464
1465 sub tunnels_validate
1466 {
1467 $in{'gateway_def'} || &check_ipaddress($in{'gateway'}) ||
1468         ($in{'gateway'} =~ /^(\S+)\/(\d+)$/ && &check_ipaddress($1)) ||
1469                 &error($text{'tunnels_egateway'});
1470 if ($in{'type'} eq "openvpn") {
1471         $in{'tport'} =~ /^\d*$/ || &error($text{'tunnels_eopenvpn'});
1472         $in{'type'} .= ":".$in{'tport'} if ($in{'tport'});
1473         }
1474 elsif ($in{'type'} eq 'generic') {
1475         $in{'tport'} =~ /^\S+$/ || &error($text{'tunnels_egeneric'});
1476         $in{'type'} .= ":".$in{'tport'};
1477         }
1478 return ( $in{'type'}, $in{'zone'},
1479          $in{'gateway_def'} ? '-' : $in{'gateway'},
1480          $in{'gzones_def'} ? '-' : join(",", split(/\s+/, $in{'gzones'})) );
1481 }
1482
1483 ################################ hosts ##################################
1484
1485 sub hosts_row
1486 {
1487 return ( $_[0], $_[1] =~ /^(\S+):(\S+)$/ ? ( $1, $2 ) : ( undef, undef ) );
1488 }
1489
1490 @host_options = ( "maclist", "routeback" );
1491 if (&version_atleast(3)) {
1492         push(@host_options, "norfc1918", "blacklist", "tcpflags",
1493                             "nosmurfs", "ipsec");
1494         }
1495
1496 sub hosts_form
1497 {
1498 print "<tr> <td><b>$text{'hosts_0'}</b></td>\n";
1499 print "<td>";
1500 &zone_field("zone", $_[0], 0, 2);
1501 print "</td> </tr>\n";
1502
1503 local ($iface, $net) = split(/:/, $_[1]);
1504 print "<tr> <td><b>$text{'hosts_1'}</b></td>\n";
1505 print "<td>";
1506 &iface_field("iface", $iface);
1507 print "</td> </tr>\n";
1508
1509 print "<tr> <td><b>$text{'hosts_2'}</b></td>\n";
1510 print "<td><input name=net size=50 value='$net'></td> </tr>\n";
1511
1512 print "<tr> <td valign=top><b>$text{'hosts_opts'}</b></td> <td>\n";
1513 &options_input("opts", $_[2], \@host_options);
1514 print "</td> </tr>\n";
1515 }
1516
1517 sub hosts_validate
1518 {
1519 &check_ipaddress($in{'net'}) ||
1520         $in{'net'} =~ /^(\S+)\/(\d+)$/ && &check_ipaddress($1) ||
1521         &error($text{'hosts_enet'});
1522 return ( $in{'zone'}, $in{'iface'}.":".$in{'net'},
1523          join(",", split(/\0/, $in{'opts'})) );
1524 }
1525
1526 ################################ blacklist ##################################
1527
1528 sub blacklist_row
1529 {
1530 return ( $_[0] eq '-' ? $text{'blacklist_any'} : $_[0],
1531          uc($_[1]) || $text{'blacklist_any'},
1532          $_[2] || $text{'blacklist_any'} );
1533 }
1534
1535 @blacklist_protos = ( undef, 'tcp', 'udp', 'icmp' );
1536
1537 sub blacklist_form
1538 {
1539 print "<tr> <td valign=top><b>$text{'blacklist_host'}</b></td> <td colspan=3>\n";
1540 local ($mode, $ipset, $mac, $ip);
1541 if ($_[0] =~ /^\+(.*)/) {
1542         $mode = 2; $ipset = $1;
1543         }
1544 elsif ($_[0] =~ /^\~(.*)$/) {
1545         $mode = 1; $mac = $1;
1546         }
1547 elsif ($_[0] eq '-') {
1548         $mode = 3;
1549         }
1550 else {
1551         $mode = 0; $ip = $_[0];
1552         }
1553 print &ui_radio("host_def", $mode,
1554     [ [ 0, &text('hosts_ip', &ui_textbox("host", $ip, 30))."<br>" ],
1555       [ 1, &text('hosts_mac', &ui_textbox("mac", $mac, 30))."<br>" ],
1556       [ 3, $text{'hosts_any'}."<br>" ],
1557       &version_atleast(3) ?
1558        ( [ 2, &text('hosts_ipset', &ui_textbox("ipset", $ipset, 15)) ] ) : ( ),
1559             ]);
1560 print "</td> </tr>\n";
1561
1562 print "<tr> <td><b>$text{'blacklist_proto'}</b></td>\n";
1563 print "<td colspan=3><select name=proto>\n";
1564 $found = !$_[1];
1565 foreach $p (@blacklist_protos) {
1566         printf "<option value='%s' %s>%s\n",
1567                 $p, $p eq $_[1] ? "selected" : "",
1568                 $p eq '' ? "&lt;$text{'list_any'}&gt;" : uc($p);
1569         $found++ if ($p eq $_[1]);
1570         }
1571 printf "<option value='*' %s>%s\n",
1572         $found ? "" : "selected", $text{'list_other'};
1573 print "</select>\n";
1574 printf "<input name=pother size=5 value='%s'></td> </tr>\n",
1575         $found ? "" : $_[1];
1576
1577 print "<tr> <td><b>$text{'blacklist_ports'}</b></td>\n";
1578 print "<td colspan=3><input name=ports size=20 value='$_[2]'></td> </tr>\n";
1579 }
1580
1581 sub blacklist_validate
1582 {
1583 local $host;
1584 if ($in{'host_def'} == 0) {
1585         &check_ipaddress($in{'host'}) ||
1586                 $in{'host'} =~ /^(\S+)\/(\d+)$/ && &check_ipaddress($1) ||
1587                 &error($text{'blacklist_ehost'});
1588         $host = $in{'host'};
1589         }
1590 elsif ($in{'host_def'} == 1) {
1591         $in{'mac'} =~ s/:/-/g;
1592         $in{'mac'} =~ /^[0-9a-f]{2}(\-[0-9a-f]{2}){5}$/ ||
1593                 &error($text{'blacklist_emac'});
1594         $host = "~".$in{'mac'};
1595         }
1596 elsif ($in{'host_def'} == 2) {
1597         $in{'ipset'} =~ /^\S+$/ || &error($text{'blacklist_eipset'});
1598         $host = "+".$in{'ipset'};
1599         }
1600 elsif ($in{'host_def'} == 3) {
1601         $host = "-";
1602         }
1603 local $proto;
1604 if ($in{'proto'} eq '*') {
1605         $in{'pother'} =~ /^\d+$/ ||
1606             defined(getprotobyname($in{'pother'})) ||
1607                 &error($text{'blacklist_eproto'});
1608         $proto = lc($in{'pother'});
1609         }
1610 else {
1611         $proto = lc($in{'proto'});
1612         }
1613 if ($proto eq "tcp" || $proto eq "udp") {
1614         $in{'ports'} =~ /^\S+$/ || &error($text{'blacklist_eports'});
1615         }
1616 elsif ($in{'ports'}) {
1617         &error($text{'blacklist_eports2'});
1618         }
1619 return ( $host, $proto, $in{'ports'} );
1620 }
1621
1622 ################################ providers ##################################
1623
1624 sub providers_row
1625 {
1626 return ( $_[0], $_[1], $_[2], $_[4], $_[5] );
1627 }
1628
1629 @providers_opts = ( "track", "balance", "loose" );
1630
1631 sub providers_form
1632 {
1633 print "<tr> <td><b>$text{'providers_name'}</b></td>\n";
1634 print "<td><input name=name size=20 value='$_[0]'></td>\n";
1635
1636 print "<td><b>$text{'providers_number'}</b></td>\n";
1637 print "<td><input name=number size=4 value='$_[1]'></td> </tr>\n";
1638
1639 print "<tr> <td><b>$text{'providers_iface'}</b></td>\n";
1640 print "<td>";
1641 &iface_field("iface", $_[4]);
1642 print "</td>\n";
1643
1644 print "<td><b>$text{'providers_mark'}</b></td>\n";
1645 print "<td><input name=mark size=4 value='$_[2]'></td> </tr>\n";
1646
1647 print "<tr> <td><b>$text{'providers_gateway'}</b></td>\n";
1648 print "<td><input name=gateway size=15 value='$_[5]'></td>\n";
1649
1650 local $ddef = $_[3] eq "-" || $_[3] eq "" ? 0 : $_[3] eq "main" ? 1 : 2;
1651 print "<td><b>$text{'providers_dup'}</b></td>\n";
1652 print "<td>",&ui_radio("dup_def", $ddef,
1653                 [ [ 0, $text{'default'} ],
1654                   [ 1, $text{'providers_main'} ],
1655                   [ 2, &ui_textbox("dup", $ddef == 2 ? $_[3] : "", 5) ] ]),
1656       "</td> </tr>\n";
1657
1658 local %opts = map { $_, 1 } split(/,/, $_[6]);
1659 print "<tr> <td valign=top><b>$text{'providers_opts'}</b></td> <td>\n";
1660 foreach my $o (@providers_opts) {
1661         print &ui_checkbox("opts", $o, $text{'providers_'.$o}, $opts{$o})."<br>\n";
1662         delete($opts{$o});
1663         }
1664 foreach my $o (keys %opts) {
1665         print &ui_hidden("opts", $o),"\n";
1666         }
1667 print "</td>\n";
1668
1669 print "<td valign=top><b>$text{'providers_copy'}</b></td>\n";
1670 print "<td valign=top><input name=copy size=15 value='$_[7]'></td> </tr>\n";
1671 }
1672
1673 sub providers_validate
1674 {
1675 $in{'name'} =~ /^\S+$/ || &error($text{'providers_ename'});
1676 $in{'number'} =~ /^\d+$/ || &error($text{'providers_enumber'});
1677 $in{'mark'} =~ /^\d+$/ || &error($text{'providers_emark'});
1678 $in{'dup_def'} < 2 || $in{'dup'} =~ /^\S+$/ || &error($text{'providers_edup'});
1679 &check_ipaddress($in{'gateway'}) || &error($text{'providers_egateway'});
1680 return ( $in{'name'}, $in{'number'}, $in{'mark'},
1681          $in{'dup_def'} == 0 ? '-' : $in{'dup_def'} == 1 ? 'main' : $in{'dup'},
1682          $in{'iface'}, $in{'gateway'},
1683          join(",", split(/\0/, $in{'opts'})) || "-",
1684          $in{'copy'} || "-" );
1685 }
1686
1687 ################################ shorewall.conf ##################################
1688
1689 sub conf_form
1690 {
1691     local ($msg1, $msg2, $msg3, $field1, $field2, $field3, $dummy) = @_;
1692
1693     $field1 =~ s/"/&#34;/g;
1694     print "<tr><td><b>$msg1</b></td>\n";
1695     print "<td><input name=var size=50 value=\"$field1\"></td></tr>\n";
1696
1697     $field2 =~ s/"/&#34;/g;
1698     print "<tr><td><b>$msg2</b></td>\n";
1699     print "<td><input name=val size=50 value=\"$field2\"></td></tr>\n";
1700
1701     $field3 =~ s/"/&#34;/g;
1702     print "<tr><td><b>$msg3</b></td>\n";
1703     print "<td><input name=comment size=50 value=\"$field3\"></td></tr>\n";
1704
1705     print "</td></tr>\n";
1706 }
1707
1708 ################################ shorewall.conf ##################################
1709
1710 sub shorewall_conf_columns
1711 {
1712     return 3;
1713 }
1714
1715 sub shorewall_conf_form
1716 {
1717     &conf_form($text{'shorewall_conf_0'}, $text{'shorewall_conf_1'}, $text{'shorewall_conf_2'}, @_);
1718 }
1719
1720 sub shorewall_conf_validate
1721 {
1722     &error($text{'shorewall_conf_varname'}) unless $in{'var'} =~ /^\w+$/;
1723     local $comment = "";
1724     $comment = "\t# ".$in{'comment'} if (exists $in{'comment'} and $in{'comment'} ne "");
1725     return ($in{'var'}.'='.$in{'val'}.$comment);
1726 }
1727
1728 ################################ params ##################################
1729
1730 sub params_columns
1731 {
1732     return 3;
1733 }
1734
1735 sub params_form
1736 {
1737     &conf_form($text{'params_0'}, $text{'params_1'}, $text{'params_2'}, @_);
1738 }
1739
1740 sub params_validate
1741 {
1742     &error($text{'params_varname'}) unless $in{'var'} =~ /^\w+$/;
1743     local $comment = "";
1744     $comment = "\t# ".$in{'comment'} if (exists $in{'comment'} and $in{'comment'} ne "");
1745     return ($in{'var'}.'='.$in{'val'}.$comment);
1746 }
1747
1748
1749 #############################################################################
1750
1751 # can_access(file)
1752 sub can_access
1753 {
1754 if ($access{'files'} eq '*') {
1755         return 1;
1756         }
1757 else {
1758         local @acc = split(/\s+/, $access{'files'});
1759         return &indexof($_[0], @acc) >= 0;
1760         }
1761 }
1762
1763 # run_before_apply_command()
1764 # Runs the before-applying command, if any. If it failes, returns the error
1765 # message output
1766 sub run_before_apply_command
1767 {
1768 if ($config{'before_apply_cmd'}) {
1769         local $out = &backquote_logged("($config{'before_apply_cmd'}) </dev/null 2>&1");
1770         return $out if ($?);
1771         }
1772 return undef;
1773 }
1774
1775 # run_after_apply_command()
1776 # Runs the after-applying command, if any
1777 sub run_after_apply_command
1778 {
1779 if ($config{'after_apply_cmd'}) {
1780         &system_logged("($config{'after_apply_cmd'}) </dev/null >/dev/null 2>&1");
1781         }
1782 }
1783
1784 # list_standard_actions()
1785 # Returns a list of standard Shorewall actions
1786 sub list_standard_actions
1787 {
1788 local @rv;
1789 foreach my $a (split(/\t+/, $config{'actions'})) {
1790         open(ACTIONS, $a);
1791         while(<ACTIONS>) {
1792                 s/\r|\n//g;
1793                 s/#.*$//;
1794                 s/\s+$//;
1795                 if (/\S/) {
1796                         push(@rv, $_);
1797                         }
1798                 }
1799         close(ACTIONS);
1800         }
1801 if (&version_atleast(3)) {
1802         # Add built-in actions
1803         push(@rv, "allowBcast", "dropBcast", "dropNotSyn", "rejNotSyn",
1804                   "dropInvalid", "allowInvalid", "allowoutUPnP", "allowinUPnP",
1805                   "forwardUPnP");
1806         }
1807 return &unique(@rv);
1808 }
1809
1810 # list_standard_macros()
1811 # Returns a list of all macro. actions
1812 sub list_standard_macros
1813 {
1814 local @rv;
1815 foreach my $a ($config{'config_dir'}, $config{'macros'}) {
1816         opendir(DIR, $a);
1817         foreach my $f (readdir(DIR)) {
1818                 push(@rv, $1) if ($f =~ /^macro\.(.*)$/);
1819                 }
1820         closedir(DIR);
1821         }
1822 return &unique(@rv);
1823 }
1824
1825 $BETA_STR = "-Beta";
1826 $BETA_NUM = "\.0000\.";
1827
1828 # get_shorewall_version(nocache)
1829 sub get_shorewall_version
1830 {
1831 local ($nocache) = @_;
1832 local $version;
1833 if (!$nocache && open(VERSION, "$module_config_directory/version")) {
1834         chop($version = <VERSION>);
1835         close(VERSION);
1836         }
1837 if (!$version) {
1838         local $out = `$config{'shorewall'} version 2>&1`;
1839         $out =~ s/\r//g;
1840         $out =~ s/$BETA_STR/$BETA_NUM/i;                # Convert beta string to version number.
1841         if ($out =~ /(\n|^)([0-9\.]+)\n/) {
1842                 $version = $2;
1843                 }
1844         }
1845 return $version;
1846 }
1847
1848 sub get_printable_version($)
1849 {
1850         local $out = $_[0];
1851         $out =~ s/$BETA_NUM/$BETA_STR/i;                # Convert version number back to string.
1852         return $out;
1853 }
1854
1855 sub list_protocols
1856 {
1857 local @stdprotos = ( 'tcp', 'udp', 'icmp' );
1858 local @otherprotos;
1859 open(PROTOS, "/etc/protocols");
1860 while(<PROTOS>) {
1861         s/\r|\n//g;
1862         s/#.*$//;
1863         push(@otherprotos, $1) if (/^(\S+)\s+(\d+)/);
1864         }
1865 close(PROTOS);
1866 @otherprotos = sort { lc($a) cmp lc($b) } @otherprotos;
1867 return &unique(@stdprotos, @otherprotos);
1868 }
1869
1870 # options_input(name, value, &opts)
1871 sub options_input
1872 {
1873 local ($name, $value, $opts) = @_;
1874 local %opts = map { $_, 1 } split(/,/, $value);
1875 print "<table width=100%>\n";
1876 local $i = 0;
1877 foreach my $o (@$opts) {
1878         print "<tr>\n" if ($i%3 == 0);
1879         printf "<td><input type=checkbox name=$name value=%s %s> %s</td>\n",
1880                 $o, $opts{$o} ? "checked" : "", $text{'opts_'.$o} || $o;
1881         print "</tr>\n" if ($i%3 == 2);
1882         delete($opts{$o});
1883         $i++;
1884         }
1885 foreach $o (keys %opts) {
1886         print "<input type=hidden name=opts value=$o>\n";
1887         }
1888 print "</table>\n";
1889 }
1890
1891 1;
1892