mirror of
https://github.com/webmin/webmin.git
synced 2026-05-06 15:20:29 +01:00
1809 lines
50 KiB
Perl
1809 lines
50 KiB
Perl
# Common functions for Nginx config file
|
|
|
|
use strict;
|
|
use warnings;
|
|
no warnings 'recursion';
|
|
use Socket;
|
|
|
|
BEGIN { push(@INC, ".."); };
|
|
eval "use WebminCore;";
|
|
&init_config();
|
|
our %access = &get_module_acl();
|
|
our ($get_config_cache, $get_config_parent_cache, %list_directives_cache,
|
|
@list_modules_cache, @open_config_files);
|
|
our (%config, %text, %in, $module_root_directory);
|
|
&set_nginx_config_defaults();
|
|
|
|
my @lock_all_config_files_cache;
|
|
|
|
# set_nginx_config_defaults()
|
|
# Fill in sensible defaults if module config has not been initialized yet
|
|
sub set_nginx_config_defaults
|
|
{
|
|
my $conf = &detect_nginx_config_file();
|
|
if ($conf && (!$config{'nginx_config'} || !-r $config{'nginx_config'})) {
|
|
$config{'nginx_config'} = $conf;
|
|
}
|
|
|
|
my $cmd = &detect_nginx_command();
|
|
if ($cmd && (!$config{'nginx_cmd'} || !&has_command($config{'nginx_cmd'}))) {
|
|
$config{'nginx_cmd'} = $cmd;
|
|
}
|
|
|
|
if (!$config{'start_cmd'} || !$config{'stop_cmd'} || !$config{'apply_cmd'}) {
|
|
if (&has_command("systemctl")) {
|
|
$config{'start_cmd'} ||= "systemctl start nginx";
|
|
$config{'stop_cmd'} ||= "systemctl stop nginx";
|
|
$config{'apply_cmd'} ||= "systemctl reload nginx";
|
|
}
|
|
elsif (&has_command("service")) {
|
|
$config{'start_cmd'} ||= "service nginx start";
|
|
$config{'stop_cmd'} ||= "service nginx stop";
|
|
$config{'apply_cmd'} ||= "service nginx reload";
|
|
}
|
|
elsif ($config{'nginx_cmd'}) {
|
|
$config{'start_cmd'} ||= $config{'nginx_cmd'};
|
|
$config{'stop_cmd'} ||= $config{'nginx_cmd'}." -s stop";
|
|
$config{'apply_cmd'} ||= $config{'nginx_cmd'}." -s reload";
|
|
}
|
|
}
|
|
}
|
|
|
|
# detect_nginx_config_file()
|
|
# Returns the first readable standard Nginx config file
|
|
sub detect_nginx_config_file
|
|
{
|
|
foreach my $file ($config{'nginx_config'},
|
|
"/etc/nginx/nginx.conf",
|
|
"/usr/local/nginx/conf/nginx.conf",
|
|
"/opt/nginx/conf/nginx.conf") {
|
|
return $file if ($file && -r $file);
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
# detect_nginx_command()
|
|
# Returns the first executable Nginx command in a standard path or PATH
|
|
sub detect_nginx_command
|
|
{
|
|
return $config{'nginx_cmd'}
|
|
if ($config{'nginx_cmd'} && &has_command($config{'nginx_cmd'}));
|
|
foreach my $cmd ("nginx", "/usr/sbin/nginx", "/usr/local/nginx/sbin/nginx",
|
|
"/opt/nginx/sbin/nginx") {
|
|
my $found = &has_command($cmd);
|
|
return $found if ($found);
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
# get_config()
|
|
# Parses the Nginx config file into an array ref
|
|
sub get_config
|
|
{
|
|
if (!$get_config_cache) {
|
|
$get_config_cache = &read_config_file($config{'nginx_config'});
|
|
}
|
|
return $get_config_cache;
|
|
}
|
|
|
|
# get_config_parent()
|
|
# Returns an object that represents the whole config file
|
|
sub get_config_parent
|
|
{
|
|
if (!$get_config_parent_cache) {
|
|
$get_config_parent_cache = { 'members' => &get_config(),
|
|
'type' => 1,
|
|
'file' => $config{'nginx_config'},
|
|
'indent' => -1,
|
|
'line' => 0,
|
|
'eline' => 0 };
|
|
foreach my $c (@{$get_config_parent_cache->{'members'}}) {
|
|
if ($c->{'file'} eq $get_config_parent_cache->{'file'} &&
|
|
$c->{'eline'} > $get_config_parent_cache->{'eline'}) {
|
|
$get_config_parent_cache->{'eline'} = $c->{'eline'}+1;
|
|
}
|
|
}
|
|
}
|
|
return $get_config_parent_cache;
|
|
}
|
|
|
|
# flush_config_cache()
|
|
# Delete all in-memory config caches
|
|
sub flush_config_cache
|
|
{
|
|
undef($get_config_parent_cache);
|
|
undef($get_config_cache);
|
|
}
|
|
|
|
# remove_hash_comment(line)
|
|
# Returns the line with comments removed
|
|
sub remove_hash_comment
|
|
{
|
|
my ($l) = @_;
|
|
if ($l =~ /".*#.*"/) {
|
|
# Comment inside quotes, so only remove any comment outside quotes
|
|
$l =~ s/#[^"]*$//;
|
|
}
|
|
else {
|
|
# Remove all comments
|
|
$l =~ s/#.*$//;
|
|
}
|
|
return $l;
|
|
}
|
|
|
|
# read_config_file(file, [preserve-includes])
|
|
# Returns an array ref of nginx config objects
|
|
sub read_config_file
|
|
{
|
|
my ($file, $noinc) = @_;
|
|
my $link = &resolve_links($file);
|
|
$link || &error("Dangling link $file");
|
|
$file = $link;
|
|
my @rv = ( );
|
|
my $addto = \@rv;
|
|
my @stack = ( );
|
|
my $lnum = 0;
|
|
my $fh = "CFILE".int(rand(1000000));
|
|
&open_readfile($fh, $file) || return [];
|
|
my @lines = <$fh>;
|
|
close($fh);
|
|
while(@lines) {
|
|
my $l = shift(@lines);
|
|
$l = &remove_hash_comment($l);
|
|
my ($indent_str) = $l =~ /^(\s*)/;
|
|
my $slnum = $lnum;
|
|
|
|
# If line doesn't end with { } or ; , it must be continued on the
|
|
# next line
|
|
while($l =~ /\S/ && $l !~ /[\{\}\;]\s*$/ && @lines) {
|
|
my $nl = shift(@lines);
|
|
if ($nl =~ /\S/) {
|
|
$nl = &remove_hash_comment($nl);
|
|
$l .= " ".$nl;
|
|
}
|
|
$lnum++;
|
|
}
|
|
|
|
if ($l =~ /^\s*if\s*\((.*)\)\s*\{\s*$/) {
|
|
# Start of an if statement
|
|
my $ns = { 'name' => 'if',
|
|
'type' => 2,
|
|
'indent' => scalar(@stack),
|
|
'indent_str' => $indent_str,
|
|
'file' => $file,
|
|
'line' => $slnum,
|
|
'eline' => $lnum,
|
|
'members' => [ ] };
|
|
my $value = $1;
|
|
&set_split_words($ns, " ".$value);
|
|
push(@stack, $addto);
|
|
push(@$addto, $ns);
|
|
$addto = $ns->{'members'};
|
|
}
|
|
elsif ($l =~ /^\s*(\S+)(\s*.*)\{\s*$/) {
|
|
# Start of a section
|
|
my $ns = { 'name' => $1,
|
|
'type' => 1,
|
|
'indent' => scalar(@stack),
|
|
'indent_str' => $indent_str,
|
|
'file' => $file,
|
|
'line' => $slnum,
|
|
'eline' => $lnum,
|
|
'members' => [ ] };
|
|
my $value = $2;
|
|
&set_split_words($ns, $value);
|
|
push(@stack, $addto);
|
|
push(@$addto, $ns);
|
|
$addto = $ns->{'members'};
|
|
}
|
|
elsif ($l =~ /^\s*}/ && @stack) {
|
|
# End of a section
|
|
$addto = pop(@stack);
|
|
$addto->[@$addto-1]->{'eline'} = $lnum;
|
|
}
|
|
elsif ($l =~ /^\s*(\S+)((\s+("([^"]*)"|'([^']*)'|[^ ;]+))*)\s*;/) {
|
|
# Found a directive
|
|
my ($name, $value) = ($1, $2);
|
|
my @words = &split_words($value);
|
|
if ($name eq "include" && !$noinc) {
|
|
# Include a file or glob
|
|
if ($words[0] !~ /^\//) {
|
|
my $filedir = $file;
|
|
$filedir =~ s/\/[^\/]+$//;
|
|
$words[0] = $filedir."/".$value;
|
|
}
|
|
foreach my $ifile (glob($words[0])) {
|
|
my $inc = &read_config_file($ifile);
|
|
push(@$addto, @$inc);
|
|
}
|
|
}
|
|
else {
|
|
# Some directive in the current section
|
|
my ($sep_str) = $l =~ /^\s*\S+(\s+)/;
|
|
my $dir = { 'name' => $name,
|
|
'value' => $words[0],
|
|
'words' => \@words,
|
|
'type' => 0,
|
|
'indent' => scalar(@stack),
|
|
'indent_str' => $indent_str,
|
|
'sep_str' => $sep_str,
|
|
'file' => $file,
|
|
'line' => $slnum,
|
|
'eline' => $lnum };
|
|
push(@$addto, $dir);
|
|
if (@stack) {
|
|
my $lastaddto = $stack[$#stack];
|
|
$lastaddto->[@$lastaddto - 1]->{'eline'} = $lnum;
|
|
}
|
|
}
|
|
}
|
|
elsif ($l =~ /\S/) {
|
|
$l =~ s/\r|\n//g;
|
|
print STDERR "Invalid Nginx config line $l at $lnum in $file\n";
|
|
}
|
|
$lnum++;
|
|
}
|
|
return \@rv;
|
|
}
|
|
|
|
# extra_dirs_to_directives(directives)
|
|
# Parses tab-separated extra directives from module config into Nginx objects
|
|
sub extra_dirs_to_directives
|
|
{
|
|
my ($extra_dirs) = @_;
|
|
return ( ) if (!$extra_dirs || $extra_dirs eq "none");
|
|
|
|
my $temp = &transname();
|
|
my $fh = "EXTRA";
|
|
&open_tempfile($fh, ">$temp", 0, 1);
|
|
&print_tempfile($fh, join("\n", split(/\t+/, $extra_dirs))."\n");
|
|
&close_tempfile($fh);
|
|
my $econf = &read_config_file($temp, 1);
|
|
&clear_directive_lines(@$econf);
|
|
&unlink_file($temp);
|
|
return @$econf;
|
|
}
|
|
|
|
# clear_directive_lines(&directive, ...)
|
|
# Removes file and line metadata from parsed directives
|
|
sub clear_directive_lines
|
|
{
|
|
foreach my $e (@_) {
|
|
delete($e->{'file'});
|
|
delete($e->{'line'});
|
|
delete($e->{'eline'});
|
|
if ($e->{'type'}) {
|
|
&clear_directive_lines(@{$e->{'members'}});
|
|
}
|
|
}
|
|
}
|
|
|
|
# split_words(string)
|
|
# Convert a string of bare or quoted words into a list
|
|
sub split_words
|
|
{
|
|
my ($value) = @_;
|
|
my @words;
|
|
while($value =~ s/^\s+"([^"]+)"// ||
|
|
$value =~ s/^\s+'([^']+)'// ||
|
|
$value =~ s/^\s+(\S+)//) {
|
|
push(@words, $1);
|
|
}
|
|
return @words;
|
|
}
|
|
|
|
# set_split_words(&directive, string)
|
|
# Set the words and value fields based on a string
|
|
sub set_split_words
|
|
{
|
|
my ($ns, $value) = @_;
|
|
my @s = &split_words($value);
|
|
$ns->{'words'} = \@s;
|
|
$ns->{'value'} = @s ? $s[0] : undef;
|
|
}
|
|
|
|
# get_add_to_file(name)
|
|
# Returns the file to add new servers to, if any
|
|
sub get_add_to_file
|
|
{
|
|
my ($name) = @_;
|
|
if (!$config{'add_to'}) {
|
|
return undef;
|
|
}
|
|
elsif (-d $config{'add_to'}) {
|
|
$name =~ s/[^a-zA-Z0-9\.\_\-]//g;
|
|
if ($name) {
|
|
return $config{'add_to'}."/".$name.".conf";
|
|
}
|
|
}
|
|
else {
|
|
return $config{'add_to'};
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
# find(name, [&config|&parent])
|
|
# Returns the object or objects with some name in the given config
|
|
sub find
|
|
{
|
|
my ($name, $conf) = @_;
|
|
$conf ||= &get_config();
|
|
if (ref($conf) eq 'HASH') {
|
|
$conf = $conf->{'members'};
|
|
}
|
|
my @rv;
|
|
foreach my $c (@$conf) {
|
|
if (lc($c->{'name'}) eq $name) {
|
|
push(@rv, $c);
|
|
}
|
|
}
|
|
return wantarray ? @rv : $rv[0];
|
|
}
|
|
|
|
# find_value(name, [config])
|
|
# Returns the value of the object or objects with some name in the given config
|
|
sub find_value
|
|
{
|
|
my ($name, $conf) = @_;
|
|
my @rv = map { my @w = @{$_->{'words'}};
|
|
(@w ? $w[0] : undef) || $_->{'value'} } &find($name, $conf);
|
|
return wantarray ? @rv : $rv[0];
|
|
}
|
|
|
|
# find_recursive(name, [&config|&parent])
|
|
# Returns all objects under some parent with the given name
|
|
sub find_recursive
|
|
{
|
|
my ($name, $conf) = @_;
|
|
$conf ||= &get_config();
|
|
if (ref($conf) eq 'HASH') {
|
|
$conf = $conf->{'members'};
|
|
}
|
|
my @rv;
|
|
foreach my $c (@$conf) {
|
|
if (lc($c->{'name'}) eq $name) {
|
|
push(@rv, $c);
|
|
}
|
|
if ($c->{'type'}) {
|
|
push(@rv, &find_recursive($name, $c));
|
|
}
|
|
}
|
|
return wantarray ? @rv : $rv[0];
|
|
}
|
|
|
|
# save_directive(&parent, name|&oldobjects, &newvalues|&newobjects, [&before])
|
|
# Updates the values of some named directive
|
|
sub save_directive
|
|
{
|
|
my ($parent, $name_or_oldstructs, $values, $before) = @_;
|
|
$values = [ $values ] if (!ref($values));
|
|
my $oldstructs = ref($name_or_oldstructs) ? $name_or_oldstructs :
|
|
[ &find($name_or_oldstructs, $parent) ];
|
|
my $name = !ref($name_or_oldstructs) ? $name_or_oldstructs :
|
|
@$name_or_oldstructs ? $name_or_oldstructs->[0]->{'name'} : undef;
|
|
my $newstructs = [ map { &value_to_struct($name, $_) } @$values ];
|
|
for(my $i=0; $i<@$newstructs || $i<@$oldstructs; $i++) {
|
|
my $o = $i<@$oldstructs ? $oldstructs->[$i] : undef;
|
|
my $n = $i<@$newstructs ? $newstructs->[$i] : undef;
|
|
my $file = $o ? $o->{'file'} :
|
|
$n && $n->{'file'} ? $n->{'file'} : $parent->{'file'};
|
|
my $lref = &read_file_lines($file);
|
|
push(@open_config_files, $file);
|
|
if ($i<@$newstructs && $i<@$oldstructs) {
|
|
# Updating some directive
|
|
my $olen = $o->{'eline'} - $o->{'line'} + 1;
|
|
my $oldline = $lref->[$o->{'line'}];
|
|
my $indent = &directive_indent($o, $parent, $lref);
|
|
$n->{'indent_str'} = $indent
|
|
if (!defined($n->{'indent_str'}));
|
|
$n->{'indent'} = $o->{'indent'}
|
|
if (defined($o->{'indent'}));
|
|
$n->{'sep_str'} = &directive_value_separator($n, $oldline)
|
|
if (!defined($n->{'sep_str'}));
|
|
my @lines = &make_directive_lines($n, $indent, $parent, $lref);
|
|
$o->{'name'} = $n->{'name'};
|
|
$o->{'value'} = $n->{'words'}->[0];
|
|
$o->{'words'} = $n->{'words'};
|
|
$o->{'indent'} = $n->{'indent'} if (defined($n->{'indent'}));
|
|
$o->{'indent_str'} = $n->{'indent_str'};
|
|
$o->{'sep_str'} = $n->{'sep_str'};
|
|
splice(@$lref, $o->{'line'}, $olen, @lines);
|
|
if ($olen != scalar(@lines)) {
|
|
# Renumber directives
|
|
&renumber($file, $o->{'line'}, $olen - scalar(@lines));
|
|
$o->{'eline'} = $o->{'line'} + scalar(@lines) - 1;
|
|
}
|
|
}
|
|
elsif ($i<@$newstructs) {
|
|
# Adding a directive
|
|
my @lines;
|
|
$n->{'value'} = $n->{'words'}->[0];
|
|
if ($n->{'file'}) {
|
|
# New file, add at start
|
|
my $indent = defined($n->{'indent_str'}) ?
|
|
$n->{'indent_str'} : "";
|
|
$n->{'indent_str'} = $indent
|
|
if (!defined($n->{'indent_str'}));
|
|
$n->{'indent'} = 0 if (!defined($n->{'indent'}));
|
|
@lines = &make_directive_lines($n, $indent, $parent, $lref);
|
|
$n->{'line'} = 0;
|
|
$n->{'eline'} = scalar(@lines) - 1;
|
|
&recursive_set_file($n, $n->{'file'}, $n->{'line'});
|
|
unshift(@{$parent->{'members'}}, $n);
|
|
}
|
|
elsif ($before) {
|
|
# Insert into parent before some other directive
|
|
my $indent = &directive_indent($before, $parent, $lref);
|
|
$n->{'indent_str'} = $indent
|
|
if (!defined($n->{'indent_str'}));
|
|
$n->{'indent'} = $parent->{'indent'} + 1
|
|
if (!defined($n->{'indent'}) &&
|
|
defined($parent->{'indent'}));
|
|
@lines = &make_directive_lines($n, $indent, $parent, $lref);
|
|
$n->{'line'} = $before->{'line'};
|
|
$n->{'eline'} = $n->{'line'} + scalar(@lines) - 1;
|
|
&recursive_set_file($n, $file, $n->{'line'});
|
|
&renumber($file, $n->{'line'}-1, scalar(@lines));
|
|
my $idx = &indexof($before, @{$parent->{'members'}});
|
|
if ($idx >= 0) {
|
|
splice(@{$parent->{'members'}}, $idx, 0, $n);
|
|
}
|
|
else {
|
|
push(@{$parent->{'members'}}, $n);
|
|
}
|
|
}
|
|
else {
|
|
# Insert into parent at end
|
|
my $indent = &new_directive_indent($parent, $lref);
|
|
$n->{'indent_str'} = $indent
|
|
if (!defined($n->{'indent_str'}));
|
|
$n->{'indent'} = $parent->{'indent'} + 1
|
|
if (!defined($n->{'indent'}) &&
|
|
defined($parent->{'indent'}));
|
|
@lines = &make_directive_lines($n, $indent, $parent, $lref);
|
|
$n->{'line'} = $parent->{'eline'};
|
|
$n->{'eline'} = $n->{'line'} + scalar(@lines) - 1;
|
|
&recursive_set_file($n, $file, $n->{'line'});
|
|
&renumber($file, $parent->{'eline'}-1, scalar(@lines));
|
|
push(@{$parent->{'members'}}, $n);
|
|
}
|
|
splice(@$lref, $n->{'line'}, 0, @lines);
|
|
}
|
|
elsif ($i<@$oldstructs) {
|
|
# Removing a directive
|
|
my $olen = $o->{'eline'} - $o->{'line'} + 1;
|
|
splice(@$lref, $o->{'line'}, $olen);
|
|
my $idx = &indexof($o, @{$parent->{'members'}});
|
|
if ($idx >= 0) {
|
|
splice(@{$parent->{'members'}}, $idx, 1);
|
|
}
|
|
&renumber($file, $o->{'line'}, -$olen);
|
|
}
|
|
}
|
|
}
|
|
|
|
# renumber(filename, line, offset, [&parent])
|
|
# Adjusts the line number of any directive after the one given by the offset
|
|
sub renumber
|
|
{
|
|
my ($file, $line, $offset, $object) = @_;
|
|
$object ||= &get_config_parent();
|
|
if ($object->{'file'} eq $file) {
|
|
$object->{'line'} += $offset if ($object->{'line'} > $line);
|
|
$object->{'eline'} += $offset if ($object->{'eline'} > $line);
|
|
}
|
|
if ($object->{'type'}) {
|
|
foreach my $m (@{$object->{'members'}}) {
|
|
&renumber($file, $line, $offset, $m);
|
|
}
|
|
}
|
|
}
|
|
|
|
# recursive_set_file(&parent, filename, start-line)
|
|
# Sets the file on some object and all children
|
|
sub recursive_set_file
|
|
{
|
|
my ($parent, $file, $line) = @_;
|
|
$parent->{'file'} ||= $file;
|
|
$parent->{'line'} ||= $line;
|
|
$parent->{'eline'} ||= $parent->{'line'};
|
|
if ($parent->{'type'}) {
|
|
my $n = 1;
|
|
foreach my $dir (@{$parent->{'members'}}) {
|
|
&recursive_set_file($dir, $file, $parent->{'line'} + $n);
|
|
$n += ($dir->{'eline'} - $dir->{'line'} + 1);
|
|
}
|
|
$parent->{'eline'} = $parent->{'line'} + $n;
|
|
}
|
|
}
|
|
|
|
# flush_config_file_lines([&parent])
|
|
# Flush all lines in the current config
|
|
sub flush_config_file_lines
|
|
{
|
|
my ($parent) = @_;
|
|
foreach my $f (&unique(@open_config_files)) {
|
|
&flush_file_lines($f);
|
|
}
|
|
@open_config_files = ( );
|
|
}
|
|
|
|
# lock_all_config_files([&parent])
|
|
# Locks all files used in the current config
|
|
sub lock_all_config_files
|
|
{
|
|
my ($parent) = @_;
|
|
@lock_all_config_files_cache = &get_all_config_files($parent);
|
|
foreach my $f (@lock_all_config_files_cache) {
|
|
&lock_file($f);
|
|
}
|
|
}
|
|
|
|
# unlock_all_config_files([&parent])
|
|
# Un-locks all files used in the current config
|
|
sub unlock_all_config_files
|
|
{
|
|
my ($parent) = @_;
|
|
foreach my $f (reverse(@lock_all_config_files_cache)) {
|
|
&unlock_file($f);
|
|
}
|
|
@lock_all_config_files_cache = ();
|
|
}
|
|
|
|
# get_all_config_files([&parent])
|
|
# Returns all files in the given config object
|
|
sub get_all_config_files
|
|
{
|
|
my ($parent) = @_;
|
|
$parent ||= &get_config_parent();
|
|
my @rv = ( $parent->{'file'} );
|
|
if ($parent->{'type'}) {
|
|
foreach my $c (@{$parent->{'members'}}) {
|
|
push(@rv, &get_all_config_files($c));
|
|
}
|
|
}
|
|
return &unique(@rv);
|
|
}
|
|
|
|
# directive_indent(&directive, &parent, &file-lines)
|
|
# Returns the exact whitespace prefix to use when writing a directive
|
|
sub directive_indent
|
|
{
|
|
my ($dir, $parent, $lref) = @_;
|
|
return $dir->{'indent_str'} if (defined($dir->{'indent_str'}));
|
|
if ($lref && defined($dir->{'line'}) && defined($lref->[$dir->{'line'}]) &&
|
|
$lref->[$dir->{'line'}] =~ /^(\s*)/) {
|
|
return $1;
|
|
}
|
|
return &new_directive_indent($parent, $lref) if ($parent);
|
|
return &indent_string($dir->{'indent'}, $lref)
|
|
if (defined($dir->{'indent'}));
|
|
return "";
|
|
}
|
|
|
|
# new_directive_indent(&parent, &file-lines)
|
|
# Returns an exact whitespace prefix for a new child directive
|
|
sub new_directive_indent
|
|
{
|
|
my ($parent, $lref) = @_;
|
|
return "" if (!$parent);
|
|
return "" if (defined($parent->{'indent'}) && $parent->{'indent'} < 0);
|
|
foreach my $m (@{$parent->{'members'} || []}) {
|
|
next if ($parent->{'file'} && $m->{'file'} &&
|
|
$parent->{'file'} ne $m->{'file'});
|
|
return $m->{'indent_str'} if (defined($m->{'indent_str'}));
|
|
}
|
|
my $pindent = defined($parent->{'indent_str'}) ? $parent->{'indent_str'} :
|
|
defined($parent->{'indent'}) ? &indent_string($parent->{'indent'}, $lref) : "";
|
|
return $pindent.&child_indent_step($parent, $lref);
|
|
}
|
|
|
|
# child_indent_step(&parent, &file-lines)
|
|
# Returns one indentation level used below a parent block
|
|
sub child_indent_step
|
|
{
|
|
my ($parent, $lref) = @_;
|
|
my $pindent = defined($parent->{'indent_str'}) ? $parent->{'indent_str'} :
|
|
defined($parent->{'indent'}) ? &indent_string($parent->{'indent'}, $lref) : "";
|
|
foreach my $m (@{$parent->{'members'} || []}) {
|
|
next if ($parent->{'file'} && $m->{'file'} &&
|
|
$parent->{'file'} ne $m->{'file'});
|
|
my $mindent = $m->{'indent_str'};
|
|
if (defined($mindent) && index($mindent, $pindent) == 0 &&
|
|
length($mindent) > length($pindent)) {
|
|
return substr($mindent, length($pindent));
|
|
}
|
|
}
|
|
return &config_indent_step($lref);
|
|
}
|
|
|
|
# config_indent_step(&file-lines)
|
|
# Returns a one-level indent already used in the current file
|
|
sub config_indent_step
|
|
{
|
|
my ($lref) = @_;
|
|
my %indents;
|
|
if ($lref) {
|
|
foreach my $l (@$lref) {
|
|
next if (!defined($l) || $l !~ /^(\s+)\S/);
|
|
$indents{$1}++;
|
|
}
|
|
}
|
|
return (sort { length($a) <=> length($b) ||
|
|
$indents{$b} <=> $indents{$a} } keys %indents)[0]
|
|
if (%indents);
|
|
return &default_indent_step();
|
|
}
|
|
|
|
# default_indent_step()
|
|
# Returns one generated indentation level for new blocks
|
|
sub default_indent_step
|
|
{
|
|
return " ";
|
|
}
|
|
|
|
# make_directive_lines(&directive, indent, [&parent], [&file-lines])
|
|
# Returns text for some directive
|
|
sub make_directive_lines
|
|
{
|
|
my ($dir, $indent, $parent, $lref) = @_;
|
|
my $indent_str = &indent_string($indent, $lref);
|
|
$dir->{'indent_str'} = $indent_str if (!defined($dir->{'indent_str'}));
|
|
my @rv;
|
|
my @w = @{$dir->{'words'}};
|
|
if ($dir->{'type'}) {
|
|
# Multi-line
|
|
if ($dir->{'name'} eq 'if') {
|
|
push(@rv, $indent_str.$dir->{'name'}.' ('.&join_words(@w).') {');
|
|
}
|
|
else {
|
|
push(@rv, $indent_str.$dir->{'name'}.
|
|
(@w ? " ".&join_words(@w) : "")." {");
|
|
}
|
|
my $step = &child_indent_step($dir, $lref);
|
|
foreach my $m (@{$dir->{'members'}}) {
|
|
my $mindent = &directive_indent($m, $dir, $lref);
|
|
$mindent = $indent_str.$step if (!defined($mindent));
|
|
push(@rv, &make_directive_lines($m, $mindent, $dir, $lref));
|
|
}
|
|
push(@rv, $indent_str."}");
|
|
}
|
|
else {
|
|
# Single line
|
|
my $sep = @w ? ($dir->{'sep_str'} || " ") : "";
|
|
push(@rv, $indent_str.$dir->{'name'}.$sep.&join_words(@w).";");
|
|
}
|
|
return wantarray ? @rv : $rv[0];
|
|
}
|
|
|
|
# indent_string(indent, [&file-lines])
|
|
# Converts an indent depth or exact whitespace to exact whitespace
|
|
sub indent_string
|
|
{
|
|
my ($indent, $lref) = @_;
|
|
return "" if (!defined($indent));
|
|
return &config_indent_step($lref) x $indent if ($indent =~ /^\d+$/);
|
|
return $indent;
|
|
}
|
|
|
|
# directive_value_separator(&directive, old-line)
|
|
# Returns whitespace between directive name and value
|
|
sub directive_value_separator
|
|
{
|
|
my ($dir, $oldline) = @_;
|
|
return undef if (!@{$dir->{'words'} || []});
|
|
return $1 if (defined($oldline) &&
|
|
$oldline =~ /^\s*\Q$dir->{'name'}\E(\s+)/);
|
|
return $dir->{'sep_str'} || " ";
|
|
}
|
|
|
|
# join_words(word, etc..)
|
|
# Returns a string made by joining directive words
|
|
sub join_words
|
|
{
|
|
my @rv;
|
|
foreach my $w (@_) {
|
|
if ($w eq "") {
|
|
push(@rv, '""');
|
|
}
|
|
elsif ($w =~ /\s|;|\$/ && $w !~ /"/ && $w !~ /^\$/) {
|
|
push(@rv, "\"$w\"");
|
|
}
|
|
elsif ($w =~ /\s|;|\$/ && $w !~ /^\$/) {
|
|
push(@rv, "'$w'");
|
|
}
|
|
else {
|
|
push(@rv, $w);
|
|
}
|
|
}
|
|
return join(" ", @rv);
|
|
}
|
|
|
|
# value_to_struct(name, value)
|
|
# Converts a string, array ref or hash ref to a config struct
|
|
sub value_to_struct
|
|
{
|
|
my ($name, $value) = @_;
|
|
if (ref($value) eq 'HASH') {
|
|
# Already in correct format
|
|
$value->{'name'} ||= $name;
|
|
return $value;
|
|
}
|
|
elsif (ref($value) eq 'ARRAY') {
|
|
# Array of words
|
|
return { 'name' => $name,
|
|
'words' => $value,
|
|
'value' => $value->[0] };
|
|
}
|
|
else {
|
|
# Single value
|
|
return { 'name' => $name,
|
|
'words' => [ $value ],
|
|
'value' => $value };
|
|
}
|
|
}
|
|
|
|
# get_nginx_version()
|
|
# Returns the version number of the installed Nginx binary
|
|
sub get_nginx_version
|
|
{
|
|
my $out = &backquote_command("$config{'nginx_cmd'} -v 2>&1 </dev/null");
|
|
return $out =~ /version:\s*nginx\/([0-9\.]+)/i ? $1 : undef;
|
|
}
|
|
|
|
# list_nginx_directives()
|
|
# Returns a hash ref of hash refs, with name, module, default and context keys
|
|
sub list_nginx_directives
|
|
{
|
|
if (!%list_directives_cache) {
|
|
my $lref = &read_file_lines(
|
|
"$module_root_directory/nginx-directives", 1);
|
|
foreach my $l (@$lref) {
|
|
my ($module, $name, $default, $context) = split(/\t/, $l);
|
|
$list_directives_cache{$name} =
|
|
{ 'module' => $module,
|
|
'name' => $name,
|
|
'default' => $default eq '-' ? undef : $default,
|
|
'context' => $context eq '-' ? undef :
|
|
[ split(/,/, $context) ],
|
|
};
|
|
}
|
|
}
|
|
return \%list_directives_cache;
|
|
}
|
|
|
|
# get_default(name)
|
|
# Returns the default value for some directive
|
|
sub get_default
|
|
{
|
|
my ($name) = @_;
|
|
my $dirs = &list_nginx_directives();
|
|
my $dir = $dirs->{$name};
|
|
return $dir ? $dir->{'default'} : undef;
|
|
}
|
|
|
|
sub get_default_server_param
|
|
{
|
|
my $ver = &get_nginx_version();
|
|
return &compare_version_numbers($ver, "0.8.21") >= 0 ?
|
|
"default_server" : "default";
|
|
}
|
|
|
|
# list_nginx_modules()
|
|
# Returns a list of enabled modules. Includes those compiled in by default
|
|
# unless disabled, plus extra compiled in at build time.
|
|
sub list_nginx_modules
|
|
{
|
|
if (!@list_modules_cache) {
|
|
@list_modules_cache = ( 'http_core', 'http_access', 'http_access',
|
|
'http_auth_basic', 'http_autoindex',
|
|
'http_browser', 'http_charset',
|
|
'http_empty_gif', 'http_fastcgi', 'http_geo',
|
|
'http_gzip', 'http_limit_req',
|
|
'http_limit_zone', 'http_map',
|
|
'http_memcached', 'http_proxy',
|
|
'http_referer', 'http_rewrite',
|
|
'http_scgi', 'http_split_clients',
|
|
'http_ssi', 'http_userid', 'http_index',
|
|
'http_uwsgi', 'http_log', 'core' );
|
|
my $out = &backquote_command("$config{'nginx_cmd'} -V 2>&1 </dev/null");
|
|
while($out =~ s/--with-(\S+)_module\s+//) {
|
|
push(@list_modules_cache, $1);
|
|
}
|
|
while($out =~ s/--without-(\S+)_module\s+//) {
|
|
@list_modules_cache = grep { $_ ne $1 } @list_modules_cache;
|
|
}
|
|
}
|
|
return @list_modules_cache;
|
|
}
|
|
|
|
# supported_directive(name, [&parent])
|
|
# Returns 1 if the module for some directive is supported on this system
|
|
sub supported_directive
|
|
{
|
|
my ($name, $parent) = @_;
|
|
my $dirs = &list_nginx_directives();
|
|
my $dir = $dirs->{$name};
|
|
return 0 if (!$dir);
|
|
return 0 if ($dir->{'context'} && $parent &&
|
|
&indexof($parent->{'name'}, @{$dir->{'context'}}) < 0);
|
|
my @mods = &list_nginx_modules();
|
|
#return 0 if (&indexof($dir->{'module'}, @mods) < 0);
|
|
return 1;
|
|
}
|
|
|
|
# nginx_onoff_input(name, &parent)
|
|
# Returns HTML for a table row for an on/off input
|
|
sub nginx_onoff_input
|
|
{
|
|
my ($name, $parent) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my $value = &find_value($name, $parent);
|
|
$value ||= &get_default($name);
|
|
$value ||= "";
|
|
return &ui_table_row($text{'opt_'.$name},
|
|
&ui_yesno_radio($name, $value =~ /on|true|yes/i ? 1 : 0));
|
|
}
|
|
|
|
# nginx_onoff_parse(name, &parent, &in)
|
|
# Updates the config with input from nginx_onoff_input
|
|
sub nginx_onoff_parse
|
|
{
|
|
my ($name, $parent, $in) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
&save_directive($parent, $name, [ $in->{$name} ? "on" : "off" ]);
|
|
}
|
|
|
|
# nginx_opt_input(name, &parent, size, prefix, suffix, [multi-value])
|
|
# Returns HTML for an optional text field
|
|
sub nginx_opt_input
|
|
{
|
|
my ($name, $parent, $size, $prefix, $suffix, $multi) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my $obj = &find($name, $parent);
|
|
my $value = $obj ? ($multi ? &join_words(@{$obj->{'words'}})
|
|
: $obj->{'value'})
|
|
: undef;
|
|
my $def = &get_default($name);
|
|
return &ui_table_row($text{'opt_'.$name},
|
|
&ui_opt_textbox($name, $value, $size,
|
|
$text{'default'}.($def ? " ($def)" : ""), $prefix).
|
|
$suffix, $size > 40 ? 3 : 1);
|
|
}
|
|
|
|
# nginx_opt_parse(name, &parent, &in, [regex], [&validator], [multi-value])
|
|
# Updates the config with input from nginx_opt_input
|
|
sub nginx_opt_parse
|
|
{
|
|
my ($name, $parent, $in, $regexp, $vfunc, $multi) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
if ($in->{$name."_def"}) {
|
|
&save_directive($parent, $name, [ ]);
|
|
}
|
|
else {
|
|
my $v = $in->{$name};
|
|
my @w = $multi ? &split_quoted_string($v) : ( $v );
|
|
$v eq '' && &error(&text('opt_missing', $text{'opt_'.$name}));
|
|
!$regexp || $v =~ /$regexp/ || &error($text{'opt_e'.$name} || $name);
|
|
my $err = $vfunc && &$vfunc($v, $name);
|
|
$err && &error($err);
|
|
&save_directive($parent, $name, [ { 'name' => $name,
|
|
'words' => \@w } ]);
|
|
}
|
|
}
|
|
|
|
# nginx_text_input(name, &parent, size, suffix, [multi-value])
|
|
# Returns HTML for a non-optional text field
|
|
sub nginx_text_input
|
|
{
|
|
my ($name, $parent, $size, $suffix, $multi) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my $obj = &find($name, $parent);
|
|
my $value = $obj ? ($multi ? &join_words(@{$obj->{'words'}})
|
|
: $obj->{'value'})
|
|
: undef;
|
|
$suffix ||= "";
|
|
return &ui_table_row($text{'opt_'.$name},
|
|
&ui_textbox($name, $value, $size).$suffix, $size > 40 ? 3 : 1);
|
|
}
|
|
|
|
# nginx_text_parse(name, &parent, &in, [regex], [&validator], [multi-value])
|
|
# Updates the config with input from nginx_text_input
|
|
sub nginx_text_parse
|
|
{
|
|
my ($name, $parent, $in, $regexp, $vfunc, $multi) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
my $v = $in->{$name};
|
|
my @w = $multi ? &split_quoted_string($v) : ( $v );
|
|
foreach my $wv (@w) {
|
|
$wv eq '' && &error(&text('opt_missing', $text{'opt_'.$name}));
|
|
!$regexp || $wv =~ /$regexp/ || &error($text{'opt_e'.$name});
|
|
my $err = $vfunc && &$vfunc($wv, $name);
|
|
$err && &error($err);
|
|
}
|
|
&save_directive($parent, $name, [ { 'name' => $name,
|
|
'words' => \@w } ]);
|
|
}
|
|
|
|
# nginx_error_log_input(name, &parent)
|
|
# Returns HTML specifically for setting the error_log directive
|
|
sub nginx_error_log_input
|
|
{
|
|
my ($name, $parent) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my $obj = &find($name, $parent);
|
|
my $def = $parent->{'name'} eq 'server' ? $text{'opt_global'}
|
|
: &get_default($name);
|
|
$def =~ s/^\$\{prefix\}\///;
|
|
return &ui_table_row($text{'opt_'.$name},
|
|
&ui_radio($name."_def", $obj ? 0 : 1,
|
|
[ [ 1, $text{'default'}.($def ? " ($def)" : "")."<br>" ],
|
|
[ 0, $text{'logs_file'} ] ])." ".
|
|
&ui_textbox($name, $obj ? $obj->{'words'}->[0] : undef, 40)." ".
|
|
$text{'logs_level'}." ".
|
|
&ui_select($name."_level", $obj ? $obj->{'words'}->[1] : "",
|
|
[ [ "", "<$text{'default'}>" ],
|
|
"debug", "info", "notice", "warn", "error", "crit" ]));
|
|
}
|
|
|
|
# nginx_error_log_parse(name, &parent, &in)
|
|
# Validate input from nginx_error_log_input
|
|
sub nginx_error_log_parse
|
|
{
|
|
my ($name, $parent, $in) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
if ($in->{$name."_def"}) {
|
|
&save_directive($parent, $name, [ ]);
|
|
}
|
|
else {
|
|
$in->{$name} || &error(&text('opt_missing', $text{'opt_'.$name}));
|
|
$in->{$name} =~ /^\/\S+$/ || &error($text{'opt_e'.$name});
|
|
my @w = ( $in->{$name} );
|
|
push(@w, $in->{$name."_level"}) if ($in->{$name."_level"});
|
|
&save_directive($parent, $name, [ { 'name' => $name,
|
|
'words' => \@w } ]);
|
|
}
|
|
}
|
|
|
|
# nginx_access_log_input(name, &parent)
|
|
# Returns HTML specifically for setting the access_log directive
|
|
sub nginx_access_log_input
|
|
{
|
|
my ($name, $parent) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my $obj = &find($name, $parent);
|
|
my $mode = !$obj ? 1 : $obj->{'value'} eq 'off' ? 2 : 0;
|
|
my $buffer = $mode == 0 && $obj->{'words'}->[2] =~ /buffer=(\S+)/ ? $1 : "";
|
|
my $def = $parent->{'name'} eq 'server' ? $text{'opt_global'}
|
|
: &get_default($name);
|
|
return &ui_table_row($text{'opt_'.$name},
|
|
&ui_radio($name."_def", $mode,
|
|
[ [ 1, $text{'default'}.($def ? " ($def)" : "")."<br>" ],
|
|
[ 2, $text{'logs_disabled'}."<br>" ],
|
|
[ 0, $text{'logs_file'} ] ])." ".
|
|
&ui_textbox($name, $mode == 0 ? $obj->{'words'}->[0] : undef, 40)." ".
|
|
$text{'logs_format'}." ".
|
|
&ui_select($name."_format", $mode == 0 ? $obj->{'words'}->[1] : "",
|
|
[ [ "", "<$text{'default'}>" ],
|
|
&list_log_formats($parent) ])." ".
|
|
$text{'logs_buffer'}." ".
|
|
&ui_textbox($name."_buffer", $buffer, 6));
|
|
}
|
|
|
|
# nginx_access_log_parse(name, &parent, &in)
|
|
# Validate input from nginx_access_log_input
|
|
sub nginx_access_log_parse
|
|
{
|
|
my ($name, $parent, $in) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
if ($in->{$name."_def"} == 1) {
|
|
&save_directive($parent, $name, [ ]);
|
|
}
|
|
elsif ($in->{$name."_def"} == 2) {
|
|
&save_directive($parent, $name, [ "off" ]);
|
|
}
|
|
else {
|
|
$in->{$name} || &error(&text('opt_missing', $text{'opt_'.$name}));
|
|
$in->{$name} =~ /^\/\S+$/ || &error($text{'opt_e'.$name});
|
|
my @w = ( $in->{$name} );
|
|
push(@w, $in->{$name."_format"}) if ($in->{$name."_format"});
|
|
my $buffer = $in->{$name."_buffer"};
|
|
if ($buffer) {
|
|
$buffer =~ /^\d+[bKMGT]?$/i || &error($text{'logs_ebuffer'});
|
|
push(@w, "buffer=$buffer");
|
|
}
|
|
&save_directive($parent, $name, [ { 'name' => $name,
|
|
'words' => \@w } ]);
|
|
}
|
|
}
|
|
|
|
# nginx_user_input(name, &parent)
|
|
# Returns HTML for a user field with an optional group
|
|
sub nginx_user_input
|
|
{
|
|
my ($name, $parent) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my $obj = &find($name, $parent);
|
|
my $def = &get_default($name);
|
|
return &ui_table_row($text{'opt_'.$name},
|
|
&ui_radio($name."_def", $obj ? 0 : 1,
|
|
[ [ 1, $text{'default'}.($def ? " ($def)" : "")."<br>" ],
|
|
[ 0, $text{'misc_username'} ] ])." ".
|
|
&ui_user_textbox($name, $obj ? $obj->{'words'}->[0] : "")." ".
|
|
$text{'misc_group'}." ".
|
|
&ui_group_textbox($name."_group", $obj ? $obj->{'words'}->[1] : ""));
|
|
}
|
|
|
|
# nginx_user_parse(name, &parent, &in)
|
|
# Validate input from nginx_user_input
|
|
sub nginx_user_parse
|
|
{
|
|
my ($name, $parent, $in) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
if ($in->{$name."_def"} == 1) {
|
|
&save_directive($parent, $name, [ ]);
|
|
}
|
|
else {
|
|
$in->{$name} || &error(&text('opt_missing', $text{'opt_'.$name}));
|
|
defined(getpwnam($in->{$name})) || &error($text{'misc_euser'});
|
|
my @w = ( $in->{$name} );
|
|
my $group = $in->{$name."_group"};
|
|
if ($group) {
|
|
defined(getgrnam($group)) || &error($text{'misc_egroup'});
|
|
push(@w, $group);
|
|
}
|
|
&save_directive($parent, $name, [ { 'name' => $name,
|
|
'words' => \@w } ]);
|
|
}
|
|
}
|
|
|
|
# nginx_logformat_input(name, parent)
|
|
# Returns HTML for entering multiple log formats
|
|
sub nginx_logformat_input
|
|
{
|
|
my ($name, $parent) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my @obj = &find($name, $parent);
|
|
my $ftable = &ui_columns_start([ $text{'logs_fname'},
|
|
$text{'logs_ftext'} ]);
|
|
my $i = 0;
|
|
foreach my $o (@obj, { 'words' => [ ] }) {
|
|
my @w = @{$o->{'words'}};
|
|
$ftable .= &ui_columns_row([
|
|
&ui_textbox($name."_name_$i", shift(@w), 20),
|
|
&ui_textbox($name."_text_$i", join(" ", @w), 60),
|
|
]);
|
|
$i++;
|
|
}
|
|
$ftable .= &ui_columns_end();
|
|
return &ui_table_row($text{'opt_'.$name}, $ftable, 3);
|
|
}
|
|
|
|
# nginx_logformat_parse(name, &parent, &in)
|
|
# Validate input from nginx_logformat_input
|
|
sub nginx_logformat_parse
|
|
{
|
|
my ($name, $parent, $in) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
my @obj;
|
|
for(my $i=0; defined(my $fname = $in{$name."_name_$i"}); $i++) {
|
|
next if (!$fname);
|
|
my $ftext = $in{$name."_text_$i"};
|
|
$fname =~ /^[a-zA-Z0-9\-\.\_]+$/ ||
|
|
&error(&text('logs_efname', $fname));
|
|
$ftext =~ /\S/ || &error(&text('logs_etext', $fname));
|
|
push(@obj, { 'name' => $name,
|
|
'words' => [ $fname, $ftext ] });
|
|
}
|
|
&save_directive($parent, $name, \@obj);
|
|
}
|
|
|
|
# nginx_multi_input(name, &parent, &options)
|
|
# Returns HTML for selecting multiple options
|
|
sub nginx_multi_input
|
|
{
|
|
my ($name, $parent, $opts) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my $def = &get_default($name);
|
|
my $obj = &find($name, $parent);
|
|
return &ui_table_row($text{'opt_'.$name},
|
|
&ui_radio($name."_def", $obj ? 0 : 1,
|
|
[ [ 1, $text{'default'}.($def ? " ($def)" : "") ],
|
|
[ 0, $text{'opt_selected'}."<br>" ] ])." ".
|
|
&ui_select($name, $obj ? $obj->{'words'} : [ ], $opts, scalar(@$opts),
|
|
1, 1));
|
|
}
|
|
|
|
# nginx_multi_parse(name, &parent)
|
|
# Validate input from nginx_multi_input
|
|
sub nginx_multi_parse
|
|
{
|
|
my ($name, $parent, $in) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
if ($in->{$name."_def"} == 1) {
|
|
&save_directive($parent, $name, [ ]);
|
|
}
|
|
else {
|
|
my @w = split(/\0/, $in->{$name});
|
|
@w || &error(&text('opt_missing', $text{'opt_'.$name}));
|
|
&save_directive($parent, $name, [ { 'name' => $name,
|
|
'words' => \@w } ]);
|
|
}
|
|
}
|
|
|
|
# nginx_param_input(name, &parent, [name-text, value-text])
|
|
# Returns HTML for entering multiple name value paramters
|
|
sub nginx_param_input
|
|
{
|
|
my ($name, $parent, $ntext, $vtext) = @_;
|
|
$ntext ||= $text{'fcgi_pname'};
|
|
$vtext ||= $text{'fcgi_pvalue'};
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my @obj = &find($name, $parent);
|
|
my $ftable = &ui_columns_start([ $ntext, $vtext ]);
|
|
my $i = 0;
|
|
foreach my $o (@obj, { 'words' => [ ] }) {
|
|
my @w = @{$o->{'words'}};
|
|
$ftable .= &ui_columns_row([
|
|
&ui_textbox($name."_name_$i", shift(@w), 20),
|
|
&ui_textbox($name."_value_$i", join(" ", @w), 60),
|
|
]);
|
|
$i++;
|
|
}
|
|
$ftable .= &ui_columns_end();
|
|
return &ui_table_row($text{'opt_'.$name}, $ftable, 3);
|
|
}
|
|
|
|
# nginx_params_parse(name, &parent, &in)
|
|
# Parses inputs from nginx_param_input
|
|
sub nginx_params_parse
|
|
{
|
|
my ($name, $parent, $in) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
my @obj;
|
|
for(my $i=0; defined(my $pname = $in{$name."_name_$i"}); $i++) {
|
|
next if (!$pname);
|
|
my $pvalue = $in{$name."_value_$i"};
|
|
$pname =~ /^[a-zA-Z0-9\-\.\_]+$/ ||
|
|
&error(&text('fcgi_epname', $pname));
|
|
$pvalue =~ /\S/ || &error(&text('fcgi_epvalue', $pname));
|
|
push(@obj, { 'name' => $name,
|
|
'words' => [ $pname, $pvalue ] });
|
|
}
|
|
&save_directive($parent, $name, \@obj);
|
|
}
|
|
|
|
# nginx_opt_list_input(name, &parent, size, prefix, suffix)
|
|
# Returns HTML for an optional text field with multiple values
|
|
sub nginx_opt_list_input
|
|
{
|
|
my ($name, $parent, $size, $prefix, $suffix) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my $obj = &find($name, $parent);
|
|
my $value = $obj ? join(" ", @{$obj->{'words'}}) : "";
|
|
my $def = &get_default($name);
|
|
return &ui_table_row($text{'opt_'.$name},
|
|
&ui_opt_textbox($name, $value, $size,
|
|
$text{'default'}.($def ? " ($def)" : ""), $prefix).
|
|
$suffix, $size > 40 ? 3 : 1);
|
|
}
|
|
|
|
# nginx_opt_list_parse(name, &parent, &in, [regex], [&validator])
|
|
# Updates the config with input from nginx_opt_list_input
|
|
sub nginx_opt_list_parse
|
|
{
|
|
my ($name, $parent, $in, $regexp, $vfunc) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
if ($in->{$name."_def"}) {
|
|
&save_directive($parent, $name, [ ]);
|
|
}
|
|
else {
|
|
my @v = &split_quoted_string($in->{$name});
|
|
@v || &error(&text('opt_missing', $text{'opt_'.$name}));
|
|
foreach my $v (@v) {
|
|
!$regexp || $v =~ /$regexp/ ||
|
|
&error(&text('opt_e'.$name, $v) || $name);
|
|
my $err = $vfunc && &$vfunc($v, $name);
|
|
$err && &error($err);
|
|
}
|
|
&save_directive($parent, $name, [ { 'name' => $name,
|
|
'words' => \@v } ]);
|
|
}
|
|
}
|
|
|
|
# nginx_textarea_input(name, &parent, width, height)
|
|
# Returns HTML for entering the values of multiple directives of the same type,
|
|
# in a text area
|
|
sub nginx_textarea_input
|
|
{
|
|
my ($name, $parent, $width, $height) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my @obj = &find($name, $parent);
|
|
return &ui_table_row($text{'opt_'.$name},
|
|
&ui_textarea($name,
|
|
join("\n", map { $_->{'words'}->[0] } @obj),
|
|
$height, $width), 3);
|
|
}
|
|
|
|
# nginx_textarea_parse(name, &parent, &in, [®ex], [&validator])
|
|
# Parses inputs from nginx_param_input
|
|
sub nginx_textarea_parse
|
|
{
|
|
my ($name, $parent, $in, $regexp, $vfunc) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
my @obj;
|
|
foreach my $v (split(/\r?\n/, $in->{$name})) {
|
|
!$regexp || $v =~ /$regexp/ ||
|
|
&error(&text('opt_e'.$name, $v) || $name);
|
|
my $err = $vfunc && &$vfunc($v, $name);
|
|
$err && &error($err);
|
|
push(@obj, { 'name' => $name,
|
|
'words' => [ $v ] });
|
|
}
|
|
&save_directive($parent, $name, \@obj);
|
|
}
|
|
|
|
# nginx_access_input(name1, name2, &parent)
|
|
# Returns HTML for setting allow and deny directives
|
|
sub nginx_access_input
|
|
{
|
|
my ($allow, $deny, $parent) = @_;
|
|
return undef if (!&supported_directive($allow, $parent));
|
|
my @obj = sort { $a->{'line'} <=> $b->{'line'} }
|
|
(&find($allow, $parent), &find($deny, $parent));
|
|
my $table = &ui_columns_start([ $text{'access_mode'},
|
|
$text{'access_value'} ], 100, 0,
|
|
[ "nowrap", "nowrap" ]);
|
|
my $i =0;
|
|
foreach my $o (@obj, { }, { }) {
|
|
my $v = $o->{'value'};
|
|
$v = "" if (lc($v) eq "all");
|
|
$table .= &ui_columns_row([
|
|
&ui_select($allow."_mode_".$i,
|
|
$o->{'name'},
|
|
[ [ "", " " ],
|
|
[ "allow", $text{'access_allow'} ],
|
|
[ "deny", $text{'access_deny'} ] ]),
|
|
&ui_opt_textbox($allow."_addr_".$i, $v, 30,
|
|
$text{'access_all'}, $text{'access_addr'}),
|
|
]);
|
|
$i++;
|
|
}
|
|
$table .= &ui_columns_end();
|
|
return &ui_table_row($text{'opt_'.$allow}, $table, 3);
|
|
}
|
|
|
|
# nginx_access_parse(name1, name2, &parent, &in)
|
|
# Parse inputs from nginx_access_input
|
|
sub nginx_access_parse
|
|
{
|
|
my ($allow, $deny, $parent, $in) = @_;
|
|
return undef if (!&supported_directive($allow, $parent));
|
|
$in ||= \%in;
|
|
my @obj;
|
|
my @old = sort { $a->{'line'} <=> $b->{'line'} }
|
|
(&find($allow, $parent), &find($deny, $parent));
|
|
for(my $i=0; defined(my $mode = $in->{$allow."_mode_".$i}); $i++) {
|
|
next if (!$mode);
|
|
my $addr;
|
|
if ($in->{$allow."_addr_".$i."_def"}) {
|
|
$addr = "all";
|
|
}
|
|
else {
|
|
$addr = $in->{$allow."_addr_".$i};
|
|
$addr || &error(&text('access_eaddrnone', $i+1));
|
|
&check_ipaddress($addr) ||
|
|
$addr =~ /^(\S+)\/(\d+)$/ &&
|
|
&check_ipaddress("$1") && $2 > 0 && $2 <= 32 ||
|
|
&check_ip6address($addr) ||
|
|
$addr =~ /^(\S+)\/(\d+)$/ && &check_ip6address("$1") ||
|
|
&error(&text('access_eaddr', $addr));
|
|
}
|
|
push(@obj, { 'name' => $mode,
|
|
'words' => [ $addr ] });
|
|
}
|
|
&save_directive($parent, \@old, \@obj);
|
|
}
|
|
|
|
# nginx_realm_input(name, &parent)
|
|
# Returns HTML for entering an authentication realm
|
|
sub nginx_realm_input
|
|
{
|
|
my ($name, $parent) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my $value = &find_value($name, $parent);
|
|
my $def = &get_default($name);
|
|
return &ui_table_row($text{'opt_'.$name},
|
|
&ui_radio($name."_def",
|
|
!$value ? 1 : $value eq "off" ? 2 : 0,
|
|
[ [ 1, $text{'default'}.($def ? " ($def)" : "") ],
|
|
[ 2, $text{'access_off'} ],
|
|
[ 0, $text{'access_realm'}." ".
|
|
&ui_textbox($name, $value eq "off" ? "" : $value, 40) ]
|
|
]), 3);
|
|
}
|
|
|
|
# nginx_realm_parse(name, &parent, &in)
|
|
# Updates the config with input from nginx_realm_input
|
|
sub nginx_realm_parse
|
|
{
|
|
my ($name, $parent, $in) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
if ($in->{$name."_def"} == 1) {
|
|
&save_directive($parent, $name, [ ]);
|
|
}
|
|
elsif ($in->{$name."_def"} == 2) {
|
|
&save_directive($parent, $name, [ "off" ]);
|
|
}
|
|
else {
|
|
my $v = $in->{$name};
|
|
$v eq '' && &error(&text('opt_missing', $text{'opt_'.$name}));
|
|
&save_directive($parent, $name, [ $v ]);
|
|
}
|
|
}
|
|
|
|
# nginx_passfile_input(name, &parent, server-id, path)
|
|
# Returns HTML for a password file field
|
|
sub nginx_passfile_input
|
|
{
|
|
my ($name, $parent, $id, $path) = @_;
|
|
my $value = &find_value($name, $parent);
|
|
my $edit;
|
|
if ($value =~ /^\/\S/) {
|
|
$edit = " <a href='list_users.cgi?file=".&urlize($value).
|
|
"&id=".&urlize($id)."&path=".&urlize($path)."'>".
|
|
$text{'access_edit'}."</a>";
|
|
}
|
|
return &nginx_opt_input($name, $parent, 50, $text{'access_pfile'},
|
|
&file_chooser_button($name).$edit);
|
|
}
|
|
|
|
# nginx_passfile_parse(name, &parent, &in)
|
|
# Parse input from nginx_passfile_input
|
|
sub nginx_passfile_parse
|
|
{
|
|
my ($name, $parent, $in) = @_;
|
|
$in ||= \%in;
|
|
$in->{$name."_def"} || &can_directory($in->{$name}) ||
|
|
&error(&text('access_ecannot',
|
|
"<tt>".&html_escape($in->{$name})."</tt>",
|
|
"<tt>".&html_escape($access{'root'})."</tt>"));
|
|
&nginx_opt_parse($name, $parent, $in, undef,
|
|
sub { return $_[0] !~ /^\// ? $text{'access_eabsolute'} :
|
|
-d $_[0] ? $text{'access_edir'} : undef });
|
|
}
|
|
|
|
# nginx_rewrite_input(name, &parent)
|
|
# Returns HTML for setting rewrite directives
|
|
sub nginx_rewrite_input
|
|
{
|
|
my ($name, $parent) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
my @obj = &find($name, $parent);
|
|
my $table = &ui_columns_start([ $text{'rewrite_from'},
|
|
$text{'rewrite_to'},
|
|
$text{'rewrite_flag'} ], 100, 0,
|
|
[ "nowrap", "nowrap" ]);
|
|
my $i =0;
|
|
foreach my $o (@obj, { }, { }) {
|
|
$table .= &ui_columns_row([
|
|
&ui_textbox($name."_from_$i", $o->{'words'}->[0], 30),
|
|
&ui_textbox($name."_to_$i", $o->{'words'}->[1], 40),
|
|
&ui_select($name."_flag_$i", $o->{'words'}->[2],
|
|
[ map { [ $_, $text{'rewrite_'.$_} ] }
|
|
('last', 'break', 'redirect', 'permanent') ]),
|
|
]);
|
|
$i++;
|
|
}
|
|
$table .= &ui_columns_end();
|
|
return &ui_table_row($text{'opt_'.$name}, $table, 3);
|
|
}
|
|
|
|
# nginx_rewrite_parse(name1, name2, &parent, &in)
|
|
# Parse inputs from nginx_rewrite_input
|
|
sub nginx_rewrite_parse
|
|
{
|
|
my ($name, $parent, $in) = @_;
|
|
return undef if (!&supported_directive($name, $parent));
|
|
$in ||= \%in;
|
|
my @obj;
|
|
for(my $i=0; defined(my $from = $in->{$name."_from_".$i}); $i++) {
|
|
next if (!$from);
|
|
$from =~ /^\S+$/ || &error(&text('rewrite_efrom', $i+1));
|
|
my $to = $in->{$name."_to_".$i};
|
|
$to =~ /^\S+$/ || &error(&text('rewrite_eto', $i+1));
|
|
my $flag = $in->{$name."_flag_".$i};
|
|
push(@obj, { 'name' => $name,
|
|
'words' => [ $from, $to, $flag ] });
|
|
}
|
|
&save_directive($parent, $name, \@obj);
|
|
}
|
|
|
|
# list_log_formats([&server])
|
|
# Returns a list of all log format names
|
|
sub list_log_formats
|
|
{
|
|
my ($server) = @_;
|
|
my $parent = &get_config_parent();
|
|
my @rv = ( "combined" );
|
|
my $http = &find("http", $parent);
|
|
foreach my $l (&find("log_format", $http)) {
|
|
push(@rv, $l->{'words'}->[0]);
|
|
}
|
|
if ($server && $server->{'name'} eq 'server') {
|
|
foreach my $l (&find("log_format", $server)) {
|
|
push(@rv, $l->{'words'}->[0]);
|
|
}
|
|
}
|
|
return &unique(@rv);
|
|
}
|
|
|
|
# is_nginx_running()
|
|
# Returns the PID if nginx is running
|
|
sub is_nginx_running
|
|
{
|
|
my $parent = &get_config_parent();
|
|
my $pidfile = &find_value("pid", $parent);
|
|
$pidfile ||= &get_default("pid");
|
|
$pidfile ||= $config{'pid_file'};
|
|
if ($pidfile =~ /^\//) {
|
|
return &check_pid_file($pidfile);
|
|
}
|
|
else {
|
|
my ($pid) = &find_byname("nginx");
|
|
return $pid;
|
|
}
|
|
}
|
|
|
|
# stop_nginx()
|
|
# Attempt to stop nginx, return an error on failure or undef on success
|
|
sub stop_nginx
|
|
{
|
|
my $out = &backquote_logged("$config{'stop_cmd'} 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# start_nginx()
|
|
# Attempt to start nginx, return an error on failure or undef on success
|
|
sub start_nginx
|
|
{
|
|
my $out = &backquote_logged("$config{'start_cmd'} 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# apply_nginx()
|
|
# Attempt to apply the nginx config, return an error on failure or undef
|
|
# on success
|
|
sub apply_nginx
|
|
{
|
|
my $out = &backquote_logged("$config{'apply_cmd'} 2>&1 </dev/null");
|
|
return $? ? $out : undef;
|
|
}
|
|
|
|
# nginx_action_links()
|
|
# Returns HTML for service actions to put in the page header
|
|
sub nginx_action_links
|
|
{
|
|
my $args = "redir=".&urlize(&this_url());
|
|
my @rv;
|
|
if (&is_nginx_running()) {
|
|
if ($access{'stop'}) {
|
|
push(@rv, &ui_link("stop.cgi?$args", $text{'index_stop'}));
|
|
}
|
|
push(@rv, &ui_link("restart.cgi?$args", $text{'index_restart'}));
|
|
}
|
|
elsif ($access{'stop'}) {
|
|
push(@rv, &ui_link("start.cgi?$args", $text{'index_start'}));
|
|
}
|
|
return join("<br>\n", @rv);
|
|
}
|
|
|
|
# this_url()
|
|
# Returns the current module URL
|
|
sub this_url
|
|
{
|
|
my $url = $ENV{'SCRIPT_NAME'};
|
|
$url .= "?$ENV{'QUERY_STRING'}"
|
|
if (defined($ENV{'QUERY_STRING'}) && $ENV{'QUERY_STRING'} ne "");
|
|
return $url;
|
|
}
|
|
|
|
# test_config()
|
|
# Returns an error message if the config is invalid
|
|
sub test_config
|
|
{
|
|
&clean_language() if (defined(&clean_language));
|
|
my $out = &backquote_logged("$config{'nginx_cmd'} -t 2>&1 </dev/null");
|
|
&reset_environment() if (defined(&clean_language));
|
|
return $? || $out !~ /syntax\s+is\s+ok/ ? $out : undef;
|
|
}
|
|
|
|
# find_server(id)
|
|
# Convenience function to find an HTTP server object with some ID
|
|
sub find_server
|
|
{
|
|
my ($id) = @_;
|
|
my $conf = &get_config();
|
|
my $http = &find("http", $conf);
|
|
return undef if (!$http);
|
|
my @servers = &find("server", $http);
|
|
my ($idname, $idrootdir) = split(/;/, $id);
|
|
foreach my $s (@servers) {
|
|
my $name = &find_value("server_name", $s);
|
|
next if ($idname ne $name);
|
|
my $rootdir = &find_value("root", $s);
|
|
if (!$rootdir) {
|
|
my @locs = &find("location", $s);
|
|
my ($rootloc) = grep { $_->{'value'} eq '/' } @locs;
|
|
$rootdir = $rootloc ? &find_value("root", $rootloc) : "";
|
|
}
|
|
next if ($idrootdir ne $rootdir);
|
|
return $s;
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
# server_id(&server)
|
|
# Given a server, return a unique ID for it as used by the module
|
|
sub server_id
|
|
{
|
|
my ($s) = @_;
|
|
my $name = &find_value("server_name", $s);
|
|
my $rootdir = &find_value("root", $s);
|
|
if (!$rootdir) {
|
|
my @locs = &find("location", $s);
|
|
my ($rootloc) = grep { $_->{'value'} eq '/' } @locs;
|
|
if ($rootloc) {
|
|
$rootdir = &find_value("root", $rootloc);
|
|
}
|
|
$rootdir ||= "";
|
|
}
|
|
return $name.";".$rootdir;
|
|
}
|
|
|
|
# find_location(&server, path)
|
|
# Finds the location with some path in a given server object
|
|
sub find_location
|
|
{
|
|
my ($server, $path) = @_;
|
|
foreach my $l (&find("location", $server)) {
|
|
return $l if (&location_path($l) eq $path);
|
|
}
|
|
return undef;
|
|
}
|
|
|
|
# location_path(&location)
|
|
# Returns the URL path or pattern from a location block
|
|
sub location_path
|
|
{
|
|
my ($location) = @_;
|
|
my @w = @{$location->{'words'}};
|
|
return @w ? $w[$#w] : "";
|
|
}
|
|
|
|
# split_ip_port(string)
|
|
# Given an ip:port pair as used in a listen directive, split them up
|
|
sub split_ip_port
|
|
{
|
|
my ($l) = @_;
|
|
if ($l =~ /^\d+$/) {
|
|
return (undef, $l);
|
|
}
|
|
elsif ($l =~ /^\[(\S+)\]:(\d+)$/) {
|
|
return ($1, $2);
|
|
}
|
|
elsif ($l =~ /^\[(\S+)\]$/) {
|
|
return ($1, 80);
|
|
}
|
|
elsif ($l =~ /^(\S+):(\d+)$/) {
|
|
return ($1, $2);
|
|
}
|
|
else {
|
|
return ($l, 80);
|
|
}
|
|
}
|
|
|
|
# server_desc(&server)
|
|
# Returns a description of a server block
|
|
sub server_desc
|
|
{
|
|
my ($server) = @_;
|
|
my $name = &find_value("server_name", $server);
|
|
return $name ? &text('server_desc', "<tt>".&html_escape($name)."</tt>")
|
|
: $text{'server_descnone'};
|
|
}
|
|
|
|
# is_default_server_block(&server)
|
|
# Returns 1 if a server block is the package default/catch-all server
|
|
sub is_default_server_block
|
|
{
|
|
my ($server) = @_;
|
|
my $name = &find_value("server_name", $server);
|
|
return 1 if (!$name || $name eq "_" || $name eq "-");
|
|
return 0;
|
|
}
|
|
|
|
# location_desc(&server, &location)
|
|
# Returns a description of a location in a server block
|
|
sub location_desc
|
|
{
|
|
my ($server, $location) = @_;
|
|
my $name = &find_value("server_name", $server);
|
|
my $path = &location_path($location);
|
|
return $name ? &text('location_desc', "<tt>".&html_escape($name)."</tt>",
|
|
"<tt>".&html_escape($path)."</tt>")
|
|
: &text('location_descnone',
|
|
"<tt>".&html_escape($path)."</tt>");
|
|
}
|
|
|
|
# match_desc(string)
|
|
# Converts a location match type like ~ into a human-readable mode
|
|
sub match_desc
|
|
{
|
|
my ($m) = @_;
|
|
return $m eq "=" ? $text{'match_exact'} :
|
|
$m eq "~" ? $text{'match_case'} :
|
|
$m eq "~*" ? $text{'match_nocase'} :
|
|
$m eq "^~" ? $text{'match_noregexp'} :
|
|
$m eq "\@" ? $text{'match_named'} :
|
|
$m eq "" ? $text{'match_default'} :
|
|
"Unknown match type $m";
|
|
}
|
|
|
|
sub list_match_types
|
|
{
|
|
return ("", "=", "~", "~*", "^~", "\@");
|
|
}
|
|
|
|
# create_server_link(&server)
|
|
# Creates a link from a directory like sites-enabled to sites-available for
|
|
# a new server block
|
|
sub create_server_link
|
|
{
|
|
my ($server) = @_;
|
|
if ($config{'add_link'}) {
|
|
my $link = $server->{'file'};
|
|
$link =~ s/^.*\///;
|
|
$link = $config{'add_link'}."/".$link;
|
|
&symlink_logged($server->{'file'}, $link);
|
|
}
|
|
}
|
|
|
|
# delete_server_link(&server)
|
|
# Deletes the link from a directory like sites-enabled to sites-available for
|
|
# a server block being removed
|
|
sub delete_server_link
|
|
{
|
|
my ($server) = @_;
|
|
if ($config{'add_link'}) {
|
|
my $file = $server->{'file'};
|
|
my $short = $file;
|
|
$short =~ s/^.*\///;
|
|
opendir(LINKDIR, $config{'add_link'});
|
|
foreach my $f (readdir(LINKDIR)) {
|
|
if ($f ne "." && $f ne ".." &&
|
|
(&resolve_links($config{'add_link'}."/".$f) eq $file ||
|
|
$short eq $f)) {
|
|
&unlink_logged($config{'add_link'}."/".$f);
|
|
}
|
|
}
|
|
closedir(LINKDIR);
|
|
}
|
|
}
|
|
|
|
# delete_server_file_if_empty(&server)
|
|
# If the file for a server is empty, delete it
|
|
sub delete_server_file_if_empty
|
|
{
|
|
my ($server) = @_;
|
|
my $lref = &read_file_lines($server->{'file'}, 1);
|
|
my $count = 0;
|
|
foreach my $l (@$lref) {
|
|
$count++ if ($l =~ /\S/);
|
|
}
|
|
if (!$count) {
|
|
&unlink_logged($server->{'file'});
|
|
}
|
|
}
|
|
|
|
# valid_cert_file(filename)
|
|
# Returns an error message if a cert file is invalid, or undef if OK
|
|
sub valid_cert_file
|
|
{
|
|
my ($file) = @_;
|
|
-r $file && !-d $file || return $text{'ssl_ecertfile'};
|
|
my $data = &read_file_contents($file);
|
|
my @lines = grep { /\S/ } split(/\r?\n/, $data);
|
|
my $begin = "-----BEGIN CERTIFICATE-----";
|
|
my $end = "-----END CERTIFICATE-----";
|
|
$data =~ /$begin/ ||
|
|
return &text('ssl_ecertbegin', "-----BEGIN CERTIFICATE-----");
|
|
$data =~ /$end/ ||
|
|
return &text('ssl_ecertend', "-----END CERTIFICATE-----");
|
|
for(my $i=0; $i<@lines; $i++) {
|
|
$lines[$i] =~ /^-----(BEGIN|END)/ ||
|
|
$lines[$i] =~ /^[A-Za-z0-9\+\/=]+$/ ||
|
|
return &text('ssl_ecertline', $i+1);
|
|
}
|
|
@lines > 4 || return &text('ssl_ecertlines', scalar(@lines));
|
|
return undef;
|
|
}
|
|
|
|
# valid_key_file(filename)
|
|
# Returns an error message if a key file is invalid, or undef if OK
|
|
sub valid_key_file
|
|
{
|
|
my ($file) = @_;
|
|
-r $file && !-d $file || return $text{'ssl_ekeyfile'};
|
|
my $data = &read_file_contents($file);
|
|
my @lines = grep { /\S/ } split(/\r?\n/, $data);
|
|
my $begin = "-----BEGIN (RSA )?PRIVATE KEY-----";
|
|
my $end = "-----END (RSA )?PRIVATE KEY-----";
|
|
$data =~ /$begin/ ||
|
|
return &text('ssl_ekeybegin', "-----BEGIN PRIVATE KEY-----");
|
|
$data =~ /$end/ ||
|
|
return &text('ssl_ekeyend', "-----END PRIVATE KEY-----");
|
|
for(my $i=0; $i<@lines; $i++) {
|
|
$lines[$i] =~ /^-----(BEGIN|END)/ ||
|
|
$lines[$i] =~ /^[A-Za-z0-9\+\/=]+$/ ||
|
|
return &text('ssl_ekeyline', $i+1);
|
|
}
|
|
@lines > 4 || return &text('ssl_ekeylines', scalar(@lines));
|
|
return undef;
|
|
}
|
|
|
|
# can_edit_server(&server)
|
|
# Returns 1 if some server can be managed
|
|
sub can_edit_server
|
|
{
|
|
my ($server) = @_;
|
|
return 1 if (!$access{'vhosts'});
|
|
my $name = &find_value("server_name", $server);
|
|
return 0 if (!$name);
|
|
return &indexoflc($name, split(/\s+/, $access{'vhosts'})) >= 0;
|
|
}
|
|
|
|
# can_directory(dir)
|
|
# Check if some directory is under one of the allowed roots
|
|
sub can_directory
|
|
{
|
|
my ($dir) = @_;
|
|
foreach my $root (split(/\s+/, $access{'root'})) {
|
|
return 1 if (&is_under_directory($root, $dir));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
# switch_write_user(mode)
|
|
# If mode is 1, switch to another user for writing password files.
|
|
# If 0, switch back to root.
|
|
sub switch_write_user
|
|
{
|
|
my ($mode) = @_;
|
|
return if ($access{'user'} eq 'root');
|
|
if ($mode) {
|
|
my @uinfo = getpwnam($access{'user'});
|
|
@uinfo || &error("Write user $access{'user'} does not exist!");
|
|
$) = $uinfo[3]." ".join(" ", $uinfo[2], &other_groups($uinfo[0]));
|
|
$> = $uinfo[2];
|
|
}
|
|
else {
|
|
$) = 0;
|
|
$> = 0;
|
|
}
|
|
}
|
|
|
|
1;
|