The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/suidperl

use strict;
our ( $debug, $list_domain, $email, $pw, $list_domain_ut, $email_ut, $pw_ut,
	$dir, @lists, $list, %list_info, $pw_match, @subs, %names,
	$auth_req );

# make tainted environment happy
#delete @ENV{'IFS', 'CDPATH', 'ENV', 'BASH_ENV'};
%ENV = ();
$ENV{PATH} = "/bin:/usr/bin";
#$debug = 1;

# set autoflush on STDOUT
$| = 1;

#
# function defintions
#

# return successful run (search completed, result may be failure or success)
sub success
{
	print "status: success\n";
	foreach ( @_ ) {
		print "$_\n";
	}
	exit 0;
}

# return fatal error
sub fail
{
	my $text = shift;
	print "status: failure\n";
	print "$text\n";
	exit 1;
}

# extract parameters for requested address from list configuration
sub read_list
{
	my $list = shift;

	open ( dumpdb_pipe, "$dir/bin/dumpdb --pickle $dir/lists/$list/config.pck |" )
		or fail "couldn't read list info for $list: $!";
	my $dumpdb = join ( '', <dumpdb_pipe> );
	close dumpdb_pipe
		or fail "couldn't close list info for $list: $!";

	my ( %result );
	if ( $dumpdb =~ /'accept_these_nonmembers': \[([^\]]+)\],/so ) {
		my $accept_these_nonmembers = $1;
		if ( $accept_these_nonmembers =~ /'$email_ut'/ ) {
			$result{accept_these_nonmembers} = 1;
		}
	}
	if ( $dumpdb =~ /'members': \{([^\}]+)\},/so ) {
		my $members = $1;
		if ( $members =~ /'$email_ut': ([0-9]+)/ ) {
			$result{member} = $1;
		}
	}
	if ( $dumpdb =~ /'passwords': \{([^\}]+)\},/so ) {
		my $passwords = $1;
		if ( $passwords =~ /'$email_ut': '([^']+)'/ ) {
			$result{password} = $1;
		}
	}
	if ( $dumpdb =~ /'user_options': \{([^\}]+)\},/so ) {
		my $user_options = $1;
		if ( $user_options =~ /'$email_ut': ([0-9]+)/ ) {
			$result{user_options} = $1;
		}
	}
	if ( $dumpdb =~ /'usernames': \{([^\}]+)\},/so ) {
		my $usernames = $1;
		if ( $usernames =~ /'$email_ut': u'([^']+)'/ ) {
			$result{usernames} = $1;
		}
	}

	return \%result;
}

#
# main
#

# check if setgid is needed
if ( $( != $) ) {
	$( = $);
}

# read parameters from STDIN (usually a pipe except for debugging)
$list_domain = <STDIN>;
$email = <STDIN>;
$pw = <STDIN>;
close STDIN;

# chomp newlines
chomp $list_domain;
$debug and print STDERR "list_domain = $list_domain\n";
chomp $email;
$debug and print STDERR "email = $email\n";
chomp $pw;
$debug and print STDERR "pw = $pw\n";

# force $list_domain and $email to lower case
$list_domain = lc($list_domain);
$email = lc($email);

# fixup domain name - remove www prefix if present
$list_domain =~ s/^www\.//;

# untaint inputs
if ( $list_domain =~ /^([a-z0-9_.-]+)$/ ) {
	$list_domain_ut = $1;
	$debug and print STDERR "list_domain_ut = $list_domain_ut\n";
}
if ( $email =~ /^([a-z0-9_.+=$&*\/-]+\@[a-z0-9_.-]+)$/ ) {
	$email_ut = $1;
	$debug and print STDERR "email_ut = $email_ut\n";
}
if ( $pw =~ /^(\S+)$/ ) {
	$pw_ut = $1;
	$debug and print STDERR "pw_ut = $pw_ut\n";
}

# check if we're doing an authenticated query
$auth_req = 1;  # default
if ( $pw eq "***unauthenticated query***" ) {
	$auth_req = 0;
}

# locate Mailman directory for this domain
$dir = "/home/mailman/$list_domain_ut";
( -d $dir ) or fail ( "lists not found for domain" );

# get list of mailman lists
opendir(DIR, "$dir/lists" ) || fail ( "can't opendir $dir/lists: $!" );
@lists = grep { ! /^\./ and -d "$dir/lists/$_" } readdir(DIR);
closedir DIR;

# loop through the lists collecting info
$pw_match = 0;
foreach $list ( @lists ) {
	my $list_res;
	( $list =~ /^(\S+)$/ ) or next;
	$list_res = read_list( $1 );
	if ( defined $list_res ) {
		$list_info{$list} = $list_res;
		if ( $list_info{$list}{password} eq $pw_ut ) {
			 $pw_match = 1;
		}
		if ( defined $list_info{$list}{member}) {
			push @subs, $list;
		}
		#if ( defined $list_info{$list}{accept_these_nonmembers}) {
		#	push @subs, $list;
		#}
		if ( defined $list_info{$list}{username}) {
			if ( length($list_info{$list}{username}) > 0 ) {
				$names{$list_info{$list}{username}} = 1;
			}
		}
	}
}

$debug and print STDERR "auth_req=$auth_req pw_match = $pw_match\n";
if ( $auth_req and !$pw_match ) {
	success ( "search-status: failure" );
}

my @name_headers;
foreach ( sort keys( %names )) {
	push @name_headers, "name: $_";
}
success ( "search-status: ".($auth_req ? "" : "unauthenticated " )."success",
	"subscriptions: ".join( " ", @subs ),
	@name_headers
);