The most recent patch contains the following extensions, modifications:
${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.
The trinary (test ? if_true : if_false) conditional macro expansion 
was unimplemented.
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.
Examples:
	foo = x
	bar = y
	${foo?foo defined:foo not defined}
		foo defined
	${foo?\$foo\: "$foo":\$foo is emtpy}
		$foo: "x"
	${foo?foo defined, bar ${bar?ditto:doesn't}:foo not defined}
		foo defined, bar ditto
        $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".
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:
>>> from /etc/postfix/main.cf <<< smtpd_recipient_restrictions = restrictions:/etc/postfix/unknown_client >>> from /etc/postfix/unknown_client <<< reject_unknown_client 450 Cannot find your hostname, [$client_address]. Ask your system manager to fix your domain name registration.
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 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.
Example for rbl map and $rbl_txt macro usage:
>>> from /etc/postfix/main.cf <<<
smtpd_recipient_restrictions = rbl:/etc/postfix/rbl_domain
>>> from /etc/postfix/rbl_domain <<<
relays.orbs.org		554 Service unavailable; [$client_address] blocked using relays.orbs.org. 
			${rbl_txt?$rbl_txt:See http://www.orbs.org/ for details.}
Example for rhsbl map usage:
>>> from /etc/postfix/main.cf <<< smtpd_recipient_restrictions = check_sender_access rhsbl:/etc/postfix/rhsbl_sender_domain, rhsbl:/etc/postfix/rhsbl_recip_domainor
>>> from /etc/postfix/main.cf <<< smtpd_recipient_restrictions = check_access \$sender rhsbl:/etc/postfix/rhsbl_sender_domain, check_access \$recipient rhsbl:/etc/postfix/rhsbl_recip_domain >>> from /etc/postfix/rbl_sender_domain <<< in.dnsbl.org 554 Service unavailable; $sender_domain blocked using in.dnsbl.org. >>> from /etc/postfix/rbl_recip_domain <<< in.dnsbl.org 554 Service unavailable; $recipient_domain blocked using in.dnsbl.org.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.
key             netblock in CIDR notation (or plain IP address as 
                individual node address)
value           anything which is valid on the RHS in mapfiles
                (OK|RELAY|REJECT|[45]xx text|list of UCE restrictions)
>>> from /etc/postfix/main.cf <<<
rbl_domain = relays.orbs.org   554 Service unavailable; 
	[\$client_address] blocked using relays.orbs.org. 
	\${rbl_txt?\$rbl_txt:See http://www.orbs.org/ for details.}
smtpd_recipient_restrictions = rbl:inline:rbl_domain
[Don't forget to escape macro expansions like in the example above.]
check_access parameter maptype:mapfilewhere the parameter may contain macro expressions from above.
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.
The example from our mail gateways:
>>> /etc/postfix/main.cf <<<
# The definition of the "atomic" restriction classes
smtpd_restriction_classes =
        class_ip,
        class_sender,
        class_dns,
        class_kfki,
        class_cern,
        class_rbl,
        class_dul,
        class_orbs,
        class_rss
class_ip = check_client_access hash:/etc/postfix/ip_address
# ...
class_rss = rbl:/etc/postfix/rbl_domain_rss
# We have per site default restriction, so the users must have
# an excplicite 'permit' at the end of their restrictions.
smtpd_recipient_restrictions =
        reject_non_fqdn_sender,
        reject_non_fqdn_recipient,
	permit_mynetworks,
	reject_unauth_destination,
	check_access \${sender}|\$recipient hash:/etc/postfix/user_exceptions,
        check_recipient_access hash:/etc/postfix/user_restrictions,
        class_ip, class_sender, class_dns, class_kfki, class_cern,
	class_rbl, class_dul, class_orbs, class_rss
>>> /etc/postfix/user_exceptions <<<
innocent1@bad.domain|one_user@our.domain		OK
innocent2@another.domain|second_user@our.domain		OK
>>> /etc/postfix/user_restrictions <<<
abuse@kfki.hu	permit
foo@kfki.hu	class_ip, class_sender, class_kfki, class_rbl, permit
bar@kfki.hu	class_ip, class_sender, class_kfki, permit
Enjoy it!
József Kadlecsik