Please note: in the examples below, long right hand side values in mapfiles
are displayed in multiple lines. (After patching the source code, you can find
simple examples on how to use these features in the files src/smtpd/smtpd_check.in?
too.)

${name?text}, $(name?text}
Conditional expansion. If the named attribute is defined,
the expansion is the given text with another iteration of
macro expansions. Otherwise, the expansion is empty.
${name:text}, $(name:text)
Conditional expansion. If the named attribute is undefined,
the expansion is the given text with another iteration of
macro expansions. Otherwise, the expansion is empty.

This patch extends Postfix with the trinary conditional macro expansion.

However, BEWARE! BEWARE! BEWARE! INCOMPATIBILITY!

In order to use the intuitive ${name?text1:text2} or $(name?text1:text2}
forms, the inverted test operator ':' has been replaced with '!'. Thus
the full list of the new conditional macro expansions are:

${name?text}, $(name?text}
Conditional expansion. If the named attribute is defined,
the expansion is the given text with another iteration of
macro expansions. Otherwise, the expansion is empty.
${name?text1:text2}, $(name?text1:text2}
Conditional expansion. If the named attribute is defined,
the expansion is the given text1 with another iteration of
macro expansions. Otherwise, the expansion is the given text2
with another iteration of macro expansions. Embedded pure ':'
characters in text1 must be escaped as '\:'.
${name!text}, $(name!text)
Conditional expansion. If the named attribute is undefined,
the expansion is the given text with another iteration of
macro expansions. Otherwise, the expansion is empty.
${name!text1:text2}, $(name!text1:text2}
Conditional expansion. If the named attribute is undefined,
the expansion is the given text1 with another iteration of
macro expansions. Otherwise, the expansion is the given text2
with another iteration of macro expansions. Embedded pure ':'
characters in text1 must be escaped as '\:'.

Please note that in the unpatched Postfix literal $name in text
had to be escaped as $$name. Now for the sake of consistency, it
must be escaped as \$name.

In UCE restrictions, map lookups may return reject messages in the form
"[45]xx text". The following new macros were implemented for the message
text:

$client_name client hostname (or "unknown")
$client_address client address
$client client hostname [client address]
$helo announced helo name
$sender sender E-mail address (sender@from.somewhere)
$sender_name the username part of the sender E-mail
address (sender)
$sender_domain the domain part of the sender E-mail address
(from.somewhere)
$recipient recipient E-mail address (recip@to.somewhere)
$recipient_name the username part of the recipient E-mail
address (recip)
$recipient_domain the domain part of the recipient E-mail
address (to.somewhere)
$rbl_txt DNS TXT lookup result for the client
in the current RBL/RHSBL domain
(see example below)
$rbl_ip the lookup result for the client
in the current RBL/RHSBL domain.
If the client is not in the domain,
it's value is "0.0.0.0".

Without modifying the source code, one cannot rewrite the error messages
returned by the builtin Postfix restrictions. A new maptype called
restrictions was implemented for rewriting the error messages:

key any one-word Postfix restriction including user defined
restriction classes
value anything which is valid on the RHS in mapfiles
(OK|RELAY|REJECT|[45]xx text|list of UCE restrictions)

The "lookup" in the map file happens as follows:

The (first) key as restriction is evaluated. If it results DUNNO,
proceed with the next key or return DUNNO if there is no key left.

If the key is evaluated to OK, return OK.

If the key is evaluated to REJECT, throw away the result and return with
evaluating the *value* from the map file belonging to the key.

Here follows a little example on how to redefine the default
Postfix error message for unknown clients.

In order to make possible individual RBL/RHSBL lookups, we implemented two
new maptypes called rbl/rhsbl. The key/value pairs in the map files are

key domain name
value anything which is valid on the RHS in mapfiles
(OK|RELAY|REJECT|[45]xx text|list of UCE restrictions)

In the case of rbl maptype:

The client address is looked up in the domains (keys) sequentially
until it is found in one.

If the client is found in one RBL domain, the value belonging to the key
is evaluated and returned as a result of the table lookup.

In the case of rhsbl maptype:

The domain part of the sender (or recipient address) is looked up
as sub-domains to the tld (i.e f.i.e.com, i.e.com, e.com) in
the domains (keys) sequentially until it is found in one.
The address can be specified either by context (xxx_restrictions,
check_xxx_access where xxx is sender or recipient) or by parametrized
access check. The most effective one is using a parametrized access check.

The max_rhsbl_subdomain (default 5) parameter controls maximally from
which subdomain to start the lookup, i.e. in the case a.b.c.d.e.com,
the lookup is started at b.c.d.e.com and continues to e.com.

If the domain is found in one RHSBL domain, the value belonging to the key
is evaluated and returned as a result of the table lookup.

Please note, rbl/rhsbl maps don't perform DNS TXT record lookups. The $rbl_txt
macro triggers the DNS TXT lookup in the current RBL/RHSBL domain. Additionally,
until a new DNS A record isn't looked up by rbl/rhsbl map, $rbl_txt
preserves the result of the last DNS TXT record lookup.

All the introduced maptypes (restrictions, rbl/rhsbl, cidr) can be used in
inline mode, which (due to the parsing in main.cf) means one-liner inline
mapfiles. Inline mapfiles helps to avoid multiple tiny files just for one
type of lookup and it speeds up a little bit the processing.
The notation is maptype:inline:variable_name. Example

At evaluating it, first the macros in the parameter are expanded, then
the result is looked up in the specified mapfile according to the maptype.

The goal at the first version of this patch was to make UCE restrictions
as configurable as possible. Let it be possible for the users to define
(setup) individual restrictions based on different local spammer databases,
DNS checkings, RBL-style databases.

The biggest complain we received after installing the patched system was:
"But how could I let in E-mail messages from foo address despite my
restriction settings?"

With the parametrized access check we can make possible for the users to set
up individual exceptions against their individual UCE settings.

Bug fixed: macros were expanded in the
whole error message, not only in the part
returned by the table lookup. Thanks to
Bernd Matthes for reporting the problem.

07.06.2002 - 1.1.10

Patch upgraded to postfix-1.1.10

Bug in rhsbl lookup fixed - lookups of domains
from addresses like foo@bar crashed the smtpd server
due to a missing initialization

Documentation made clearer about the max_rhsbl_subdomain
variable

01.03.2002 - 1.1.4

Patch upgraded to postfix-1.1.4

14.01.2002 - 20020107

Arrgh, I'm blind :-(. Macros $[sender|recipient]_name
and $[sender|recipient]_domain was mixed up, plus
there was still a bug present in the evaluation
of these macros (thanks to
Simson L.Garfinkel for his patience)

08.01.2002 - 20020107

Patch upgraded to snapshot-20020107

Bug in $[sender|recipient]_[name|domain] macro
evaluations fixed

$helo macro was really named as $helo_name in
the source, fixed

rhsbl maptype added with configuration parameter
max_rhsbl_subdomain (default 5). The parameter
controls maximally from which subdomain to start
the lookup, i.e. in the case a.b.c.d.e.com,
the lookup is started at b.c.d.e.com and continues
to e.com.