Previous   Next   Contents       (Exim 4.30 Specification)

22. The redirect router

The redirect router handles several kinds of address redirection. Its most common uses are for resolving local part aliases from a central alias file (usually called /etc/aliases) and for handling users' personal .forward files, but it has many other potential uses. The incoming address can be redirected in several different ways:

The generic transport option must not be set for redirect routers. However, there are some private options which define transports for delivery to files and pipes, and for generating autoreplies. See the file_transport, pipe_transport and reply_transport descriptions below.

22.1. Redirection data

The router operates by interpreting a text string which it obtains either by expanding the contents of the data option, or by reading the entire contents of a file whose name is given in the file option. These two options are mutually exclusive. The first is commonly used for handling system aliases, in a configuration like this:

  system_aliases:
    driver = redirect
    data = ${lookup{$local_part}lsearch{/etc/aliases}}

If the lookup fails, causing the expanded string to be empty, the router declines. A configuration using file is commonly used for handling users' .forward files, like this:

  userforward:
    driver = redirect
    check_local_user
    file = $home/.forward
    no_verify

If the file does not exist, or causes no action to be taken (for example, it is empty or consists only of comments), the router declines. Warning: This is not the case when the file contains syntactically valid items that happen to yield empty addresses, for example, items containing only RFC 2822 address comments.

22.2. Forward files and address verification

It is usual to set no_verify on redirect routers which handle users' .forward files, as in the example above. There are two reasons for this:

22.3. Interpreting redirection data

The contents of the data string, whether obtained from data or file, can be interpreted in two different ways:

When a message is redirected to a file (a “mail folder”), the file name given in a non-filter redirection list must always be an absolute path. A filter may generate a relative path – how this is handled depends on the transport's configuration. See section 26.1 for a discussion of this issue for the appendfile transport.

22.4. Items in a non-filter redirection list

When the redirection data is not an Exim or Sieve filter, for example, if it comes from a conventional alias or forward file, it consists of a list of addresses, file names, pipe commands, or certain special items (see section 22.6 below). The special items can be individually enabled or disabled by means of options whose names begin with allow_ or forbid_, depending on their default values. The items in the list are separated by commas or newlines. If a comma is required in an item, the entire item must be enclosed in double quotes.

Lines starting with a # character are comments, and are ignored, and # may also appear following a comma, in which case everything between the # and the next newline character is ignored.

If an item is entirely enclosed in double quotes, these are removed. Otherwise double quotes are retained because some forms of mail address require their use (but never to enclose the entire address). In the following description, “item” refers to what remains after any surrounding double quotes have been removed.

Warning: If you use an Exim expansion to construct a redirection address, and the expansion contains a reference to $local_part, you should make use of the quote expansion operator, in case the local part contains special characters. For example, to redirect all mail for the domain obsolete.example, retaining the existing local part, you could use this setting:

  data = ${quote:$local_part}@newdomain.example

22.5. Redirecting to a local mailbox

A redirection item may safely be the same as the address currently under consideration. This does not cause a routing loop, because a router is automatically skipped if any ancestor of the address that is being processed has the same local part and was processed by that router. The address is therefore passed to the following routers, so it is handled as if there were no redirection. When making this loop-avoidance test, the complete local part, including any prefix or suffix, is used.

Specifying the same local part without a domain is a common usage in personal filter files when the user wants to have messages delivered to the local mailbox and also forwarded elsewhere. For example, the user spqr might have a .forward file containing this:

  spqr, Sam.Reman@other.domain.example

For compatibility with other MTAs, such local parts may be preceeded by “\”, but this is not a requirement for loop prevention. However, it does make a difference if more than one domain is being handled synonymously.

If an item begins with “\” and the rest of the item parses as a valid RFC 2822 address that does not include a domain, the item is qualified using the domain of the incoming address. In the absence of a leading “\”, unqualified addresses are qualified using the value in qualify_recipient, but you can force the incoming domain to be used by setting qualify_preserve_domain.

