Message Filtering

As a message passes through Exim, it can be inspected by a number of different filters, filters work as follows:

Sieve is now the standard for filtering which is defined in RFC3028. The first line of users filter determines which kind of filter it is:

Example of Filter Commands

Here are some example filters, all will be explained below

Simple # Exim filter
deliver paul.valle@datadisk.co.uk

Note: standard .forward file instructing to deliver to the above address
Vacation handling

# Exim filter
unseen pipe "/usr/ucb/vacation $local_part"

Note: pass the message via a pipe using the above command

Vacation handling # Exim filter
if personal then vacation endif

Note: this is being handle entirely by a Exim filter without running another program
Sort into different mailboxes # Exim filter
if $header_subject: contains "empire" or $header_subject contains "foundation"
then
   save $home/mail/f+e
endif
Discarding Messages # Exim filter
if $sender_address contains "@spam.address" and $sender_address does not contain "postmaster@"
then
   seen finish
endif
Mulitple personal mailboxes # Exim filter
if $local_part_suffix is "-foo"
then
   save $home/mail/foo
elif $local_part_suffix is "-bar"
then
   save $home/mail/bar
endif

User Filter

Users filters are handled by the redirect router as an alternative form of forwarding information.If the allow_filter option is set foe the router and the first line of the redirection begins with

the file is interpreted as a filter instead of a normal forwarding file. You can stop filtering using the options forbid_sieve_filter and forbid_exim_filter. You can disable certain features

System Filter

A system filter differs from a users filter in that it only runs once per delivery process, you enable a system filter by specifying the system_filter option. Sieve filtering cannot be used for system filtering.

System filter example system_filter = /etc/mail/exim.filter

There are a number of other options available

Testing Filters

You should always test your filters, Exim has a way to test both exim and sieve filters before implementing them, you can also use this method to test .forward files.

Testing a Filter # exim -bf new-filter <test-message
# exim -bvf new-filter <test-message                                ## adding verbose
# exim -bf new-filter -f paul.valle@datadisk.co.uk <test-message    ## specify the sender
# exim -bF system-filter <test-message                              ## test a system filter

There are a number of options you can pass to the exim command to change the address

change the domain -bfd
change the local part -bfl
specify the prefix -bfp
specify the suffix -bfs

Format of Exim Filters

The first line of an Exim filter must be the following, this distinguishes it from a Sieve filter or a .forward file.

The remainder of the filter is a sequence of commands, these consist of keywords and data values seperated by whitespace or line breaks, except when using conditional statements like if.

filter command deliver paul.valle@datadisk.co.uk

Note: deliver is the keyword and paul.valle@datadisk.co.uk is the data value

Filter Commands

There are a number of fileter commands

add increament a users variable
deliver deliver to an email address
fail fail delivery (system filter only)
finish end filter processing
freeze freeze delivery (system filter only)
headers add/remove header lines (system filter only)
if test condition
logfile define log file
logwrite write to log file
mail send a message
pipe pipe to a command
save save to a file
testprint print while testing
vacation a special version of mail

The add command provides a basic means of counting within a filter, the syntax is as follows

add example

## add <number> to <user variable>

add 2 to n3
add -1 to n3                    ## using add to take away

The names of the user variables consist of the letter n followed by a single digit, therefore there are 10 user variables n0-n9, you get at the variable by using $n3. All variables are initialized with zero to start with, at the end of a system variable their values are copied into $sn0-$sn9 so they can be referenced in users filters. You can subtract by using a negative number.

The deliver command sets up message deliveries, such deliveries are significant actions unless the command is preceded by unseen

deliver example

## deliver <mail address>

deliver paul.valle@datadisk.co.uk

The above example is the same as putting a forwarding address in the .forward file.

The save command causes a copy of the message to be appended to the given file, you can use multiple save commands, however this may be forbidden by setting the forbid_file on the router.

save example

## save <file name>

save $home/mail/vallep                     ## system filter you must use an absolute path
save vallep                                ## user filter uses the $home variable

Again there are a number of router options that can affect the save command: filter_prepend_home, system_filter_directory_transport, directory_transport.

The pipe command causes a seprate process to be run, and a copy of the message is passed to it on its standard input.

pipe example

## pipe <command>

pipe "/usr/bin/countmail $sender_address"          ## use a script to count mail

You can ignore deliver error messages by using unseen with noerror

ignoring error messages unseen noerror pipe $home/bin/mailscan

Note:
unseen - ensures normal delivery is not affected
noerror - ensures that a failure of the pipe doe not cause a bounce message to be generated

The mail and vacation commands sends a message

mail and vacation example

## mail to <address-list>
##      cc <address-list>
##      bcc <address-list>
##      from <address>
##      reply_to <address>
##      subject <text>
##      text <text>
##      [expand] file <filename>
##      return message
##      once <note file name>
##      once_repeat <time interval>
##      log <log file name>

mail text "Got you message about $h_subject:"

vacation once_repeat 14d                        ## send a message 14 days after the last one was sent

The logfile can keep the actions taken by a filter, again some options can impact the logfile command. The logfile is updated immediately while the filter is running, the logwrite command write to the specified logfile

logfile example

## logfile <file name>
##
## logwrite <some string of text>

logfile $home/filter.log

logwrite "$tod_log $message_id processed"

logging to a different file

warn
   senders = *@datadisk.co.uk
   recipients = *@datadisk.co.uk
   !hosts = mailhost
   logwrite = :reject: Possible spoofed email sender: $sender_address($sender_host_address)\
                       recipient: $local_part_$domain

Note: the :reject: means that this will be logged to the reject log instead of mainlog. you can point to any of the logfiles (mainlog, reject and panic), you can also write to a number of logfiles at the same time using :main,reject:

:reject:     - write to the reject logfile
:main,panic: - write to both main and panic log files

The testprint command enable you to print out variable values, it has no impact on the mail being delivered

testprint example

## testprint <text>

testprint "home=$home reply_address=$reply_address"

The finish command which has no arugements, causes Exim to stop interpreting the filter

It is possible to have conditional statements in a filter

conditional if ## if <condition>
## then <commands>
## elif <condition>
## then <commands>
## else <commands>
## endif
String Test Conditions
start of string <text1> begins <text2>
<text1> does not begin <text2>
end of string <text1> ends <text2>
<text1> does not end <text2>
exact string <text1> is <text2>
<text1> is not <text2>
partial string <text1> contains <text2>
<text1> does not contain <text2>
regular expression string <text1> matchess <text2>
<text1> does not match <text2>
Number Test Conditions
number testing

<number1> is above <number2>
<number1> is not above <number2>
<number1> is below <number2>
<number1> is not below <number2>

Other Test Conditions
personal mail personal
significant actions if not delivered than save mail/anomalous
error message if error_message then finish endif
list of addresses foranyaddress <string> (<condition>)

Additional System Filters

The fail command prevents any deliveries of the message from taking place

fail example

## fail text <text>

fail text "admin rejection"

The freeze command prevents any deliveries other than those previously set up in the filter from taking place

freeze example

## freeze text <text>

freeze text "admin rejection"

The headers command add's or remove's the text to the end of the message's header line

header example

## headers add <text>

headers add " - $primary_hostname"
headers remove "Return-Receipt-To"