sub _validate()

in spamd-apache2/lib/Mail/SpamAssassin/Spamd/Config.pm [167:320]


sub _validate {
  my ($self) = @_;

  # sanity checking on parameters: if --socketpath is used, it means that we're
  # using UNIX domain sockets, none of the IP params are allowed. The code would
  # probably work ok if we didn't check it, but it's better if we detect the
  # error and report it lest the admin find surprises.
  if (exists $self->{'socketpath'}) {
    die "ERROR: --socketpath mutually exclusive with"
      . " --allowed-ips/--ssl/--auth-ident/--port params"
      if exists $self->{'allowed-ips'} && @{ $self->{'allowed-ips'} } > 0
      || exists $self->{'ssl'}
      || exists $self->{'auth-ident'}
      || exists $self->{'port'};
  }
  else {
    die "ERROR: --socketowner/group/mode requires --socketpath param"
      if exists $self->{'socketowner'}
      || exists $self->{'socketgroup'}
      || exists $self->{'socketmode'};

    # These can be changed on command line with -A flag,
    # but only if we're not using UNIX domain sockets
    # warning: no validation here
    $self->{'allowed-ips'} =
      exists $self->{'allowed-ips'} && @{ $self->{'allowed-ips'} }
      ? [map { split /,/, $_; } @{ $self->{'allowed-ips'} }]
      : ['127.0.0.1'];

    $self->{'listen-ip'} =
      !exists $self->{'listen-ip'}
      ? ['127.0.0.1']
      : defined $self->{'listen-ip'} && grep(length, @{ $self->{'listen-ip'} })
        ? [grep length, map { split /,/, $_; } @{ $self->{'listen-ip'} }]
        : undef;          # handle !defined elsewhere
  }

  # bug 2228: make the values of (almost) all parameters which accept file paths
  # absolute, so they are still valid after daemonize()
  for my $opt (
    grep(exists $self->{$_},
    qw(configpath siteconfigpath socketpath pidfile server-cert server-key
       PREFIX DEF_RULES_DIR LOCAL_RULES_DIR LOCAL_STATE_DIR)),
    grep { exists $self->{$_} && $self->{$_} }
    qw(home_dir_for_helpers)    # value is optional
    )
  {
    $self->{$opt} = Mail::SpamAssassin::Util::untaint_file_path(
      File::Spec->rel2abs($self->{$opt})    # rel2abs taints the new value!
    );
  }

  # -d
  for my $opt (
    grep(exists $self->{$_},
    qw(configpath siteconfigpath
       PREFIX DEF_RULES_DIR LOCAL_RULES_DIR LOCAL_STATE_DIR)),
    grep { exists $self->{$_} && $self->{$_} }
    qw(home_dir_for_helpers)    # value is optional
    )
  {
    die "ERROR: --$opt='$self->{$opt}' does not exist or not a directory\n"
      unless -d $self->{$opt};
  }

  # >= 0
  for my $opt (grep exists $self->{$_}, qw(min-spare max-spare)) {
    die "ERROR: --$opt must be >= 0\n" if $self->{$opt} <= 0;
  }

  # >= 1
  for my $opt (
    grep exists $self->{$_},
    qw(timeout-tcp timeout-child min-children max-children max-conn-per-child)
    )
  {
    next if $self->{$opt} >= 1;
    warn "ERROR: --$opt must be >= 1, ignoring\n";    # die?
    delete $self->{$opt};
  }

  # ident-based spamc user authentication
  if ($self->{'auth-ident'}) {
    eval { sub Net::Ident::_export_hooks(); require Net::Ident };
    die "spamd: ident-based authentication requested,",
      " but Net::Ident is unavailable\n"
      if $@;

    if (exists $self->{'ident-timeout'} && $self->{'ident-timeout'} <= 0) {
      die "ERROR: --ident-timeout must be > 0\n";
    }
    ##Net::Ident->import(qw(ident_lookup));
  }

  # let's not modify %ENV here...
  my $home =
    (exists $ENV{HOME} && defined $ENV{HOME} && -d $ENV{HOME})
    ? $ENV{HOME}
    : undef;

  if (exists $self->{username})
  {    # spamd is going to run as another user, so reset $HOME
    if (my $nh = (getpwnam($self->{username}))[7]) {
      $home = $nh;
    }
    else {
      die "spamd: unable to determine home directory for user"
        . " '$self->{username}'\n";
    }
  }

  if (!exists $self->{home_dir_for_helpers}) {
    die "ERROR: \$HOME='$home' does not exist or not a directory\n"
      unless defined $home && -d $home;
    $self->{home_dir_for_helpers} = $home;
  }

  if (exists $self->{'max-spare'}) {
    if (exists $self->{'min-spare'}) {
      ## emulate Apache behaviour:
      ## http://httpd.apache.org/docs-2.0/mod/prefork.html#maxspareservers
      $self->{'max-spare'} = $self->{'min-spare'} + 1
        if $self->{'max-spare'} < $self->{'min-spare'};
    }
    else {
      $self->{'min-spare'} = $self->{'max-spare'};
    }
  }
  elsif (exists $self->{'min-spare'}) {
    $self->{'max-spare'} = $self->{'min-spare'};
  }

  # set other defaults
  for my $opt (keys %defaults) {
    $self->{$opt} = $defaults{$opt} if !exists $self->{$opt};
  }

  # check for server certs, must be done after setting other defaults
  if ($self->{'ssl'}) {
    $self->{'server-key'}  ||= "$self->{LOCAL_RULES_DIR}/certs/server-key.pem";
    $self->{'server-cert'} ||= "$self->{LOCAL_RULES_DIR}/certs/server-cert.pem";
    eval { require IO::Socket::SSL };
    die "spamd: SSL encryption requested, but IO::Socket::SSL is unavailable\n"
      if $@;
    die "spamd: server key file '$self->{'server-key'}' does not exist\n"
      unless -f $self->{'server-key'};
    die "spamd: server certificate file '$self->{'server-cert'}' does not exist\n"
      unless -f $self->{'server-cert'};
  }

  # XXX: delete LOCAL_{RULES,STATE}_DIR and PREFIX if eq $defaults{$_}?

  1;
}