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


47. 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. If a system message filter is defined, it is run each time a delivery process is started for a message. It is also possible to run a centrally-defined filter file once for each local address, as part of the directing for that address.

47.1 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 (however many recipients is has) at the start of a delivery attempt, before any routing or directing is done. If a message fails to be completely delivered at the first attempt, the filter is run again at the start of every retry.

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.

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, the message's own recipient list is ignored. Non-significant deliveries, however, are added to the message's existing recipients.

Warning: If a system filter uses the first_delivery condition to specify an `unseen' (non-significant) delivery, and that delivery does not succeed, it will not be tried again.

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, the Exim uid is used if available and if seteuid() is available; otherwise root is used.

Important: If the system filter generates any deliveries directly to files or pipes (via the save or pipe commands), transports to handle these deliveries must be specified by setting message_filter_file_transport and message_filter_pipe_transport, respectively. Similarly, message_filter_reply_transport must be set to handle any autoreplies.

The filter file can contain any of the normal filtering commands, as described in the separate document Exim's interface to mail filtering. However, remember that the system filter is run just once, at the start of a delivery process, however many recipients the message may have. For this reason, the variables $local_part and $domain are 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.

47.2 Additional commands for system filters

In a system filter, if a deliver command is followed by

errors_to <some address>

in order to change the envelope sender (and hence the error reporting) for that delivery, any address is permitted. (In a user filter, only the current user's address can be set.) For example, if some mail is being monitored, you might use

unseen deliver monitor@spying.example errors_to root@local.domain

to take a copy which would not be sent back to the normal error reporting address if its delivery failed.

There are also 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.

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 interpretation of a system filter file ceases after a freeze or fail command is obeyed. However, any deliveries that were set up earlier in the filter file are honoured, so you can use a sequence such as

mail ...
freeze

to send a specified message when the system filter is freezing (or failing) something. The normal deliveries for the message do not, of course, take place.

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. Leading white space is ignored, and if the string is otherwise empty, or if the expansion is forced to fail, the command has no effect. A newline is added at the end of the string if it lacks one. More than one header may be added in one command by including `\n' within 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.

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.

47.3 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 (described above) or the $recipients variable. As in the case of a system filter, freeze and fail cause filter interpretation to cease, but any deliveries that were previously set up are honoured.


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