Care must be taken if there are alias names for local users. For example if the system alias file contains

  Sam.Reman: spqr

then

  Sam.Reman, spqr@reme.elsewhere.example

in spqr's forward file fails on an incoming message addressed to Sam.Reman. The redirect router does not process Sam.Reman the second time round, because it has previously routed it, and the following routers presumably cannot handle the alias. The forward file should really contain

  spqr, spqr@reme.elsewhere.example

but because this is such a common error, the check_ancestor option (see below) exists to provide a way to get round it. This is normally set on a redirect router that is handling users' .forward files.

22.6. Special items in redirection lists

In addition to addresses, the following types of item may appear in redirection lists (that is, in non-filter redirection data):

22.7. Duplicate addresses

Exim removes duplicate addresses from the list to which it is delivering, so as to deliver just one copy to each address. This does not apply to deliveries routed to pipes by different immediate parent addresses, but an indirect aliasing scheme of the type

  pipe:       |/some/command $local_part
  localpart1: pipe
  localpart2: pipe

does not work with a message that is addressed to both local parts, because when the second is aliased to the intermediate local part “pipe” it gets discarded as being the same as a previously handled address. However, a scheme such as

  localpart1: |/some/command $local_part
  localpart2: |/some/command $local_part

does result in two different pipe deliveries, because the immediate parents of the pipes are distinct.

22.8. Repeated redirection expansion

When a message cannot be delivered to all of its recipients immediately, leading to two or more delivery attempts, redirection expansion is carried out afresh each time for those addresses whose children were not all previously delivered. If redirection is being used as a mailing list, this can lead to new members of the list receiving copies of old messages. The one_time option can be used to avoid this.

22.9. Errors in redirection lists

If skip_syntax_errors is set, a malformed address that causes a parsing error is skipped, and an entry is written to the main log. This may be useful for mailing lists that are automatically managed. Otherwise, if an error is detected while generating the list of new addresses, the original address is deferred. See also syntax_errors_to.

22.10. Private options for the redirect router

The private options for the redirect router are as follows:


allow_defer

Type:  boolean
Default:  false

Setting this option allows the use of :defer: in non-filter redirection data, or the defer command in an Exim filter file.


allow_fail

Type:  boolean
Default:  false

If this option is true, the :fail: item can be used in a redirection list, and the fail command may be used in a filter file.


allow_filter

Type:  boolean
Default:  false

Setting this option allows Exim to interpret redirection data that starts with “#Exim filter” or “#Sieve filter” as a set of filtering instructions. There are some features of Exim filter files that some administrators may wish to lock out; see the forbid_filter_xxx options below. The filter is run using the uid and gid set by the generic user and group options. These take their defaults from the password data if check_local_user is set, so in the normal case of users' personal filter files, the filter is run as the relevant user. When allow_filter is set true, Exim insists that either check_local_user or user is set.


allow_freeze

Type:  boolean
Default:  false

Setting this option allows the use of the freeze command in an Exim filter. This command is more normally encountered in system filters, and is disabled by default for redirection filters because it isn't something you usually want to let ordinary users do.


check_ancestor

Type:  boolean
Default:  false

This option is concerned with handling generated addresses which are the same as some address in the list of redirection ancestors of the current address. Although it is turned off by default in the code, it is set in the default configuration file for handling users' .forward files. It is recommended for this use of the redirect router.

When check_ancestor is set, if a generated address is the same as any ancestor of the current address, it is replaced by a copy of the current address. This helps in the case where local part A is aliased to B, and B has a .forward file pointing back to A. For example: “Joe.Bloggs” is aliased to “jb” and ~jb/.forward contains:

  \Joe.Bloggs, <other item(s)>

Without the check_ancestor setting, either local part (“jb” or “joe.bloggs”) gets processed once by each router and so ends up as it was originally. If “jb” is the real mailbox name, mail to “jb” gets delivered (having been turned into “joe.bloggs” by the .forward file and back to “jb” by the alias), but mail to “joe.bloggs” fails. Setting check_ancestor on the redirect router that handles the .forward file prevents it from turning “jb” back into “joe.bloggs” when that was the original address. See also the repeat_use option below.


