summaryrefslogtreecommitdiff
path: root/localmkpostfixvirtual
blob: b404d504bfc60fc96a59cae21f5cb01bf444341b (plain)
  1. #!/usr/bin/perl -wT
  2. #
  3. # /usr/local/sbin/localmkpostfixvirtual
  4. # Copyright 2001-2006 Jonas Smedegaard <dr@jones.dk>
  5. #
  6. # $Id: localmkpostfixvirtual,v 1.24 2006-08-22 22:45:47 jonas Exp $
  7. #
  8. # Generate virtual file for postfix
  9. #
  10. # Hints are stored in the "Other" field (using chfn).
  11. #
  12. # Each user should have space-separated hints like this:
  13. # "mailname1@ mailname2@mailgroup1 mailname3@mailgroup2 mailname4@maildomain7".
  14. #
  15. # The user of each mailgroup should have hints like "@domain1 @domain2"
  16. # for each hosted maildomain.
  17. #
  18. # Generate virtual file like this:
  19. #
  20. # # ( cd /etc/postfix && localmkpostfixvirtual > virtual.new && diff virtual virtual.new )
  21. #
  22. # ..and if the changes are correct, activate the new virtual file:
  23. #
  24. # # ( cd /etc/postfix && mv virtual.new virtual && postmap virtual )
  25. #
  26. # Optional: Several mailgroups can be tied together (when amount of hints
  27. # exceeds the limit of the "Other" field: List them all in
  28. # "Office" or "roomnumber" field of primary mailgroup (include the
  29. # primary mailgroup itself!).
  30. #
  31. # Optional: root can have hints like "postmaster@ hostmaster@ support@"
  32. # for default accounts tied to the sysadmin (default: "postmaster@").
  33. #
  34. # Suggestion: Add mailgroup users like this:
  35. # adduser --system --no-create-home --group --disabled-password <uid>
  36. #
  37. use strict;
  38. use User::pwent;
  39. use User::grent;
  40. #use Data::Dumper;
  41. my (%username, %fullname, %office, %workphone, %homephone, %other);
  42. my (%username_by_gid, %addresshints, %domains);
  43. while (my $pw = getpwent()) {
  44. $username_by_gid{$pw->gid} = $pw->name;
  45. ($fullname{$pw->name}, $office{$pw->name}, $workphone{$pw->name}, $homephone{$pw->name}, $other{$pw->name}) = split /\s*,\s*/, $pw->gecos;
  46. if (defined($other{$pw->name})) {
  47. @{$addresshints{$pw->name}} = grep {/^([\.[:alnum:]_-]+|\+)?@([\.[:alnum:]_-]+)?$/} split /\s+/, $other{$pw->name};
  48. }
  49. #DEBUG: ($pw->name eq "annette") and print STDERR Dumper(@{$addresses{$pw->name}});
  50. }
  51. my (%owner, %members, %groups);
  52. while (my $gr = getgrent()) {
  53. $owner{$gr->name} = $username_by_gid{$gr->gid};
  54. $members{$gr->name} = $gr->members;
  55. foreach my $member (@{$members{$gr->name}}) {
  56. push @{$groups{$member}}, $gr->name unless (defined($owner{$gr->name}) and $gr->name eq $owner{$gr->name});
  57. }
  58. }
  59. my (%warned);
  60. sub warnonce($$$) {
  61. my ($test, $id, $warning) = @_;
  62. if ($test) {
  63. return 1;
  64. }
  65. if ($warned{$id}) {
  66. return '';
  67. }
  68. print STDERR 'W: ' . $warning . "\n";
  69. $warned{$id} = 1;
  70. return '';
  71. }
  72. sub print_accounts($$$$) {
  73. my ($username, $mailgroup, $maildomain, $pre_text, $post_fallback_text) = @_;
  74. ($pre_text) && print $pre_text . "\n";
  75. my $user_seen;
  76. my $joker_seen;
  77. #DEBUG: print STDERR "$mailgroup $maildomain\n";
  78. #DEBUG: ($username eq "annette") and print STDERR Dumper(@{$addresshints{$username}});
  79. if (not &warnonce(defined(@{$addresshints{$username}}), "addresshints_$username", "Skipping non-hinted username \"$username\".")) {
  80. return '';
  81. }
  82. my @localparthints = @{$addresshints{$username}};
  83. my @localparts = grep {s/(.+)@($mailgroup|$maildomain)?$/$1/} @localparthints;
  84. foreach my $localpart (@localparts) {
  85. for ($localpart) {
  86. if (/^\+$/) {
  87. if (!$joker_seen) {
  88. print "\@$maildomain $username\n";
  89. $joker_seen = $username;
  90. } else {
  91. print "#WARNING: Catch-all for $maildomain already set to $joker_seen!";
  92. }
  93. } else {
  94. print "$localpart\@$maildomain $username\n";
  95. }
  96. }
  97. $user_seen++;
  98. }
  99. print $post_fallback_text . "\n" if ($post_fallback_text && !$user_seen);
  100. print "\n";
  101. }
  102. sub usercomment($) {
  103. my $user = shift;
  104. my @s = ();
  105. if (&warnonce(defined($fullname{$user}), "fullname_$user", "User \"$user\" lacks fullname.")) {
  106. push @s, ' ' . $fullname{$user};
  107. }
  108. if (&warnonce(defined(@{$groups{$user}}), "groups_$user", "User \"$user\" belongs to no (secondary) group.")) {
  109. push @s, ' (' . join(' ', @{$groups{$user}}) . ')';
  110. }
  111. if (@s) {
  112. unshift @s, '#';
  113. }
  114. my $string = join(' ', @s);
  115. return "$string";
  116. }
  117. my $loop;
  118. my @mailgroups = @ARGV ? @ARGV : @{$members{'maildomains'}};
  119. foreach my $mailgroup (@mailgroups) {
  120. if (not &warnonce(defined(@{$addresshints{$mailgroup}}), "addresshints_$mailgroup", "Skipping empty mailgroup \"$mailgroup\".")) {
  121. return '';
  122. }
  123. my @maildomainhints = @{$addresshints{$mailgroup}};
  124. my @maildomains = grep {s/^@(.+)/$1/} @maildomainhints;
  125. foreach my $maildomain (@maildomains) {
  126. my (@mailgroupowners, @mailusers);
  127. my @mailgroupgroups = split / +/, $office{$mailgroup};
  128. push @mailgroupgroups, $mailgroup unless (@mailgroupgroups);
  129. foreach my $mailgroupgroup (@mailgroupgroups) {
  130. push @mailgroupowners, $owner{$mailgroupgroup} if ($owner{$mailgroupgroup});
  131. push @mailusers, @{$members{$mailgroupgroup}};
  132. }
  133. my @mailgroupowners_sorted = sort @mailgroupowners;
  134. my @mailusers_sorted = sort @mailusers;
  135. #DEBUG: print STDERR Dumper(@mailusers); die;
  136. if ($loop) {
  137. print "\n##################################################################\n\n";
  138. }
  139. $loop++;
  140. &print_accounts('root', $mailgroup, $maildomain, "$maildomain VIRTUAL", "postmaster\@$maildomain root");
  141. # Do mailgroup owners (and don't warn if there's no addresses attached)
  142. foreach my $mailgroupowner (@mailgroupowners_sorted) {
  143. &print_accounts($mailgroupowner, $mailgroup, $maildomain, '# ' . $fullname{$mailgroupowner} . ' (' . join(' ', @{$groups{$mailgroupowner}}) . ')');
  144. }
  145. # Do secondary mailgroup members
  146. foreach my $mailuser (@mailusers_sorted) {
  147. # &print_accounts($mailuser, $mailgroup, $maildomain, '# ' . $fullname{$mailuser} . ' (' . join(' ', @{$groups{$mailuser}}) . ')', "#WARNING: No addresses for $mailuser");
  148. &print_accounts($mailuser, $mailgroup, $maildomain, &usercomment($mailuser), "#WARNING: No addresses for $mailuser");
  149. }
  150. }
  151. }