- #!/usr/bin/perl -wT
- #
- # /usr/local/sbin/localmkpostfixvirtual
- # Copyright 2001-2006 Jonas Smedegaard <dr@jones.dk>
- #
- # $Id: localmkpostfixvirtual,v 1.31 2006-08-22 23:25:23 jonas Exp $
- #
- # Generate virtual file for postfix
- #
- # Hints are stored in the "Other" field (using chfn).
- #
- # Each user should have space-separated hints like this:
- # "mailname1@ mailname2@mailgroup1 mailname3@mailgroup2 mailname4@maildomain7".
- #
- # The user of each mailgroup should have hints like "@domain1 @domain2"
- # for each hosted maildomain.
- #
- # Generate virtual file like this:
- #
- # # ( cd /etc/postfix && localmkpostfixvirtual > virtual.new && diff virtual virtual.new )
- #
- # ..and if the changes are correct, activate the new virtual file:
- #
- # # ( cd /etc/postfix && mv virtual.new virtual && postmap virtual )
- #
- # Optional: Several mailgroups can be tied together (when amount of hints
- # exceeds the limit of the "Other" field: List them all in
- # "Office" or "roomnumber" field of primary mailgroup (include the
- # primary mailgroup itself!).
- #
- # Optional: root can have hints like "postmaster@ hostmaster@ support@"
- # for default accounts tied to the sysadmin (default: "postmaster@").
- #
- # Suggestion: Add mailgroup users like this:
- # adduser --system --no-create-home --group --disabled-password <uid>
- #
- use strict;
- use User::pwent;
- use User::grent;
- my (%username, %fullname, %office, %workphone, %homephone, %other);
- my (%username_by_gid, %addresshints, %domains);
- while (my $pw = getpwent()) {
- $username_by_gid{$pw->gid} = $pw->name;
- ($fullname{$pw->name}, $office{$pw->name}, $workphone{$pw->name}, $homephone{$pw->name}, $other{$pw->name}) = split /\s*,\s*/, $pw->gecos;
- if (defined($other{$pw->name})) {
- @{$addresshints{$pw->name}} = grep {/^([\.[:alnum:]_-]+|\+)?@([\.[:alnum:]_-]+)?$/} split /\s+/, $other{$pw->name};
- }
- }
- my (%owner, %members, %groups);
- while (my $gr = getgrent()) {
- $owner{$gr->name} = $username_by_gid{$gr->gid};
- $members{$gr->name} = $gr->members;
- foreach my $member (@{$members{$gr->name}}) {
- push @{$groups{$member}}, $gr->name unless (defined($owner{$gr->name}) and $member eq $owner{$gr->name});
- }
- }
- my (%warned);
- sub warnonce($$$) {
- my ($test, $id, $warning) = @_;
- if ($test) {
- return 1;
- }
- if ($warned{$id}) {
- return '';
- }
- print STDERR 'W: ' . $warning . "\n";
- $warned{$id} = 1;
- return '';
- }
- sub print_accounts($$$$) {
- my ($username, $mailgroup, $maildomain, $pre_text, $post_fallback_text) = @_;
- ($pre_text) && print $pre_text . "\n";
- my $joker_seen;
- if (&warnonce(defined(@{$addresshints{$username}}), "addresshints_$username", "Skipping non-hinted username \"$username\".")) {
- my @localparthints = @{$addresshints{$username}};
- my @localparts = grep {s/(.+)@($mailgroup|$maildomain)?$/$1/} @localparthints;
- foreach my $localpart (@localparts) {
- for ($localpart) {
- if (/^\+$/) {
- if (!$joker_seen) {
- print "\@$maildomain $username\n";
- $joker_seen = $username;
- } else {
- print "#WARNING: Catch-all for $maildomain already set to $joker_seen!";
- }
- } else {
- print "$localpart\@$maildomain $username\n";
- }
- }
- }
- } else {
- print $post_fallback_text . "\n" if ($post_fallback_text);
- }
- print "\n";
- }
- sub usercomment($) {
- my $user = shift;
- my @s = ();
- if (&warnonce(defined($fullname{$user}), "fullname_$user", "User \"$user\" lacks fullname.")) {
- push @s, $fullname{$user};
- }
- if (&warnonce(defined(@{$groups{$user}}), "groups_$user", "User \"$user\" belongs to no (secondary) group.")) {
- my @groups_sorted = sort @{$groups{$user}};
- push @s, '(' . join(' ', @groups_sorted) . ')';
- }
- if (@s) {
- unshift @s, '#';
- }
- my $string = join(' ', @s);
- return "$string";
- }
- my $loop;
- my @mailgroups = @ARGV ? @ARGV : @{$members{'maildomains'}};
- foreach my $mailgroup (@mailgroups) {
- if (not &warnonce(defined(@{$addresshints{$mailgroup}}), "addresshints_$mailgroup", "Skipping empty mailgroup \"$mailgroup\".")) {
- next;
- }
- my @maildomainhints = @{$addresshints{$mailgroup}};
- my @maildomains = grep {s/^@(.+)/$1/} @maildomainhints;
- foreach my $maildomain (@maildomains) {
- my (@mailgroupowners, @mailusers);
- my @mailgroupgroups = split / +/, $office{$mailgroup};
- push @mailgroupgroups, $mailgroup unless (@mailgroupgroups);
- foreach my $mailgroupgroup (@mailgroupgroups) {
- push @mailgroupowners, $owner{$mailgroupgroup} if ($owner{$mailgroupgroup});
- push @mailusers, @{$members{$mailgroupgroup}};
- }
- my @mailgroupowners_sorted = sort @mailgroupowners;
- my @mailusers_sorted = sort @mailusers;
- if ($loop) {
- print "\n##################################################################\n\n";
- }
- $loop++;
- &print_accounts('root', $mailgroup, $maildomain, "$maildomain VIRTUAL", "postmaster\@$maildomain root");
- # Do mailgroup owners (and don't warn if there's no addresses attached)
- foreach my $mailgroupowner (@mailgroupowners_sorted) {
- &print_accounts($mailgroupowner, $mailgroup, $maildomain, &usercomment($mailgroupowner));
- }
- # Do secondary mailgroup members
- foreach my $mailuser (@mailusers_sorted) {
- &print_accounts($mailuser, $mailgroup, $maildomain, &usercomment($mailuser), "#WARNING: No addresses for $mailuser");
- }
- }
- }
|