diff options
-rw-r--r-- | lib/Apache/Config/Preproc.pm | 51 | ||||
-rw-r--r-- | lib/Apache/Config/Preproc/ifdefine.pm | 137 | ||||
-rw-r--r-- | t/05ifdefine00.t | 46 | ||||
-rw-r--r-- | t/05ifdefine01.t | 28 | ||||
-rw-r--r-- | t/05ifdefine02.t | 28 | ||||
-rw-r--r-- | t/99all00.t | 9 |
6 files changed, 282 insertions, 17 deletions
diff --git a/lib/Apache/Config/Preproc.pm b/lib/Apache/Config/Preproc.pm index 76afac7..18f0be0 100644 --- a/lib/Apache/Config/Preproc.pm +++ b/lib/Apache/Config/Preproc.pm @@ -98,7 +98,7 @@ Apache::Config::Preproc - Preprocess Apache configuration files use Apache::Config::Preproc; $x = new Apache::Config::Preproc '/path/to/httpd.conf', - -expand => [ qw(include compact macro ifmodule) ] + -expand => [ qw(include compact macro ifmodule ifdefine) ] or die $Apache::Admin::Config::ERROR; =head1 DESCRIPTION @@ -108,7 +108,7 @@ file, expanding the syntactic constructs selected by the B<-expand> option. In the simplest case, the argument to that option is a reference to the list of names. Each name in the list identifies a module responsible for processing specific Apache configuration keywords. For convenience, most -modules are named after the keyword they process, so that B<include> is +modules are named after the keyword they process, so that, e.g. B<include> is responsible for inclusion of the files listed with B<Include> and B<IncludeOptional> statements. The list of built-in module names follows: @@ -127,25 +127,29 @@ with the content of the corresponding files. Expands the B<E<lt>IfModuleE<gt>> statements. +=item B<ifdefine> + +Expands the B<E<lt>IfDefineE<gt>> statements. + =item B<macro> Expands the B<E<lt>MacroE<gt>> statements. =back -See the section B<PREPROCESSING> for a detailed description of these modules. +See the section B<MODULES> for a detailed description of these modules. New modules can be easily implemented by supplying a corresponding -expansion module (see the section B<MODULES> below). +expansion module (see the section B<MODULE INTERNALS> below). If the B<-expand> argument is not supplied, the following default is used: [ 'include' ] -Rest of module functionality is inherited from B<Apache::Admin::Config>. +The rest of methods is inherited from B<Apache::Admin::Config>. -=head1 METHODS +=head1 CONSTRUCTOR =head2 new @@ -161,11 +165,11 @@ keywords are: =item B<-expand> =E<gt> I<$arrayref> -Define what expansions are to be performed. Argument is a reference to -array of module names with optional parameters. To specify parameters, -use either list reference where the first element is the module +Define what expansions are to be performed. B<$arrayref> is a reference to +array of module names with optional arguments. To supply arguments, +use either a list reference where the first element is the module name and rest of elements are arguments, or a hash reference with the name -of the module as a key and a reference to the list of arguments as its value. +of the module as key and a reference to the list of arguments as its value. Consider, for example: -expand => [ 'include', { ifmodule => { probe => 1 } } ] @@ -226,6 +230,10 @@ Same as comment grouping but for blank lines. =back +=head1 METHODS + +All methods are inherited from B<Apache::Admin::Config>. + =head1 MODULES The preprocessing phases to be performed on the parsed configuration text are @@ -291,6 +299,17 @@ The option is a shorthand for probe => [qw(/usr/sbin/httpd /usr/sbin/apache2)] + +=back + +=head2 ifdefine + +Eliminates the B<Define> and B<UnDefine> statements and expands the +B<E<lt>IfDefineE<gt>> statements in the Apache configuration parse +tree. Optional arguments to the constructor are treated as the names +of symbols to define (similar to the B<httpd> B<-D> options). Example: + + -expand => [ { ifdefine => [ qw(SSL FOREGROUND) ] } ] =head2 macro @@ -335,12 +354,12 @@ expansion is to be stored. =back -The function returns true if if did process the I<$subtree>. In this case, +The function returns true if it did process the I<$subtree>. In this case, the subtree will be removed from the parse tree and the items from B<@$repl> -will be inserted instead. Thus, to simply remove the I<$subtree> the B<expand> -method must return true and not touch I<$repl>. For example, the following -is the B<expand> method definition from the B<Apache::Config::Preproc::compact> -module: +will be inserted in its place. Thus, to simply remove the I<$subtree> the +B<expand> method must return true and not touch I<$repl>. For example, the +following is the B<expand> method definition from the +B<Apache::Config::Preproc::compact> module: sub expand { my ($self, $d, $repl) = @_; @@ -348,7 +367,7 @@ module: } Notice, that B<expand> does not need to recurse into section objects. This -will be done by the caller. +is taken care of by the caller. =head1 EXAMPLE diff --git a/lib/Apache/Config/Preproc/ifdefine.pm b/lib/Apache/Config/Preproc/ifdefine.pm new file mode 100644 index 0000000..834d602 --- /dev/null +++ b/lib/Apache/Config/Preproc/ifdefine.pm @@ -0,0 +1,137 @@ +package Apache::Config::Preproc::ifdefine; +use strict; +use warnings; +use Carp; + +sub new { + my $class = shift; + my $conf = shift; + my $self = bless {}, $class; + @{$self->{D}}{@_} = (1) x @_; + return $self; +} + +sub find_define { + my ($self,$elt,$id) = @_; + + while (my $p = $elt->parent) { + if (grep { + (split /\s+/, $_->value)[0] eq $id + } $p->directive('define')) { + return 1; + } + } + return; +} + +sub _changed_in_section { + my ($self, $section, $id, $before) = @_; + foreach my $d (reverse $section->select) { + if ($before) { + if ($d == $before) { + $before = undef; + } + next; + } + + if ($d->type eq 'directive') { + if ($d->name =~ m/^(un)?define$/i && $d->value eq $id) { + if ($1) { + $self->undefine($id); + } else { + $self->define($id); + } + return 1; + } + } elsif ($d->type eq 'section' && lc($d->name) eq 'virtualhost') { + # Need to descend into VirtualHost, because accorind to the + # Apache docs + # see (https://httpd.apache.org/docs/2.4/mod/core.html#define): + # + # While [the Define] directive is supported in virtual host + # context, the changes it makes are visible to any later + # configuration directives, beyond any enclosing virtual host. + # + # The same is said about the UnDefine directive. + + return 1 if $self->_changed_in_section($d, $id); + } + } + return 0; +} + +sub is_defined { + my ($self,$id,$d) = @_; + + unless ($self->{D}{$id}) { + my $before = $d; + while ($d = $d->parent) { + last if $self->_changed_in_section($d, $id, $before); + $before = undef; + } + } + return $self->{D}{$id}; +} + +sub define { + my ($self,$id) = @_; + $self->{D}{$id} = 1; +} + +sub undefine { + my ($self,$id) = @_; + delete $self->{D}{$id}; +} + +sub expand { + my ($self, $d, $repl) = @_; + if ($d->type eq 'section') { + if (lc($d->name) eq 'ifdefine') { + my $id = $d->value; + my $negate = $id =~ s/^!//; + my $res = $self->is_defined($id, $d); + $res = !$res if $negate; + if ($res) { + push @$repl, $d->select; + } + return 1; + } + } + if ($d->type eq 'directive') { + my $name = lc($d->name); + if ($name eq 'define') { + $self->define($d->value); + return 1; + } elsif ($name eq 'undefine') { + $self->undefine($d->value); + return 1; + } + } + return 0; +} + +1; +__END__ + +=head1 NAME + +Apache::Config::Preproc::ifmodule - expand IfDefine sections + +=head1 SYNOPSIS + + $x = new Apache::Config::Preproc '/path/to/httpd.conf', + -expand => [ qw(ifdefine) ]; + + $x = new Apache::Config::Preproc '/path/to/httpd.conf', + -expand => [ + { ifdefine => [qw(SYM1 SYM2)] } + ]; + +=head1 DESCRIPTION + +Eliminates the B<Define> and B<UnDefine> statements and expands the +B<E<lt>IfDefineE<gt>> statements in the Apache configuration parse +tree. Optional arguments to the constructor are treated as the names +of symbols to define (similar to the B<httpd> B<-D> options). + + diff --git a/t/05ifdefine00.t b/t/05ifdefine00.t new file mode 100644 index 0000000..e488101 --- /dev/null +++ b/t/05ifdefine00.t @@ -0,0 +1,46 @@ +# -*- perl -*- +use lib qw(t lib); +use strict; +use Test; +plan test => 3; + +use TestPreproc; + +my $obj = new TestPreproc -expand => ['ifdefine']; +ok($obj->dump_raw, $obj->dump_expect); + +my $obj = new TestPreproc -expand => [ { ifdefine => [qw(VAR)] } ]; +ok($obj->dump_raw, $obj->dump_expect); + +my $obj = new TestPreproc -expand => ['ifdefine']; +ok($obj->dump_raw, $obj->dump_expect); + +__DATA__ +!>httpd.conf +ServerAdmin foo +<IfDefine VAR> + ServerName localhost +</IfDefine> +!= +ServerAdmin foo +!$ +__END__ +!>httpd.conf +ServerAdmin foo +<IfDefine VAR> + ServerName localhost +</IfDefine> +!= +ServerAdmin foo + ServerName localhost +!$ +__END__ +!>httpd.conf +ServerAdmin foo +<IfDefine !VAR> + ServerName localhost +</IfDefine> +!= +ServerAdmin foo + ServerName localhost +!$ diff --git a/t/05ifdefine01.t b/t/05ifdefine01.t new file mode 100644 index 0000000..da2e5f9 --- /dev/null +++ b/t/05ifdefine01.t @@ -0,0 +1,28 @@ +# -*- perl -*- +use lib qw(t lib); +use strict; +use Test; +plan test => 1; + +use TestPreproc; + +my $obj = new TestPreproc -expand => ['ifdefine']; +ok($obj->dump_raw, $obj->dump_expect); + +__DATA__ +!>httpd.conf +Define FOO +ServerName localhost +<IfDefine FOO> + ServerAlias remote +</IfDefine> +UnDefine FOO +DocumentRoot /var +<IfDefine FOO> + Require all granted +</IfDefine> +!= +ServerName localhost + ServerAlias remote +DocumentRoot /var +!$
\ No newline at end of file diff --git a/t/05ifdefine02.t b/t/05ifdefine02.t new file mode 100644 index 0000000..030ca7b --- /dev/null +++ b/t/05ifdefine02.t @@ -0,0 +1,28 @@ +# -*- perl -*- +use lib qw(t lib); +use strict; +use Test; +plan test => 1; + +use TestPreproc; + +my $obj = new TestPreproc -expand => ['ifdefine']; +ok($obj->dump_raw, $obj->dump_expect); + +__DATA__ +!>httpd.conf +<VirtualHost bar> + ServerName localhost + ServerAdmin root + Define FOO +</VirtualHost> +<IfDefine FOO> + Alias /foo /bar +</IfDefine> +!= +<VirtualHost bar> + ServerName localhost + ServerAdmin root +</VirtualHost> + Alias /foo /bar +!$ diff --git a/t/99all00.t b/t/99all00.t index a70dd6a..cf79c27 100644 --- a/t/99all00.t +++ b/t/99all00.t @@ -6,7 +6,7 @@ plan test => 1; use TestPreproc; -my $obj = new TestPreproc -expand => [qw(compact include ifmodule macro)]; +my $obj = new TestPreproc -expand => [qw(compact include ifmodule macro ifdefine)]; ok($obj->dump_raw, $obj->dump_expect); __DATA__ !>httpd.conf @@ -19,6 +19,10 @@ Include conf.d/*.conf Include mpm.conf Include log.conf Include vhost.conf +Include def.conf +<IfDefine FOO> + Listen 8080 +</IfDefine> Timeout 300 !>conf.d/load.conf # Load prefork mpm @@ -39,6 +43,8 @@ LoadModule logio_module lib/httpd/modules/mod_logio.so </VirtualHost> </Macro> +!>def.conf +Define FOO !>mpm.conf <IfModule !mpm_netware_module> PidFile "/var/run/httpd.pid" @@ -124,5 +130,6 @@ LoadModule logio_module lib/httpd/modules/mod_logio.so Require all granted </Directory> </VirtualHost> + Listen 8080 Timeout 300 !$ |