my
%defaults
= (
'user-config'
=> 1,
'ident-timeout'
=> 5.0,
'server-scale-period'
=> 2,
'min-children'
=> 1,
'min-spare'
=> 1,
'max-spare'
=> 2,
'max-children'
=> 5,
'max-conn-per-child'
=> 200,
'timeout-child'
=> 300,
'timeout-tcp'
=> 30,
PREFIX
=>
'/usr'
,
DEF_RULES_DIR
=>
'/usr/share/spamassassin'
,
LOCAL_RULES_DIR
=>
'/etc/mail/spamassassin'
,
LOCAL_STATE_DIR
=>
'/var/lib'
,
);
sub
new {
my
(
$class
,
$parms
) =
@_
;
$parms
||= {};
die
'usage: '
. __PACKAGE__
.
'->new({ argv=>\@, defaults=>\%, moreopts=>\@ })'
if
ref
$parms
ne
'HASH'
or
exists
$parms
->{argv} &&
ref
$parms
->{argv} ne
'ARRAY'
or
exists
$parms
->{defaults} &&
ref
$parms
->{defaults} ne
'HASH'
or
exists
$parms
->{moreopts} &&
ref
$parms
->{moreopts} ne
'ARRAY'
;
$parms
->{argv} ||= \
@ARGV
;
local
*ARGV
= [@{
$parms
->{argv} }];
my
$self
= {
exists
$parms
->{defaults} ? %{
$parms
->{defaults} } : () };
Getopt::Long::Configure(
'bundling'
);
GetOptions(
$self
,
qw(
allowed-ips|A=s@ round-robin!
allow-tell|l server-cert=s
auth-ident server-key=s
cf=s@ setuid-with-ldap
configpath|C=s setuid-with-sql|Q
create-prefs|c! siteconfigpath=s
daemonize|d! socketgroup=s
debug|D:s socketmode=s
groupname|g=s socketowner=s
help|h socketpath=s
ident-timeout=f sql-config|q!
ldap-config! ssl
listen-ip|ip-address|i:s@ syslog-socket=s
local|L! syslog|s=s
max-children|m=i timeout-child|t=i
max-conn-per-child=i timeout-tcp|T=i
max-spare=i user-config!
min-children=i username|u=s
min-spare=i version|V
paranoid|P! virtual-config-dir=s
pidfile|r=s vpopmail|v!
port|p=s
home_dir_for_helpers|helper-home-dir|H:s
PREFIX=s
DEF_RULES_DIR=s
LOCAL_RULES_DIR=s
LOCAL_STATE_DIR=s
)
,
x
=>
sub
{
$self
->{
'user-config'
} = 0 },
'F:i'
=>
sub
{
die
"spamd: the -F option has been removed from spamd,"
,
" please remove from your commandline and re-run\n"
;
},
'add-from!'
=>
sub
{
die
"spamd: the --add-from option has been removed from spamd,"
,
" please remove from your commandline and re-run\n"
;
},
'stop-at-threshold|S'
=>
sub
{
warn
"spamd: the --stop-at-threshold|-S option has been deprecated"
,
" and is no longer supported, ignoring\n"
;
},
(
exists
$parms
->{moreopts} ? @{
$parms
->{moreopts}} : ()),
) or
die
'GetOptions() failed'
;
bless
$self
,
$class
;
$self
->_validate_logging;
$self
->_validate;
$self
;
}
sub
_validate {
my
(
$self
) =
@_
;
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'
};
$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
;
}
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)
)
{
$self
->{
$opt
} = Mail::SpamAssassin::Util::untaint_file_path(
File::Spec->rel2abs(
$self
->{
$opt
})
);
}
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)
)
{
die
"ERROR: --$opt='$self->{$opt}' does not exist or not a directory\n"
unless
-d
$self
->{
$opt
};
}
for
my
$opt
(
grep
exists
$self
->{
$_
},
qw(min-spare max-spare)
) {
die
"ERROR: --$opt must be >= 0\n"
if
$self
->{
$opt
} <= 0;
}
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"
;
delete
$self
->{
$opt
};
}
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"
;
}
}
my
$home
=
(
exists
$ENV
{HOME} &&
defined
$ENV
{HOME} && -d
$ENV
{HOME})
?
$ENV
{HOME}
:
undef
;
if
(
exists
$self
->{username})
{
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'
}) {
$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'
};
}
for
my
$opt
(
keys
%defaults
) {
$self
->{
$opt
} =
$defaults
{
$opt
}
if
!
exists
$self
->{
$opt
};
}
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"
;
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'
};
}
1;
}
sub
_validate_logging {
my
$self
=
shift
;
$self
->{debug} ||=
'all'
if
exists
$self
->{debug};
$self
->{debug} ||=
'info'
;
$self
->{
'syslog-socket'
} =
lc
$self
->{
'syslog-socket'
} ||
'unix'
;
$self
->{
'log-facility'
} =
$self
->{syslog} ||
'mail'
;
$self
->{
'log-file'
} =
'spamd.log'
;
if
(
$self
->{
'log-facility'
} =~ /[^a-z0-9]/) {
$self
->{
'log-file'
} =
$self
->{
'log-facility'
};
$self
->{
'syslog-socket'
} =
'file'
;
}
elsif
(
$self
->{
'log-facility'
} eq
'file'
) {
$self
->{
'syslog-socket'
} =
'file'
;
}
else
{
$self
->{
'log-facility'
} =
lc
$self
->{
'log-facility'
};
}
if
(
$self
->{
'syslog-socket'
} eq
'file'
) {
$self
->{
'log-facility'
} =
'file'
;
}
elsif
(
$self
->{
'syslog-socket'
} eq
'none'
) {
$self
->{
'log-facility'
} =
'stderr'
;
}
$self
->{
'syslog-socket'
} =
'file'
if
$self
->{
'log-facility'
} eq
'stderr'
;
1;
}
sub
option {
my
(
$self
,
$opt
) =
@_
;
return
exists
$self
->{
$opt
}
?
$self
->{
$opt
}
:
undef
;
}
1;