#!/bin/bash

#  This is filter-mail, a procmail-based mail filtering utility.
#  Copyright (C) 2002-3 Mike Mammarella <filter@crystalorb.net>
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

VERSION="0.9.4"

# Get configuration information

. ~/.filter-config

# File locations
SENDER_FILE=~/.filter-senders
DOMAIN_FILE=~/.filter-domains
CACHE_FILE=~/.filter-cache
DEFAULT_LOG_FILE=/dev/null

# End configuration section

# Date manipulation
epoch_to_date() {
	date -d "1jan70 -`date -d 1jan70 +%s` seconds +${1:-0} seconds"
}

date_to_epoch() {
	date -d "${1:-`epoch_to_date 0`}" +%s
}

# Accept a message
accept_email() {
	cat $TEMPFILE >> /var/mail/`whoami`
	echo "Accepted email from $FROM" >> $LOG_FILE
}

# Remove expired codes
clip_cache() {
	CLIP=`tempfile`
	NOW=`date +%s`
	echo $NOW.5 > $CLIP
	cat $CACHE_FILE >> $CLIP
	sort -n $CLIP | grep -A `wc -l < $CLIP` "^$NOW.5$" | grep -v "^$NOW.5$" > $CACHE_FILE
	rm $CLIP
}

# Begin main program

# Ensure we have all the files we need
touch $SENDER_FILE
touch $DOMAIN_FILE
touch $CACHE_FILE
touch ${LOG_FILE:=$DEFAULT_LOG_FILE}

echo "Filter invoked: `date`" >> $LOG_FILE
echo "Version: filter-mail $VERSION" >> $LOG_FILE

# Create a temporary file and store the message in it
TEMPFILE=`tempfile`
cat > $TEMPFILE

# Catch all email, so we don't miss any while debugging
[ "$SAFETY_BOX" ] && cat $TEMPFILE >> $SAFETY_BOX

# Extract information from the SMTP headers
FROM=`grep "^From: " $TEMPFILE | head -n 1 | cut -d\  -f 2-`
SUBJECT=`grep "^Subject: " $TEMPFILE | head -n 1 | cut -d\  -f 2-`

echo "Message apparently from: $FROM" >> $LOG_FILE
echo "Subject apparently is: \"$SUBJECT\"" >> $LOG_FILE

# Parse both standard forms of sender address
# Also parse an older form occasionally still used, especially in spam
if echo "$FROM" | grep "<.*>$" > /dev/null
then
	EMAIL=`echo "$FROM" | cut -d\< -f 2 | cut -d\> -f 1`
elif echo "$FROM" | grep "(.*)$" > /dev/null
then
	# Spam has this a lot, even though it's obsolete
	EMAIL=`echo "$FROM" | sed -e 's/\(.*\) ([^(]*)$/\1/'`
else
	EMAIL=$FROM
fi

# Extract the domain from the address
DOMAIN=`echo $EMAIL | cut -d@ -f 2`

echo "Email apparently is: \"$EMAIL\", domain \"$DOMAIN\"" >> $LOG_FILE

if [ -z "$EMAIL" ]
then
	echo "No email address detected!" >> $LOG_FILE
	mail "$ERROR_EMAIL" < $TEMPFILE
else
	# Check if they're already approved
	if grep -i "^$EMAIL$" $SENDER_FILE > /dev/null || grep -i "^$DOMAIN$" $DOMAIN_FILE > /dev/null
	then
		# If the message is from us, we only allow a special subject
		# line. Otherwise it is silently rejected, because generating a
		# response would only bother us. (Some spam has the same From:
		# and To: fields to confuse address-based filters.)
		if [ "$EMAIL" != "$SECRETARY_ADDRESS" -o "$SUBJECT" == "$MAGIC_SUBJECT" ]
		then
			accept_email
		else
			echo "Dropping self-addressed message without magic subject line." >> $LOG_FILE
		fi
	elif echo "$EMAIL" | grep "MAILER-DAEMON" > /dev/null
	then
		# Handle getting returned mail from email we automatically sent
		CODE=`grep "^X-Filter-Mail-Code: " $TEMPFILE | cut -d\  -f 2`
		if [ "$CODE" ]
		then
			if grep " $CODE :: " $CACHE_FILE > /dev/null
			then
				echo "Failed code $CODE is in the cache, dropping MAILER-DAEMON notification." >> $LOG_FILE
			else
				echo "Failed code $CODE is not in the cache, accepting MAILER-DAEMON notification." >> $LOG_FILE
				accept_email
			fi
		else
			echo "Could not find a filter code, accepting MAILER-DAEMON notification." >> $LOG_FILE
			accept_email
		fi
	else
		clip_cache
		ENTRY=`grep -i " $SUBJECT :: $EMAIL$" $CACHE_FILE`
		if [ ! -z "$ENTRY" ]
		then
			echo "Adding $EMAIL to approved senders list." >> $LOG_FILE
			echo "$EMAIL" >> $SENDER_FILE
			(grep -v "^Return-Path: <>$" $TEMPFILE | formail -r; echo "You have been added to the list of approved senders and the message below has been accepted."; echo; cat $TEMPFILE; echo) | $SENDMAIL "-F$SECRETARY_NAME" "-f$SECRETARY_ADDRESS" -oi -t
			accept_email
		else
			ENTRY=`grep -i " :: $EMAIL$" $CACHE_FILE`
			if [ -z "$ENTRY" ]
			then
				# Generate the expiry date and magic subject
				EPOCH=`date -d "next week" +%s`
				DATE=`epoch_to_date $EPOCH`
				CODE=`echo $DATE $EMAIL | md5sum`
				echo "$EPOCH $CODE :: $EMAIL" >> $CACHE_FILE
				(grep -v "^Return-Path: <>$" $TEMPFILE | formail -r -I "X-Filter-Mail-Code: $CODE" -I "Auto-Submitted: auto-replied"; echo_response $CODE; echo; echo "Your original message follows."; echo; sed -e "s/^/> /" $TEMPFILE; echo) | $SENDMAIL "-F$SECRETARY_NAME" "-f$SECRETARY_ADDRESS" -oi -t
				echo "Mail from $FROM (Email: $EMAIL) bounced with code $CODE (expires $DATE)." >> $LOG_FILE
			else
				echo "Duplicate mail from $FROM (Email: $EMAIL) rejected, subject $SUBJECT." >> $LOG_FILE
			fi
		fi
	fi
fi

rm $TEMPFILE

echo >> $LOG_FILE
