Recently I wrote a procmail filter which filters mail for a ticketing system (request-tracker). It filters mails which addresses the ticketing system in Cc as well as mails with attachments bigger than 10 MB. I went through quite a learning curve while writing this filter so I’d like to share it here.
There is a need for an attachment-filter since request-tracker cant deal with big attachments as of now (version 4.4.1) and simply drops them. We ideally want the user to get a response informing him about the attachment limit instead of request-tracker silently dropping the attachment.
The need for a CC filter comes from the ticketing system functionality. Mails should address the ticketing system in “TO” without Ccs optimally. If the ticketing system gets addressed in Cc there definately is going to be a problem since every following mail from the mail participents is going to create a new ticket with a new ticket id.
My initial idea was to simply copy together parts from procmail tutorials to have a quick and solid solution, however there aren’t many procmail tutorials out there which deal with html emails. There are plenty of procmail filters available to drop html attachments or html emails all together - not very helpful in this case. Procmail as well as its users seem to come from an age when html emails (and html attachments especially) where evil.
The basics
I will reference guides here to back my code up. Question the code but dont question the references. ;)
This guide shows some of the important elements to understand the following filter: http://porkmail.org/era/procmail/quickref.html
- VAR1=value # create variables
- Conditions in procmail recipes are optional
- The “c” flag
- Lockfiles are defined in the first procmail line after the flags: :0 flags:lockfile
The code
The procmail file procmail.support
SHELL=/bin/bash # always always always always always always
MAILDIR=$HOME/MyMail
DEFAULT=$HOME/MyMail/prc.out
VERBOSE=${VERBOSE:-yeah}
LOGFILE = $HOME/procmail.log
LOGABSTRACT = "all"
#Define getting the sender's address, discard any leading and trailing whitespaces
FROM_=`formail -rt -xTo: \
| expand | sed -e 's/^[ ]*//g' -e 's/[ ]*$//g'`
MAXSIZE=15728640
REJECTED_FILE="procmail.support_rejected"
MAILBOX="support@request-tracker.com"
MAILBOXR="support@request-tracker\.com"
MAILBOXALIAS1="support.de@request-tracker.com"
MAILBOXALIAS1R="support\.de@request-tracker\.com"
MAILQUEUE="General"
# this is the prefered exit, if none of our filters meet just pass it on to RT
:0:$HOME/procmail.lock
* $ !^Cc.*${MAILBOX}
* $ !^Cc.*${MAILBOXALIAS1}
* B ?? < $MAXSIZE
| /opt/rt4/bin/rt-mailgate --url http://request-tracker.com --action correspond --queue $MAILQUEUE
# this part saves the mail to the "_rejected" file (doesn't need a condition)
:0c:$HOME/procmail.lock
/home/rtracker/$REJECTED_FILE
# Send a notification back that the mail was too big and exit
:0
* B ?? > $MAXSIZE
* $ ! ^X-Loop: ${MAILBOXR}
* $ ! ^X-Loop: ${MAILBOXALIAS1R}
{
# Prepare and send the rejection, be sure to customize your sendmail path
:0i:$HOME/procmail.lock
| (formail -r -I"Subject: Rejected mail: Email size limit exceeded" \
-I "MIME-Version: 1.0" \
-I "Content-Type: text/html;" \
-I "Content-Transfer-Encoding: quoted-printable" \
-A "From: ${MAILBOX}" \
-A "Precendence: junk" \
-A "X-Loop: ${MAILBOX}" ; \
cat /home/rtracker/procmail-message-size ) \
| $SENDMAIL -t -oi
}
# Send a notification back that the ticketsystem shouldn't be addressed in Cc and exit
:0
* $ ^Cc.*${MAILBOX} |\
^Cc.*${MAILBOXALIAS1}
* $ ! ^X-Loop: ${MAILBOXR}
* $ ! ^X-Loop: ${MAILBOXALIAS1R}
{
# Make a temporary file of the message to be returned
:0c:$HOME/procmail.lock # Discard whitespaces, insert a leading blank
| expand > message.msg
# Prepare and send the rejection
# Be sure to customize your sendmail path
:0i:$HOME/procmail.lock
| (formail -r -I"Subject: Rejected mail: Addressed as CC" \
-I "MIME-Version: 1.0" \
-I "Content-Type: text/html;" \
-I "Content-Transfer-Encoding: quoted-printable" \
-A "From: ${MAILBOX}" \
-A "Precendence: junk" \
-A "X-Loop: ${MAILBOX}" ; \
cat /home/rtracker/procmail-message ; \
echo "<hr />" ; \
sed -n "/<html/,/<\/html>/p" message.msg ) \
| $SENDMAIL -t -oi
}
Notice how all addresses used are defined in the variable section at the top of the file. This makes maintenance a little easier.
Also the actual response messages are pasted in from two external files, which also helps with maintenance:
- procmail-message (complains about the Cc addressing)
- procmail-message-size (complains about attachment)
The fetchmail configuration which hands the mail to procmail:
poll xx.xx.xx.xx
proto pop3
user "mailuser@request-tracker.com"
password "xxxxx"
is "rtracker" here
options
fetchall
nokeep
#mda "/opt/rt4/bin/rt-mailgate --url http://request-tracker.com --action correspond --queue Support"
mda "IFS=' '&&exec /usr/bin/procmail -f- /home/rtracker/procmail.support||exit 75 #rtracker"
set no syslog
set logfile /var/log/fetchmail.log
We see how the fetchmail configuration contains two “mda” commands: the original one which directs the mail directly to the request-tracker mailgate, and the new one which directs the mail to our procmail filter which takes responsibility for the mail from that point on.
#mda "/opt/rt4/bin/rt-mailgate --url http://request-tracker.com --action correspond --queue Support"
mda "IFS=' '&&exec /usr/bin/procmail -f- /home/rtracker/procmail.support||exit 75 #rtracker"
The resulting files
You might have noticed that the procmail and fetchmail rules work with a user called “rtracker”. This user exists on the ticketing system server and is basically only needed for fetchmail, since fetchmail doesn’t allow to be run by root. I find the resulting filenames to be quite convenient for terminal file navigation.
ls -l /home/rtracker/procmail.*
-rw-r--r-- rtracker rtracker procmail.support
-rw-r--r-- rtracker rtracker procmail.support_rejected
-rw------- rtracker rtracker procmail.log
Looking at the rejected mail
Interestingly the rejected mails file can be viewed and edited using mutt, which comes in very handy:
mutt -f /home/rtracker/procmail.support_rejected