check_group

Type:  boolean
Default:  see below

When the file option is used, the group owner of the file is checked only when this option is set. The permitted groups are those listed in the owngroups option, together with the user's default group if check_local_user is set. If the file has the wrong group, routing is deferred. The default setting for this option is true if check_local_user is set and the modemask option permits the group write bit, or if the owngroups option is set. Otherwise it is false, and no group check occurs.


check_owner

Type:  boolean
Default:  see below

When the file option is used, the owner of the file is checked only when this option is set. If check_local_user is set, the local user is permitted; otherwise the owner must be one of those listed in the owners option. The default value for this option is true if check_local_user or owners is set. Otherwise the default is false, and no owner check occurs.


data

Type:  string, expanded
Default:  unset

This option is mutually exclusive with file. One or other of them must be set, but not both. The contents of data are expanded, and then used as the list of forwarding items, or as a set of filtering instructions. If the expansion is forced to fail, or the result is an empty string or a string that has no effect (consists entirely of comments), the router declines.

When filtering instructions are used, the string must begin with “#Exim filter”, and all comments in the string, including this initial one, must be terminated with newline characters. For example:

  data = #Exim filter\n\
         if $h_to: contains Exim then save $home/mail/exim endif

If you are reading the data from a database where newlines cannot be included, you can use the ${sg} expansion item to turn the escape string of your choice into a newline.


directory_transport

Type:  string, expanded
Default:  unset

A redirect router sets up a direct delivery to a directory when a path name ending with a slash is specified as a new “address”. The transport used is specified by this option, which, after expansion, must be the name of a configured transport. This should normally be an appendfile transport.


file

Type:  string, expanded
Default:  unset

This option specifies the name of a file that contains the redirection data. It is mutually exclusive with the data option. The string is expanded before use; if the expansion is forced to fail, the router declines. Other expansion failures cause delivery to be deferred. The result of a successful expansion must be an absolute path. The entire file is read and used as the redirection data. If the data is an empty string or a string that has no effect (consists entirely of comments), the router declines.

If the attempt to open the file fails with a “does not exist” error, Exim runs a check on the containing directory, unless ignore_enotdir is true (see below). If the directory does not appear to exist, delivery is deferred. This can happen when users' .forward files are in NFS-mounted directories, and there is a mount problem. If the containing directory does exist, but the file does not, the router declines.


file_transport

Type:  string, expanded
Default:  unset

A redirect router sets up a direct delivery to a file when a path name not ending in a slash is specified as a new “address”. The transport used is specified by this option, which, after expansion, must be the name of a configured transport. This should normally be an appendfile transport. When it is running, the file name is in $address_file.


forbid_blackhole

Type:  boolean
Default:  false

If this option is true, the :blackhole: item may not appear in a redirection list.


forbid_file

Type:  boolean
Default:  false

If this option is true, this router may not generate a new address that specifies delivery to a local file or directory, either from a filter or from a conventional forward file. This option is forced to be true if one_time is set. It applies to Sieve filters as well as to Exim filters, but if true, it locks out the Sieve's “keep” facility.


forbid_filter_existstest

Type:  boolean
Default:  false

If this option is true, string expansions in Exim filters are not allowed to make use of the exists condition.


forbid_filter_logwrite

Type:  boolean
Default:  false

