String Expansion
You can configure Exim in many different ways by using string expansions and lookups. String expansion is triggered by a dollar sign ($), the expander copies the string from left-to-right until it hits a dollar symbol at which point it reads to the end of the expansion item, does what ever processing is required and adds the resulting substring to its output before continuing to read the rest of the original string. Most of the expansions use curly brackets as delimiters but not all.
String expansion example | Before-${substr_4_2:$local_part}-After Note: |
Sometimes you may what to include a dollar symbol, braces, slashes, etc. Exim makes it possible by escaping a character which means to take the character literally. There are two ways to perform this
Using \ | ${if match {$local_part} {dollar\$@datadisk.co.uk}} |
Using \N when you have lots of escaping | ${if match {$local_part} {\Ndollar$$$$$$$@datadisk.co.uk\N}} Note: if you have lots of escaping, then use \N |
Variables substitution can come in two favors, and you can use the def condition to see if a varaible has been defined or not.
Variable substitution | $local_part ${local_part} ${if def:sender_host_address {remote}{local}} # yields the string remote if empty and local otherwise |
The contents of a specific message header line can be inserted into a string by an item of the form $header_from:, the abbrevation $h can be used instead of $header.
Exim can operation on some portions of string having expanded the string first, ther are in the form of ${<operator-name>:<substring>}. In some cases the operator name is followed by one or more argument values separated by underscores. The string starts immediately after the colon, and may have leading and/or trailing whitespace.
Extracting the initial part of a substring | # Lets assume $local_part equals paul # Extracting from a file Note: |
Extracting an arbitrary part of a substring | ${substr_6_4:${lookup{root}lsearch{/etc/passwd}}} # produced root Note: |
Hashing | ${hash_5:${lookup{root}lsearch{/etc/passwd}}} # produced megiq ${nhash_5:${lookup{root}lsearch{/etc/passwd}}} # produced 0 Note: |
Forcing case letters | $home/mail/${lc:$sender_address} # force to lower case $home/mail/${uc:$sender_address} # force to upper case |
IP address masking | ${mask:$sender_host_address/26} # expand sender_host_address |
Quoting local parts | ${quote_local_part:$local_part}@datadisk.co.uk # exim double quotes the local_part |
Quoting data from regular expressions | ${if match{$h_to:}{^.*${rxquote:$local_part@datadisk.co.uk}} {.. Note: rxquote inserts a backslash before any non-alphanumeric characters |
You can perform some simple arithmetic in Exim
Simple Arithmetic | ${eval:1+1} # produces 2 |
The tr expansion item translates single characters in strings into different characters, according to its arguments.
tr example | ${tr {a,b,c}{,}{:}} # produces a:b:c Note: {,} pattern to match {:} the replacement pattern |
In Perl you can use the s (substition) with the g (global) to replace test, Exim has a simular feature
Text substitution | ${sg {abcdefabcdef}{abc}{***}} # produces ***def***def ${sg {abcdef}{^(...)(...)\$}{\N$2$1\N}} # produces defabc, (...)(...) is $1 and $2 |
Exim has the capabilities to use condition expressions using the if statement, the if statement has a number of comparsion operators both string and numeric.
String Comparison |
|
eq | Equal |
eqi | Equal, case independent |
ge | Greater or equal |
gei | Greater or equal, case independent |
gt | Greater |
gti | Greater, case independent |
le | less or equal |
lei | less or equal, case independent |
lt | less |
lti | less, case independent |
Numeric Comparison |
|
= | equal |
== | equal |
> | greater |
>= | great or equal |
< | less |
<= | less or equal |
Now for some examples
if statement | ${if <condition> {<string1>}{<string2>}} Note: if the condition is meet then string1 is expanded, other string2 is expanded |
if examples | ## String ${if ! eq{$local_part}{paul} {/var/mail/$local_part} {/home/paul/inbox}} # using negation ${if match {$local_part}{\N^x(\d\d)\N}{$1}} # capture the match in $1 ## Numerical Note: you can of course nest if statements, there are other conditions that you can use, i will you to investigate these match_ip |
You can check for empty variables and non-existent header lines and file existance
Empty variable | ${if def:sender_host_address {remote}{local}} # yields the string remote if empty and local otherwise |
File existance | ${if exists{/var/oldmail/$local_part}{old}{new}mail/$local_part} Note: expands to /var/oldmail/paul if it exists otherwise /var/newmail/paul |
You can check the statement of the message delivery
Check delivery | condition = ${first_delivery} |
You can force the expansion failure, by using two substrings "true" and "false", by using "false" you force the string expansion to fail.
Force failure | header_add = ${if eq{$sender_host_address} {}{X-Postmaster: <postmaster@datadisk.co.uk>} fail} Note: when $sender_host_address is not empty false forces the string expansion to fail which causes the header addition to be cancelled |
You can perform powerful lookups with Exim, it is in the form of a conditional expansion containing two substrings following the specification of the lookup, if the lookup succeeds the first substring is expanded and used, during its expansion the variable $value contains the data that was looked up. If the lookup fails the second substring is expanded and used, just in case of an if condition, the second substring may be absent or the word fail may used as described in the previous section.
single-key lookups | ${lookup {$local_part} lsearch{/the/file} {$value}{/var/mail/$local_part}} {$local_part} - is the key to lookup {/the/file} - is the file that we lookup the key value from above {$value} - is the value obtained from the file using the key {/var/mail/$local_part} - is used if the lookup fails , no key value found in the file |
It is possible to insert a whole file into a expansion string by using readfile
Inserting whole files | ${readfile{/etc/company_message}{<end-of-line string>}} |
Extracting Fields from Substrings
You can extract data fields from substrings, having expanded them first
splitting up addresses | local part is ${localpart:$sender_address} domain is ${domain:$sender_address} |
extracting name fields | # The file may contain pvalle: uid=100 gid=100 home=/export/home/pvalle # To extract the uid using a lookup ${extract{uid}{ ${lookup{pvalle}lsearch{/the/file}} }} # Using a text string ${extract{uid} {uid=100 gid=100 home=/export/home/pvalle}} |
extract field location | ${extract{4}{:} {0:1:2:3:4:5} } # produces 3 (remember we start at 0 and use : as the delimiter) Note: {4} field number we want {:} the delimiter we use |
You can communicate with other process using a socket, running an external program and using embedded Perl. I provide a basic syntax but I will leave you to investigate further.
Socket | ${readsocket{/socket/name}{$auth1:$auth2}} |
Running external program | ${run {<command> <args>} {<string1>}{<string2>}} |
Embedded Perl | ${perl{func}{argument1}{argument2} ... } |
Exim provides a way to test your string expansions without actually sending a message or causing any problems with Exim, by usng the -be option
Testing string expansions | $ exim -be > ${lookup {root} lsearch{/etc/passwd}} x:0:1:Super User:/:/bin/sh > |