#!/usr/local/bin/perl # # -*-Perl-*- #============================================================================ # MJ_BUILD_ALIASES # # This routine builds a Majordomo aliases using the ownership # info in the individual list config files. Lists without an # "owner" defined will not be activated in the aliases file. # The resulting aliases file can then be combined with other # system aliases to make a complete aliases file for sendmail. # # In addition, the "owner" field is used to automatically define # a "majordomo-users" mailing list. Addresses listed in the # %ignore_owners array will not be included in the list. # #============================================================================ # THE BEST WAY TO IMPLEMENT: # # 1) Create a file called .args (usually mj_build_aliases.args) # in the Majordomo directory containing all the arguments that would # normally be used on the command-line (eg, -a -d -o root). # # 2) Add a cronjob (timing of your choice) for: # /path/to/wrapper mj_build_aliases @ # # 3) Implement mj_create_list/mj_delete_list (in MajorCool /contrib/). # These programs automatically support "mj_build_aliases @". # # You will be left with a system that updates its aliases upon every # list add or delete, as well as hourly/daily/cron-ly, based upon the # configuration you have defined in mj_build_aliases.args. # # Cool! (MajorCool!) # #============================================================================ # HISTORY # # 2.1 1998/11/09 18:34:22 bhoule # - Fixed "nobody" expansion on resend command line. # # 2.0 1998/02/24 20:34:22 bhoule # - @filename arg support so build can be called from create/delete # scripts without hardcoding any command-line arguments. # - Added -a/-d archive/digest command line support. # ... -n for sendmail 8.8 ,nobody expansion # ... -k to keep lists associated with owners in list-owner file # ... -s outgoing suffix flag # - Changed meaning of -O, -o, and -A to make more sense. # # 1.3 1997/07/16 15:16:35 ricky # - Removed RCS tagging of alias file. # # 1.2 1997/07/16 15:13:01 ricky # - Adding a new flag: -m (mail) to send via e-mail a report of changes, # computed from the old $aliases file. (Verbose mode will also print # the changes on STDERR. # - Changing the command line options parsing, now it uses Getopts. # # 1.1 1997/07/16 15:02:34 ricky # Initial revision by ricky@ornet.co.il # # 1.0 # Released as part of MajorCool package. # #============================================================================ $main'program_name = 'mj_build_aliases'; $MJBA_revision = ' $Revision: 2.0 $ '; $MJBA_revision =~ s/^\D*(\d+\.\d+)\D*$/$1/; $| = 0; # change these to match the programs you might use # $mj_resend = "resend"; $mj_archive = "archive"; $mj_digest = "digest"; $mj_log = "mj_log"; $mj_filter = "mj_filter"; $mj_owner_bounce = "mj_owner_bounce"; $mj_list_unavail = "mj_list-unavail"; #============================================================================ # DEFAULTS #============================================================================ $cf = $ENV{"MAJORDOMO_CF"} || "/etc/majordomo.cf"; $aliases = "majordomo_aliases"; $owner_list = "majordomo-users"; $owner = "postmaster"; $suffix = "outgoing"; # these owners will not be added to the $owner_list list # %ignore_owners = ( 'nobody', 1, 'postmaster', 1, ); require "getopts.pl"; # This code loosely based on resend's use of '@' files. If the # first argument is "@filename", we will read the real arguments # from "filename" and shove them onto the ARGV for processing # by &Getopts(). # # If '@' but no filename is apecified, then '@.args' # is assumed. This lets us call mj_build_aliases from other # programs without hardcoding the arguments within. # if ($ARGV[0] =~ /^\@/) { $fn = shift(@ARGV); $fn =~ s/^@//; $fn = "$0.args" unless $fn; open(AV, $fn) || die("open(AV, \"$fn\"): $!\nStopped"); undef($/); # set input field separator $av = ; # read whole file into string close(AV); @av = split(/\s+/, $av); unshift(@ARGV, @av); $/ = "\n"; } die(" Usage: $main'program_name [-C config_file] [-A aliases_file] [-O owners_list] [-o owner] [-v] [-f] [-m] [-t] [-n] Options: -C config_file Majordomo config ($cf) -A aliases_file destination aliases file ($aliases) -O owners_list list-owners list ($owner_list) -o owner Majordomo mail owner ($owner) -s suffix outgoing alias suffix ($suffix) -a create archive aliases -d create digest aliases -n use sendmail 8.8 'nobody' expansion -k keep lists with owners in $owner_list -m mail results to owner -f force rebuild -v verbose -t test only ") unless &Getopts("C:A:O:o:s:adnkmftv"); $cf = $opt_C if $opt_C; $aliases = $opt_A if $opt_A; $owner_list = $opt_O if $opt_O; $owner = $opt_o if $opt_o; $suffix = $opt_s if $opt_s; $nobody = ",nobody" if $opt_n; #============================================================================ # MAJORDOMO INITIALIZATION #============================================================================ # Read and execute the .cf file die("$cf not readable; stopped") unless -r $cf; die("require of majordomo.cf failed $@") unless require $cf; # Go to the home directory specified by the .cf file chdir("$homedir"); # All these should be in the standard PERL library unshift(@INC, $homedir); require "ctime.pl"; # To get MoY definitions for month abbrevs require "majordomo_version.pl"; # What version of Majordomo is this? require "majordomo.pl"; # all sorts of general-purpose Majordomo subs require "shlock.pl"; # NNTP-style file locking require "config_parse.pl"; # functions to parse the config files # Here's where the fun begins... # check to see if the cf file is valid die("listdir not defined. Is majordomo.cf being included correctly?") if !defined($listdir); # where do we look for files, by default? $filedir = $listdir unless defined($filedir); $filedir_suffix = ".archive" unless defined($filedir_suffix); #============================================================================ # ALIAS COMMANDS #============================================================================ $wrapper = "|$homedir/wrapper"; $majordomo_cmd = "$wrapper majordomo"; # "-l $list" optional $digest_cmd = "$wrapper $mj_digest -rlC"; # "$list", "$list-$suffix" $resend_cmd = "$wrapper $mj_resend -l"; # "$list", "$list-$suffix" $archive_cmd = "$wrapper $mj_archive -a -f"; # archive-base, freq (-[dmy]) $log_cmd = "$wrapper $mj_log"; # "$list" needed $filter_cmd = "$wrapper $mj_filter"; # no args needed $bounce_cmd = "$wrapper $mj_owner_bounce"; # no args needed $unavail_cmd = "$wrapper $mj_list_unavail"; # "$list" needed # check if process already running # unless ($opt_t || &shlock("$listdir/.build.LOCK")) { printf STDERR "$main'program_name: build already running\n" if $opt_v; exit; } # check if $listdir or config files have changed # if (-r "$ENV{HOME}/$aliases" && !$opt_f) { printf STDERR "$main'program_name: checking for file changes\n" if $opt_v; unless (`find $listdir \\( -type d -name $listdir -o -type f -name '*.config' \\) -follow -newer $ENV{HOME}/$aliases -print`) { print STDERR "$main'program_name: no Majordomo list changes detected\n"; exit(0); } } # open alias file # unless ($opt_t) { printf STDERR "$main'program_name: rebuilding $aliases\n" if $opt_v; open(STDOUT, ">$ENV{HOME}/$aliases.tmp"); } # match all lists # local($nlists_total, $nlists_active) = 0; opendir(RD_DIR, $listdir) || die("opendir failed $!"); @lists = readdir(RD_DIR); closedir(RD_DIR); # these are top-level MD aliases # printf STDERR "$main'program_name: adding Majordomo aliases\n" if $opt_v; print <<"EOM"; #----------------------------------------------------------------------- # BEGIN MAJORDOMO CONFIGURATION ($aliases) # Built by $main'program_name (Rev. $MJBA_revision) #----------------------------------------------------------------------- # majordomo: "$majordomo_cmd" majordomo-owner: $owner$nobody owner-majordomo: $owner$nobody owner-owner: $owner$nobody listmanager: $owner$nobody corp-pubs: "$filter_cmd" EOM foreach (sort @lists) { local($list) = $_; $list =~ s,^.*/,,; # strip off leading path $list =~ /[^-_0-9a-zA-Z]/ && next; # skip non-list files (*.info, etc.) # get/make config file &get_config($listdir, $list) if !&cf_ck_bool($list, '', 1); # make alias entries $owner = &do_list_aliases($list); if ($owner) { $owner =~ tr/A-Z/a-z/; # match lowercase next if $ignore_owners{$owner}; # some we ignore $owner_list{$owner} .= "$list,"; # save the owner } } print <<"EOM"; #----------------------------------------------------------------------- # END MAJORDOMO CONFIGURATION #----------------------------------------------------------------------- EOM exit(0) if $opt_t; close(STDOUT); # Make a backup copy of the $aliases file # rename("$ENV{HOME}/$aliases", "$ENV{HOME}/$aliases.bak") if -f "$ENV{HOME}/$aliases"; rename("$ENV{HOME}/$aliases.tmp", "$ENV{HOME}/$aliases"); &mail_diff("$ENV{HOME}/$aliases", "$ENV{HOME}/$aliases.bak") if $opt_m; printf STDERR "$main'program_name: $nlists_total lists processed ($nlists_active active)\n" if $opt_v; # open owner-list file # printf STDERR "$main'program_name: rebuilding $owner_list\n" if $opt_v; open(FILE, ">$listdir/$owner_list"); foreach (keys(%owner_list)) { chop($owner_list{$_}); print FILE "$_ ".($opt_k ? "($owner_list{$_})\n" : "\n"); } close(FILE); unlink("$listdir/.build.LOCK"); # do all additional tasks as background process # if (-x "$homedir/mj_background_tasks") { printf STDERR "$main'program_name: invoking background tasks\n" if $opt_v; system "nohup $homedir/wrapper mj_background_tasks" . ($opt_v ? " -v" : "") . "&"; } #============================================================================ # MISC FUNCTIONS #============================================================================ sub do_list_aliases { local($list) = shift; local($list_owner,$list_moderator,$supplemental); $list_owner = $config_opts{$list,'owner'}; $list_moderator = $config_opts{$list,'moderator'}; $list_moderator = $list_owner unless ($list_moderator); # 'owner' keyword is not implemented! default to Mj owner $list_owner = $list_moderator = $owner unless defined($config_opts{$list,'owner'}); # check validity of owner/moderator addresses $addrs = ""; foreach (split(',', $list_owner)) { $addrs = join(',', $addrs, &valid_addr($_)); } $list_owner = $addrs; $list_owner =~ s/^,//; # $addrs = ""; foreach (split(/,/, $list_moderator)) { $addrs = join(',', $addrs, &valid_addr($_)); } $list_moderator = $addrs; $list_moderator =~ s/^,//; # Archive and Digest are stubs for work-in-progress if (&cf_ck_bool($list,'archive') && $opt_a) { local($dir,$freq); printf STDERR "$main'program_name: <$list> is archived\n" if $opt_v; $dir = $config_opts{$list,'archive_dir'}; $dir = "$filedir/$list$filedir_suffix" unless $dir; unless (-d $dir) { printf STDERR "$main'program_name: creating archive directory\n" if $opt_v; printf STDERR "$main'program_name: $dir mkdir failed ($!)\n" unless mkdir($dir, 0770); } $freq = $config_opts{$list,'archive_frequency'}; $freq = "-d" if $freq eq "daily"; $freq = "-m" if $freq eq "monthly"; $freq = "-y" if $freq eq "yearly"; $freq = "-m" unless $freq; # default $supplemental .= ", \"$archive_cmd $dir/archive $freq\""; } if (&cf_ck_bool($list,'digest') && $opt_d) { printf STDERR "$main'program_name: <$list> is digested\n" if $opt_v; $supplemental .= ", \"$digest_cmd $list-digest $list-digest-$suffix\""; } $nlists_total++; unless ($list_owner && $list_moderator) { printf STDERR "$main'program_name: owner/moderator invalid or not specified for <$list>\n" if $opt_v; print <<"EOM"; # # $list (UNAVAILABLE) # $list: "$unavail_cmd $list" $list-request: "$majordomo_cmd -l $list" EOM return; } #--- # Discontinue the new-list feature #--- # unless (`wc -l <$listdir/$list` > 0) { # printf STDERR "$main'program_name: no members in <$list>\n" # if $opt_v; # print <<"EOM"; ## ## $list (NEW) ## #$list: "$newlist_cmd $list" #$list-request: "$majordomo_cmd -l $list" #$list-approval: majordomo-owner@$whereami$nobody #$list-owner: majordomo-owner@$whereami$nobody #owner-$list: majordomo-owner@$whereami$nobody #EOM # return; # } $nlists_active++; printf STDERR "$main'program_name: active list <$list>\n" if $opt_v; print <<"EOM"; # # $list (ACTIVE) # $list: "$resend_cmd $list $list-$suffix$nobody", "$log_cmd $list" $list-request: "$majordomo_cmd -l $list" $list-$suffix: :include:$listdir/$list $supplemental $list-approval: $list_moderator$nobody $list-owner: $list_owner$nobody $list-$suffix-owner: $list-owner@$whereami owner-$list: $list_owner$nobody owner-$list-$suffix: $list-owner@$whereami EOM if ($config_opts{$list,"digest"} && $opt_d) { print <<"EOM"; $list-digest: $list $list-digest-request: "$majordomo_cmd -l $list-digest" $list-digest-$suffix: :include:$listdir/$list-digest $list-digest-approval: $list_moderator$nobody $list-digest-owner: $list_owner$nobody $list-digest-$suffix-owner: $list-owner@$whereami owner-$list-digest: $list_owner$nobody owner-$list-digest-$suffix: $list-owner@$whereami EOM } return $list_owner; } sub valid_addr { local($addr) = shift; return $addr; $_ = $addr; # all comparisons in l/c tr/A-Z/a-z/; # remove local domain names s/sandiegoca.ncr.com//; s/elsegundoca.ncr.com//; # remove trailing ., @ s/\.$//; s/\@$//; # here will end up with: # userid # first.last # anything@localhost # anything@non-local.domain # skip anything non-local return $addr if /\./ && /\@/; # skip all @localhost return $addr if /\@/; local(@trace); @trace = `/usr/local/bin/rolo -u1 -L name $_ 2>&1` if /\./; @trace = `/bin/ypmatch -k $_ aliases 2>&1` if /^[_\w]+$/; return ($? ? "" : "$_"); } sub mail_diff { local($new_file,$old_file) = @_; local(@newies,@oldies); local($desc); local(%MER); grep(($MER{$_}++ ) && 0, &get_aliases_from_file($old_file)); grep(($MER{$_} += 2) && 0, &get_aliases_from_file($new_file)); foreach $element (keys %MER) { # # case of $MER{$element}: # 1 OLD # 2 NEW # 3 No change... push(@oldies,$element) if $MER{$element} == 1; push(@newies,$element) if $MER{$element} == 2; } $desc .= join(' ',"\nDELETED or MODIFIED:\n", sort @oldies) if @oldies; $desc .= join(' ',"\nADDED or MODIFIED:\n", sort @newies) if @newies; $desc = "No changes recorded." unless $desc; print STDERR "$main'program_name: alias changes:\n--\n$desc\n--\n" if ($opt_v); &set_mailer($bounce_mailer ? $bounce_mailer : $mailer); &set_mail_sender($whoami_owner); &set_mail_from($whoami_owner); &sendmail(MAIL, $opt_o, "Mail aliases changed by $main'program_name"); print MAIL <<"EOF"; The following changes were made to Majordomo's alias file: ------------------------------------------------------------------------------ $desc ------------------------------------------------------------------------------ A copy of the file was saved as '$old_file'. EOF close(MAIL); } sub get_aliases_from_file { local($filename) = @_; local(@tmp_ref); if (-e $filename) { printf STDERR "$main'program_name: parsing $filename\n" if ($opt_v); open(ALIASFILE, "$filename"); @tmp_ref = ; @tmp_ref = grep(/:/, @tmp_ref); @tmp_ref = grep(!/^\#/, @tmp_ref); close(ALIASFILE); } return @tmp_ref; } #============================================================================= # Do NOT remove these lines. They set mode in Emacs. # perl-mode is OK for font locking. # #@ Local Variables: *** #@ mode: perl *** #@ End: *** #=============================================================================