# Simple mailing list distributor and filter # # Usage: simple_forwarder [--open] [--reply-to-list] # [--bounces-to bounce-address] listname # # The mail is expected on stdin. The script # # - if "--open" is NOT present on the command line: # checks if the From:-address is in ALLOWED_SENDERS. If it isn't, an # error message (REJECT_SUBJECT, REJECT_FROM, REJECT_TEXT) is sent # back to the envelope sender (unless the message has been tagged by # SpamAssassin as spam with score >= 5). # # - forwards the message to all recipients in LISTDIR/listname.recipients. # If --bounces-to is set, the envelope sender is bounce-address rather # than the original envelope-from. # # - If --reply-to-list is set, the Reply-To header is changed to # listname@DOMAIN. # # All files must be newline-separated lists of mail addresses. # # For use from within /etc/aliases (Postfix): # - group nogroup must have read access on ALLOWED_SENDERS, LISTDIR and # all files within LISTDIR # - group nogroup must have write access on LOGFILE # # Change the settings below to suit your needs before using. # # Author: Heinrich Moser, mail@heinzi.at # Version: 1.3, 2007-07-30 # Settings LOGFILE=/var/log/bestlists.log LISTDIR=/etc/bestlists ALLOWED_SENDERS=$LISTDIR/senders SENDMAIL=/usr/lib/sendmail DOMAIN="@best.eu.invalid" # Mailing list suffix used for loop detection DETECT_SPAM="X-Spam-Level: *****" # If this is found (grep -Fi), message is spam CONFIG_FILE="/etc/simple_forwarder.conf" # Rejection mail REJECT_SUBJECT="Your message to _LIST_$DOMAIN has been rejected" REJECT_FROM="do_not_reply$DOMAIN" REJECT_TEXT="Please register your e-mail address _EMAIL_ to be allowed to send mail to the restricted mailing list _LIST_$DOMAIN. The following message was rejected: From: _FROM_ To: _TO_ Subject: _SUBJECT_ Date: _DATE_ Message-ID: _MSGID_ If there are any problems, ask your administrator for help. Hugs, the Administrators" # Load configuration values from config file, if exists [ -e "$CONFIG_FILE" ] && source "$CONFIG_FILE" # Functions log() { echo "`date "+%Y-%m-%d %H:%M:%S"` $1" >> $LOGFILE } leave() { # clean up and exit if [ \( -n "$MAILFILE" \) -a \( -f "$MAILFILE" \) ]; then rm -f -- $MAILFILE fi exit 0 } leave_usage() { # show usage message and exit with error echo "Usage: simple_forwarder [--open] [--bounces-to bounce-address] listname" exit 1 } # Save mail from stdin to temporary file MAILFILE=`tempfile` cat - > $MAILFILE # Parse command line arguments REDIRECT_BOUNCES=0 OPEN=0 REPLYTO=0 LIST= while [ -n "$1" ]; do if [ "$1" = "--bounces-to" ]; then REDIRECT_BOUNCES=1 BOUNCES_TO=$2 shift 2 elif [ "$1" = "--open" ]; then OPEN=1 shift 1 elif [ "$1" = "--reply-to-list" ]; then REPLYTO=1 shift 1 elif [ -z "$LIST" ]; then LIST=$1 shift else leave_usage fi done if [ -z "$LIST" ]; then leave_usage fi # Parse mail MSGID=`cat "$MAILFILE" | formail -czx Message-ID:` SUBJECT=`cat "$MAILFILE" | formail -czx Subject:` FROM=`cat "$MAILFILE" | formail -czx From:` ENV_SENDER=`cat "$MAILFILE" | formail -rczx To:` log "received: mail $MSGID for $LIST from $FROM ($ENV_SENDER): $SUBJECT" if [ "$REDIRECT_BOUNCES" -eq "0" ]; then BOUNCES_TO="$ENV_SENDER" fi # Detect mail loops cat "$MAILFILE" | formail -czx X-Loop: | grep -Fix "$LIST$DOMAIN" if [ "$?" -eq "0" ]; then log "dropped: loop detected in mail $MSGID" leave fi # Check if sender is allowed if [ "$OPEN" -eq "0" ]; then # Get sender address (only address): remove Reply-To, generate reply header, get "To:", grep FROM_EMAIL=`cat "$MAILFILE" | formail -fI Reply-To: | formail -rctzx To:` grep -Fixq "$FROM_EMAIL" $ALLOWED_SENDERS RET=$? if [ "$RET" -eq "2" ]; then log "dropped: error during sender check of mail $MSGID ($FROM_EMAIL, $ALLOWED_SENDERS)" leave elif [ "$RET" -eq "1" ]; then if cat "$MAILFILE" | grep -Fiq "$DETECT_SPAM"; then # Message was spam - drop it silently log "rejected: $FROM_EMAIL (mail $MSGID) does not have permission - dropped" else # Get some more data about the mail TO=`cat "$MAILFILE" | formail -czx To:` DATE=`cat "$MAILFILE" | formail -czx Date:` # Send error message to envelope sender S="${REJECT_SUBJECT//_LIST_/$LIST}" T="${REJECT_TEXT//_LIST_/$LIST}" T="${T//_EMAIL_/$FROM_EMAIL}" T="${T//_FROM_/$FROM}" T="${T//_TO_/$TO}" T="${T//_SUBJECT_/$SUBJECT}" T="${T//_DATE_/$DATE}" T="${T//_MSGID_/$MSGID}" cat "$MAILFILE" | ( formail -r ; echo "$T" ) | \ formail -bA "X-Loop: $LIST$DOMAIN" -I "Subject: $S" -I "From: $REJECT_FROM" | \ $SENDMAIL -bm -f "" -it log "rejected: $FROM_EMAIL (mail $MSGID) does not have permission - sender notified" fi leave fi fi # Prepare mail manipulations FORMAIL="formail -bf -A 'X-Loop: $LIST$DOMAIN' " if [ "$REDIRECT_BOUNCES" -eq "1" ]; then FORMAIL="$FORMAIL -A 'Errors-To: $BOUNCES_TO' " fi if [ "$REPLYTO" -eq "1" ]; then FORMAIL="$FORMAIL -i 'Reply-To: $LIST$DOMAIN' " fi # Forward mail to intended recipients RECIPIENTS_FILE=${LISTDIR}/${LIST}.recipients if [ ! -f $RECIPIENTS_FILE ]; then log "dropped: recipients file $RECIPIENTS_FILE for mail $MSGID not found" leave fi for RECIPIENT in `cat "$RECIPIENTS_FILE"`; do cat "$MAILFILE" | eval "$FORMAIL" | \ $SENDMAIL -bm -f "$BOUNCES_TO" -i "$RECIPIENT" done log "sent: message $MSGID sent to list $LIST" leave