#!/usr/bin/perl
# gen-validrcptto.pl
#
# Author: David J. Weller-Fahy <dave dash qmw at weller dash fahy dot com>
#
# Note: Inspired by John Simpson's mkvalidrcptto script (one could almost say
# it's just a heavily modified version of that script).
#
# Generates a list of valid email addresses from the following sources.
# 	- An "assign" file (man qmail-users) on standard input or command-line.
# 	- Any .qmail files in the $HOME directory of a virtual domain's
# 	  controlling user.
# 	- Any .qmail files in /var/qmail/alias.
#
# Obviously, this won't work for normal qmail installations, but for a setup
# similar to that described by the ["Qmail Single UID Howto"][qsuh] it will
# list all email addresses.
#
# [qsuh]: http://www.pgregg.com/projects/qmail/singleuid/index.php
#
# Use: gen-validrcptto.pl /var/qmail/users/assign
#      gen-validrcptto.pl < /var/qmail/users/assign

use strict;
use warnings;

my($vq) = "/var/qmail";

# Get a list of domains for which we accept messages.
my($rcpthosts, @domains);
$rcpthosts = "$vq/control/rcpthosts";
open(FILE, $rcpthosts) or error("unable to open $rcpthosts", 0, 111);
chomp(@domains = <FILE>);
close(FILE);

# Get the host we are claiming to be.
my($me) = "$vq/control/me";
open(FILE, "$me") or error("unable to open $me", 0, 111);
chomp($me = <FILE>);
close(FILE);

# Get a list of the users in the alias directory.
my($alias);
$alias = "$vq/alias" or error("unable to chdir to $alias", 0, 111);
chdir $alias;
for (glob(".qmail-*")) {
	substr($_, 0, 7) = "";
	print "$_\@$me\n";
}

# Build a hash from domain:user pairs in virtualdomains.
my($vdomains, %vdom, $key, $value);
$vdomains = "$vq/control/virtualdomains";
open(FILE, $vdomains) or error("unable to open $vdomains", 0, 111);
while (<FILE>) {
	chomp(($key, $value) = split(/:/));
	$vdom{$key} = $value;
}
close(FILE);

# Grab email addresses in the root of the virtual domain's controlling user.
# Primarily useful for a virtual domain used as a mailing list host.
foreach $key (sort(keys %vdom)) {
	my (undef, undef, undef, undef, undef, undef, undef, $dir, undef, undef) = getpwnam($vdom{$key});
	opendir(DIR, "$dir");
	my $file;
	foreach $file (grep(/^\.qmail-/,readdir(DIR))) {
		substr($file, 0, 7) = "";
		print "$file\@$key\n";
	}
	closedir(DIR);
}

# Iterate over the files specified in @ARGV, or anything coming in on stdin.
while (<>) {
	chomp;

	# Strip leading/trailing whitespace.
	s/^\s+//;
	s/\s+$//;

	# Skip lines which are not assignments.
	next unless m/^[=+]/x;

	# Split each line and get assignment type (see qmail-users(5)).
	my($local, $user, $uid, $gid, $homedir, $dash, $ext, undef) = split(/:/);
	my($type) = substr($local, 0, 1, "");

	# Put domain listed in virtualdomains controlled by $user in $value.
	$value = "";
	foreach $key (sort(keys %vdom)) {
		$value = $key if ($local =~ m/^$vdom{$key}/);
	}

	my($file);
	if ($value) {
		substr($local, 0, length("$vdom{$value}-")) = "";
		if ($type eq "=") {
			print "$local\@$value\n";
		} elsif ($type eq "+") {
			opendir(DIR, "$homedir");
			foreach $file (grep(/^\.qmail$ext/,readdir(DIR))) {
				substr($file, 0, 7) = "";
				print "$local$file\@$value\n" if ($file);
			}
			closedir(DIR);
		}
	} else {
		opendir(DIR, "$homedir");
		foreach $file (grep(/^\.qmail$ext/,readdir(DIR))) {
			substr($file, 0, 7) = "";
			print "$user-$file\@$me\n" if ($file eq $local);
		}
		closedir(DIR);
	}
}

sub error {
my ($message, $sleep, $exitcode) = @_;
	print(STDERR "stop: $message");
	sleep($sleep);
	exit($exitcode);
}
