#!/bin/bash
########################################################################
#
# NAME
#   gen-combined-list - create a mailman list with members from other lists
#
# SYNOPSIS
#   gen-combined-list [--match pattern] [--except pattern] combined_list
#
# DESCRIPTION
#   This script updates the mailman list combined_list to contain all
#   the members present in all the other lists on the server.  It
#   ignores members set to  nomail and preserves regular vs. digest
#   subscriptions.  Have cron run this script hourly and the combined
#   member list will be updated automatically every hour.
#
# OPTIONS
#   --match pattern
#	Only look for members in lists with names matching the pattern.
#   --except pattern
#       Ignore looking for members from lists with names matching the
#	pattern.
#
# EXAMPLE
#   Update the membership of a list called psas-all, based on all lists
#   that have a name starting with psas- except for the psas-announce
#   list:
#
#      gen-combined-list --match "^psas-" --except "^psas-announce$" psas-all
#
# CAVEATS
#   Most member options are not preserved, including digest type, password
#   and the like.
#
#   Tested only on Mailman version 2.1.2.
#   
# AUTHOR
#   James T Perkins, james@loowit.net, 09 Oct 2003
#   Originally inspired by an example script on www.list.org, contributed
#   by Mark Merlins, Jon Carnes, and Shane Beasley 
#
########################################################################

MAILMAN=/var/lib/mailman		# location of mailman
LIST_PAT=				# original list names match this pattern
EXCEPT_PAT=				# except this pattern
COMBINED_LIST=				# combined membership output list name

# temporary files
TMP_LOC=${TMPDIR:-/tmp}/list$$		# directory for all temp files
LISTS_ORIG=$TMP_LOC/lists		# lists from which output is created
LISTS_TMP=$TMP_LOC/lists_tmp		# lists from which output is created
REGULAR_MEMBERS=$TMP_LOC/plain		# sorted/unduped plain list recipients
DIGEST_MEMBERS=$TMP_LOC/digest		# sorted/unduped digest list recipients

# set up execution PATH
PATH=$MAILMAN/bin:/bin:/usr/bin

# print out script usage message
usage()
{
    echo "usage: gen-combined-list [--match=pattern] [--except=pattern] combined_list" >&2
    exit 1
}

# parse options
while [ $# -gt 0 ]; do
    case $1 in
	--match=*)  LIST_PAT="$(expr substr "$1" 9 999)" ;;
	--except=*) EXCEPT_PAT="$(expr substr "$1" 10 999)" ;;
	-*)         usage ;;
	*)
	    if [ -n "$COMBINED_LIST" ]; then
		usage
	    fi
	    COMBINED_LIST="$1"
	    ;;
    esac
    shift
done
if [ -z "$COMBINED_LIST" ]; then
    usage
fi

# clean up temp files on exit: normal, hangup, interrupt and terminate
trap 'status=$?; rm -rf $TMP_LOC; exit $status' 0 1 2 15

# create temp dir
mkdir $TMP_LOC || exit 1

# validate that the combined_list exists
list_lists --bare | grep "^$COMBINED_LIST$" > $LISTS_TMP
if [ ! -s $LISTS_TMP ]; then
    echo "gen-combined-list: combined list $COMBINED_LIST does not exist" >&2
    exit 1
fi

# find all input lists
list_lists --bare | grep -v "^$COMBINED_LIST$" > $LISTS_ORIG
if [ -n "$LIST_PAT" ]; then
    grep "$LIST_PAT" $LISTS_ORIG > $LISTS_TMP
    mv $LISTS_TMP $LISTS_ORIG
fi
if [ -n "$EXCEPT_PAT" ]; then
    grep -v "$EXCEPT_PAT" $LISTS_ORIG > $LISTS_TMP
    mv $LISTS_TMP $LISTS_ORIG
fi
if [ ! -s $LISTS_ORIG ]; then
    echo "gen-combined-list: found no input lists" >&2
    exit 1
fi

# create list of plain members getting mail, sort & remove duplicates
while read list; do
    list_members --regular --nomail=enabled $list
done < $LISTS_ORIG | sort -u > $REGULAR_MEMBERS

# create list of digest members getting mail, sort & remove duplicates
while read list; do
    list_members --digest --nomail=enabled $list
done < $LISTS_ORIG | sort -u > $DIGEST_MEMBERS

# note: "sync_members" will sort and remove dups, but its nice to do anyway

# put all members into the combined list. sync_members sets the plain
# subscribers, and add_members appends the digest members.
# ignore stdout because we don't need a who-was-subscribed report
sync_members --welcome-msg=no --goodbye-msg=no --notifyadmin=no \
    -f $REGULAR_MEMBERS $COMBINED_LIST >/dev/null
add_members --welcome-msg=no --admin-notify=no \
    --digest-members-file=$DIGEST_MEMBERS $COMBINED_LIST >/dev/null

# all done, trap will clean up temp files
exit 0
