mirror of
https://github.com/webmin/webmin.git
synced 2026-06-10 06:40:32 +01:00
250 lines
10 KiB
Perl
250 lines
10 KiB
Perl
#!/usr/bin/perl
|
|
use strict;
|
|
use warnings;
|
|
use Test::More;
|
|
use Cwd qw(abs_path);
|
|
use File::Temp qw(tempdir);
|
|
|
|
sub script_dir
|
|
{
|
|
my $path = $0;
|
|
if ($path =~ m{^/}) {
|
|
$path =~ s{/[^/]+$}{};
|
|
return $path;
|
|
}
|
|
my $cwd = `pwd`;
|
|
chomp($cwd);
|
|
if ($path =~ m{/}) {
|
|
$path =~ s{/[^/]+$}{};
|
|
return $cwd.'/'.$path;
|
|
}
|
|
return $cwd;
|
|
}
|
|
|
|
my $bindir = script_dir();
|
|
my $rootdir = abs_path("$bindir/../..") or die "rootdir: $!";
|
|
|
|
my $confdir = tempdir(CLEANUP => 1);
|
|
my $vardir = tempdir(CLEANUP => 1);
|
|
|
|
# Global Webmin config
|
|
open(my $cfh, ">", "$confdir/config") or die "config: $!";
|
|
print $cfh "os_type=linux\nos_version=0\n";
|
|
close($cfh);
|
|
open(my $vfh, ">", "$confdir/var-path") or die "var-path: $!";
|
|
print $vfh "$vardir\n";
|
|
close($vfh);
|
|
|
|
# A main.cf to drive the config parser
|
|
my $maincf = "$confdir/main.cf";
|
|
my $mastercf = "$confdir/master.cf";
|
|
open(my $mc, ">", $maincf) or die "main.cf: $!";
|
|
print $mc <<'EOF';
|
|
myhostname = mail.example.com
|
|
mydestination = example.com,
|
|
mail.example.com,
|
|
localhost
|
|
compatibility_level = 2
|
|
util_lt = {{$compatibility_level} < {3} ? {old} : {new}}
|
|
util_ge = {{$compatibility_level} >= {3} ? {high} : {low}}
|
|
util_eq = {{$compatibility_level} == {2} ? {match} : {nomatch}}
|
|
subtest = key1 valueA key2 valueB
|
|
alias_maps = hash:/etc/postfix/aliases, hash:/etc/aliases
|
|
EOF
|
|
close($mc);
|
|
|
|
# A master.cf with one enabled and one disabled service, plus a continuation
|
|
open(my $ms, ">", $mastercf) or die "master.cf: $!";
|
|
print $ms <<'EOF';
|
|
smtp inet n - y - - smtpd
|
|
#qmgr unix n - n 300 1 qmgr
|
|
pickup unix n - y 60 1 pickup
|
|
-o content_filter=foo
|
|
EOF
|
|
close($ms);
|
|
|
|
# Per-module config
|
|
mkdir "$confdir/postfix" or die "postfix confdir: $!";
|
|
open(my $mod, ">", "$confdir/postfix/config") or die "module config: $!";
|
|
print $mod "postfix_config_file=$maincf\n";
|
|
print $mod "postfix_master=$mastercf\n";
|
|
print $mod "postfix_config_command=/bin/true\n";
|
|
print $mod "postfix_control_command=/bin/true\n";
|
|
print $mod "prefix_cmts=0\n";
|
|
close($mod);
|
|
|
|
# Pre-seed the version file so loading the lib does not shell out to postconf.
|
|
open(my $ver, ">", "$confdir/postfix/version") or die "version: $!";
|
|
print $ver "3.4.0\n";
|
|
close($ver);
|
|
|
|
$ENV{'WEBMIN_CONFIG'} = $confdir;
|
|
$ENV{'WEBMIN_VAR'} = $vardir;
|
|
$ENV{'FOREIGN_MODULE_NAME'} = 'postfix';
|
|
$ENV{'FOREIGN_ROOT_DIRECTORY'} = $rootdir;
|
|
|
|
chdir("$bindir/..") or die "chdir: $!";
|
|
|
|
require "$bindir/../postfix-lib.pl";
|
|
our (%config, $postfix_version, $virtual_maps, $ldap_timeout, $config_dir);
|
|
|
|
# --- load-time globals -----------------------------------------------------
|
|
is($postfix_version, '3.4.0', 'postfix_version read from version file');
|
|
is($virtual_maps, 'virtual_alias_maps', 'virtual_maps for >= 2.x');
|
|
is($ldap_timeout, 'ldap_timeout', 'ldap_timeout for >= 2.x');
|
|
is($config_dir, $confdir, 'guess_config_dir is dirname of main.cf');
|
|
|
|
# --- file_map_type ---------------------------------------------------------
|
|
ok(file_map_type('hash'), 'hash is a file map type');
|
|
ok(file_map_type('pcre'), 'pcre is a file map type');
|
|
ok(file_map_type('cidr'), 'cidr is a file map type');
|
|
ok(!file_map_type('mysql'),'mysql is not a file map type');
|
|
ok(!file_map_type('ldap'), 'ldap is not a file map type');
|
|
|
|
# --- get_maps_types_files --------------------------------------------------
|
|
is_deeply([ get_maps_types_files('hash:/etc/postfix/canonical') ],
|
|
[ [ 'hash', '/etc/postfix/canonical' ] ],
|
|
'single type:file parsed');
|
|
is_deeply([ get_maps_types_files(
|
|
'hash:/etc/postfix/canonical, proxy:pcre:/etc/postfix/x') ],
|
|
[ [ 'hash', '/etc/postfix/canonical' ],
|
|
[ 'pcre', '/etc/postfix/x' ] ],
|
|
'multiple maps and proxy: prefix parsed');
|
|
is_deeply([ get_maps_types_files('mysql:/etc/postfix/m.cf') ],
|
|
[ [ 'mysql', '/etc/postfix/m.cf' ] ],
|
|
'mysql backend type:file parsed');
|
|
is_deeply([ get_maps_types_files('') ], [],
|
|
'empty value yields no maps');
|
|
is_deeply([ get_maps_types_files('garbage-without-colon') ], [],
|
|
'unparseable value yields no maps');
|
|
|
|
# --- get_maps_files (path extraction) --------------------------------------
|
|
is_deeply([ get_maps_files('hash:/etc/postfix/aliases,hash:/etc/aliases') ],
|
|
[ '/etc/postfix/aliases', '/etc/aliases' ],
|
|
'get_maps_files extracts both file paths');
|
|
is_deeply([ get_maps_files('static:foo') ], [],
|
|
'get_maps_files ignores non-path map values');
|
|
|
|
# --- get_current_value (main.cf parser) ------------------------------------
|
|
is(get_current_value('myhostname', 1), 'mail.example.com',
|
|
'single-line parameter parsed');
|
|
my $dest = get_current_value('mydestination', 1);
|
|
like($dest, qr/example\.com/, 'continuation line: first value present');
|
|
like($dest, qr/localhost/, 'continuation line: trailing value joined');
|
|
is(get_current_value('no_such_param', 1), undef,
|
|
'unknown parameter with nodef returns undef (no postconf fallback)');
|
|
is(get_current_value('subtest:key1', 1), 'valueA',
|
|
'sub-parameter extraction (foo:bar) returns value after the key');
|
|
|
|
# --- resolve_current_value (compatibility_level conditionals) --------------
|
|
# Exercises the operator-dispatch rewrite of the old stringy eval.
|
|
is(resolve_current_value('util_lt'), 'old',
|
|
'resolve "<": level 2 is < 3, takes true branch');
|
|
is(resolve_current_value('util_ge'), 'low',
|
|
'resolve ">=": level 2 is not >= 3, takes false branch');
|
|
is(resolve_current_value('util_eq'), 'match',
|
|
'resolve "==": level 2 == 2, takes true branch');
|
|
is(resolve_current_value('myhostname'), 'mail.example.com',
|
|
'resolve passes through a plain value unchanged');
|
|
|
|
# --- master.cf parser + serializer -----------------------------------------
|
|
my $master = get_master_config();
|
|
is(ref($master), 'ARRAY', 'get_master_config returns array ref');
|
|
is(scalar(@$master), 3, 'three service entries parsed');
|
|
|
|
my ($smtp) = grep { $_->{'name'} eq 'smtp' } @$master;
|
|
my ($qmgr) = grep { $_->{'name'} eq 'qmgr' } @$master;
|
|
my ($pickup) = grep { $_->{'name'} eq 'pickup' } @$master;
|
|
|
|
ok($smtp, 'smtp service parsed');
|
|
ok($smtp->{'enabled'}, 'smtp is enabled');
|
|
is($smtp->{'type'}, 'inet', 'smtp type');
|
|
is($smtp->{'chroot'}, 'y', 'smtp chroot column');
|
|
is($smtp->{'command'}, 'smtpd','smtp command');
|
|
|
|
ok($qmgr, 'commented service still parsed');
|
|
ok(!$qmgr->{'enabled'}, 'commented service is disabled');
|
|
|
|
ok($pickup, 'pickup service parsed');
|
|
is($pickup->{'command'}, 'pickup -o content_filter=foo',
|
|
'continuation line appended to command');
|
|
|
|
# master_line round-trips an entry back to its on-disk form
|
|
is(master_line($smtp),
|
|
"smtp\tinet\tn\t-\ty\t-\t-\tsmtpd",
|
|
'master_line serializes an enabled service with tabs');
|
|
is(master_line($qmgr),
|
|
"#qmgr\tunix\tn\t-\tn\t300\t1\tqmgr",
|
|
'master_line prefixes a disabled service with #');
|
|
|
|
# --- is_table_comment / make_table_comment ---------------------------------
|
|
{
|
|
local $config{'prefix_cmts'} = 0;
|
|
is(is_table_comment('# hello world'), 'hello world',
|
|
'plain comment text extracted when prefix_cmts off');
|
|
is_deeply([ make_table_comment('a note') ], [ '# a note' ],
|
|
'make_table_comment emits a plain # line');
|
|
is_deeply([ make_table_comment('') ], [],
|
|
'make_table_comment emits nothing for empty comment');
|
|
}
|
|
{
|
|
local $config{'prefix_cmts'} = 1;
|
|
is(is_table_comment('# Webmin: tagged'), 'tagged',
|
|
'Webmin-tagged comment extracted when prefix_cmts on');
|
|
is(is_table_comment('# untagged'), undef,
|
|
'untagged comment ignored when prefix_cmts on');
|
|
is_deeply([ make_table_comment('z') ], [ '# Webmin: z' ],
|
|
'make_table_comment emits a Webmin-tagged line when prefix_cmts on');
|
|
}
|
|
|
|
# --- in_props --------------------------------------------------------------
|
|
is(in_props([ qw(cn foo objectClass top) ], 'objectClass'), 'top',
|
|
'in_props returns value following a matched name');
|
|
is(in_props([ qw(cn foo) ], 'mail'), undef,
|
|
'in_props returns undef for an absent name');
|
|
is(in_props([ qw(CN foo) ], 'cn'), 'foo',
|
|
'in_props matches case-insensitively');
|
|
|
|
# --- get_ldap_key ----------------------------------------------------------
|
|
is_deeply([ get_ldap_key({}) ],
|
|
[ 'mailacceptinggeneralid', '(mailacceptinggeneralid=*)' ],
|
|
'get_ldap_key default attribute and filter');
|
|
is_deeply([ get_ldap_key({ 'query_filter' => 'mail=%s' }) ],
|
|
[ 'mail', '(mail=*)' ],
|
|
'get_ldap_key derives attribute and filter from query_filter');
|
|
|
|
# --- make_map_ldap_dn ------------------------------------------------------
|
|
{
|
|
local $config{'ldap_doms'} = 1; # allow sub-domain DNs
|
|
local $config{'ldap_id'} = undef; # default to cn
|
|
my $conf = { 'search_base' => 'dc=example,dc=com', 'scope' => 'sub' };
|
|
is(make_map_ldap_dn({ 'name' => 'user@dom.com' }, $conf),
|
|
'cn=user,cn=dom.com,dc=example,dc=com',
|
|
'make_map_ldap_dn builds a per-user DN inside a domain');
|
|
is(make_map_ldap_dn({ 'name' => '@dom.com' }, $conf),
|
|
'cn=default,cn=dom.com,dc=example,dc=com',
|
|
'make_map_ldap_dn builds a catch-all DN for a domain');
|
|
is(make_map_ldap_dn({ 'name' => 'literal' }, $conf),
|
|
'cn=literal,dc=example,dc=com',
|
|
'make_map_ldap_dn builds a flat DN for a non-address name');
|
|
}
|
|
|
|
# --- get_backend_config ----------------------------------------------------
|
|
my $backend = "$confdir/mysql.cf";
|
|
open(my $bfh, ">", $backend) or die "backend: $!";
|
|
print $bfh "user = postfix\npassword = secret\n# a comment\nhosts = localhost\n";
|
|
close($bfh);
|
|
my $bc = get_backend_config($backend);
|
|
is($bc->{'user'}, 'postfix', 'backend config user parsed');
|
|
is($bc->{'password'}, 'secret', 'backend config password parsed');
|
|
is($bc->{'hosts'}, 'localhost', 'backend config hosts parsed');
|
|
|
|
# --- list_smtpd_restrictions (version-dependent constant) ------------------
|
|
my @restr = list_smtpd_restrictions();
|
|
ok((grep { $_ eq 'permit_mynetworks' } @restr),
|
|
'smtpd restrictions include permit_mynetworks');
|
|
ok((grep { $_ eq 'reject_unknown_reverse_client_hostname' } @restr),
|
|
'>= 2.3 uses reject_unknown_reverse_client_hostname');
|
|
|
|
done_testing();
|