#!/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 <progname>.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 '@<progname>.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 = <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 = <ALIASFILE>;
@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: ***
#=============================================================================
syntax highlighted by Code2HTML, v. 0.9.1