#!/bin/sh -
#### set MAILFROM per site ####
MAILFROM=postmaster
IGNORE='(: hold: |: replace: |: filter: |broken.example.com|greylist| warning|DISCARD|unverified.address: Address verification in progress|Operation timed out)'
SECTIONMAX=5000
LOGS='/var/log/mail* /var/log/messages*'
OLDLOGS=''
DEBUG= #[Y/y/else N] (to display instead of email, see also -d flag)
#### redefine $host only if localhost is not the mailhost
host=`uname -n|awk -F. '{print $1}'`
#### use: MAILCMD="sendmail -oem -oi -t -f $MAILFROM" with sendmail >V8.8
#### "-t" might work with Postfix versions 20030611 and newer
MAILCMD="sendmail -oem -oi -f $MAILFROM"
#### customizable end of report
SIGNATURE="www.postconf.com/docs/spamrep"
######################################################################
# spamrep_yesterday_bydom spamrep_today_bydom (Postfix version)
#
#
#
# License terms at:
# HTML version available with PostConf
######################################################################
PATH=/usr/ucb:/bin:/usr/bin:/usr/sbin:/sbin:/usr/lib
_POSIX2_VERSION=199209
LANG=C
LESS=-ce
TIMEZONE="`date | awk '{print $5}'`"
TMP="/tmp/.spamrepd.$$"
umask 077
set -a
trap "rm -f $TMP $TMP.spam $TMP.sum $TMP.today ; exit 1" 0 1 2 3 15
#### test minlines whenever changing the report ####
MINLINES=9
if [ $# -eq 0 ] || [ "$1" = "-help" ] || [ "$1" = "-h" ] || [ "$1" = "-H" ]; then
echo " USAGE: `basename $0` [-d|-h] \*@domain "
exit
elif [ "$1" = "-d" ] || [ "$1" = "-D" ]; then
DEBUG=Y
REPORTON=$2
MAILTO=nobody
shift
elif [ $# = 1 ]; then
DEBUG=Y
REPORTON=$1
MAILTO=nobody
else
REPORTON=$1
MAILTO=$2
fi
if [ "`date '+%d' | sed 's/0*//'`" = 1 ]; then
LOGS="${LOGS} ${OLDLOGS}"
fi
if [ "`basename $0 | grep -i yesterday`" != "" ]; then
####
#### pgmname = *yesterday* ...
####
if [ "`date '+%d' | sed 's/0*//'`" = 1 ]; then
#### unreliable hack for 1st of the month:
TZ="`date| awk '{print $5}'`+31"
LOGDATE="`date|awk '{printf "%3s%3s", $2, $3}'`"
DAY="`date|awk '{print $2, $3, $NF}'`"
else
LOGDATE="`date|awk '{printf "%3s%3s", $2, $3-1}'`"
DAY="`date|awk '{print $2, $3-1, $NF}'`"
fi
else
####
#### pgmname = *today* ...
####
LOGDATE="`date|awk '{printf "%3s%3s", $2, $3}'`"
DAY="`date|awk '{print $1, $2, $3, $NF}'`"
fi
printSecHead () {
SS="`wc -l $TMP.spam 2>/dev/null | awk '{print $1}'`"
echo "" >> $TMP
if [ $SS -eq 1 ]; then
barheader "$SS $sectionTitle1" >> $TMP
echo " $SS $sectionTitle1" >> $TMP.sum
elif [ $SS -gt $SECTIONMAX ]; then
barheader "displaying $SECTIONMAX of $SS $sectionTitle2" >> $TMP
echo " displaying $SECTIONMAX of $SS $sectionTitle2" >> $TMP.sum
else
barheader "$SS $sectionTitle2" >> $TMP
echo " $SS $sectionTitle2" >> $TMP.sum
fi
}
printSecFoot () {
echo "" >> $TMP
head -${SECTIONMAX} $TMP.spam >> $TMP
rm -f $TMP.spam
}
printRBLdetail () {
RBLS=`grep blocked.using $TMP.spam | awk -F\; '{ print $2 }' | awk '{ print $NF }' | sort -u`
#RBLS=`sed -e 's/^.*blocked using //' -e 's/, reason.*$//' -e 's/;.*$//' $TMP.spam | sort -u`
if [ "`echo $RBLS | wc -w`" -gt 1 ]; then
rblCalc () {
for rbl in $RBLS ; do
rblhits="`grep $rbl $TMP.spam 2>/dev/null | wc -l 2>/dev/null`"
if [ $rblhits -ge 1 ]; then
echo " $rblhits - $rbl (`expr 100 \* $rblhits / $SS`%)"
fi
done
}
rblCalc | sort -rn >> $TMP.sum
fi
}
barheader () {
echo "------[ $1 ]----------------------------------------------------------------" | \
awk -F"\n" '{ printf "%-.75s", $1 }' 2>/dev/null
}
report_on_dom () {
#################### email headers ###################################
rm -f $TMP.spam $TMP.sum $TMP
#### "To: ${MAILTO}" is required but may be ignored if recipient is
#### specified on the command line.
echo "From: ${MAILFROM}
To: ${MAILTO}
Reply-To: ${MAILFROM}
Subject: $DAY mailstats for $REPORTON
" > $TMP.sum
#################### report headers ##################################
echo "" >> $TMP
echo " Reply to this email or forward to $MAILFROM if" >> $TMP
echo " you find any false-positives or have questions about" >> $TMP
echo " spam filtering." >> $TMP
echo " " >> $TMP
echo " Important reminder: please do not reply to unsolicited " >> $TMP
echo " email." >> $TMP
#
echo "" >> $TMP.sum
barheader "$DAY mailstats for $REPORTON" >> $TMP.sum
echo "" >> $TMP.sum
echo "" >> $TMP.sum
#################### list by filter type #############################
grep -v " reject_warning: " $TMP.today | grep 'from=<' | \
sed -e 's/^.* from= ' -e 's/> to=<.*$/>/' | sort -t\< +1f -u \
> $TMP.spam
if [ -s $TMP.spam ]; then
sectionTitle1="unique sender filtered:"
sectionTitle2="unique senders filtered:"
printSecHead
printSecFoot
fi
grep -i "blocked.using" $TMP.today | grep -v " reject_warning: " | \
sed -e 's/ Service unavailable//' \
-e 's/Barracuda Reputation, see/b.barracudacentral.org;/' \
-e 's/ Client host .* blocked using / blocked using /' > $TMP.spam
if [ -s $TMP.spam ]; then
sectionTitle1="rejected by subscription"
sectionTitle2="$sectionTitle1"
printSecHead
printRBLdetail
printSecFoot
fi
egrep -i '(reject: header|discard:|message.size.exceeds|access.denied|Message.content.rejected)' $TMP.today | \
egrep -iv '(user.unknown|discard: header X-Amavis.*: INFECTED|discard: header X-Spam| reject_warning: |Recipient.address.rejected|helo.command.rejected|domain.not.found|need.fully-qualified|Relay access denied)' | \
sed 's/: Access denied//' > $TMP.spam
if [ -s $TMP.spam ]; then
sectionTitle1="rejected by localhost"
sectionTitle2="$sectionTitle1"
printSecHead
printSecFoot
fi
egrep -i '(no such user|user.unknown)' $TMP.today | egrep -v '( reject_warning: )' | \
sed 's/ in local recipient table//' > $TMP.spam
if [ -s $TMP.spam ]; then
sectionTitle1="user unknown"
sectionTitle2="$sectionTitle1"s
printSecHead
printSecFoot
fi
grep 'Relay access denied' $TMP.today > $TMP.spam
if [ -s $TMP.spam ]; then
sectionTitle1="relay attempt"
sectionTitle2="$sectionTitle1"s
printSecHead
printSecFoot
fi
egrep -i '(helo.command.rejected|domain.not.found|need.fully-qualified)' $TMP.today | \
egrep -iv '( reject_warning: |reject: header|discard:|message.size.exceeds|access.denied|Recipient.address.rejected|Message.content.rejected)' | \
sed 's/: Access denied//' > $TMP.spam
if [ -s $TMP.spam ]; then
sectionTitle1="protocol error"
sectionTitle2="$sectionTitle1"s
printSecHead
printSecFoot
fi
grep -i "discard: header X-Spam" $TMP.today | \
sed -e 's/ proto=ESMTP helo=.localhost.//' -e 's/from localhost.*127.0.0.1.; //' \
-e 's/ from local;//' > $TMP.spam
if [ -s $TMP.spam ]; then
sectionTitle1="discarded by spamassassin"
sectionTitle2="$sectionTitle1"
printSecHead
printSecFoot
fi
#### assumes clam-av, amavis ####
egrep '(discarded, .* - VIRUS|discard: header X-Amavis.*: INFECTED)' $TMP.today | \
sed -e 's/'$host' .*: to/'$TIMEZONE' virus: to/' \
-e 's/ proto=ESMTP helo=.*$//' \
-e 's/ from localhos.*\[127.0.0.1\]//' \
-e 's/, relay=127.0.0.1\[127.0.0.1\].*status=sent//' > $TMP.spam
if [ -s $TMP.spam ]; then
sectionTitle1="discarded by anti-virus"
sectionTitle2="$sectionTitle1"
printSecHead
printSecFoot
fi
egrep -i '( warning: | error: | fatal: | panic: )' $TMP.today | \
egrep -v '( reject_warning: |older than source)' > $TMP.spam
if [ -s $TMP.spam ]; then
sectionTitle1="notice"
sectionTitle2="$sectionTitle1"s
printSecHead
printSecFoot
fi
grep " reject_warning: " $TMP.today | \
sed -e 's/blocked using/listed by/' \
-e 's/reject_warning: /warn_only: /' \
-e 's/ ... Service unavailable; //' > $TMP.spam
if [ -s $TMP.spam ]; then
sectionTitle1="warn-only notice"
sectionTitle2="$sectionTitle1"s
printSecHead
printSecFoot
fi
#################### finish header and email ##################
echo "" >> $TMP
if [ "`wc -l $TMP | awk '{print $1 }'`" -lt $MINLINES ]; then
rm -f $TMP.spam $TMP.sum $TMP $TMP.today
exit
else
cat $TMP >> $TMP.sum
barheader "end of report - $SIGNATURE" >> $TMP.sum
echo "" >> $TMP.sum
rm -f $TMP $TMP.spam
fi
if [ "$DEBUG" = y ] || [ "$DEBUG" = Y ]; then
more $TMP.sum
else
$MAILCMD $MAILTO < $TMP.sum
fi
rm -f $TMP $TMP.spam $TMP.sum $TMP.today
}
#################### run the reports #################################
######### may need to customize sed per local syslog format ##########
grep -h "^$LOGDATE" $LOGS 2>/dev/null | grep postfix | egrep -iv "$IGNORE" | \
grep -i " to=.${REPORTON}" | grep -v 'postfix/cleanup.*relay=none' | sort -Mr | \
sed -e 's/ NOQUEUE://' \
-e 's/ proto=ESMTP / /' -e 's/ proto=SMTP / /' \
-e 's/'"$host"' postfix.* reject:/'$TIMEZONE' reject:/' \
-e 's/'"$host"' postfix.* discard:/'$TIMEZONE' discard:/' \
-e 's/'"$host"' postfix.* error:/'$TIMEZONE' error:/' \
-e 's/'"$host"' postfix.* fatal:/'$TIMEZONE' fatal:/' \
-e 's/'"$host"' postfix.* panic:/'$TIMEZONE' panic:/' \
-e 's/'"$host"' postfix.* reject_warning:/'$TIMEZONE' reject_warning:/' \
-e 's/'"$host"' postfix.* warning:/'$TIMEZONE' warning:/' \
-e 's/'"$host"' postfix.*smtp.*mail.info]/'$TIMEZONE'/' > $TMP.today
report_on_dom
################### cleanup ##########################################
rm -f $TMP.spam $TMP.sum $TMP $TMP.today