---
title: "Mail filtering"
date: "2015-01-08"
category: "email"
slug: "mail-filtering"
---

## Overview

Message filtering is handled prior to delivery via maildrop. Each message passes through two levels of filters:

1. **Global filter**: processed first from `/etc/maildroprc`
2. **Per-user filter**: processed second from `$HOME/.mailfilter`

Full syntax and usage can be found in [mailfilter(7)](http://apnscp.com/linux-man/man7/maildropfilter.7.html).

> **Important:** On [[determining-platform-version]] (less than v6), always run [dos2unix](http://apnscp.com/linux-man/man1/dos2unix.1.html) or use EOL conversion "Windows -> Unix" (**Files** > **File Manager** > **Properties** action) on filter files after editing. maildrop cannot read files with Windows or Mac line endings, which prevents mail delivery until corrected.

## SpamAssassin Integration

[SpamAssassin](http://wiki.apnscp.com/index.php/SpamAssassin) is invoked from the global filter `/etc/maildroprc`. The following block passes messages smaller than 128 KB to SpamAssassin:

if ($SIZE < 131072)
{
        xfilter "/usr/bin/spamc -u $RECIPIENT"
}

These 4 lines are required for SpamAssassin filtering. Removing them causes unfiltered delivery. Opening and closing braces **must** be on their own lines -- [K&R/KNF style braces](http://en.wikipedia.org/wiki/Indent_style#BSD_KNF_style) do not work.

## Default Filter

File: `/etc/maildroprc`

\# Global maildrop rules go here
# See http://www.courier-mta.org/maildrop/maildropfilter.html for syntax
if ($SIZE < 131072)
{
        exception {
                xfilter "/usr/bin/spamc -u $RECIPIENT"
        }
}
 
DELETE\_THRESHOLD=10.0
if (/^X-Spam-Flag: YES/)
{
        /X-Spam-Score: (\\d+)/
        if ($MATCH1 >= $DELETE\_THRESHOLD)
        {
                to /dev/null
        }
        else
        {
                to Mail/.Spam/
        }
}

**How it works:** Messages under 128 KB are scanned by SpamAssassin. If flagged as spam, the score is checked against `DELETE_THRESHOLD` (default: 10). Messages at or above the threshold are deleted (`/dev/null`); those below are delivered to the Spam folder. The [[accessing-spam-folder]] can be accessed via [[accessing-e-mail]] or IMAP.

## Filter Recipes

### Globally Disabling Per-User Filters

Add `to $DEFAULT` at the end of the global filter to deliver all messages to the default mailbox (`$HOME/Mail`) and stop further processing.

### Selectively Disabling Per-User Filters

Use the `LOGNAME` variable to skip filtering for a specific user:

File: `/etc/maildroprc`

if ($SIZE < 131072)
{
        xfilter "/usr/bin/spamc -u $RECIPIENT"
}
# User "bill" loves his spam
if ($LOGNAME ne "bill" && /^X-Spam-Flag: YES/)
{
        to /dev/null
}

To also prevent the user from having their own filter file:

if ($SIZE < 131072)
{
        xfilter "/usr/bin/spamc -u $RECIPIENT"
}
# User "bill" loves his spam
if ($LOGNAME ne "bill" && /^X-Spam-Flag: YES/)
{
        to /dev/null
}
# But he's prohibited from adding any filter rules
if ($LOGNAME eq "bill")
{
        to $DEFAULT
}

Note: `eq`, `lt`, `le`, `gt`, `ge`, `ne` are for string comparisons; `==`, `<`, `<=`, `>`, `>=`, `!=` are for numeric comparisons.

### Deleting All Spam

> **Warning:** This is strongly discouraged because (1) young accounts have high scoring variability and (2) no failure notice is generated -- neither sender nor recipient knows the message was deleted.

if (/^X-Spam-Flag: YES/)
{
to /dev/null
}

### Filtering to an External Program

maildrop's [xfilter](http://apnscp.com/linux-man/man7/maildropfilter.7.html#lbBI) directive pipes messages to an external script. Ensure the script is executable (`chmod 700`).

`.mailfilter`

xfilter "$HOME/reverse.sh"

`reverse.sh`

#!/bin/sh
exec 6<&0
while read -u 6 line ;
do 
        echo $line | rev
done
exec 6<&-
exit 0

### Creating a Spam Trap

A spam trap is a hidden address that only spam bots discover. All mail to this address is fed directly to [SpamAssassin](http://wiki.apnscp.com/index.php/SpamAssassin) for training. The following assumes `spam@mydomain.com` maps to a virtual mailbox owned by `myuser`:

**Mailbox Routes**

| Username | Domain | Destination | Type |
|----------|--------|-------------|------|
| spam | mydomain.com | /home/myuser/Mail | V |
| myuser | mydomain.com | /home/myuser/Mail | V |

**Filter recipe:**

if (hasaddr("spam@mydomain.com")) 
{
to "|/usr/bin/spamc --spam -u $RECIPIENT"
}

### Using a Single SpamAssassin Instance

Delegate all SpamAssassin filtering to one user account by replacing `$RECIPIENT` with a fixed login in `/etc/maildroprc`. For example, to let `example@example.com` handle filtering for all users:

File: `/etc/maildroprc`

\# Global maildrop rules go here
# See http://www.courier-mta.org/maildrop/maildropfilter.html for syntax
if ($SIZE < 131072)
{
        exception {
                xfilter "/usr/bin/spamc -u example@example.com"
        }
}
# rest of the rules ...

**Pros:**

- New users immediately benefit from existing spam/ham training data
- Low-volume accounts get [Bayesian classifier](http://wiki.apache.org/spamassassin/BayesInSpamAssassin) coverage (adds -2 to +3 points per message)

**Cons:**

- Privacy -- bits of e-mail used by SpamAssassin may be visible to the designated user
- Dilution -- SpamAssassin has finite token storage; high variability across users may reduce effectiveness

### Complex Filtering

Additional examples can be found in the [third installment](http://updates.apnscp.com/2007/09/weekly-tip-3-categorizing-e-mails-with-maildrop/) of the Weekly Tip.

## Forwarding via Filter

Edit `.mailfilter` in the user's home directory:

**Forward only:**

to "!user@mydomain.com"

**Forward and keep a local copy:**

cc "!user@mydomain.com"
