diff --git a/apache/apache-lib.pl b/apache/apache-lib.pl index 2dc9fdabc..a192a6141 100755 --- a/apache/apache-lib.pl +++ b/apache/apache-lib.pl @@ -779,10 +779,9 @@ foreach my $dir (@$dirs) { $dir->{'line'} = $line; $dir->{'file'} = $file; if ($dir->{'type'}) { - # Do sub-members too - &recursive_set_lines_files($dir->{'members'}, $line+1, $file); - $line += scalar(grep { $_->{'name'} ne 'dummy' } - @{$dir->{'members'}})+1; + # Continue after every line used by nested members + $line = &recursive_set_lines_files($dir->{'members'}, + $line+1, $file); } $dir->{'eline'} = $line; $line++ if ($dir->{'name'} ne 'dummy'); diff --git a/apache/t/directive-lines.t b/apache/t/directive-lines.t new file mode 100644 index 000000000..3b9277b22 --- /dev/null +++ b/apache/t/directive-lines.t @@ -0,0 +1,91 @@ +#!/usr/bin/perl +# Apache directive line numbers must match their serialized positions. + +use strict; +use warnings; +use Test::More; +use File::Basename qw(dirname); +use File::Path qw(make_path); +use File::Spec; +use File::Temp qw(tempdir); +use Cwd qw(abs_path); + +my $root = abs_path(File::Spec->catdir(dirname(__FILE__), '..', '..')); +my $tmp = abs_path(tempdir(CLEANUP => 1)); +my $webmin_config = File::Spec->catdir($tmp, 'webmin-config'); +my $webmin_var = File::Spec->catdir($tmp, 'webmin-var'); +my $apache_root = File::Spec->catdir($tmp, 'apache2'); +my $apache_conf = File::Spec->catfile($apache_root, 'apache2.conf'); + +make_path($webmin_config, $webmin_var, "$webmin_config/apache", + "$webmin_var/apache", $apache_root); + +sub write_text +{ +my ($file, $text) = @_; +open(my $fh, '>', $file) || die "Failed to write $file: $!"; +print $fh $text; +close($fh) || die "Failed to close $file: $!"; +} + +# Load the Apache module with an isolated Webmin configuration. +write_text(File::Spec->catfile($webmin_config, 'config'), + "os_type=debian-linux\n". + "os_version=12\n"); +write_text(File::Spec->catfile($webmin_config, 'miniserv.conf'), + "root=$root\n"); +write_text(File::Spec->catfile($webmin_config, 'apache', 'config'), + "httpd_dir=$apache_root\n". + "httpd_path=/bin/true\n". + "httpd_conf=$apache_conf\n". + "apachectl_path=/bin/true\n". + "httpd_version=2.4.57\n"); +write_text($apache_conf, "Listen 80\n"); + +$ENV{'WEBMIN_CONFIG'} = $webmin_config; +$ENV{'WEBMIN_VAR'} = $webmin_var; +$ENV{'FOREIGN_MODULE_NAME'} = 'apache'; +$ENV{'FOREIGN_ROOT_DIRECTORY'} = $root; +$ENV{'REMOTE_USER'} = 'root'; + +unshift(@INC, $root); +require File::Spec->catfile($root, 'apache', 'apache-lib.pl'); + +# Model a template containing inside , followed by a +# directive whose position must include every line in both nested blocks. +my $inner_require = { + 'name' => 'Require', 'value' => 'all denied', 'indent' => 8, + }; +my $files = { + 'name' => 'Files', 'value' => '*.php', 'type' => 1, 'indent' => 4, + 'members' => [ + { 'name' => 'dummy', 'type' => 0 }, + $inner_require, + ], + }; +my $directory = { + 'name' => 'Directory', 'value' => '/srv/example', 'type' => 1, + 'indent' => 0, + 'members' => [ + { 'name' => 'dummy', 'type' => 0 }, + { 'name' => 'Require', 'value' => 'all granted', 'indent' => 4 }, + $files, + ], + }; +my $alias = { + 'name' => 'ScriptAlias', 'value' => '/cgi-bin/ /srv/cgi-bin/', + 'indent' => 0, + }; +my @directives = ($directory, $alias); +my @lines = main::directive_lines(@directives); +my $next = main::recursive_set_lines_files(\@directives, 10, '/tmp/test.conf'); + +is($directory->{'line'}, 10, 'outer block starts at the first line'); +is($files->{'line'}, 12, 'nested block starts after the outer directive'); +is($inner_require->{'line'}, 13, 'nested member has its serialized line'); +is($files->{'eline'}, 14, 'nested block ends after all of its members'); +is($directory->{'eline'}, 15, 'outer block includes the nested closing line'); +is($alias->{'line'}, 16, 'following directive starts after the outer block'); +is($next, 10 + scalar(@lines), 'returned line follows serialized output'); + +done_testing(); diff --git a/bind8/save_record.cgi b/bind8/save_record.cgi index 406eb71a6..240fcea4a 100755 --- a/bind8/save_record.cgi +++ b/bind8/save_record.cgi @@ -394,9 +394,14 @@ else { my $dmarc = $r ? &parse_dmarc(@{$r->{'values'}}) : { }; $dmarc->{'p'} = $in{'dmarcp'}; - $in{'dmarcpct'} =~ /^\d+$/ && $in{'dmarcpct'} >= 0 && - $in{'dmarcpct'} <= 100 || &error($text{'edit_edmarcpct'}); - $dmarc->{'pct'} = $in{'dmarcpct'}; + if ($in{'dmarcpct'} ne '') { + $in{'dmarcpct'} =~ /^\d+$/ && $in{'dmarcpct'} >= 0 && + $in{'dmarcpct'} <= 100 || &error($text{'edit_edmarcpct'}); + $dmarc->{'pct'} = $in{'dmarcpct'}; + } + else { + delete($dmarc->{'pct'}); + } if ($in{'dmarcsp'}) { $dmarc->{'sp'} = $in{'dmarcsp'};