File: //var/usr/webmin-1.941/majordomo/majordomo-lib.pl
# majordomo-lib.pl
# Common majordomo functions
BEGIN { push(@INC, ".."); };
use WebminCore;
&init_config();
%MAJOR_ENV = ( 'HOME', $config{'program_dir'} );
# get_config()
# Parse and return the majordomo config file
sub get_config
{
local(@rv, $line);
open(CONF, $config{'majordomo_cf'});
while(<CONF>) {
s/\r|\n//g;
if (/^\s*\$(\S+)\s*=\s*"(.*)";\s*$/ ||
/^\s*\$(\S+)\s*=\s*'(.*)';\s*$/) {
# static config option
push(@rv, { 'name' => $1,
'value' => &perl_unescape($2),
'line' => $line,
'eline' => $line });
}
elsif (/^\s*\$(\S+)\s*=\s*<<\s*'(\S+)';\s*$/) {
# multiline config option
local $o = { 'name' => $1,
'line' => $2 };
local $end = $3;
while(<CONF>) {
$line++;
last if ($_ =~ /^$end[\r\n]+$/);
$o->{'value'} .= $_;
}
$o->{'eline'} = $line;
push(@rv, $o);
}
# elsif (/^\s*\$(\S+)\s*=\s*\$ENV\{['"]([^'"]+)['"]\};\s*$/) {
# # from majordomo environment variable
# push(@rv, { 'name' => $1,
# 'value' => $MAJOR_ENV{$2},
# 'line' => $line,
# 'eline' => $line });
# }
elsif (/^\s*\$(\S+)\s*=\s*(.*);\s*$/) {
# computed config option
push(@rv, { 'name' => $1,
'value' => $2,
'computed' => 1,
'line' => $line,
'eline' => $line });
}
$line++;
}
close(CONF);
return \@rv;
}
# save_directive(&config, name, value)
# Update some directive in the global config file
sub save_directive
{
local $old = &find($_[1], $_[0]);
return if (!$old);
local $lref = &read_file_lines($config{'majordomo_cf'});
local $olen = $old->{'eline'} - $old->{'line'} + 1;
local $v = $_[2];
$v =~ s/\n$//;
if ($v =~ /\n/) {
splice(@$lref, $old->{'line'}, $olen,
( "\$$_[1] = <<'END';", split(/\n/, $v, -1), "END" ));
}
else {
$v =~ s/\@/\\@/g;
splice(@$lref, $old->{'line'}, $olen, "\$$_[1] = \"$v\";");
}
}
# find(name, &array)
sub find
{
local($c, @rv);
foreach $c (@{$_[1]}) {
if ($c->{'name'} eq $_[0]) {
push(@rv, $c);
}
}
return @rv ? wantarray ? @rv : $rv[0]
: wantarray ? () : undef;
}
# find_value(name, &array)
sub find_value
{
local(@v);
@v = &find($_[0], $_[1]);
@v = grep { !$_->{'computed'} } @v;
if (!@v) { return undef; }
elsif (wantarray) { return map { $_->{'value'} } @v; }
else { return $v[0]->{'value'}; }
}
# list_lists(&config)
# Returns a list of mailing list names
sub list_lists
{
local ($l, @rv);
local $ldir = &perl_var_replace(&find_value("listdir", $_[0]), $_[0]);
opendir(DIR, $ldir);
while($l = readdir(DIR)) {
if ($l =~ /^(\S+)\.config$/ && $1 !~ /\.old$/) {
push(@rv, $1);
}
}
closedir(DIR);
return @rv;
}
# get_list(name, &config)
# Returns the details of some list
sub get_list
{
local $ldir = &perl_var_replace(&find_value("listdir", $_[1]), $_[1]);
local %list;
return undef if (!-r "$ldir/$_[0].config");
$list{'name'} = $_[0];
$list{'members'} = "$ldir/$_[0]";
$list{'config'} = "$ldir/$_[0].config";
$list{'info'} = "$ldir/$_[0].info";
$list{'intro'} = "$ldir/$_[0].intro";
return \%list;
}
# get_list_config(file)
sub get_list_config
{
local(@rv, $line);
$lnum = 0;
open(CONF, $_[0]);
while(<CONF>) {
s/\r|\n//g;
s/#.*$//g;
if (/^\s*(\S+)\s*=\s*(.*)$/) {
# single value
push(@rv, { 'name' => $1,
'value' => $2,
'index' => scalar(@rv),
'line' => $lnum,
'eline' => $lnum });
}
elsif (/^\s*(\S+)\s*<<\s*(\S+)/) {
# multi-line value
local $c = { 'name' => $1,
'index' => scalar(@rv),
'line' => $lnum };
local $end = $2;
while(<CONF>) {
$lnum++;
last if (/^$end[\r\n]+$/);
s/^--/-/;
s/^-\n/\n/;
$c->{'value'} .= $_;
}
$c->{'eline'} = $lnum;
push(@rv, $c);
}
$lnum++;
}
return \@rv;
}
# save_list_directive(&config, file, name, value, multiline)
sub save_list_directive
{
local $old = &find($_[2], $_[0]);
local $lref = &read_file_lines($_[1]);
local ($pos, $olen, $nlen);
if ($old) {
$olen = $old->{'eline'} - $old->{'line'} + 1;
$pos = $old->{'line'};
}
else {
$olen = 0;
$pos = @$lref;
}
if ($_[4]) {
local $ov = $old->{'value'};
$ov =~ s/\n$//;
local $v = $_[3];
$v =~ s/\n$//;
local @lines = split(/\n/, $v, -1);
@lines = map { s/^-/--/; s/^$/-/; $_ } @lines;
splice(@$lref, $pos, $olen, ("$_[2] << END", @lines, "END"))
if (!$old || $v ne $ov);
$nlen = (!$old || $v ne $ov) ? @lines + 2 : $olen;
}
else {
splice(@$lref, $pos, $olen, "$_[2] = $_[3]")
if (!$old || $_[3] ne $old->{'value'});
$nlen = 1;
}
if ($old && $nlen != $olen) {
foreach $c (@{$_[0]}) {
if ($c->{'line'} > $old->{'eline'}) {
$c->{'line'} += ($nlen - $olen);
$c->{'eline'} += ($nlen - $olen);
}
}
}
}
# get_aliases_file()
# Returns the paths to the sendmail-style aliases files
sub get_aliases_file
{
if ($config{'aliases_file'} eq 'postfix') {
# Get from Postfix config
&foreign_require("postfix", "postfix-lib.pl");
local @afiles = &postfix::get_aliases_files(
&postfix::get_current_value("alias_maps"));
$aliases_module = "postfix";
return \@afiles;
}
else {
&foreign_require("sendmail", "sendmail-lib.pl");
&foreign_require("sendmail", "aliases-lib.pl");
$aliases_module = "sendmail";
if ($config{'aliases_file'} eq '') {
# Get from Sendmail
local $sm_conf = &sendmail::get_sendmailcf();
return &sendmail::aliases_file($sm_conf);
}
else {
# Use fixed file
return [ $config{'aliases_file'} ];
}
}
}
# perl_unescape(string)
# Converts a string like "hello\@there\\foo" to "hello@there\foo"
sub perl_unescape
{
local $v = $_[0];
$v =~ s/\\(.)/$1/g;
return $v;
}
# perl_var_replace(string, &config)
# Replaces variables like $foo in a string with their value from
# the config file
sub perl_var_replace
{
local $str = $_[0];
local %donevar;
while($str =~ /\$([A-z0-9\_]+)/ && !$donevar{$1}) {
$donevar{$1}++;
local $val = &find_value($1, $_[1]);
$str =~ s/\$([A-z0-9\_]+)/$val/;
}
return $str;
}
# set_permissions(file)
# Sets the ownership and permissions on some file or directory,
# based on the ownership of the lists directory
sub set_permissions
{
local $conf = &get_config();
local $ldir = &perl_var_replace(&find_value("listdir", $conf), $conf);
local @ldir = stat($ldir);
chown($ldir[4], $ldir[5], $_[0]);
if ($config{'perms'}) {
chmod(-d $_[0] ? 0755 : 0644, $_[0]);
}
else {
chmod(-d $_[0] ? 0775 : 0664, $_[0]);
}
}
# choice_input(name, text, &config, [opt, display]+)
sub choice_input
{
local $v = &find_value($_[0], $_[2]);
local $rv = "<td><b>$_[1]</b></td> <td nowrap>";
for($i=3; $i<@_; $i+=2) {
local $ch = $v eq $_[$i] ? "checked" : "";
$rv .= "<input name=$_[0] type=radio value='$_[$i]' $ch> ".$_[$i+1];
}
$rv .= "</td>\n";
return $rv;
}
# save_choice(&config, file, name)
sub save_choice
{
&save_list_directive($_[0], $_[1], $_[2], $in{$_[2]});
}
# opt_input(name, text, &config, default, size, [units])
sub opt_input
{
local $v = &find_value($_[0], $_[2]);
local $rv = "<td><b>$_[1]</b></td> <td nowrap ".
($_[4] > 30 ? "colspan=3" : "").">";
$rv .= sprintf "<input type=radio name=$_[0]_def value=1 %s> $_[3]\n",
$v eq "" ? "checked" : "";
$rv .= sprintf "<input type=radio name=$_[0]_def value=0 %s>\n",
$v eq "" ? "" : "checked";
local $passwd = $_[0] =~ /passwd/ ? "type=password" : "";
$rv .= "<input $passwd name=$_[0] size=$_[4] value=\"$v\"> $_[5]</td>\n";
return $rv;
}
# save_opt(&config, file, name, [&func])
sub save_opt
{
if ($in{"$_[2]_def"}) { &save_list_directive($_[0], $_[1], $_[2], ""); }
elsif ($_[3] && ($err = &{$_[3]}($in{$_[2]}))) { &error($err); }
else { &save_list_directive($_[0], $_[1], $_[2], $in{$_[2]}); }
}
# select_input(name, text, &config, [opt, display]+)
sub select_input
{
local $v = &find_value($_[0], $_[2]);
local $rv = "<td><b>$_[1]</b></td> <td nowrap><select name=$_[0]>";
for($i=3; $i<@_; $i+=2) {
local $ch = $v eq $_[$i] ? "selected" : "";
$rv .= "<option value='$_[$i]' $ch>".$_[$i+1]."</option>";
}
$rv .= "</select></td>\n";
return $rv;
}
# save_select(&config, file, name)
sub save_select
{
&save_list_directive($_[0], $_[1], $_[2], $in{$_[2]});
}
# multi_input(name, text, &config)
sub multi_input
{
local $v = &find_value($_[0], $_[2]);
local $rv = "<td valign=top><b>$_[1]</b></td> <td colspan=3>".
"<textarea rows=4 cols=80 name=$_[0]>\n$v</textarea></td>\n";
return $rv;
}
# save_multi(&config, file, name)
sub save_multi
{
$in{$_[2]} =~ s/\r//g;
&save_list_directive($_[0], $_[1], $_[2], $in{$_[2]}, 1);
}
# can_edit_list(&access, name)
sub can_edit_list
{
foreach (split(/\s+/, $_[0]->{'lists'})) {
return 1 if ($_ eq "*" || $_ eq $_[1]);
}
return 0;
}
# homedir_valid(&config)
sub homedir_valid
{
local $homedir = &find_value("homedir", $_[0]);
if (!-d $homedir) {
local $homeused;
foreach $c (@$conf) {
$homeused++ if ($c->{'value'} =~ /\$homedir/);
}
if ($homeused) {
return 0;
}
}
return 1;
}
1;