Maildrop Syntax Tutorial
An Introduction to Maildrop filtering syntax
SWCP uses Maildrop to handle its anti-spam filtering. However, Maildrop also allows for a lot of other types of filtering, most of which require manually editing .mailfilter files (and reading your email on the server via TWIG, pine, mutt, etc.). This tutorial covers the basics of manually editing a .mailfilter for an assortment of uses.
Additionally, SWCP provides a web interface for our end users to create a user's .mailfilter file, and most of our users use the web interface to do so. Manual editing is incompatible with web interface editing. There's a "magic cookie" at the top of the .mailfilter file generated by the web interface. You need to remove that string to keep the web interface from overwriting your changes. Also note that after you remove the magic cookie, changes to the web interface settings will NOT be propagated to your .mailfilter file.
Getting Started
If you wish to manually edit your .mailfilter, first log into https://members.swcp.com/mailfilter and set the option "Filtering" to No. Wait 5 minutes or so. You should have a new file called .mailfilter.swcp. From this point you can hand edit the file then rename it to .mailfilter. To keep SWCP or the web interface from mistakenly overwriting your .mailfilter file, be sure to remove the very top line, which will look something like this:
# SWCP:MAILFILTER:NNNNN Version n.n.n
Important Syntax Note
For the most part the .mailfilter syntax is pretty standard-looking code with arbitrary white space between elements. The one big exception to this is the opening brace on an "if" statement. It MUST be on a line by itself, not at the end of the line with the "if" on it. For example:
BAD (will produce mail delivery errors):
if (/strange ach offer/:b) {
$THISISSPAM = 1
}
GOOD:
if (/strange ach offer/:b)
{
$THISISSPAM = 1
}
Variables
Variables can be declared as below.
Without Quotes
DEFAULT=/var/mail/sampleuser
With variable substitution
SPAMCAN=/var/spool/mail/spamdrop/$LOGNAME
With quotes
SUPPORT="/users/sampleuser/mail/support"
With quotes and variable substitution
HELP="/users/$LOGNAME/mail/help"
Logging your filtered email
I have found that logging filtered email makes troubleshooting much easier. To make this function operate you'll want to set the logfile variable. Then in each recipe add a log line with a comment. Here's an example.
logfile "/home/sampleuser/.mailfilter.log" ...
if (/^To:.*sampleuser@swcp.com /)
{
log "Ptrn: sampleuser@swcp.com whitelist"
to "$DEFAULT"
}
Now when this rule is matched and delivers a message your .mailfilter.log will have a set of lines in it similar to this.
Ptrn: sampleuser@swcp.com whitelist Date: Wed Apr 23 17:21:44 2003 From: Some Sender <someone@somewhere.com> Subj: Some Subject File: /var/spool/mail/sampleuser
As you can see this makes finding an elusive problem somewhat easier. There is one unfortunate side effect, of course there had to be one :-) Depending on how quickly your mailserver is getting email you may find that the 'Ptrn:' line and the 'Date: From: Subj: File:' lines are out of sync. These are difficult problems to solve without modifying the source to maildrop.
Filter Contents of the To: Cc: From: or Subject: fields.
Visit http://www.flounder.net/~mrsam/maildrop/maildropfilter.html for complete regex syntax.
if (/^To:.*sampleuser@swcp.com /)
{
to "$DEFAULT"
}
if (/^Cc:.*help@swcp.com/ )
{
to "$HELP"
}
if (/^From: My Enemy <anotheruser@swcp.com>/)
{
to "$SPAMCAN"
}
if (/^Subject: About your puppies/)
{
to "$DEFAULT"
}
Filter Contents of the header, body, both, or case.
You can match header, body, header & body, or case (upper or lower).
Header is always assumed, so you don't have to put :h after most matches Be very careful to limit the size of your search if you use :b see below.
- :h = match in the header only
- :b = match in the body only
- :w = match in the header and the body
- :D = match case
- :p = match only the first 100 lines of the body (SWCP Specific)
First Version
This version matches the words opt-in, opt in, opt-out, opt out in the body with no reguard for the size of the message.
if (/(opt-in|opt in|opt-out|opt out) /:b )
{
to "$SPAMCAN"
}
A More Complicated Version
This version matches the words opt-in, opt in, opt-out, opt out in the body but constrains the message size to less than 500 lines and less than 15 kilobytes.
if (($LINES < 500 && $SIZE < 15000) && /(opt-in|opt in|opt-out|opt out) /:b)
{
to "$SPAMCAN"
}
Using Matching
If you have multiple entries to match say from the Subject line, consider the following.
The special character '!' tells maildrop to stick everything it matches up to the end of line '$' into one of its special MATCH variables.
Note: It should be noted that maildrop has (what I consider to be) a documented bug. Different types of matches cause different MATCH variables to be used. In particular if statements using this match syntax dump their variable contents into MATCH2, foreach matches dump their match contents into a variable called MATCH, see below.
if (/^Subject: *!.*$/)
{
if ($MATCH2 =~ /Updates from the hospital about (mom|dad|brother)/ )
{
to "$DEFAULT"
}
if ($MATCH2 =~ /Win / && $MATCH2 =~ /Opt/:D && /casino/:p)
{
to "$SPAMCAN"
}
}
Looping with Foreach
If you wish to match to & cc in the same statement you must use a foreach
foreach /^(To|Cc): *!.*$/
{
if ( $MATCH =~ /@netris.com/)
{
to "$DEFAULT"
}
if ($MATCH =~ /rob@nowhere.com / && /casino/:p )
{
to "$SPAMCAN"
}
}
Dropping a message entirely
If you want to send something to /dev/null, use the keyword 'exit'. It should send any matched email messages to /dev/null.
if ( /To: sampleuser@netris.com/)
{
exit
}
Filter Then Forward
If you want to filter then forward or cc a message, simply place a ! at the begining of your delivery line. This tells maildrop to forward all mail matching that criteria to someone@someotheraddress.com. If you want to forward all email simply put the 'cc' line outside the body of the regex.
if ( /To: sampleuser@netris.com/)
{
cc "!someone@someotheraddress.com"
}
Modify The Message
You can use the "xfilter" command to modify the message "in place" before delivery. For example, if you wanted to tag the subject line of any message from your friend Bubbba, you could use this:
# Flag messages from Bubba in the subject
if (/^From:.*bubba@bubbaville.com/)
{
xfilter "sed -e 's/^Subject: /Subject: [FROM BUBBA] /'"
}
Testing your new filter rules
Log into a a host that is running maildrop and run the following command*:
maildrop -V 4 -d logname < msg.txt
Where logname is your username and msg.txt is a file containing 1 email message saved from pine or mutt.
You'll probably want to log into a host other than your mailserver. If your system has a systemwide maildroprc you'll see all the debug from that too. That's probably not what you want :-)
A real example
MAILDIR="/users/$LOGNAME/mail"
DEFAULT="/var/mail/$LOGNAME"
SPAMCAN="/users/$LOGNAME/mail/SPAM"
logfile "/users/$LOGNAME/maildrop_log"
#
if(/^Subject: *!.*$/)
{
if($MATCH2 =~ /Dialup Session Report/)
{
log "Ptrn: Dialup Session Report"
to "$MAILDIR/portmasters"
}
if($MATCH2 =~ /OMS#.*Path Built/)
{
log "Ptrn: Path Built"
to "$MAILDIR/pathbuilt"
}
if($MATCH2 =~ /Domain request:/)
{
"Ptrn: Domain request"
to "$MAILDIR/domreg"
}
}
# end of Subject exceptions
#
# begin To/Cc exceptions
foreach /^(To|Cc): *!.*$/
{
if($MATCH =~ /ilikespam@/)
{
log "Ptrn: ilikespam spam"
to "$MAILDIR/throwaway"
}
if($MATCH =~ /abc@/)
{
log "Ptrn: abc spam"
to "$MAILDIR/throwaway"
}
if($MATCH =~ /puppyfriends@yahoogroups.com/)
{
log "Ptrn: Puppy email"
to "$MAILDIR/puppy"
}
}
# end of To and Cc exceptions
#
# beginning of From exceptions
if(/^From:*!.*$/)
{
if($MATCH2 =~ /joejoe@uswest.com/)
{
log "Ptrn: joejoe"
to "$MAILDIR/joe"
}
if($MATCH2 =~ /catchthisemail@swcp.com/)
{
log "Ptrn: catchthisemail spam"
to "$MAILDIR/junk"
}
if($MATCH2 =~ /((samplemom|sampledad|samplebrother|sistersample)@swcp.com)|marie@nm.net/)
{
log "Ptrn: family match"
to "$DEFAULT"
}
if($MATCH2 =~ /(majordomo@swcp.com|replies@opensrs.org)/)
{
log "Ptrn: majordomo-opensrs"
to "$DEFAULT"
}
}
# asdf end of From: exceptions
#
log "Ptrn: final match"
to "$SPAMCAN"
If you have questions, please email the SWCP Helpdesk.