If this option is true, use of the logging facility in Exim filters is not permitted. Logging is in any case available only if the filter is being run under some unprivileged uid (which is normally the case for ordinary users' .forward files).


forbid_filter_lookup

Type:  boolean
Default:  false

If this option is true, string expansions in Exim filter files are not allowed to make use of lookup items.


forbid_filter_perl

Type:  boolean
Default:  false

This option is available only if Exim is built with embedded Perl support. If it is true, string expansions in Exim filter files are not allowed to make use of the embedded Perl support.


forbid_filter_readfile

Type:  boolean
Default:  false

If this option is true, string expansions in Exim filter files are not allowed to make use of readfile items.


forbid_filter_readsocket

Type:  boolean
Default:  false

If this option is true, string expansions in Exim filter files are not allowed to make use of readsocket items.


forbid_filter_reply

Type:  boolean
Default:  false

If this option is true, this router may not generate an automatic reply message. Automatic replies can be generated only from Exim filter files, not from traditional forward files or Sieve filters. This option is forced to be true if one_time is set.


forbid_filter_run

Type:  boolean
Default:  false

If this option is true, string expansions in Exim filter files are not allowed to make use of run items.


forbid_include

Type:  boolean
Default:  false

If this option is true, items of the form

  :include:<path name>

are not permitted in non-filter redirection lists.


forbid_pipe

Type:  boolean
Default:  false

If this option is true, this router may not generate a new address which specifies delivery to a pipe, either from an Exim filter or from a conventional forward file. This option is forced to be true if one_time is set.


hide_child_in_errmsg

Type:  boolean
Default:  false

If this option is true, it prevents Exim from quoting a child address if it generates a bounce or delay message for it. Instead it says “an address generated from <the top level address>”. Of course, this applies only to bounces generated locally. If a message is forwarded to another host, its bounce may well quote the generated address.


ignore_eacces

Type:  boolean
Default:  false

If this option is set and an attempt to open a redirection file yields the EACCES error (permission denied), the redirect router behaves as if the file did not exist.


ignore_enotdir

Type:  boolean
Default:  false

If this option is set and an attempt to open a redirection file yields the ENOTDIR error (something on the path is not a directory), the redirect router behaves as if the file did not exist.

Setting ignore_enotdir has another effect as well: When a redirect router that has the file option set discovers that the file does not exist (the ENOENT error), it tries to stat() the parent directory, as a check against unmounted NFS directories. If the parent can not be statted, delivery is deferred. However, it seems wrong to do this check when ignore_enotdir is set, because that option tells Exim to ignore “something on the path is not a directory” (the ENOTDIR error). This is a confusing area, because it seems that some operating systems give ENOENT where others give ENOTDIR.


include_directory

Type:  string
Default:  unset

If this option is set, the path names of any :include: items in a redirection list must start with this directory.


modemask

Type:  octal integer
Default:  022

This specifies mode bits which must not be set for a file specified by the file option. If any of the forbidden bits are set, delivery is deferred.


one_time

Type:  boolean
Default:  false

Sometimes the fact that Exim re-evaluates aliases and reprocesses redirection files each time it tries to deliver a message causes a problem when one or more of the generated addresses fails be delivered at the first attempt. The problem is not one of duplicate delivery – Exim is clever enough to handle that – but of what happens when the redirection list changes during the time that the message is on Exim's queue. This is particularly true in the case of mailing lists, where new subscribers might receive copies of messages that were posted before they subscribed.

If one_time is set and any addresses generated by the router fail to deliver at the first attempt, the failing addresses are added to the message as “top level” addresses, and the parent address that generated them is marked “delivered”. Thus, redirection does not happen again at the next delivery attempt.

Warning 1: This means that any header line addition or removal that is specified by this router would be lost if delivery did not succeed at the first attempt. For this reason, the headers_add and headers_remove generic options are not permitted when one_time is set.

Warning 2: To ensure that the router generates only addresses (as opposed to pipe or file deliveries or auto-replies) forbid_file, forbid_pipe, and forbid_filter_reply are forced to be true when one_time is set.

The original top-level address is remembered with each of the generated addresses, and is output in any log messages. However, any intermediate parent addresses are not recorded. This makes a difference to the log only if all_parents log selector is set. It is expected that one_time will typically be used for mailing lists, where there is normally just one level of expansion.


owners

Type:  string list
Default:  unset

This specifies a list of permitted owners for the file specified by file. This list is in addition to the local user when check_local_user is set. See check_owner above.


owngroups

Type:  string list
Default:  unset

This specifies a list of permitted groups for the file specified by file. The list is in addition to the local user's primary group when check_local_user is set. See check_group above.


pipe_transport

Type:  string, expanded
Default:  unset

A redirect router sets up a direct delivery to a pipe when a string starting with a vertical bar character is specified as a new “address”. The transport used is specified by this option, which, after expansion, must be the name of a configured transport. This should normally be a pipe transport. When the transport is run, the pipe command is in $address_pipe.


qualify_preserve_domain

Type:  boolean
Default:  false

If this is set and an unqualified address (one without a domain) is generated, it is qualified with the domain of the incoming address instead of the global setting in qualify_recipient.


repeat_use

Type:  boolean
Default:  true

If this option is set false, the router is skipped for a child address that has any ancestor that was routed by this router. This test happens before any of the other preconditions are tested. Exim's default anti-looping rules skip only when the ancestor is the same as the current address. See also check_ancestor above and the generic redirect_router option.


reply_transport

Type:  string, expanded
Default:  unset

A redirect router sets up an automatic reply when a mail or vacation command is used in a filter file. The transport used is specified by this option, which, after expansion, must be the name of a configured transport. This should normally be an autoreply transport. Other transports are unlikely to do anything sensible or useful.


rewrite

Type:  boolean
Default:  true

If this option is set false, addresses generated by the router are not subject to address rewriting. Otherwise, they are treated like new addresses and are rewritten according to the global rewriting rules.


skip_syntax_errors

Type:  boolean
Default:  false

If skip_syntax_errors is set, syntactically malformed addresses in non-filter redirection data are skipped, and each failing address is logged. If syntax_errors_to is set, a message is sent to the address it defines, giving details of the failures. If syntax_errors_text is set, its contents are expanded and placed at the head of the error message generated by syntax_errors_to. Usually it is appropriate to set syntax_errors_to to be the same address as the generic errors_to option. The skip_syntax_errors option is often used when handling mailing lists.

If all the addresses in a redirection list are skipped because of syntax errors, the router declines to handle the original address, and it is passed to the following routers.

If skip_syntax_errors is set when an Exim filter is interpreted, any syntax error in the filter causes filtering to be abandoned without any action being taken. The incident is logged, and the router declines to handle the address, so it is passed to the following routers.

Currently, any syntax errors in a Sieve filter file cause the “keep” action to occur. The values of skip_syntax_errors, syntax_errors_to, and syntax_errors_text are not used.

skip_syntax_errors can be used to specify that errors in users' forward lists or filter files should not prevent delivery. The syntax_errors_to option, used with an address that does not get redirected, can be used to notify users of these errors, by means of a router like this:

  userforward:
    driver = redirect
    allow_filter
    check_local_user
    file = $home/.forward
    file_transport = address_file
    pipe_transport = address_pipe
    reply_transport = address_reply
    no_verify
    skip_syntax_errors
    syntax_errors_to = real-$local_part@$domain
    syntax_errors_text = \
      This is an automatically generated message. An error has\n\
      been found in your .forward file. Details of the error are\n\
      reported below. While this error persists, you will receive\n\
      a copy of this message for every message that is addressed\n\
      to you. If your .forward file is a filter file, or if it is\n\
      a non-filter file containing no valid forwarding addresses,\n\
      a copy of each incoming message will be put in your normal\n\
      mailbox. If a non-filter file contains at least one valid\n\
      forwarding address, forwarding to the valid addresses will\n\
      happen, and those will be the only deliveries that occur.

You also need a router to ensure that local addresses that are prefixed by real- are recognized, but not forwarded or filtered. For example, you could put this immediately before the userforward router:

  real_localuser:
    driver = accept
    check_local_user
    local_part_prefix = real-
    transport = local_delivery

syntax_errors_text

Type:  string, expanded
Default:  unset

See skip_syntax_errors above.


syntax_errors_to

Type:  string
Default:  unset

See skip_syntax_errors above.




Previous  Next  Contents       (Exim 4.30 Specification)