Go to the first, previous, next, last section, table of contents.


System-wide message filtering

The previous chapters describe checks that can be applied to messages before they are accepted by a host. There are also mechanisms for checking messages once they have been received, but before they are delivered. A system message filter can be run once for each message; it is also possible to run a centrally-defined filter file once for each address.

The system message filter

The system message filter operates in a similar manner to users' filter files, but it is run just once per message at the start of a delivery attempt, before any routing or directing is done.

The `message_filter' option names the filter file, while `message_filter_user' and `message_filter_group' specify the uid and gid to be used while processing it. If they are not set, then the exim uid is used if available and if `seteuid()' is available; otherwise root is used. There are also options for specifying which transports are to be used if the filter generates any file, pipe or autoreply deliveries.

The filter file can contain any of the normal filtering commands, as described in the separate document Exim's User interface to mail filtering. However, because the system filter is run just once per message, the variable `$local_part' is not available, nor does the `personal' condition make any sense.

The filter variables `$n0' -- `$n9' can be used in a system filter; when it finishes, their values are copied into `$sn0' -- `$sn9' and are thereby made available to users' filter files. Thus a system filter can, for example, set up a `score' for a message to which users' filter files can refer.

In addition to the filter commands available in user's files there are some extra commands which are available only in system filter files:


fail
freeze
headers add <string>
headers remove <string>

As well as the additional commands, there is also an extra expansion variable, `$recipients', containing a list of all the recipients of the message, separated by commas and white space. The extra commands and variable are not available in ordinary users' filter files. They are faulted in normal use and in testing via `-bf', but not if `-bF' is used.

There are two special conditions which, though available in users' filter files, are designed for use in system filters. The condition `first_delivery' is true only for the first attempt at delivering a message, while `manually_thawed' is true only if the message has been frozen, and subsequently thawed by an admin user. An explicit forced delivery counts as a manual thaw, but thawing as a result of the `auto_thaw' setting does not.

The `freeze' and `fail' commands can optionally be followed by the word `text' and a string containing an error message, for example:


fail text "this message looks like spam to me"

The keyword `text' is optional if the next character is a double quote. The `fail' command causes all recipients to be failed, while `freeze' suspends all delivery attempts. It is ignored if the message has been manually unfrozen and not manually frozen since. This means that automatic freezing by a system filter can be used as a way of checking out suspicious messages. If a message is found to be all right, manually unfreezing it allows it to be delivered.

The argument for the `headers add' is a string which is expanded and then added to the end of the message's headers. It is the responsibility of the filter maintainer to make sure it conforms to RFC 822 syntax. More than one header may be added in one command by including `\n' in the string.

The argument for `headers remove' is a colon-separated list of header names. This command applies only to those headers that are stored with the message; ones such as `Envelope-To:' and `Return-Path:' that are added at delivery time cannot be removed by this means.

If the filter sets up any deliveries of its own, an extra header line is added to them with the name `X-Envelope-to:'. This contains up to 100 of the original message's envelope recipients. If the filter specifies any significant deliveries, then the message's own recipient list is ignored; otherwise it is added to any recipients set up by the filter.

Take great care with the `fail' command when basing the decision to fail on the contents of the message, because this option causes a normal delivery error message to be generated, and it will of course include the contents of the original message and will therefore trigger the `fail' command again (causing a mail loop) unless steps are taken to prevent this. Testing the `error_message' condition is one way to prevent this. You could use, for example


if
  $message_body contains "this is spam" and not error_message
then
  fail text "spam is not wanted here"
endif

though of course that might still let through unwanted messages. The alternative is clever checking of the body and/or headers to detect error messages caused by the filter.

Per-address filtering

In contrast to the system filter, which is run just once per message for each delivery attempt, it is also possible to set up a system-wide filtering operation that runs once for each address, for local addresses only. In this case, variables such as `$local_part' and `$domain' can be used, and indeed, the choice of filter file could be made dependent on them. This is an example of a director which implements such a filter:


central_filter:
  driver = forwardfile
  file = /central/filters/${local_part}
  no_check_local_user
  no_verify
  filter
  allow_system_actions

The setting of `allow_system_actions' permits the use of `freeze' and `fail' in the filter file, but not the `headers' command or the `$recipients' variable.


Go to the first, previous, next, last section, table of contents.