]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.3-20050308
authorWietse Venema <wietse@porcupine.org>
Tue, 8 Mar 2005 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:30:50 +0000 (06:30 +0000)
37 files changed:
postfix/HISTORY
postfix/README_FILES/ADDRESS_REWRITING_README
postfix/README_FILES/LDAP_README
postfix/README_FILES/MYSQL_README
postfix/README_FILES/PGSQL_README
postfix/RELEASE_NOTES-2.2
postfix/auxiliary/postfinger/postfinger [deleted file]
postfix/conf/post-install
postfix/html/ADDRESS_REWRITING_README.html
postfix/html/LDAP_README.html
postfix/html/MYSQL_README.html
postfix/html/PGSQL_README.html
postfix/html/ldap_table.5.html
postfix/html/mysql_table.5.html
postfix/html/pgsql_table.5.html
postfix/html/postconf.5.html
postfix/man/man5/ldap_table.5
postfix/man/man5/mysql_table.5
postfix/man/man5/pgsql_table.5
postfix/man/man5/postconf.5
postfix/proto/ADDRESS_REWRITING_README.html
postfix/proto/LDAP_README.html
postfix/proto/MYSQL_README.html
postfix/proto/PGSQL_README.html
postfix/proto/ldap_table
postfix/proto/mysql_table
postfix/proto/pgsql_table
postfix/proto/postconf.proto
postfix/src/global/Makefile.in
postfix/src/global/db_common.c [new file with mode: 0644]
postfix/src/global/db_common.h [new file with mode: 0644]
postfix/src/global/dict_ldap.c
postfix/src/global/dict_mysql.c
postfix/src/global/dict_pgsql.c
postfix/src/global/mail_version.h
postfix/src/smtp/smtp_addr.c
postfix/src/smtpd/smtpd_check.c

index c84702b75e4ec339ebf8c11f274c63331e24e44a..72120d6b16a58965f663f708c7bbe5f4bceeabd4 100644 (file)
@@ -10341,6 +10341,13 @@ Apologies for any names omitted.
        Cleanup: documented the myorigin/mydomain address rewriting
        in canonical, generic and virtual alias maps.
 
+       Feature: updated LDAP and *SQL query interfaces using a
+       common infrastructure so that all have the same feature set
+       where possible. Victor Duchovni and many others. This code
+       was tested separately and was merged into the main stream
+       20050308. Files: global/db_common.[hc], global/dict_ldap.c,
+       global/dict_mysql.c, global/dict_pgsql.c, plus documentation.
+
 20050210
 
        Bugfix: spurious fallback_relay warnings after 20050202.
@@ -10445,6 +10452,11 @@ Apologies for any names omitted.
        webpage. As proof of authenticity the new PGP key is signed
        with Wietse's old PGP key.
 
+       Cleanup: check_mumble_{ns,mx}_access no longer attempt to
+       do MX or NS lookups for address literals. An address literal
+       is treated as its own MX host; there is no meaningful
+       equivalent for NS access control. File: smtpd/smtpd_check.c.
+
 Open problems:
 
        Med: disable header address rewriting after XCLIENT?
index 745e7dec44dcbe0cde44d7af73fdc4f00af49bb6..8d4936e919777b17181665c5b68cb3d5b6c17ec7 100644 (file)
@@ -129,58 +129,59 @@ this document for the first time, skip forward to "Address rewriting when mail
 is received". Once you've finished reading the remainder of this document, the
 table will help you to quickly find what you need.
 
-     _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b 
-    |A\bAd\bdd\bdr\bre\bes\bss\bs     |S\bSc\bco\bop\bpe\be   |D\bDa\bae\bem\bmo\bon\bn  |G\bGl\blo\bob\bba\bal\bl t\btu\bur\brn\bn-\b-o\bon\bn      |S\bSe\bel\ble\bec\bct\bti\biv\bve\be t\btu\bur\brn\bn-\b-o\bof\bff\bf      |
-    |m\bma\ban\bni\bip\bpu\bul\bla\bat\bti\bio\bon\bn|        |        |c\bco\bon\bnt\btr\bro\bol\bl             |c\bco\bon\bnt\btr\bro\bol\bl                 |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Rewrite     |        |trivial-|append_at_myorigin, |                        |
-    |addresses to|all mail|rewrite |append_dot_mydomain,|none                    |
-    |standard    |        |(8)     |swap_bangpath,      |                        |
-    |form        |        |        |allow_percent_hack  |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Canonical   |        |cleanup |                    |                        |
-    |address     |all mail|(8)     |canonical_maps      |receive_override_options|
-    |mapping     |        |        |                    |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Address     |all mail|cleanup |masquerade_domains  |receive_override_options|
-    |masquerading|        |(8)     |                    |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Automatic   |        |cleanup |always_bcc,         |                        |
-    |BCC         |new mail|(8)     |sender_bcc_maps,    |receive_override_options|
-    |recipients  |        |        |recipient_bcc_maps  |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Virtual     |all mail|cleanup |virtual_alias_maps  |receive_override_options|
-    |aliasing    |        |(8)     |                    |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Resolve     |        |trivial-|                    |                        |
-    |address to  |all mail|rewrite |none                |none                    |
-    |destination |        |(8)     |                    |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Mail        |        |trivial-|                    |                        |
-    |transport   |all mail|rewrite |transport_maps      |none                    |
-    |switch      |        |(8)     |                    |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Relocated   |        |trivial-|                    |                        |
-    |users table |all mail|rewrite |relocated_maps      |none                    |
-    |            |        |(8)     |                    |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Generic     |outgoing|        |                    |                        |
-    |mapping     |SMTP    |smtp(8) |smtp_generic_maps   |none                    |
-    |table       |mail    |        |                    |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Local alias |local   |        |                    |                        |
-    |database    |mail    |local(8)|alias_maps          |none                    |
-    |            |only    |        |                    |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Local per-  |local   |        |                    |                        |
-    |user        |mail    |local(8)|forward_path        |none                    |
-    |.forward    |only    |        |                    |                        |
-    |files       |        |        |                    |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
-    |Local catch-|local   |        |                    |                        |
-    |all address |mail    |local(8)|luser_relay         |none                    |
-    |            |only    |        |                    |                        |
-    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+     _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b 
+    |A\bAd\bdd\bdr\bre\bes\bss\bs     |S\bSc\bco\bop\bpe\be   |D\bDa\bae\bem\bmo\bon\bn  |G\bGl\blo\bob\bba\bal\bl t\btu\bur\brn\bn-\b-o\bon\bn      |S\bSe\bel\ble\bec\bct\bti\biv\bve\be t\btu\bur\brn\bn-\b-o\bof\bff\bf c\bco\bon\bnt\btr\bro\bol\bl   |
+    |m\bma\ban\bni\bip\bpu\bul\bla\bat\bti\bio\bon\bn|        |        |c\bco\bon\bnt\btr\bro\bol\bl             |                             |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Rewrite     |        |trivial-|append_at_myorigin, |                             |
+    |addresses to|all mail|rewrite |append_dot_mydomain,|local_header_rewrite_clients,|
+    |standard    |        |(8)     |swap_bangpath,      |remote_header_rewrite_domain |
+    |form        |        |        |allow_percent_hack  |                             |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Canonical   |        |cleanup |                    |receive_override_options,    |
+    |address     |all mail|(8)     |canonical_maps      |local_header_rewrite_clients,|
+    |mapping     |        |        |                    |remote_header_rewrite_domain |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Address     |        |cleanup |                    |receive_override_options,    |
+    |masquerading|all mail|(8)     |masquerade_domains  |local_header_rewrite_clients,|
+    |            |        |        |                    |remote_header_rewrite_domain |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Automatic   |        |cleanup |always_bcc,         |                             |
+    |BCC         |new mail|(8)     |sender_bcc_maps,    |receive_override_options     |
+    |recipients  |        |        |recipient_bcc_maps  |                             |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Virtual     |all mail|cleanup |virtual_alias_maps  |receive_override_options     |
+    |aliasing    |        |(8)     |                    |                             |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Resolve     |        |trivial-|                    |                             |
+    |address to  |all mail|rewrite |none                |none                         |
+    |destination |        |(8)     |                    |                             |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Mail        |        |trivial-|                    |                             |
+    |transport   |all mail|rewrite |transport_maps      |none                         |
+    |switch      |        |(8)     |                    |                             |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Relocated   |        |trivial-|                    |                             |
+    |users table |all mail|rewrite |relocated_maps      |none                         |
+    |            |        |(8)     |                    |                             |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Generic     |outgoing|        |                    |                             |
+    |mapping     |SMTP    |smtp(8) |smtp_generic_maps   |none                         |
+    |table       |mail    |        |                    |                             |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Local alias |local   |        |                    |                             |
+    |database    |mail    |local(8)|alias_maps          |none                         |
+    |            |only    |        |                    |                             |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Local per-  |local   |        |                    |                             |
+    |user        |mail    |local(8)|forward_path        |none                         |
+    |.forward    |only    |        |                    |                             |
+    |files       |        |        |                    |                             |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
+    |Local catch-|local   |        |                    |                             |
+    |all address |mail    |local(8)|luser_relay         |none                         |
+    |            |only    |        |                    |                             |
+    |_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b|_\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b _\b |
 
 A\bAd\bdd\bdr\bre\bes\bss\bs r\bre\bew\bwr\bri\bit\bti\bin\bng\bg w\bwh\bhe\ben\bn m\bma\bai\bil\bl i\bis\bs r\bre\bec\bce\bei\biv\bve\bed\bd
 
@@ -213,11 +214,11 @@ table, it first rewrites the address to the standard
 rewrite(8) daemon. The purpose of rewriting to standard form is to reduce the
 number of entries needed in lookup tables.
 
-Postfix versions 2.2 and later do not rewrite message headers from remote SMTP
-clients at all, unless a non-empty domain name is specified with the
-remote_header_rewrite_domain configuration parameter. The
-local_header_rewrite_clients parameter controls what SMTP clients Postfix
-considers local.
+NOTE: Postfix versions 2.2 and later rewrite message headers from remote SMTP
+clients only if the client matches the local_header_rewrite_clients parameter,
+or if the remote_header_rewrite_domain configuration parameter specifies a non-
+empty value. To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".
 
 The Postfix trivial-rewrite(8) daemon implements the following hard-coded
 address manipulations:
@@ -246,10 +247,13 @@ address manipulations:
         of Postfix components expect that all addresses have the form
         "user@domain".
 
-        Postfix versions 2.2 and later either do not rewrite message headers
-        from remote SMTP clients at all, or they append the domain name
-        specified with the remote_header_rewrite_domain configuration
-        parameter.
+        NOTE: Postfix versions 2.2 and later rewrite message headers from
+        remote SMTP clients only if the client matches the
+        local_header_rewrite_clients parameter; otherwise they append the
+        domain name specified with the remote_header_rewrite_domain
+        configuration parameter, if one is specified. To get the behavior
+        before Postfix 2.2, specify "local_header_rewrite_clients = static:
+        all".
 
         If your machine is not the main machine for $myorigin and you wish to
         have some users delivered locally without going via that main machine,
@@ -262,9 +266,13 @@ address manipulations:
         (default: yes). The purpose is to get consistent treatment of different
         forms of the same hostname.
 
-        Postfix versions 2.2 and later either do not rewrite message headers
-        from remote clients at all, or they append the domain name specified
-        with the remote_header_rewrite_domain configuration parameter.
+        NOTE: Postfix versions 2.2 and later rewrite message headers from
+        remote SMTP clients only if the client matches the
+        local_header_rewrite_clients parameter; otherwise they append the
+        domain name specified with the remote_header_rewrite_domain
+        configuration parameter, if one is specified. To get the behavior
+        before Postfix 2.2, specify "local_header_rewrite_clients = static:
+        all".
 
         Some will argue that rewriting "host" to "host.domain" is bad. That is
         why it can be turned off. Others like the convenience of having
@@ -281,11 +289,11 @@ message envelopes and in message headers. By default all header and envelope
 addresses are rewritten; this is controlled with the canonical_classes
 configuration parameter.
 
-Postfix versions 2.2 and later do not rewrite message headers from remote
-clients at all, unless a non-empty domain name is specified with the
-remote_header_rewrite_domain configuration parameter. The
-local_header_rewrite_clients parameter controls what SMTP clients Postfix
-considers local.
+NOTE: Postfix versions 2.2 and later rewrite message headers from remote SMTP
+clients only if the client matches the local_header_rewrite_clients parameter,
+or if the remote_header_rewrite_domain configuration parameter specifies a non-
+empty value. To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".
 
 Address rewriting is done for local and remote addresses. The mapping is useful
 to replace login names by "Firstname.Lastname" style addresses, or to clean up
@@ -345,11 +353,11 @@ Address masquerading is a method to hide hosts inside a domain behind their
 mail gateway, and to make it appear as if the mail comes from the gateway
 itself, instead of from individual machines.
 
-Postfix versions 2.2 and later do not rewrite message headers from remote SMTP
-clients at all, unless a non-empty domain name is specified with the
-remote_header_rewrite_domain configuration parameter. The
-local_header_rewrite_clients parameter controls what SMTP clients Postfix
-considers local.
+NOTE: Postfix versions 2.2 and later rewrite message headers from remote SMTP
+clients only if the client matches the local_header_rewrite_clients parameter,
+or if the remote_header_rewrite_domain configuration parameter specifies a non-
+empty value. To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".
 
 Address masquerading is disabled by default, and is implemented by the cleanup
 (8) server. To enable, edit the masquerade_domains parameter in the main.cf
index 39dc85c910e391d1f439cf558eb4a74516af7387..12f31d1105efd33be6cbded0ea670d27f69219e6 100644 (file)
@@ -180,7 +180,7 @@ N\bNo\bot\bte\bes\bs a\ban\bnd\bd t\bth\bhi\bin\bng\bgs\bs t\bto\bo t\bth\bhi\bin\bnk\bk a\bab\bbo\bou\bu
   * If you use an LDAP map for lookups other than aliases, you may have to make
     sure the lookup makes sense. In the case of virtual lookups, maildrops
     other than mail addresses are pretty useless, because Postfix can't know
-    how to set the ownership for program or file delivery. Your query_filter
+    how to set the ownership for program or file delivery. Your q\bqu\bue\ber\bry\by_\b_f\bfi\bil\blt\bte\ber\br
     should probably look something like this:
 
         query_filter = (&(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")
@@ -196,7 +196,7 @@ N\bNo\bot\bte\bes\bs a\ban\bnd\bd t\bth\bhi\bin\bng\bgs\bs t\bto\bo t\bth\bhi\bin\bnk\bk a\bab\bbo\bou\bu
     some thought on your part to implement safely, considering the
     ramifications of this type of delivery. You may decide it's not worth the
     bother to allow any of that nonsense in LDAP lookups, ban it in the
-    query_filter, and keep things like majordomo lists in local alias
+    q\bqu\bue\ber\bry\by_\b_f\bfi\bil\blt\bte\ber\br, and keep things like majordomo lists in local alias
     databases.
 
         query_filter = (&(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")
@@ -236,12 +236,17 @@ C\bCr\bre\bed\bdi\bit\bts\bs
     the work on RFC 2254 escaping in queries. Spotted a bug in binding.
   * Sami Haahtinen: Referral chasing and v3 support.
   * Victor Duchovni: ldap_bind() timeout. With fixes from LaMont Jones:
-    OpenLDAP cache deprecation. Limits on recursion, expansion and query
+    OpenLDAP cache deprecation. Limits on recursion, expansion and search
     results size. LDAP connection sharing for maps differing only in the query
     parameters.
   * Liviu Daia: Support for SSL/STARTTLS. Support for storing map definitions
     in external files (ldap:/path/ldap.cf) needed to securely store passwords
     for plain auth.
+  * Liviu Daia revised the configuration interface and added the main.cf
+    configuration feature.
+  * Liviu Daia with further refinements from Jose Luis Tallon and Victor
+    Duchovni developed the common query, result_format, domain and
+    expansion_limit interface for LDAP, MySQL and PosgreSQL.
 
 And of course Wietse.
 
index d5b7e27c574c435b6286292a170cbb2948bf192f..6ebbc234eb636680218c59365758695545839587 100644 (file)
@@ -62,22 +62,16 @@ password = some_password
 # The database name on the servers.
 dbname = customer_database
 
-# The table name.
-table = mxaliases
+# For Postfix 2.2 and later The SQL query template.
+# See mysql_table(5) for details.
+query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
 
-# Query components, see below.
+# For Postfix releases prior to 2.2. See mysql_table(5) for details.
 select_field = forw_addr
+table = mxaliases
 where_field = alias
-
-# You may specify additional_conditions or leave this empty.
-additional_conditions = and status = 'paid'
-
-# The above variables will result in a query of the form:
-#
-# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
-#
-# ($lookup is escaped so if it contains single quotes or other odd
-# characters, it will not cause trouble).
+# Don't forget the leading "AND"!
+additional_conditions = AND status = 'paid'
 
 A\bAd\bdd\bdi\bit\bti\bio\bon\bna\bal\bl n\bno\bot\bte\bes\bs
 
@@ -100,4 +94,7 @@ C\bCr\bre\bed\bdi\bit\bts\bs
     Group, Inc.
   * Liviu Daia revised the configuration interface and added the main.cf
     configuration feature.
+  * Liviu Daia with further refinements from Jose Luis Tallon and Victor
+    Duchovni developed the common query, result_format, domain and
+    expansion_limit interface for LDAP, MySQL and PosgreSQL.
 
index 439781022aaf26ddd957cc6cdcbbdd683089ee8b..c7d8c279fcd2c6977afcd387107ca6bdd7d1036d 100644 (file)
@@ -63,25 +63,15 @@ password = some_password
 # The database name on the servers.
 dbname = customer_database
 
-# The table name.
-table = mxaliases
+# Postfix 2.2 and later The SQL query template. See pgsql_table(5).
+query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
 
-# Query components, see below.
+# For Postfix releases prior to 2.2. See pgsql_table(5) for details.
 select_field = forw_addr
+table = mxaliases
 where_field = alias
-
-# You may specify additional_conditions or leave this empty.
-additional_conditions = and status = 'paid'
-
-# The above variables will result in a query of the form:
-#
-# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
-#
-# ($lookup is escaped so if it contains single quotes or other odd
-# characters, it will not cause problems).
-#
-# You may also override the built-in SELECT template. See pgsql_table(5)
-# for details.
+# Don't forget the leading "AND"!
+additional_conditions = AND status = 'paid'
 
 U\bUs\bsi\bin\bng\bg m\bmi\bir\brr\bro\bor\bre\bed\bd d\bda\bat\bta\bab\bba\bas\bse\bes\bs
 
@@ -107,4 +97,9 @@ C\bCr\bre\bed\bdi\bit\bts\bs
   * LaMont Jones was the initial Postfix pgsql maintainer.
   * Liviu Daia revised the configuration interface and added the main.cf
     configuration feature.
+  * Liviu Daia revised the configuration interface and added the main.cf
+    configuration feature.
+  * Liviu Daia with further refinements from Jose Luis Tallon and Victor
+    Duchovni developed the common query, result_format, domain and
+    expansion_limit interface for LDAP, MySQL and PosgreSQL.
 
index a973f43e098ac24134b47f81a4542705aa9af748..45ac18ed548783d2dadb095bd17575698d0be6af 100644 (file)
@@ -20,13 +20,17 @@ the following sections of this document.
 - TLS and IPv6 support are now built into Postfix, based on code
 from third-party patches.
 
+- Extended query interface for LDAP, MySQL and PostgreSQL with free
+form SQL queries, and domain filters to reduce unnecessary lookups.
+
 - SMTP client-side connection reuse. This can dramatically speed
 up deliveries to high-volume destinations that have some servers
 that respond, and some non-responding mail servers.
 
 - By default, message header address rewriting is now disabled for
-SMTP mail from other systems.  Thus, spam from poorly written
-software no longer looks like it came from a local user.
+SMTP mail from other systems (including masquerading and canonical
+mapping).  Thus, spam from poorly written software no longer looks
+like it came from a local user.
 
 - When your machine does not have its own domain name, Postfix can
 now replace your "home network" email address by your ISP account
@@ -273,6 +277,12 @@ it does not turn off the actual features in the SMTP server.
 Major changes - database support
 --------------------------------
 
+[Feature 20050209] Extended LDAP, MySQL and PgSQL query interface
+with free form SQL queries, the domain filter optimization that was
+already available with LDAP and more. This code was worked on by
+many people but Victor Duchovni took the lead. See the respective
+{LDAP,MYSQL,PGSQL}_README and {ldap,mysql,pgsql}_table documents.
+
 [Feature 20041210] You can now dump an entire database with the new
 postmap/postalias "-s" option. This works only for database types
 with Postfix sequence operator support: hash, btree, dbm, and sdbm.
diff --git a/postfix/auxiliary/postfinger/postfinger b/postfix/auxiliary/postfinger/postfinger
deleted file mode 100755 (executable)
index ec43099..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-#!/bin/sh
-# postfinger - captures Postfix configuration for reporting errors
-#
-#    Inspired by comments on the postfix-users mailing list.
-#    Copyright (C) 2003 Simon J. Mudd (sjmudd@pobox.com)
-#    With help from:
-#      Matthias Andree <ma@dt.e-technik.uni-dortmund.de>
-#      Victor Duchovni <Victor.Duchovni@morganstanley.com>
-#      Sasa Babic <sasab@hygia.pharmacy.bg.ac.yu>
-#      Iñaki Arenaza <iarenaza@escomposlinux.org>
-#      Jorge Gordoy <gordoy@g2ctech.com>
-#    $Revision: 1.29 $
-#
-# License:
-#    This program is free software; you can redistribute it and/or
-#    modify it under the terms of the GNU General Public License
-#    as published by the Free Software Foundation; either version 2
-#    of the License, or (at your option) any later version.
-#    
-#    This program is distributed in the hope that it will be useful,
-#    but WITHOUT ANY WARRANTY; without even the implied warranty of
-#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-#    GNU General Public License for more details.
-#    
-#    You may have received a copy of the GNU General Public License
-#    along with this program; if not, write to the Free Software
-#    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
-#    USA.
-#    
-#    An on-line copy of the GNU General Public License can be found
-#    http://www.fsf.org/copyleft/gpl.html.
-
-version_number=1.29    # don't use rcs version here
-version="version: ${version_number}"
-BACKUP_IFS=$IFS
-usage="postfinger ${version}: a Postfix configuration extraction utility
-Usage: postfinger [options]
-
-Options can be any of:
-    --all              Show all configuration information
-    --system           Show basic system environment (os/kernel/...) [default]
-    --package          Show packaging information [default]
-    --locking          Show mailbox locking methods
-    --tables           Show supported lookup tables
-    --main             Show main.cf non-default configuration values [default]
-    --defaultsinmain   Show main.cf defined values which are identical to defaults
-    --master           Show master.cf configuration [default]
-    --permissions      Show some of the spool_directory permissions
-    --libraries                Show the Postfix libraries dependencies
-
-    --nosystem         Do not show basic system environment (os/kernel/...)
-    --nomain           Do not show main.cf non-default configuration values
-    --nomaster         Do not show master.cf configuration
-    --nowarn           Do not warn about private information being leaked to
-                       outsiders
-    --version          print the version of postfinger being used and exit
-
-Mail bug reports and suggestions to <postfinger@WL0.org>".
-
-system=1; package=1; locking=; tables=; main=1; master=1; permissions=; libraries=;warn=1;defaultsinmain=
-
-for arg
-do
-    case $arg in
-      --version) echo "postfinger ${version}"; exit 0;;
-      --all) system=1; package=1; locking=1; tables=1; main=1; master=1; permissions=1; libraries=1; warn=1;;
-      --system) system=1;;
-      --package) package=1;;
-      --locking) locking=1;;
-      --tables) tables=1;;
-      --main) main=1;;
-      --defaultsinmain) defaultsinmain=1;;
-      --master) master=1;;
-      --permissions) permissions=1;;
-      --libraries) libraries=1;;
-      --nosystem) system=;;
-      --nomain) main=;;
-      --nomaster) master=;;
-      --nowarn) warn=;;
-      --help) echo "${usage}"; exit 0;;
-           *) echo "Error: ${usage}" 1>&2; exit 1;;
-    esac
-    shift
-done
-
-echo "postfinger - postfix configuration on `LANG=C date`"
-echo ${version}
-echo ''
-
-[ "${warn}" = 1 ] && {
-cat <<END
-Warning: postfinger output may show private configuration information,
-such as ip addresses and/or domain names which you do not want to show
-to the public.  If this is the case it is your responsibility to modify
-the output to hide this private information.  [Remove this warning with
-the --nowarn option.]
-
-END
-}
-
-# Look for postconf, using environment variable if given
-[ -n "${POSTCONF}" ] && [ ! -x "${POSTCONF}" ] && POSTCONF=
-[ -z "${POSTCONF}" ] && [ -x /usr/sbin/postconf ] && POSTCONF=/usr/sbin/postconf
-[ -z "${POSTCONF}" ] && [ -x /usr/local/sbin/postconf ] && POSTCONF=/usr/local/sbin/postconf
-[ -z "${POSTCONF}" ] && {
-    echo "$0: can not find postconf"
-    echo "set POSTCONF to postconf's location and try again"
-    exit 1
-}
-
-# Look for smtpd, using environment variable if given
-[ -z "${SMTPD}" ] && {
-    SMTPD=`${POSTCONF} -h daemon_directory`/smtpd
-    [ -d "${SMTPD}" -o ! -x "${SMTPD}" ] && SMTPD=
-}
-[ -z "${SMTPD}" ] && {
-    echo "$0: can not find smtpd"
-    echo "set SMTPD to smtpd's location and try again"
-    exit 1
-}
-
-[ "${system}" = 1 ] && {
-    echo '--System Parameters--'
-    ${POSTCONF} -d mail_version
-    echo "hostname = `hostname`"
-    echo "uname = `uname -a`"
-    echo ""
-}
-
-# check for different packaging systems and try to identify if this postfix
-# (smtpd) comes from a package.
-# I would appreciate help in adapting this part to include other packaging
-# systems.
-[ "${package}" = 1 ] && {
-    echo "--Packaging information--"
-
-    DPKG=
-    [ -x /usr/bin/dpkg ] && DPKG=/usr/bin/dpkg
-    [ -z "${DPKG}" ] && [ -x /usr/local/bin/dpkg ] && DPKG=/usr/local/bin/dpkg
-    [ -n "${DPKG}" ] && {
-        ${DPKG} -S ${SMTPD} >/dev/null 2>/dev/null && {
-           package=`${DPKG} -S ${SMTPD} | awk  -F: '{print $1}' | head -n 1`
-            package_ver=`COLUMNS=132 ${DPKG} -l ${package} | grep ii | grep -v "documentation" | awk '{print $3}'`
-            echo "looks like this postfix comes from deb package: ${package}-${package_ver}"
-        }
-    }
-
-    RPM=
-    [ -x /bin/rpm ] && RPM=/bin/rpm
-    [ -z "${RPM}" ] && [ -x /usr/local/bin/rpm ] && RPM=/usr/local/bin/rpm
-    [ -n "${RPM}" ] && {
-        ${RPM} -qf ${SMTPD} >/dev/null 2>/dev/null && \
-            echo "looks like this postfix comes from RPM package: `${RPM} -qf ${SMTPD}`"
-    }
-
-    BSDPKG=
-    [ -x /usr/sbin/pkg_info ] && BSDPKG=/usr/sbin/pkg_info
-    [ -n "${BSDPKG}" ] && {
-       ${BSDPKG} -q -W ${SMTPD} >/dev/null 2>/dev/null && \
-           echo "looks like this postfix comes from BSD package: `${BSDPKG} -q -W ${SMTPD}`"
-    }
-
-    echo ""
-}
-
-IFS="
-"
-[ "${locking}" = 1 ] && {
-    echo "--Mailbox locking methods--"
-    locking_methods=`${POSTCONF} -l`
-    echo $locking_methods
-    echo ""
-}
-
-[ "${tables}" = 1 ] && {
-    echo "--Supported Lookup tables--"
-    lookup_tables=`${POSTCONF} -m`
-    echo $lookup_tables
-    echo ""
-}
-
-[ "${main}" = 1 -o "${defaultsinmain}" = 1 ] && {
-    if [ "x`find . -prune  \( -perm 020 -o -perm 002 \) -print`" != "x" ]
-    then
-       echo 2>&2 "Do not run this in a public- or group-writable directory"
-       exit 1
-    fi
-
-    rm -f postfinger.$$.d postfinger.$$.n
-    ${POSTCONF} -d | tr -s [:blank:] | sort > postfinger.$$.d
-    ${POSTCONF} -n | tr -s [:blank:] | sort > postfinger.$$.n
-
-    [ "$main" = 1 ] && {
-        echo "--main.cf non-default parameters--"
-        comm -13 postfinger.$$.d postfinger.$$.n
-        echo ""
-    }
-
-    [ "${defaultsinmain}" = 1 ] && {
-        echo "--main.cf parameters defined as per defaults--"
-        comm -12 postfinger.$$.d postfinger.$$.n
-        echo ""
-    }
-
-    rm -f postfinger.$$.d postfinger.$$.n
-}
-
-[ "${master}" = 1 ] && {
-    echo "--master.cf--"
-    # Remove blank and commented lines to reduce the output
-    # Note: the second grep contains a space followed by a tab character
-    cat `${POSTCONF} -h config_directory`/master.cf | \
-       grep -v '^#' | \
-       grep -v '^[     ]*$'
-    echo ""
-}
-
-[ "${permissions}" = 1 ] && {
-    echo "--Specific file and directory permissions--"
-    ls -ld `${POSTCONF} -h queue_directory`/maildrop
-    ls -ld `${POSTCONF} -h queue_directory`/public
-    ls -l  `${POSTCONF} -h queue_directory`/public 2>/dev/null || {
-        echo 'WARNING: No access to $queue_directory/public'
-        echo '  Try running postfinger as user root or postfix'
-    }
-    ls -ld `${POSTCONF} -h queue_directory`/private
-    ls -l  `${POSTCONF} -h queue_directory`/private 2>/dev/null || {
-        echo 'WARNING: No access to $queue_directory/private'
-        echo '  Try running postfinger as user root or postfix'
-    }
-    ls -l `${POSTCONF} -h command_directory`/postdrop
-    ls -l `${POSTCONF} -h command_directory`/postqueue
-    echo ""
-}
-
-[ "${libraries}" = 1 ] && {
-    echo "--Library dependencies--"
-    echo "${SMTPD}:"
-    ldd ${SMTPD}  || echo "WARNING: Can not find ldd.  Check you have it installed and in your path"
-}
-
-echo "-- end of postfinger output --"
index 95775d0646a3148bd1d80316f3c04cac33e9ee58..1119bcec646f715a0a40dc3b4fdf8ffb69516022 100644 (file)
@@ -594,7 +594,7 @@ EOF
     if [ -z "$has_lrm" -a -z "$has_lrjc" ]
     then
        echo SAFETY: editing main.cf, setting $unknown_local=450.
-       echo See the RELEASE_NOTES and LOCAL_RECIPIENT_README files for details.
+       echo See the LOCAL_RECIPIENT_README file for details.
        $POSTCONF -e "$unknown_local = 450" || exit 1
     fi
 
index 8079a7357535096c2aafd319a203f75c14bd6e84..9a7939aa959b0ef0c354d51037a6b1ba5bda5f67 100644 (file)
@@ -317,15 +317,18 @@ turn-off control </th> </tr>
 <tr> <td> <a href="#standard"> Rewrite addresses to standard form</a>
 </td> <td nowrap> all mail </td> <td> <a href="trivial-rewrite.8.html">trivial-<br>rewrite(8)</a> </td>
 <td> <a href="postconf.5.html#append_at_myorigin">append_at_myorigin</a>, <a href="postconf.5.html#append_dot_mydomain">append_dot_mydomain</a>, <a href="postconf.5.html#swap_bangpath">swap_bangpath</a>,
-<a href="postconf.5.html#allow_percent_hack">allow_percent_hack</a> </td> <td> none </td> </tr>
+<a href="postconf.5.html#allow_percent_hack">allow_percent_hack</a> </td> <td> <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> </td> </tr>
 
-<tr> <td> <a href="#canonical"> Canonical address mapping </a>
-</td> <td nowrap> all mail </td> <td> <a href="cleanup.8.html">cleanup(8)</a> </td> <td>
-<a href="postconf.5.html#canonical_maps">canonical_maps</a> </td> <td> <a href="postconf.5.html#receive_override_options">receive_override_options</a> </td> </tr>
+<tr> <td> <a href="#canonical"> Canonical address mapping </a> </td>
+<td nowrap> all mail </td> <td> <a href="cleanup.8.html">cleanup(8)</a> </td> <td> <a href="postconf.5.html#canonical_maps">canonical_maps</a>
+</td> <td> <a href="postconf.5.html#receive_override_options">receive_override_options</a>, <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> </td> </tr>
 
-<tr> <td> <a href="#canonical"> Address masquerading </a> </td>
-<td nowrap> all mail </td> <td> <a href="cleanup.8.html">cleanup(8)</a> </td> <td> <a href="postconf.5.html#masquerade_domains">masquerade_domains</a>
-</td> <td> <a href="postconf.5.html#receive_override_options">receive_override_options</a> </td> </tr>
+<tr> <td> <a href="#masquerade"> Address masquerading </a> </td> <td
+nowrap> all mail </td> <td> <a href="cleanup.8.html">cleanup(8)</a> </td> <td> <a href="postconf.5.html#masquerade_domains">masquerade_domains</a>
+</td> <td> <a href="postconf.5.html#receive_override_options">receive_override_options</a>, <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> </td> </tr>
 
 <tr> <td> <a href="#auto_bcc"> Automatic BCC recipients </a> </td>
 <td nowrap> new mail </td> <td> <a href="cleanup.8.html">cleanup(8)</a> </td> <td> <a href="postconf.5.html#always_bcc">always_bcc</a>,
@@ -411,11 +414,12 @@ mapping lookup table, it first rewrites the address to the standard
 form is to reduce the number of entries needed in lookup tables.
 </p>
 
-<p> Postfix versions 2.2 and later do not rewrite message headers
-from remote SMTP clients at all, unless a non-empty domain name is
-specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration
-parameter.  The <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter controls
-what SMTP clients Postfix considers local. </p>
+<p> NOTE: Postfix versions 2.2 and later rewrite message headers
+from remote SMTP clients only if the client matches the
+<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter, or if the
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration parameter specifies a
+non-empty value. To get the behavior before Postfix 2.2, specify
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all".  </p>
 
 <p> The Postfix <a href="trivial-rewrite.8.html">trivial-rewrite(8)</a> daemon implements the following
 hard-coded address manipulations: </p>
@@ -455,10 +459,13 @@ parameter (default: yes).  You should never turn off this feature,
 because a lot of Postfix components expect that all addresses have
 the form "user@domain".  </p>
 
-<p> Postfix versions 2.2 and later either do not rewrite message
-headers from remote SMTP clients at all, or they append the domain
-name specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration
-parameter.  </p>
+<p> NOTE: Postfix versions 2.2 and later rewrite message headers
+from remote SMTP clients only if the client matches the
+<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter; otherwise they append the
+domain name specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a>
+configuration parameter, if one is specified. To get the behavior
+before Postfix 2.2, specify "<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> =
+static:all". </p>
 
 <p> If your machine is not the main machine for $<a href="postconf.5.html#myorigin">myorigin</a> and you
 wish to have some users delivered locally without going via that
@@ -476,10 +483,13 @@ Rewrite "user@host" to "user@host.$<a href="postconf.5.html#mydomain">mydomain</
 parameter (default: yes).  The purpose is to get consistent treatment
 of different forms of the same hostname. </p>
 
-<p> Postfix versions 2.2 and later either do not rewrite message
-headers from remote clients at all, or they append the domain name
-specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration
-parameter. </p>
+<p> NOTE: Postfix versions 2.2 and later rewrite message headers
+from remote SMTP clients only if the client matches the
+<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter; otherwise they append the
+domain name specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a>
+configuration parameter, if one is specified. To get the behavior
+before Postfix 2.2, specify "<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> =
+static:all". </p>
 
 <p> Some will argue that rewriting "host" to "host.domain"
 is bad. That is why it can be turned off. Others like the convenience
@@ -502,11 +512,12 @@ addresses in message envelopes and in message headers.  By default
 all header and envelope addresses are rewritten; this is controlled
 with the <a href="postconf.5.html#canonical_classes">canonical_classes</a> configuration parameter.  </p>
 
-<p> Postfix versions 2.2 and later do not rewrite message headers
-from remote clients at all, unless a non-empty domain name is
-specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration
-parameter.  The <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter controls
-what SMTP clients Postfix considers local. </p>
+<p> NOTE: Postfix versions 2.2 and later rewrite message headers
+from remote SMTP clients only if the client matches the
+<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter, or if the
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration parameter specifies a
+non-empty value. To get the behavior before Postfix 2.2, specify
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all".  </p>
 
 <p> Address rewriting is 
 done for local and remote addresses.  The mapping is useful to
@@ -585,11 +596,12 @@ behind their mail gateway, and to make it appear as if the mail
 comes from the gateway itself, instead of from individual machines.
 </p>
 
-<p> Postfix versions 2.2 and later do not rewrite message headers
-from remote SMTP clients at all, unless a non-empty domain name is
-specified with the <a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration
-parameter.  The <a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter controls
-what SMTP clients Postfix considers local. </p>
+<p> NOTE: Postfix versions 2.2 and later rewrite message headers
+from remote SMTP clients only if the client matches the
+<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> parameter, or if the
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> configuration parameter specifies a
+non-empty value. To get the behavior before Postfix 2.2, specify
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all".  </p>
 
 <p> Address masquerading is disabled by default, and is implemented
 by the <a href="cleanup.8.html">cleanup(8)</a> server. To enable, edit the <a href="postconf.5.html#masquerade_domains">masquerade_domains</a>
index 4e7e5184c177b6422fc97ebee36b063fae4551b3..e828b284814005958c28f9491372b53fa5b35625 100644 (file)
@@ -257,7 +257,7 @@ maildrop: this, that, theother
   make sure the lookup makes sense. In the case of virtual lookups,
   maildrops other than mail addresses are pretty useless, because
   Postfix can't know how to set the ownership for program or file
-  delivery. Your query_filter should probably look something like this: </p>
+  delivery. Your <b>query_filter</b> should probably look something like this: </p>
 
 <blockquote>
 <pre>
@@ -276,7 +276,7 @@ query_filter = (&amp;(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")(maildrop="*
   require some thought on your part to implement safely, considering the
   ramifications of this type of delivery. You may decide it's not worth
   the bother to allow any of that nonsense in LDAP lookups, ban it in
-  the query_filter, and keep things like majordomo lists in local alias
+  the <b>query_filter</b>, and keep things like majordomo lists in local alias
   databases. </p>
 
 <blockquote>
@@ -334,13 +334,20 @@ contents, please include the applicable bits of some directory entries. </p>
 
 <li>Victor Duchovni: ldap_bind() timeout. With fixes from LaMont Jones:
                  OpenLDAP cache deprecation. Limits on recursion, expansion
-                 and query results size. LDAP connection sharing for maps
+                 and search results size. LDAP connection sharing for maps
                  differing only in the query parameters.
 
 <li>Liviu Daia: Support for SSL/STARTTLS. Support for storing map definitions in
             external files (<a href="ldap_table.5.html">ldap</a>:/path/ldap.cf) needed to securely store
             passwords for plain auth.
 
+<li>Liviu Daia revised the configuration interface and added the main.cf
+    configuration feature.</li>
+    
+<li>Liviu Daia with further refinements from Jose Luis Tallon and
+Victor Duchovni developed the common query, result_format, domain and
+expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
+
 </ul>
 
 And of course Wietse.
index e014783df5ddbc1b32c6d1013fc216456658e6a6..f243075c20a68171f0da4c12a88e7e62b6797302 100644 (file)
@@ -89,22 +89,16 @@ password = some_password
 # The database name on the servers.
 dbname = customer_database
 
-# The table name.
-table = mxaliases
+# For Postfix 2.2 and later The SQL query template.
+# See <a href="mysql_table.5.html">mysql_table(5)</a> for details.
+query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
 
-# Query components, see below.
+# For Postfix releases prior to 2.2. See <a href="mysql_table.5.html">mysql_table(5)</a> for details.
 select_field = forw_addr
+table = mxaliases
 where_field = alias
-
-# You may specify additional_conditions or leave this empty.
-additional_conditions = and status = 'paid'
-
-# The above variables will result in a query of the form:
-#
-# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
-#
-# ($lookup is escaped so if it contains single quotes or other odd
-# characters, it will not cause trouble).
+# Don't forget the leading "AND"!
+additional_conditions = AND status = 'paid'
 </pre>
 
 <h2>Additional notes</h2>
@@ -129,10 +123,14 @@ will be deferred until at least one of those hosts is reachable.
 <ul>
 
 <li> The initial version was contributed by Scott Cotton and Joshua
-Marcus, IC Group, Inc.
+Marcus, IC Group, Inc.</li>
+
+<li> Liviu Daia revised the configuration interface and added the
+main.cf configuration feature.</li>
 
-<li>Liviu Daia revised the configuration interface and added the
-main.cf configuration feature.
+<li> Liviu Daia with further refinements from Jose Luis Tallon and
+Victor Duchovni developed the common query, result_format, domain and
+expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
 
 </ul>
 
index 35bb67491c21729ee26cca11e643d3a5ac36c196..254f580dd3291e55bf3d9ceca2921b0272c053e5 100644 (file)
@@ -88,25 +88,15 @@ password = some_password
 # The database name on the servers.
 dbname = customer_database
 
-# The table name.
-table = mxaliases
+# Postfix 2.2 and later The SQL query template. See <a href="pgsql_table.5.html">pgsql_table(5)</a>.
+query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
 
-# Query components, see below.
+# For Postfix releases prior to 2.2. See <a href="pgsql_table.5.html">pgsql_table(5)</a> for details.
 select_field = forw_addr
+table = mxaliases
 where_field = alias
-
-# You may specify additional_conditions or leave this empty.
-additional_conditions = and status = 'paid'
-
-# The above variables will result in a query of the form:
-#
-# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
-#
-# ($lookup is escaped so if it contains single quotes or other odd
-# characters, it will not cause problems).
-#
-# You may also override the built-in SELECT template. See <a href="pgsql_table.5.html">pgsql_table(5)</a>
-# for details.
+# Don't forget the leading "AND"!
+additional_conditions = AND status = 'paid'
 </pre>
 
 <h2>Using mirrored databases</h2>
@@ -130,17 +120,24 @@ those hosts is reachable. </p>
 <ul>
 
 <li> This code is based upon the Postfix mysql map by Scott Cotton
-and Joshua Marcus, IC Group, Inc.
+and Joshua Marcus, IC Group, Inc.</li>
 
-<li> The PostgreSQL changes were done by Aaron Sethman.
+<li> The PostgreSQL changes were done by Aaron Sethman.</li>
 
 <li> Updates for Postfix 1.1.x and PostgreSQL 7.1+ and support for
-calling stored procedures were added by Philip Warner.
+calling stored procedures were added by Philip Warner.</li>
 
-<li> LaMont Jones was the initial Postfix pgsql maintainer.
+<li> LaMont Jones was the initial Postfix pgsql maintainer.</li>
 
 <li> Liviu Daia revised the configuration interface and added the
-main.cf configuration feature.
+main.cf configuration feature.</li>
+
+<li> Liviu Daia revised the configuration interface and added the main.cf
+configuration feature.</li>
+
+<li> Liviu Daia with further refinements from Jose Luis Tallon and
+Victor Duchovni developed the common query, result_format, domain and
+expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
 
 </ul>
 
index 7c1c51e843339ade1c5f98df5ad4f80001aaa2c4..c989d3089719c97d04bfb1b083dc2befa7f8a68b 100644 (file)
@@ -54,6 +54,20 @@ LDAP_TABLE(5)                                                    LDAP_TABLE(5)
        Support  for this form will be removed in a future Postfix
        version.
 
+       Postfix 2.2 has enhanced query interfaces  for  MySQL  and
+       PostgreSQL,  these  now include features previously avail-
+       able only in the Postfix LDAP client. This work also  cre-
+       ated  an  opportunity  for improvements in the LDAP inter-
+       face. The primary compatibility issue is that  <b>result_fil-</b>
+       <b>ter</b> (a name that has caused some confusion as to its mean-
+       ing in the past) has been renamed to  <b>result_format</b>.   For
+       backwards  compatibility  with  the  pre  2.2 LDAP client,
+       <b>result_filter</b> can for now be used instead  of  <b>result_for-</b>
+       <b>mat</b>,  when  the latter parameter is not also set.  The new
+       name better reflects the function of the  parameter.  This
+       compatibility   interface  may  be  removed  in  a  future
+       release.
+
 <b>LIST MEMBERSHIP</b>
        When using  LDAP  to  store  lists  such  as  $<a href="postconf.5.html#mynetworks">mynetworks</a>,
        $<a href="postconf.5.html#mydestination">mydestination</a>,   $<a href="postconf.5.html#relay_domains">relay_domains</a>,   $<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a>,
@@ -114,15 +128,57 @@ LDAP_TABLE(5)                                                    LDAP_TABLE(5)
               The port the LDAP server listens on, e.g.
                   server_port = 778
 
+       <b>timeout (default: 10 seconds)</b>
+              The number of seconds a search can take before tim-
+              ing out, e.g.
+                  timeout = 5
+
        <b>search_base (No default; you must configure this)</b>
               The <a href="http://www.faqs.org/rfcs/rfc2253.html">RFC2253</a> base DN at which to conduct the search,
               e.g.
                   search_base = dc=your, dc=com
 
-       <b>timeout (default: 10 seconds)</b>
-              The number of seconds a search can take before tim-
-              ing out, e.g.
-                  timeout = 5
+              With  Postfix 2.2 and later this parameter supports
+              the following '%' expansions:
+
+              <b>%%</b>     This is replaced by a literal '%' character.
+
+              <b>%s</b>     This is replaced by the input key.  <a href="http://www.faqs.org/rfcs/rfc2253.html">RFC 2253</a>
+                     quoting is used to make sure that the  input
+                     key  does not add unexpected metacharacters.
+
+              <b>%u</b>     When the input key is an address of the form
+                     user@domain,  <b>%u</b>  is  replaced  by  the (RFC
+                     2253) quoted  local  part  of  the  address.
+                     Otherwise,  <b>%u</b>  is  replaced  by  the entire
+                     search string.  If the localpart  is  empty,
+                     the  search  is  suppressed  and  returns no
+                     results.
+
+              <b>%d</b>     When the input key is an address of the form
+                     user@domain,  <b>%d</b>  is  replaced  by  the (RFC
+                     2253) quoted domain  part  of  the  address.
+                     Otherwise,  the  search  is  suppressed  and
+                     returns no results.
+
+              <b>%[SUD]</b> For the <b>search_base</b>  parameter,  the  upper-
+                     case  equivalents  of  the  above expansions
+                     behave  identically  to   their   lower-case
+                     counter-parts. With the <b>result_format</b> param-
+                     eter (previously  called  <b>result_filter</b>  see
+                     the  COMPATIBILITY  section and below), they
+                     expand to the  corresponding  components  of
+                     input key rather than the result value.
+
+              <b>%[1-9]</b> The  patterns %1, %2, ... %9 are replaced by
+                     the corresponding most significant component
+                     of  the input key's domain. If the input key
+                     is <i>user@mail.example.com</i>, then %1 is <b>com</b>, %2
+                     is  <b>example</b> and %3 is <b>mail</b>. If the input key
+                     is  unqualified  or  does  not  have  enough
+                     domain  components to satisfy all the speci-
+                     fied patterns, the search is suppressed  and
+                     returns no results.
 
        <b>query_filter (default: mailacceptinggeneralid=%s)</b>
               The  <a href="http://www.faqs.org/rfcs/rfc2254.html">RFC2254</a>  filter  used to search the directory,
@@ -133,21 +189,51 @@ LDAP_TABLE(5)                                                    LDAP_TABLE(5)
               This  parameter  supports  the following '%' expan-
               sions:
 
-              <b>%s</b>     This is replaced by the input key. <a href="http://www.faqs.org/rfcs/rfc2254.html">RFC  2254</a>
-                     quoting  is used to make sure that the input
-                     key does not add unexpected  metacharacters.
+              <b>%%</b>     This is replaced by a literal '%' character.
+                     (Postfix 2.2 and later).
+
+              <b>%s</b>     This is replaced by the input key.  <a href="http://www.faqs.org/rfcs/rfc2254.html">RFC 2254</a>
+                     quoting is used to make sure that the  input
+                     key  does not add unexpected metacharacters.
 
               <b>%u</b>     When the input key is an address of the form
-                     user@domain, <b>%u</b>  is  replaced  by  the  (RFC
-                     2254) quoted local part of the address. Oth-
-                     erwise, <b>%u</b> is replaced by the entire  search
-                     string.
+                     user@domain,  <b>%u</b>  is  replaced  by  the (RFC
+                     2254) quoted  local  part  of  the  address.
+                     Otherwise,  <b>%u</b>  is  replaced  by  the entire
+                     search string.  If the localpart  is  empty,
+                     the  search  is  suppressed  and  returns no
+                     results.
 
               <b>%d</b>     When the input key is an address of the form
-                     user@domain, <b>%d</b>  is  replaced  by  the  (RFC
-                     2254)  quoted  domain  part  of the address.
-                     Otherwise, <b>%d</b>  is  replaced  by  the  entire
-                     search string.
+                     user@domain,  <b>%d</b>  is  replaced  by  the (RFC
+                     2254) quoted domain  part  of  the  address.
+                     Otherwise,  the  search  is  suppressed  and
+                     returns no results.
+
+              <b>%[SUD]</b> The  upper-case  equivalents  of  the  above
+                     expansions behave in the <b>query_filter</b> param-
+                     eter   identically   to   their   lower-case
+                     counter-parts. With the <b>result_format</b> param-
+                     eter (previously  called  <b>result_filter</b>  see
+                     the  COMPATIBILITY  section and below), they
+                     expand to the  corresponding  components  of
+                     input key rather than the result value.
+
+                     The  above  %S,  %U  and  %D  expansions are
+                     available with Postfix 2.2 and later.
+
+              <b>%[1-9]</b> The patterns %1, %2, ... %9 are replaced  by
+                     the corresponding most significant component
+                     of the input key's domain. If the input  key
+                     is <i>user@mail.example.com</i>, then %1 is <b>com</b>, %2
+                     is <b>example</b> and %3 is <b>mail</b>. If the input  key
+                     is  unqualified  or  does  not  have  enough
+                     domain components to satisfy all the  speci-
+                     fied  patterns, the saerch is suppressed and
+                     returns no results.
+
+                     The above %1, ..., %9 expansions are  avail-
+                     able with Postfix 2.2 and later.
 
               The  "domain"  parameter described below limits the
               input keys to addresses in matching  domains.  When
@@ -156,34 +242,53 @@ LDAP_TABLE(5)                                                    LDAP_TABLE(5)
               matching  domains  are  suppressed  and  return  no
               results.
 
-              NOTE: DO NOT put quotes around the query filter.
+              NOTE: DO NOT put  quotes  around  the  <b>query_filter</b>
+              parameter.
 
-       <b>result_filter (default: %s</b>)
-              Format template applied to result attributes.  Sup-
-              ports  the same expansions as the query_filter, and
-              can be easily used to  append  (or  prepend)  text.
-              This  parameter  supports  the following '%' expan-
-              sions:
+       <b>result_format (default: %s</b>)
+              Called  <b>result_filter</b>  in Postfix releases prior to
+              2.2.  Format template applied to result attributes.
+              Most  commonly  used to append (or prepend) text to
+              the result. This parameter supports  the  following
+              '%' expansions:
+
+              <b>%%</b>     This is replaced by a literal '%' character.
+                     (Postfix 2.2 and later).
 
               <b>%s</b>     This is replaced by the value of the  result
-                     attribute.
+                     attribute.   When  result  is  empty  it  is
+                     skipped.
 
-              <b>%u</b>     When   the  result  attribute  value  is  an
+              <b>%u</b>     When  the  result  attribute  value  is   an
                      address  of  the  form  user@domain,  <b>%u</b>  is
-                     replaced  by  the local part of the address.
-                     Otherwise, <b>%u</b>  is  replaced  by  the  entire
-                     attribute value.
-
-              <b>%d</b>     When  a result attribute value is an address
-                     of the form user@domain, <b>%d</b> is  replaced  by
-                     the  domain  part  of  the  attribute value.
-                     Otherwise, <b>%d</b>  is  replaced  by  the  entire
-                     attribute value.
-
-              For  example,  using  "result_filter  =  <a href="smtp.8.html">smtp</a>:[%s]"
+                     replaced by the local part of  the  address.
+                     When the result has an empty localpart it is
+                     skipped.
+
+              <b>%d</b>     When a result attribute value is an  address
+                     of  the  form user@domain, <b>%d</b> is replaced by
+                     the domain part of the attribute value. When
+                     the result is unqualified it is skipped.
+
+              <b>%[SUD1-9]</b>
+                     The  upper-case and decimal digit expansions
+                     interpolate  the  parts  of  the  input  key
+                     rather  than  the result. Their behaviour is
+                     identical to that described with  <b>query_fil-</b>
+                     <b>ter</b>,  and  in  fact because the input key is
+                     known in advance, lookups whose key does not
+                     contain all the information specified in the
+                     result template are suppressed and return no
+                     results.
+
+                     The  above %S, %U, %D and %1, ..., %9 expan-
+                     sions are available  with  Postfix  2.2  and
+                     later.
+
+              For  example,  using  "result_format  =  <a href="smtp.8.html">smtp</a>:[%s]"
               allows one to use a mailHost attribute as the basis
               of  a <a href="transport.5.html">transport(5)</a> table. After applying the result
-              filter, multiple values are concatenated  as  comma
+              format, multiple values are concatenated  as  comma
               separated    strings.   The   expansion_limit   and
               size_limit parameters explained below allow one  to
               restrict  the number of values in the result, which
@@ -193,77 +298,84 @@ LDAP_TABLE(5)                                                    LDAP_TABLE(5)
               The  default value <b>%s</b> specifies that each attribute
               value should be used as is.
 
-              NOTE: DO NOT put quotes around the result filter!
+              This parameter was called <b>result_filter</b> in  Postfix
+              releases  prior  to  2.2.  If no "result_format" is
+              specified, the value  of  "result_filter"  will  be
+              used instead before resorting to the default value.
+              This provides compatibility with old  configuration
+              files.
+
+              NOTE: DO NOT put quotes around the result format!
 
        <b>domain (default: no domain list)</b>
-              This is a list of domain names, paths to files,  or
-              dictionaries.  When specified, only fully qualified
-              search keys with  a  *non-empty*  localpart  and  a
-              matching  domain  are  eligible  for lookup: 'user'
-              lookups, bare domain lookups and "@domain"  lookups
-              are  not  performed.  This can significantly reduce
+              This  is a list of domain names, paths to files, or
+              dictionaries. When specified, only fully  qualified
+              search  keys  with  a  *non-empty*  localpart and a
+              matching domain are  eligible  for  lookup:  'user'
+              lookups,  bare domain lookups and "@domain" lookups
+              are not performed. This  can  significantly  reduce
               the query load on the LDAP server.
                   domain = postfix.org, hash:/etc/postfix/search-
               domains
 
-              It  is  best  not  to use LDAP to store the domains
+              It is best not to use LDAP  to  store  the  domains
               eligible for LDAP lookups.
 
-              NOTE: DO NOT define  this  parameter  for  <a href="local.8.html">local(8)</a>
+              NOTE:  DO  NOT  define  this parameter for <a href="local.8.html">local(8)</a>
               aliases.
 
        <b>result_attribute (default: maildrop)</b>
-              The  attribute(s) Postfix will read from any direc-
+              The attribute(s) Postfix will read from any  direc-
               tory entries returned by the lookup, to be resolved
               to an email address.
-                  result_attribute = mailbox,maildrop
+                  result_attribute = mailbox, maildrop
 
        <b>special_result_attribute (No default)</b>
               The attribute(s) of directory entries that can con-
-              tain DNs or URLs. If found, a recursive  subsequent
+              tain  DNs or URLs. If found, a recursive subsequent
               search is done using their values.
                   special_result_attribute = member
 
-              DN  recursion  retrieves the same result_attributes
+              DN recursion retrieves the  same  result_attributes
               as the main query, including the special attributes
-              for  further  recursion.  URI  processing retrieves
-              only those attributes that are included in the  URI
-              definition     and    are    *also*    listed    in
-              "result_attribute". If the URI  lists  any  of  the
-              map's  special  result  attributes,  these are also
+              for further  recursion.  URI  processing  retrieves
+              only  those attributes that are included in the URI
+              definition    and    are    *also*    listed     in
+              "result_attribute".  If  the  URI  lists any of the
+              map's special result  attributes,  these  are  also
               retrieved and used recursively.
 
        <b>scope (default: sub)</b>
-              The LDAP search scope: <b>sub</b>, <b>base</b>,  or  <b>one</b>.   These
+              The  LDAP  search  scope: <b>sub</b>, <b>base</b>, or <b>one</b>.  These
               translate into LDAP_SCOPE_SUBTREE, LDAP_SCOPE_BASE,
               and LDAP_SCOPE_ONELEVEL.
 
        <b>bind (default: yes)</b>
-              Whether or not to bind to the  LDAP  server.  Newer
+              Whether  or  not  to bind to the LDAP server. Newer
               LDAP implementations don't require clients to bind,
               which saves time. Example:
                   bind = no
 
-              If you do need to bind, you might consider  config-
-              uring  Postfix to connect to the local machine on a
-              port that's an SSL tunnel to your LDAP  server.  If
-              your  LDAP server doesn't natively support SSL, put
+              If  you do need to bind, you might consider config-
+              uring Postfix to connect to the local machine on  a
+              port  that's  an SSL tunnel to your LDAP server. If
+              your LDAP server doesn't natively support SSL,  put
               a tunnel (wrapper, proxy, whatever you want to call
-              it)  on  that  system  too. This should prevent the
-              password from traversing the network in the  clear.
+              it) on that system too.  This  should  prevent  the
+              password  from traversing the network in the clear.
 
        <b>bind_dn (default: empty)</b>
-              If  you  do  have  to bind, do it with this distin-
+              If you do have to bind, do  it  with  this  distin-
               guished name. Example:
                   bind_dn = uid=postfix, dc=your, dc=com
 
        <b>bind_pw (default: empty)</b>
-              The password for the distinguished name  above.  If
+              The  password  for the distinguished name above. If
               you have to use this, you probably want to make the
               map configuration file readable only by the Postfix
-              user.  When using the obsolete <a href="ldap_table.5.html">ldap</a>:ldapsource syn-
+              user. When using the obsolete <a href="ldap_table.5.html">ldap</a>:ldapsource  syn-
               tax, with map parameters in main.cf, it is not pos-
-              sible  to securely store the bind password. This is
+              sible to securely store the bind password. This  is
               because main.cf needs to be world readable to allow
               local accounts to submit mail via the sendmail com-
               mand. Example:
@@ -274,43 +386,43 @@ LDAP_TABLE(5)                                                    LDAP_TABLE(5)
        <b>cache_expiry (IGNORED with a warning)</b>
 
        <b>cache_size (IGNORED with a warning)</b>
-              The above parameters are  NO  LONGER  SUPPORTED  by
+              The  above  parameters  are  NO LONGER SUPPORTED by
               Postfix.   Cache  support  has  been  dropped  from
               OpenLDAP as of release 2.1.13.
 
        <b>recursion_limit (default: 1000)</b>
-              A limit on the nesting depth of DN and URL  special
-              result  attribute  evaluation.  The limit must be a
+              A  limit on the nesting depth of DN and URL special
+              result attribute evaluation. The limit  must  be  a
               non-zero positive number.
 
        <b>expansion_limit (default: 0)</b>
-              A limit on the  total  number  of  result  elements
-              returned  (as  a  comma separated list) by a lookup
-              against the map.  A setting of  zero  disables  the
-              limit.  Lookups  fail with a temporary error if the
-              limit is exceeded.  Setting the limit to 1  ensures
+              A  limit  on  the  total  number of result elements
+              returned (as a comma separated list)  by  a  lookup
+              against  the  map.   A setting of zero disables the
+              limit. Lookups fail with a temporary error  if  the
+              limit  is exceeded.  Setting the limit to 1 ensures
               that lookups do not return multiple values.
 
        <b>size_limit (default: $expansion_limit)</b>
-              A  limit  on the number of LDAP entries returned by
-              any single LDAP query  performed  as  part  of  the
-              lookup.  A setting of 0 disables the limit.  Expan-
-              sion of DN and URL references involves nested  LDAP
-              queries,  each  of which is separately subjected to
+              A limit on the number of LDAP entries  returned  by
+              any  single  LDAP  search  performed as part of the
+              lookup. A setting of 0 disables the limit.   Expan-
+              sion  of DN and URL references involves nested LDAP
+              queries, each of which is separately  subjected  to
               this limit.
 
-              Note: even a single LDAP entry can generate  multi-
-              ple  lookup results, via multiple result attributes
-              and/or multi-valued result attributes.  This  limit
-              caps the per query resource utilization on the LDAP
-              server, not the final multiplicity  of  the  lookup
-              result.  It  is  analogous  to  the  "-z" option of
-              "ldapsearch".
+              Note:  even a single LDAP entry can generate multi-
+              ple lookup results, via multiple result  attributes
+              and/or  multi-valued  result attributes. This limit
+              caps the per search  resource  utilization  on  the
+              LDAP  server,  not  the  final  multiplicity of the
+              lookup result. It is analogous to the  "-z"  option
+              of "ldapsearch".
 
        <b>dereference (default: 0)</b>
-              When to dereference LDAP aliases. (Note  that  this
+              When  to  dereference LDAP aliases. (Note that this
               has nothing do with Postfix aliases.) The permitted
-              values are those legal  for  the  OpenLDAP/UM  LDAP
+              values  are  those  legal  for the OpenLDAP/UM LDAP
               implementations:
 
               0      never
@@ -322,99 +434,99 @@ LDAP_TABLE(5)                                                    LDAP_TABLE(5)
               3      always
 
               See ldap.h or the ldap_open(3) or ldapsearch(1) man
-              pages  for more information. And if you're using an
+              pages for more information. And if you're using  an
               LDAP package that has other possible values, please
-              bring   it   to   the  attention  of  the  postfix-
+              bring  it  to  the  attention   of   the   postfix-
               users@postfix.org mailing list.
 
        <b>chase_referrals (default: 0)</b>
-              Sets (or clears) LDAP_OPT_REFERRALS (requires  LDAP
+              Sets  (or clears) LDAP_OPT_REFERRALS (requires LDAP
               version 3 support).
 
        <b>version (default: 2)</b>
               Specifies the LDAP protocol version to use.
 
        <b>debuglevel (default: 0)</b>
-              What  level  to  set  for debugging in the OpenLDAP
+              What level to set for  debugging  in  the  OpenLDAP
               libraries.
 
 <b>LDAP SSL AND STARTTLS PARAMETERS</b>
-       If you're using the OpenLDAP libraries compiled  with  SSL
-       support,  Postfix  can connect to LDAP SSL servers and can
+       If  you're  using the OpenLDAP libraries compiled with SSL
+       support, Postfix can connect to LDAP SSL servers  and  can
        issue the STARTTLS command.
 
-       LDAP SSL service can be requested by using a LDAP SSL  URL
+       LDAP  SSL service can be requested by using a LDAP SSL URL
        in the server_host parameter:
            server_host = ldaps://ldap.example.com:636
 
        STARTTLS can be turned on with the start_tls parameter:
            start_tls = yes
 
-       Both  forms  require LDAP protocol version 3, which has to
+       Both forms require LDAP protocol version 3, which  has  to
        be set explicitly with:
            version = 3
 
        If any of the Postfix programs querying the map is config-
-       ured  in  master.cf  to run chrooted, all the certificates
+       ured in master.cf to run chrooted,  all  the  certificates
        and keys involved have to be copied to the chroot jail. Of
-       course,  the  private  keys should only be readable by the
+       course, the private keys should only be  readable  by  the
        user "postfix".
 
-       The following parameters are  relevant  to  LDAP  SSL  and
+       The  following  parameters  are  relevant  to LDAP SSL and
        STARTTLS:
 
        <b>start_tls (default: no)</b>
               Whether or not to issue STARTTLS upon connection to
-              the server.  Don't set this with LDAP SSL (the  SSL
+              the  server.  Don't set this with LDAP SSL (the SSL
               session is setup automatically when the TCP connec-
               tion is opened).
 
-       <b>tls_ca_cert_dir  (No   default;   set   either   this   or</b>
+       <b>tls_ca_cert_dir   (No   default;   set   either   this  or</b>
        <b>tls_ca_cert_file)</b>
               Directory  containing  X509  Certificate  Authority
-              certificates  in  PEM format which are to be recog-
-              nized by the client  in  SSL/TLS  connections.  The
-              files  each  contain one CA certificate.  The files
-              are looked up by the CA subject  name  hash  value,
-              which  must hence be available. If more than one CA
-              certificate with the same name  hash  value  exist,
-              the  extension  must be different (e.g. 9d66eef0.0,
-              9d66eef0.1 etc). The search  is  performed  in  the
-              ordering  of  the  extension  number, regardless of
+              certificates in PEM format which are to  be  recog-
+              nized  by  the  client  in SSL/TLS connections. The
+              files each contain one CA certificate.   The  files
+              are  looked  up  by the CA subject name hash value,
+              which must hence be available. If more than one  CA
+              certificate  with  the  same name hash value exist,
+              the extension must be different  (e.g.  9d66eef0.0,
+              9d66eef0.1  etc).  The  search  is performed in the
+              ordering of the  extension  number,  regardless  of
               other  properties  of  the  certificates.  Use  the
               c_rehash utility (from the OpenSSL distribution) to
               create the necessary links.
 
-       <b>tls_ca_cert_file  (No  default;   set   either   this   or</b>
+       <b>tls_ca_cert_file   (No   default;   set   either  this  or</b>
        <b>tls_ca_cert_dir)</b>
               File containing the X509 Certificate Authority cer-
-              tificates  in PEM format which are to be recognized
-              by the client in SSL/TLS connections. This  setting
+              tificates in PEM format which are to be  recognized
+              by  the client in SSL/TLS connections. This setting
               takes precedence over tls_ca_cert_dir.
 
        <b>tls_cert (No default; you must set this)</b>
-              File  containing  client's  X509  certificate to be
+              File containing client's  X509  certificate  to  be
               used by the client in SSL/ TLS connections.
 
        <b>tls_key (No default; you must set this)</b>
-              File containing the private  key  corresponding  to
+              File  containing  the  private key corresponding to
               the above tls_cert.
 
        <b>tls_require_cert (default: no)</b>
               Whether or not to request server's X509 certificate
-              and check its validity  when  establishing  SSL/TLS
+              and  check  its  validity when establishing SSL/TLS
               connections.
 
        <b>tls_random_file (No default)</b>
-              Path  of  a  file  to  obtain random bits from when
-              /dev/[u]random is not available, to be used by  the
+              Path of a file to  obtain  random  bits  from  when
+              /dev/[u]random  is not available, to be used by the
               client in SSL/TLS connections.
 
        <b>tls_cipher_suite (No default)</b>
               Cipher suite to use in SSL/TLS negotiations.
 
 <b>EXAMPLE</b>
-       Here's  a basic example for using LDAP to look up <a href="local.8.html">local(8)</a>
+       Here's a basic example for using LDAP to look up  <a href="local.8.html">local(8)</a>
        aliases.  Assume that in main.cf, you have:
            <a href="postconf.5.html#alias_maps">alias_maps</a> = hash:/etc/aliases,
                <a href="ldap_table.5.html">ldap</a>:/etc/postfix/ldap-aliases.cf
@@ -423,14 +535,14 @@ LDAP_TABLE(5)                                                    LDAP_TABLE(5)
            server_host = ldap.my.com
            search_base = dc=my, dc=com
 
-       Upon receiving mail for a local  address  "ldapuser"  that
-       isn't  found  in  the  /etc/aliases database, Postfix will
-       search  the  LDAP  server  listening  at   port   389   on
-       ldap.my.com.   It  will  bind  anonymously, search for any
-       directory entries whose  mailacceptinggeneralid  attribute
-       is  "ldapuser",  read  the  "maildrop" attributes of those
-       found, and build a list of their maildrops, which will  be
-       treated  as  <a href="http://www.faqs.org/rfcs/rfc822.html">RFC822</a> addresses to which the message will be
+       Upon  receiving  mail  for a local address "ldapuser" that
+       isn't found in the  /etc/aliases  database,  Postfix  will
+       search   the   LDAP   server  listening  at  port  389  on
+       ldap.my.com.  It will bind  anonymously,  search  for  any
+       directory  entries  whose mailacceptinggeneralid attribute
+       is "ldapuser", read the  "maildrop"  attributes  of  those
+       found,  and build a list of their maildrops, which will be
+       treated as <a href="http://www.faqs.org/rfcs/rfc822.html">RFC822</a> addresses to which the message  will  be
        delivered.
 
 <b>SEE ALSO</b>
@@ -444,13 +556,13 @@ LDAP_TABLE(5)                                                    LDAP_TABLE(5)
        <a href="LDAP_README.html">LDAP_README</a>, Postfix LDAP client guide
 
 <b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 <b>AUTHOR(S)</b>
-       Carsten Hoeger, Hery  Rakotoarisoa,  John  Hensley,  Keith
-       Stevenson,  LaMont Jones, Liviu Daia, Manuel Guesdon, Mike
-       Mattice, Prabhat K Singh, Sami Haahtinen, Samuel  Tardieu,
+       Carsten  Hoeger,  Hery  Rakotoarisoa,  John Hensley, Keith
+       Stevenson, LaMont Jones, Liviu Daia, Manuel Guesdon,  Mike
+       Mattice,  Prabhat K Singh, Sami Haahtinen, Samuel Tardieu,
        Victor Duchovni, and many others.
 
                                                                  LDAP_TABLE(5)
index 74c2088925d2bc9fc47e4a8b9b225585a8e47774..fc75c4d2cfc5c3e52314988fb31f2de6d44bb8d5 100644 (file)
@@ -28,7 +28,7 @@ MYSQL_TABLE(5)                                                  MYSQL_TABLE(5)
        as the Postfix main.cf file, and can specify  the  parame-
        ters described below.
 
-<b>ALTERNATIVE CONFIGURATION</b>
+<b>BACKWARDS COMPATIBILITY</b>
        For  compatibility with other Postfix lookup tables, MySQL
        parameters can also be defined in main.cf.  In order to do
        that,  specify  as  MySQL source a name that doesn't begin
@@ -44,6 +44,27 @@ MYSQL_TABLE(5)                                                  MYSQL_TABLE(5)
        Support  for this form will be removed in a future Postfix
        version.
 
+       Postfix 2.2 has enhanced query interfaces  for  MySQL  and
+       PostreSQL,  these  include  features  previously available
+       only in the Postfix LDAP client. In the new interface  the
+       SQL  query  is  specified  via  a  single  <b>query</b> parameter
+       (described in more detail  below).   When  the  new  <b>query</b>
+       parameter  is not specified in the map definition, Postfix
+       reverts to the old interface,  with  the  SQL  query  con-
+       structed  from  the  <b>select_field</b>,  <b>table</b>, <b>where_field</b> and
+       <b>additional_conditions</b> parameters.  The old interface  will
+       be  gradually  phased out. To migrate to the new interface
+       set:
+
+           <b>query</b> = SELECT [<i>select</i><b>_</b><i>field</i>]
+               FROM [<i>table</i>]
+               WHERE [<i>where</i><b>_</b><i>field</i>] = '%s'
+                   [<i>additional</i><b>_</b><i>conditions</i>]
+
+       Insert the value, not the name, of each legacy  parameter.
+       Note  that the <b>additional_conditions</b> parameter is optional
+       and if not empty, will always start with <b>AND</b>.
+
 <b>LIST MEMBERSHIP</b>
        When using SQL to store lists such as $<a href="postconf.5.html#mynetworks">mynetworks</a>, $<a href="postconf.5.html#mydestination">mydes</a>-
        <a href="postconf.5.html#mydestination">tination</a>,  $<a href="postconf.5.html#relay_domains">relay_domains</a>, $<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a>, etc., it
@@ -91,29 +112,189 @@ MYSQL_TABLE(5)                                                  MYSQL_TABLE(5)
        <b>dbname</b> The database name on the servers. Example:
                   dbname = customer_database
 
-       The  following  parameters  are  used  to fill in a SELECT
-       query template of the form:
-           select [<b>select_field</b>] from [<b>table</b>] where
-               [<b>where_field</b>] = '$lookup' [<b>additional_conditions</b>]
+       <b>query</b>  The SQL query template used to search the database,
+              where <b>%s</b> is a substitute for the address Postfix is
+              trying to resolve, e.g.
+                  query  =  SELECT replacement FROM aliases WHERE
+              mailbox = '%s'
+
+              This parameter supports the  following  '%'  expan-
+              sions:
+
+              <b>%%</b>     This is replaced by a literal '%' character.
+
+              <b>%s</b>     This is replaced  by  the  input  key.   SQL
+                     quoting  is used to make sure that the input
+                     key does not add unexpected  metacharacters.
+
+              <b>%u</b>     When the input key is an address of the form
+                     user@domain,  <b>%u</b>  is  replaced  by  the  SQL
+                     quoted  local  part  of the address.  Other-
+                     wise, <b>%u</b> is replaced by  the  entire  search
+                     string.   If  the  localpart  is  empty, the
+                     query is suppressed and returns no  results.
+
+              <b>%d</b>     When the input key is an address of the form
+                     user@domain,  <b>%d</b>  is  replaced  by  the  SQL
+                     quoted  domain  part of the address.  Other-
+                     wise, the query is suppressed and returns no
+                     results.
+
+              <b>%[SUD]</b> The  upper-case  equivalents  of  the  above
+                     expansions behave  in  the  <b>query</b>  parameter
+                     identically  to  their  lower-case  counter-
+                     parts.   With  the  <b>result_format</b>  parameter
+                     (see  below),  they  expand  the  input  key
+                     rather than the result value.
+
+              <b>%[1-9]</b> The patterns %1, %2, ... %9 are replaced  by
+                     the corresponding most significant component
+                     of the input key's domain. If the input  key
+                     is <i>user@mail.example.com</i>, then %1 is <b>com</b>, %2
+                     is <b>example</b> and %3 is <b>mail</b>. If the input  key
+                     is  unqualified  or  does  not  have  enough
+                     domain components to satisfy all the  speci-
+                     fied  patterns,  the query is suppressed and
+                     returns no results.
+
+              The <b>domain</b> parameter  described  below  limits  the
+              input  keys  to addresses in matching domains. When
+              the <b>domain</b> parameter is non-empty, SQL queries  for
+              unqualified  addresses or addresses in non-matching
+              domains are suppressed and return no results.
+
+              This parameter is available with  Postfix  2.2.  In
+              prior  releases  the  SQL  query was built from the
+              separate    parameters:    <b>select_field</b>,     <b>table</b>,
+              <b>where_field</b>  and <b>additional_conditions</b>. The mapping
+              from the old parameters to the equivalent query is:
+
+                  SELECT [<b>select_field</b>]
+                  FROM [<b>table</b>]
+                  WHERE [<b>where_field</b>] = '%s'
+                        [<b>additional_conditions</b>]
+
+              The '%s' in the <b>WHERE</b> clause expands to the escaped
+              search  string.   With  Postfix  2.2  these  legacy
+              parameters  are  used if the <b>query</b> parameter is not
+              specified.
+
+              NOTE: DO NOT put quotes around the query parameter.
+
+       <b>result_format (default: %s</b>)
+              Format  template applied to result attributes. Most
+              commonly used to append (or prepend)  text  to  the
+              result.  This  parameter supports the following '%'
+              expansions:
+
+              <b>%%</b>     This is replaced by a literal '%' character.
+
+              <b>%s</b>     This  is replaced by the value of the result
+                     attribute.  When  result  is  empty  it   is
+                     skipped.
+
+              <b>%u</b>     When   the  result  attribute  value  is  an
+                     address  of  the  form  user@domain,  <b>%u</b>  is
+                     replaced  by  the local part of the address.
+                     When the result has an empty localpart it is
+                     skipped.
+
+              <b>%d</b>     When  a result attribute value is an address
+                     of the form user@domain, <b>%d</b> is  replaced  by
+                     the domain part of the attribute value. When
+                     the result is unqualified it is skipped.
+
+              <b>%[SUD1-9]</b>
+                     The upper-case and decimal digit  expansions
+                     interpolate  the  parts  of  the  input  key
+                     rather than the result. Their  behaviour  is
+                     identical  to that described with <b>query</b>, and
+                     in fact because the input key  is  known  in
+                     advance,  queries whose key does not contain
+                     all the information specified in the  result
+                     template   are   suppressed  and  return  no
+                     results.
+
+              For  example,  using  "result_format  =  <a href="smtp.8.html">smtp</a>:[%s]"
+              allows one to use a mailHost attribute as the basis
+              of a <a href="transport.5.html">transport(5)</a> table. After applying the  result
+              format,  multiple  values are concatenated as comma
+              separated strings. The expansion_limit and  parame-
+              ter explained below allows one to restrict the num-
+              ber of values in the result,  which  is  especially
+              useful for maps that must return at most one value.
+
+              The default value <b>%s</b>  specifies  that  each  result
+              value should be used as is.
+
+              This  parameter  is  available with Postfix 2.2 and
+              later.
+
+              NOTE: DO NOT put quotes around the result format!
+
+       <b>domain (default: no domain list)</b>
+              This is a list of domain names, paths to files,  or
+              dictionaries.  When specified, only fully qualified
+              search keys with  a  *non-empty*  localpart  and  a
+              matching  domain  are  eligible  for lookup: 'user'
+              lookups, bare domain lookups and "@domain"  lookups
+              are  not  performed.  This can significantly reduce
+              the query load on the MySQL server.
+                  domain = postfix.org, hash:/etc/postfix/search-
+              domains
+
+              It is best not to use SQL to store the domains eli-
+              gible for SQL lookups.
+
+              This parameter is available with  Postfix  2.2  and
+              later.
+
+              NOTE:  DO  NOT  define  this parameter for <a href="local.8.html">local(8)</a>
+              aliases, because the input keys are always unquali-
+              fied.
+
+       <b>expansion_limit (default: 0)</b>
+              A  limit  on  the  total  number of result elements
+              returned (as a comma separated list)  by  a  lookup
+              against  the  map.   A setting of zero disables the
+              limit. Lookups fail with a temporary error  if  the
+              limit  is exceeded.  Setting the limit to 1 ensures
+              that lookups do not return multiple values.
+
+       The following parameters can be used to fill in  a  SELECT
+       template statement of the form:
+
+           SELECT [<b>select_field</b>]
+           FROM [<b>table</b>]
+           WHERE [<b>where_field</b>] = '%s'
+                 [<b>additional_conditions</b>]
+
+       The  specifier %s is replaced by the search string, and is
+       escaped so if it contains single quotes or other odd char-
+       acters, it will not cause a parse error, or worse, a secu-
+       rity problem.
 
-       $lookup contains the search string, and is escaped  so  if
-       it contains single quotes or other odd characters, it will
-       not cause a parse error, or worse, a security problem.
+       As of Postfix  2.2  this  interface  is  obsolete,  it  is
+       replaced  by  the  more  general <b>query</b> interface described
+       above.  If the <b>query</b>  parameter  is  defined,  the  legacy
+       parameters  are  ignored. Please migrate to the new inter-
+       face as the legacy interface may be removed  in  a  future
+       release.
 
        <b>select_field</b>
               The SQL "select" parameter. Example:
-                  select_field = forw_addr
+                  <b>select_field</b> = forw_addr
 
        <b>table</b>  The SQL "select .. from" table name. Example:
-                  table = mxaliases
+                  <b>table</b> = mxaliases
 
        <b>where_field</b>
               The SQL "select .. where" parameter. Example:
-                  where_field = alias
+                  <b>where_field</b> = alias
 
        <b>additional_conditions</b>
               Additional conditions to the SQL query. Example:
-                  additional_conditions = and status = 'paid'
+                  <b>additional_conditions</b> = AND status = 'paid'
 
 <b>SEE ALSO</b>
        <a href="postmap.1.html">postmap(1)</a>, Postfix lookup table maintenance
@@ -126,7 +307,7 @@ MYSQL_TABLE(5)                                                  MYSQL_TABLE(5)
        <a href="MYSQL_README.html">MYSQL_README</a>, Postfix MYSQL client guide
 
 <b>LICENSE</b>
-       The  Secure  Mailer  license must be distributed with this
+       The Secure Mailer license must be  distributed  with  this
        software.
 
 <b>HISTORY</b>
index fbf1009accfb8dfb54f44abc2c9b3e7302822849..28412b4285eccef8dd447d8d8cf58f7c67fb99a9 100644 (file)
@@ -29,7 +29,7 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
        as  the  Postfix main.cf file, and can specify the parame-
        ters described below.
 
-<b>ALTERNATIVE CONFIGURATION</b>
+<b>BACKWARDS COMPATIBILITY</b>
        For compatibility with other Postfix lookup tables,  Post-
        greSQL  parameters  can  also  be  defined in main.cf.  In
        order to do that, specify as PostgreSQL source a name that
@@ -45,44 +45,72 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
        readable.  Support for this form  will  be  removed  in  a
        future Postfix version.
 
+       Postfix  2.2  has  enhanced query interfaces for MySQL and
+       PostgreSQL, these include  features  previously  available
+       only  in the Postfix LDAP client. In the new interface the
+       SQL query  is  specified  via  a  single  <b>query</b>  parameter
+       (described  in  more  detail  below).   In Postfix 2.1 the
+       parameter  precedence  was,  from   highest   to   lowest,
+       <b>select_function</b>, <b>query</b> and finally <b>select_field</b>, ...
+
+       With  Postfix  2.2  the <b>query</b> parameter has highest prece-
+       dence, and is used in preference to the  still  supported,
+       but    slated   to   be   phased   out,   <b>select_function</b>,
+       <b>select_field</b>, <b>table</b>, <b>where_field</b> and <b>additional_conditions</b>
+       parameters. To migrate to the new interface set:
+
+           <b>query</b> = SELECT <i>select</i><b>_</b><i>function</i>('%s')
+
+       or  in the absense of <b>selection_function</b>, the lower prece-
+       dence:
+
+           <b>query</b> = SELECT <i>select</i><b>_</b><i>field</i>
+               FROM <i>table</i>
+               WHERE <i>where</i><b>_</b><i>field</i> = '%s'
+                   <i>additional</i><b>_</b><i>conditions</i>
+
+       Use the value, not the name,  of  each  legacy  parameter.
+       Note  that the <b>additional_conditions</b> parameter is optional
+       and if not empty, will always start with <b>AND</b>.
+
 <b>LIST MEMBERSHIP</b>
        When using SQL to store lists such as $<a href="postconf.5.html#mynetworks">mynetworks</a>, $<a href="postconf.5.html#mydestination">mydes</a>-
-       <a href="postconf.5.html#mydestination">tination</a>, $<a href="postconf.5.html#relay_domains">relay_domains</a>, $<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a>, etc.,  it
-       is  important to understand that the table must store each
-       list member as a separate key. The table  lookup  verifies
-       the  *existence*  of  the  key.  See "Postfix lists versus
-       tables" in the <a href="DATABASE_README.html">DATABASE_README</a> document for a  discussion.
-
-       Do  NOT create tables that return the full list of domains
-       in $<a href="postconf.5.html#mydestination">mydestination</a> or $<a href="postconf.5.html#relay_domains">relay_domains</a> etc., or IP  addresses
+       <a href="postconf.5.html#mydestination">tination</a>,  $<a href="postconf.5.html#relay_domains">relay_domains</a>, $<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a>, etc., it
+       is important to understand that the table must store  each
+       list  member  as a separate key. The table lookup verifies
+       the *existence* of the  key.  See  "Postfix  lists  versus
+       tables"  in the <a href="DATABASE_README.html">DATABASE_README</a> document for a discussion.
+
+       Do NOT create tables that return the full list of  domains
+       in  $<a href="postconf.5.html#mydestination">mydestination</a> or $<a href="postconf.5.html#relay_domains">relay_domains</a> etc., or IP addresses
        in $<a href="postconf.5.html#mynetworks">mynetworks</a>.
 
        DO create tables with each matching item as a key and with
-       an arbitrary value. With SQL databases it is not  uncommon
+       an  arbitrary value. With SQL databases it is not uncommon
        to return the key itself or a constant value.
 
 <b>PGSQL PARAMETERS</b>
-       <b>hosts</b>  The  hosts  that Postfix will try to connect to and
+       <b>hosts</b>  The hosts that Postfix will try to connect  to  and
               query from.  Specify <i>unix:</i> for UNIX-domain sockets,
               <i>inet:</i> for TCP connections (default).  Example:
                   hosts = host1.some.domain host2.some.domain
                   hosts = unix:/file/name
 
-              The  hosts are tried in random order, with all con-
+              The hosts are tried in random order, with all  con-
               nections  over  UNIX  domain  sockets  being  tried
-              before  those  over TCP.  The connections are auto-
-              matically closed  after  being  idle  for  about  1
+              before those over TCP.  The connections  are  auto-
+              matically  closed  after  being  idle  for  about 1
               minute, and are re-opened as necessary.
 
               NOTE: the <i>unix:</i> and <i>inet:</i> prefixes are accepted for
-              backwards compatibility reasons, but  are  actually
+              backwards  compatibility  reasons, but are actually
               ignored.  The PostgreSQL client library will always
               try to connect to an UNIX socket if the name starts
-              with  a slash, and will try a TCP connection other-
+              with a slash, and will try a TCP connection  other-
               wise.
 
        <b>user, password</b>
-              The user name and password to log  into  the  pgsql
+              The  user  name  and password to log into the pgsql
               server.  Example:
                   user = someone
                   password = some_password
@@ -90,58 +118,155 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
        <b>dbname</b> The database name on the servers. Example:
                   dbname = customer_database
 
-       The  following  parameters can be used to fill in a SELECT
-       template statement of the form:
-           select [<b>select_field</b>] from [<b>table</b>] where
-               [<b>where_field</b>] = '$lookup' [<b>additional_conditions</b>]
-
-       $lookup contains the search string, and is escaped  so  if
-       it contains single quotes or other odd characters, it will
-       not cause a parse error, or worse, a security problem.
-
-       <b>select_field</b>
-              The SQL "select" parameter. Example:
-                  select_field = forw_addr
-
-       <b>table</b>  The SQL "select .. from" table name. Example:
-                  table = mxaliases
-
-       <b>where_field</b>
-              The SQL "select .. where" parameter. Example:
-                  where_field = alias
-
-       <b>additional_conditions</b>
-              Additional conditions to the SQL query. Example:
-                  additional_conditions = and status = 'paid'
-
-       The following parameters  provide  ways  to  override  the
-       default  SELECT  statement.   Setting  them  will instruct
-       Postfix  to  ignore   the   above   <b>table</b>,   <b>select_field</b>,
-       <b>where_field</b> and <b>additional_conditions</b> parameters:
+       <b>query</b>  The SQL query template used to search the database,
+              where <b>%s</b> is a substitute for the address Postfix is
+              trying to resolve, e.g.
+                  query = SELECT replacement FROM  aliases  WHERE
+              mailbox = '%s'
 
-       <b>query</b>  This  parameter  specifies  a  complete  SQL query.
-              Example:
-                  query = select forw_addr from mxaliases where
-                      alias = '%s' and status = 'paid'
-
-              This parameter supports the  following  '%'  expan-
+              This  parameter  supports  the following '%' expan-
               sions:
 
-              <b>%s</b>     This  is  replaced by the input key. Quoting
-                     is used to make sure that the input key does
-                     not add unexpected metacharacters.
+              <b>%%</b>     This is replaced by a literal '%' character.
+                     (Postfix 2.2 and later)
+
+              <b>%s</b>     This  is  replaced  by  the  input key.  SQL
+                     quoting is used to make sure that the  input
+                     key  does not add unexpected metacharacters.
 
               <b>%u</b>     When the input key is an address of the form
-                     user@domain, <b>%u</b> is replaced  by  the  quoted
-                     local  part of the address.  If no domain is
-                     specified, <b>%u</b>  is  replaced  by  the  entire
-                     search string.
+                     user@domain,  <b>%u</b>  is  replaced  by  the  SQL
+                     quoted local part of  the  address.   Other-
+                     wise,  <b>%u</b>  is  replaced by the entire search
+                     string.  If  the  localpart  is  empty,  the
+                     query  is suppressed and returns no results.
 
               <b>%d</b>     When the input key is an address of the form
-                     user@domain, <b>%d</b> is replaced  by  the  quoted
-                     domain  part of the address.  When the input
-                     key has no domain qualifier, <b>%d</b> is  replaced
-                     by the entire search string.
+                     user@domain,  <b>%d</b>  is  replaced  by  the  SQL
+                     quoted domain part of the  address.   Other-
+                     wise, the query is suppressed and returns no
+                     results.
+
+              <b>%[SUD]</b> The  upper-case  equivalents  of  the  above
+                     expansions  behave  in  the  <b>query</b> parameter
+                     identically  to  their  lower-case  counter-
+                     parts.   With  the  <b>result_format</b>  parameter
+                     (see  below),  they  expand  the  input  key
+                     rather than the result value.
+
+                     The  above  %S,  %U  and  %D  expansions are
+                     available with Postfix 2.2 and later
+
+              <b>%[1-9]</b> The patterns %1, %2, ... %9 are replaced  by
+                     the corresponding most significant component
+                     of the input key's domain. If the input  key
+                     is <i>user@mail.example.com</i>, then %1 is <b>com</b>, %2
+                     is <b>example</b> and %3 is <b>mail</b>. If the input  key
+                     is  unqualified  or  does  not  have  enough
+                     domain components to satisfy all the  speci-
+                     fied  patterns,  the query is suppressed and
+                     returns no results.
+
+                     The above %1, ... %9 expansions  are  avail-
+                     able with Postfix 2.2 and later
+
+              The  <b>domain</b>  parameter  described  below limits the
+              input keys to addresses in matching  domains.  When
+              the  <b>domain</b> parameter is non-empty, SQL queries for
+              unqualified addresses or addresses in  non-matching
+              domains are suppressed and return no results.
+
+              The  precedence  of this parameter has changed with
+              Postfix 2.2, in prior releases the precedence  was,
+              from  highest  to  lowest,  <b>select_function</b>, <b>query</b>,
+              <b>select_field</b>, ...
+
+              With Postfix 2.2 the <b>query</b>  parameter  has  highest
+              precedence, see COMPATIBILITY above.
+
+              NOTE: DO NOT put quotes around the <b>query</b> parameter.
+
+       <b>result_format (default: %s</b>)
+              Format template applied to result attributes.  Most
+              commonly  used  to  append (or prepend) text to the
+              result. This parameter supports the  following  '%'
+              expansions:
+
+              <b>%%</b>     This is replaced by a literal '%' character.
+
+              <b>%s</b>     This is replaced by the value of the  result
+                     attribute.   When  result  is  empty  it  is
+                     skipped.
+
+              <b>%u</b>     When  the  result  attribute  value  is   an
+                     address  of  the  form  user@domain,  <b>%u</b>  is
+                     replaced by the local part of  the  address.
+                     When the result has an empty localpart it is
+                     skipped.
+
+              <b>%d</b>     When a result attribute value is an  address
+                     of  the  form user@domain, <b>%d</b> is replaced by
+                     the domain part of the attribute value. When
+                     the result is unqualified it is skipped.
+
+              <b>%[SUD1-9]</b>
+                     The  upper-case and decimal digit expansions
+                     interpolate  the  parts  of  the  input  key
+                     rather  than  the result. Their behaviour is
+                     identical to that described with <b>query</b>,  and
+                     in  fact  because  the input key is known in
+                     advance, queries whose key does not  contain
+                     all  the information specified in the result
+                     template  are  suppressed  and   return   no
+                     results.
+
+              For  example,  using  "result_format  =  <a href="smtp.8.html">smtp</a>:[%s]"
+              allows one to use a mailHost attribute as the basis
+              of  a <a href="transport.5.html">transport(5)</a> table. After applying the result
+              format, multiple values are concatenated  as  comma
+              separated  strings. The expansion_limit and parame-
+              ter explained below allows one to restrict the num-
+              ber  of  values  in the result, which is especially
+              useful for maps that must return at most one value.
+
+              The  default  value  <b>%s</b>  specifies that each result
+              value should be used as is.
+
+              This parameter is available with  Postfix  2.2  and
+              later.
+
+              NOTE: DO NOT put quotes around the result format!
+
+       <b>domain (default: no domain list)</b>
+              This  is a list of domain names, paths to files, or
+              dictionaries. When specified, only fully  qualified
+              search  keys  with  a  *non-empty*  localpart and a
+              matching domain are  eligible  for  lookup:  'user'
+              lookups,  bare domain lookups and "@domain" lookups
+              are not performed. This  can  significantly  reduce
+              the query load on the PostgreSQL server.
+                  domain = postfix.org, hash:/etc/postfix/search-
+              domains
+
+              It is best not to use SQL to store the domains eli-
+              gible for SQL lookups.
+
+              This  parameter  is  available with Postfix 2.2 and
+              later.
+
+              NOTE: DO NOT define  this  parameter  for  <a href="local.8.html">local(8)</a>
+              aliases, because the input keys are always unquali-
+              fied.
+
+       <b>expansion_limit (default: 0)</b>
+              A limit on the  total  number  of  result  elements
+              returned  (as  a  comma separated list) by a lookup
+              against the map.  A setting of  zero  disables  the
+              limit.  Lookups  fail with a temporary error if the
+              limit is exceeded.  Setting the limit to 1  ensures
+              that lookups do not return multiple values.
+
+       Pre-Postfix 2.2 legacy interfaces:
 
        <b>select_function</b>
               This  parameter specifies a database function name.
@@ -149,18 +274,51 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
                   select_function = my_lookup_user_alias
 
               This is equivalent to:
-                  query = select my_lookup_user_alias('%s')
+                  query = SELECT my_lookup_user_alias('%s')
+
+              This parameter overrides the  legacy  table-related
+              fields  (described  below).  With  Postfix versions
+              prior to 2.2, it also overrides the  <b>query</b>  parame-
+              ter. Starting with Postfix 2.2, the <b>query</b> parameter
+              has highest precedence, and this parameter is  dep-
+              recated.  Please migrate to the new <b>query</b> interface
+              as this interface is slated to be phased out.
+
+       The following parameters (with lower precedence  than  the
+       <b>select_function</b>  interface described above) can be used to
+       build the SQL select statement as follows:
+
+           SELECT [<b>select_field</b>]
+           FROM [<b>table</b>]
+           WHERE [<b>where_field</b>] = '%s'
+                 [<b>additional_conditions</b>]
+
+       The specifier %s is  replaced  with  each  lookup  by  the
+       lookup  key and is escaped so if it contains single quotes
+       or other odd characters, it will not cause a parse  error,
+       or worse, a security problem.
+
+       Starting  with Postfix 2.2, this interface is obsoleted by
+       the more  general  <b>query</b>  interface  described  above.  If
+       higher  precedence the <b>query</b> or <b>select_function</b> parameters
+       described above are defined, these parameters are ignored.
+       Please  migrate  to the new <b>query</b> interface as this inter-
+       face is slated to be phased out.
 
-              and overrides both the <b>query</b> parameter and the  ta-
-              ble-related fields above.
+       <b>select_field</b>
+              The SQL "select" parameter. Example:
+                  <b>select_field</b> = forw_addr
 
-              As  of  June 2002, if the function returns a single
-              row and a single column AND  that  value  is  NULL,
-              then  the  result will be treated as if the key was
-              not in the dictionary.
+       <b>table</b>  The SQL "select .. from" table name. Example:
+                  <b>table</b> = mxaliases
 
-              Future versions  will  allow  functions  to  return
-              result sets.
+       <b>where_field</b>
+              The SQL "select .. where" parameter. Example:
+                  <b>where_field</b> = alias
+
+       <b>additional_conditions</b>
+              Additional conditions to the SQL query. Example:
+                  <b>additional_conditions</b> = AND status = 'paid'
 
 <b>SEE ALSO</b>
        <a href="postmap.1.html">postmap(1)</a>, Postfix lookup table manager
@@ -173,7 +331,7 @@ PGSQL_TABLE(5)                                                  PGSQL_TABLE(5)
        <a href="PGSQL_README.html">PGSQL_README</a>, Postfix PostgreSQL client guide
 
 <b>LICENSE</b>
-       The Secure Mailer license must be  distributed  with  this
+       The  Secure  Mailer  license must be distributed with this
        software.
 
 <b>HISTORY</b>
index c712a8068f2fd77fef0dbb64a1707a8c0f3d679b..db9d2883e58026af83f462ad6bdf5830e33378ae 100644 (file)
@@ -559,6 +559,24 @@ Enable the rewriting of the form "user%domain" to "user@domain".
 This is enabled by default.
 </p>
 
+<p> Note: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true: </p>
+
+<ul>
+
+<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
+
+<li> The message is received from a network client that matches
+$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
+
+<li> The message is received from the network, and the
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
+
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+
 <p>
 Example:
 </p>
@@ -689,10 +707,28 @@ append the string "@$<a href="postconf.5.html#remote_header_rewrite_domain">remo
 </p>
 
 <p>
-This feature is enabled by default and must not be turned off.
+Note 1: This feature is enabled by default and must not be turned off.
 Postfix does not support domain-less addresses.
 </p>
 
+<p> Note 2: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true: </p>
+
+<ul>
+
+<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
+
+<li> The message is received from a network client that matches
+$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
+
+<li> The message is received from the network, and the
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
+
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+
 
 </DD>
 
@@ -707,11 +743,29 @@ instead.
 </p>
 
 <p>
-This feature is enabled by default. If disabled, users will not be
+Note 1: This feature is enabled by default. If disabled, users will not be
 able to send mail to "user@partialdomainname" but will have to
 specify full domain names instead.
 </p>
 
+<p> Note 2: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true: </p>
+
+<ul>
+
+<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
+
+<li> The message is received from a network client that matches
+$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
+
+<li> The message is received from the network, and the
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
+
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+
 
 </DD>
 
@@ -1105,6 +1159,24 @@ will become visible after a minute or so.  Use "<b>postfix reload</b>"
 to eliminate the delay.
 </p>
 
+<p> Note: with Postfix version 2.2, message header address mapping
+happens only when message header address rewriting is enabled: </p>
+
+<ul>
+
+<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
+
+<li> The message is received from a network client that matches
+$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
+
+<li> The message is received from the network, and the
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
+
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+
 <p>
 Examples:
 </p>
@@ -2659,7 +2731,8 @@ Examples:
 <p> The Internet protocols Postfix will attempt to use when making
 or accepting connections. Specify one or more of "ipv4"
 or "ipv6", separated by whitespace or commas. The form
-"all" is equivalent to "ipv4, ipv6". </p>
+"all" is equivalent to "ipv4, ipv6" or "ipv4", depending
+on whether the operating system implements IPv6. </p>
 
 <p> This feature is available in Postfix version 2.2 and later. </p>
 
@@ -3881,6 +3954,24 @@ does not change "user@any.thing.foo.example.com" or "user@foo.example.com",
 but strips "user@any.thing.else.example.com" to "user@example.com".
 </p>
 
+<p> Note: with Postfix version 2.2, message header address masquerading
+happens only when message header address rewriting is enabled: </p>
+
+<ul>
+
+<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
+
+<li> The message is received from a network client that matches
+$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
+
+<li> The message is received from the network, and the
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
+
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+
 <p>
 Example:
 </p>
@@ -9044,6 +9135,24 @@ necessary if your machine is connected to UUCP networks.  It is
 enabled by default.
 </p>
 
+<p> Note: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true: </p>
+
+<ul>
+
+<li> The message is received with the Postfix <a href="sendmail.1.html">sendmail(1)</a> command,
+
+<li> The message is received from a network client that matches
+$<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a>,
+
+<li> The message is received from the network, and the
+<a href="postconf.5.html#remote_header_rewrite_domain">remote_header_rewrite_domain</a> parameter specifies a non-empty value.
+
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"<a href="postconf.5.html#local_header_rewrite_clients">local_header_rewrite_clients</a> = static:all". </p>
+
 <p>
 Example:
 </p>
index 594f4c4e6f8bce4fd46a4e6cc982880d54310757..c4b1d8f8abda304ebfa4b0992bf13a525467c7e2 100644 (file)
@@ -53,6 +53,18 @@ parameter below would be defined in main.cf as
 Note: with this form, the passwords for the LDAP sources are
 written in main.cf, which is normally world-readable.  Support
 for this form will be removed in a future Postfix version.
+
+Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL,
+these now include features previously available only in the
+Postfix LDAP client. This work also created an opportunity for
+improvements in the LDAP interface. The primary compatibility
+issue is that \fBresult_filter\fR (a name that has caused some
+confusion as to its meaning in the past) has been renamed to
+\fBresult_format\fR.  For backwards compatibility with the pre
+2.2 LDAP client, \fBresult_filter\fR can for now be used instead
+of \fBresult_format\fR, when the latter parameter is not also set.
+The new name better reflects the function of the parameter. This
+compatibility interface may be removed in a future release.
 .SH "LIST MEMBERSHIP"
 .na
 .nf
@@ -128,14 +140,50 @@ server_host = ldapi://%2Fsome%2Fpath
 The port the LDAP server listens on, e.g.
 .ti +4
 server_port = 778
-.IP "\fBsearch_base (No default; you must configure this)\fR"
-The RFC2253 base DN at which to conduct the search, e.g.
-.ti +4
-search_base = dc=your, dc=com
 .IP "\fBtimeout (default: 10 seconds)\fR"
 The number of seconds a search can take before timing out, e.g.
 .ti +4
 timeout = 5
+.IP "\fBsearch_base (No default; you must configure this)\fR"
+The RFC2253 base DN at which to conduct the search, e.g.
+.ti +4
+search_base = dc=your, dc=com
+.IP
+With Postfix 2.2 and later this parameter supports the
+following '%' expansions:
+.RS
+.IP "\fB\fB%%\fR\fR"
+This is replaced by a literal '%' character.
+.IP "\fB\fB%s\fR\fR"
+This is replaced by the input key.
+RFC 2253 quoting is used to make sure that the input key
+does not add unexpected metacharacters.
+.IP "\fB\fB%u\fR\fR"
+When the input key is an address of the form user@domain, \fB%u\fR
+is replaced by the (RFC 2253) quoted local part of the address.
+Otherwise, \fB%u\fR is replaced by the entire search string.
+If the localpart is empty, the search is suppressed and returns
+no results.
+.IP "\fB\fB%d\fR\fR"
+When the input key is an address of the form user@domain, \fB%d\fR
+is replaced by the (RFC 2253) quoted domain part of the address.
+Otherwise, the search is suppressed and returns no results.
+.IP "\fB\fB%[SUD]\fR\fR"
+For the \fBsearch_base\fR parameter, the upper-case equivalents
+of the above expansions behave identically to their lower-case
+counter-parts. With the \fBresult_format\fR parameter (previously
+called \fBresult_filter\fR see the COMPATIBILITY section and below),
+they expand to the corresponding components of input key rather
+than the result value.
+.IP "\fB\fB%[1-9]\fR\fR"
+The patterns %1, %2, ... %9 are replaced by the corresponding
+most significant component of the input key's domain. If the
+input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
+%2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
+unqualified or does not have enough domain components to satisfy
+all the specified patterns, the search is suppressed and returns
+no results.
+.RE
 .IP "\fBquery_filter (default: mailacceptinggeneralid=%s)\fR"
 The RFC2254 filter used to search the directory, where \fB%s\fR
 is a substitute for the address Postfix is trying to resolve,
@@ -145,20 +193,43 @@ query_filter = (&(mail=%s)(paid_up=true))
 
 This parameter supports the following '%' expansions:
 .RS
+.IP "\fB\fB%%\fR\fR"
+This is replaced by a literal '%' character. (Postfix 2.2 and later).
 .IP "\fB\fB%s\fR\fR"
-This is replaced by the input key. RFC 2254 quoting is used
-to make sure that the input key does not add unexpected
-metacharacters.
+This is replaced by the input key.
+RFC 2254 quoting is used to make sure that the input key
+does not add unexpected metacharacters.
 .IP "\fB\fB%u\fR\fR"
-When the input key is an address of the form user@domain,
-\fB%u\fR is replaced by the (RFC 2254) quoted local part of the
-address. Otherwise, \fB%u\fR is replaced by the entire
-search string.
+When the input key is an address of the form user@domain, \fB%u\fR
+is replaced by the (RFC 2254) quoted local part of the address.
+Otherwise, \fB%u\fR is replaced by the entire search string.
+If the localpart is empty, the search is suppressed and returns
+no results.
 .IP "\fB\fB%d\fR\fR"
-When the input key is an address of the form user@domain,
-\fB%d\fR is replaced by the (RFC 2254) quoted domain part of the
-address. Otherwise, \fB%d\fR is replaced by the entire
-search string.
+When the input key is an address of the form user@domain, \fB%d\fR
+is replaced by the (RFC 2254) quoted domain part of the address.
+Otherwise, the search is suppressed and returns no results.
+.IP "\fB\fB%[SUD]\fR\fR"
+The upper-case equivalents of the above expansions behave in the
+\fBquery_filter\fR parameter identically to their lower-case
+counter-parts. With the \fBresult_format\fR parameter (previously
+called \fBresult_filter\fR see the COMPATIBILITY section and below),
+they expand to the corresponding components of input key rather
+than the result value.
+.IP
+The above %S, %U and %D expansions are available with Postfix 2.2
+and later.
+.IP "\fB\fB%[1-9]\fR\fR"
+The patterns %1, %2, ... %9 are replaced by the corresponding
+most significant component of the input key's domain. If the
+input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
+%2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
+unqualified or does not have enough domain components to satisfy
+all the specified patterns, the saerch is suppressed and returns
+no results.
+.IP
+The above %1, ..., %9 expansions are available with Postfix 2.2
+and later.
 .RE
 .IP
 The "domain" parameter described below limits the input
@@ -167,30 +238,42 @@ parameter is non-empty, LDAP queries for unqualified
 addresses or addresses in non-matching domains are suppressed
 and return no results.
 
-NOTE: DO NOT put quotes around the query filter.
-.IP "\fBresult_filter (default: \fB%s\fR)\fR"
-Format template applied to result attributes. Supports the
-same expansions as the query_filter, and can be easily used
-to append (or prepend) text.  This parameter supports the
-following '%' expansions:
+NOTE: DO NOT put quotes around the \fBquery_filter\fR parameter.
+.IP "\fBresult_format (default: \fB%s\fR)\fR"
+Called \fBresult_filter\fR in Postfix releases prior to 2.2.
+Format template applied to result attributes. Most commonly used
+to append (or prepend) text to the result. This parameter supports
+the following '%' expansions:
 .RS
+.IP "\fB\fB%%\fR\fR"
+This is replaced by a literal '%' character. (Postfix 2.2 and later).
 .IP "\fB\fB%s\fR\fR"
-This is replaced by the value of the result attribute.
+This is replaced by the value of the result attribute. When
+result is empty it is skipped.
 .IP "\fB%u\fR
 When the result attribute value is an address of the form
 user@domain, \fB%u\fR is replaced by the local part of the
-address.  Otherwise, \fB%u\fR is replaced by the entire
-attribute value.
+address. When the result has an empty localpart it is skipped.
 .IP "\fB\fB%d\fR\fR"
 When a result attribute value is an address of the form
 user@domain, \fB%d\fR is replaced by the domain part of
-the attribute value.  Otherwise, \fB%d\fR is replaced by
-the entire attribute value.
+the attribute value. When the result is unqualified it
+is skipped.
+.IP "\fB\fB%[SUD1-9]\fR\fB"
+The upper-case and decimal digit expansions interpolate
+the parts of the input key rather than the result. Their
+behaviour is identical to that described with \fBquery_filter\fR,
+and in fact because the input key is known in advance, lookups
+whose key does not contain all the information specified in
+the result template are suppressed and return no results.
+.IP
+The above %S, %U, %D and %1, ..., %9 expansions are available with
+Postfix 2.2 and later.
 .RE
 .IP
-For example, using "result_filter = smtp:[%s]" allows one
+For example, using "result_format = smtp:[%s]" allows one
 to use a mailHost attribute as the basis of a transport(5)
-table. After applying the result filter, multiple values
+table. After applying the result format, multiple values
 are concatenated as comma separated strings. The expansion_limit
 and size_limit parameters explained below allow one to
 restrict the number of values in the result, which is
@@ -200,7 +283,13 @@ value.
 The default value \fB%s\fR specifies that each
 attribute value should be used as is.
 
-NOTE: DO NOT put quotes around the result filter!
+This parameter was called \fBresult_filter\fR in Postfix
+releases prior to 2.2. If no "result_format" is specified,
+the value of "result_filter" will be used instead before
+resorting to the default value. This provides compatibility
+with old configuration files.
+
+NOTE: DO NOT put quotes around the result format!
 .IP "\fBdomain (default: no domain list)\fR"
 This is a list of domain names, paths to files, or
 dictionaries. When specified, only fully qualified search
@@ -220,7 +309,7 @@ The attribute(s) Postfix will read from any directory
 entries returned by the lookup, to be resolved to an email
 address.
 .ti +4
-result_attribute = mailbox,maildrop
+result_attribute = mailbox, maildrop
 .IP "\fBspecial_result_attribute (No default)\fR"
 The attribute(s) of directory entries that can contain DNs
 or URLs. If found, a recursive subsequent search is done
@@ -287,14 +376,14 @@ limit to 1 ensures that lookups do not return multiple
 values.
 .IP "\fBsize_limit (default: $expansion_limit)\fR"
 A limit on the number of LDAP entries returned by any single
-LDAP query performed as part of the lookup. A setting of
+LDAP search performed as part of the lookup. A setting of
 0 disables the limit.  Expansion of DN and URL references
 involves nested LDAP queries, each of which is separately
 subjected to this limit.
 
 Note: even a single LDAP entry can generate multiple lookup
 results, via multiple result attributes and/or multi-valued
-result attributes. This limit caps the per query resource
+result attributes. This limit caps the per search resource
 utilization on the LDAP server, not the final multiplicity
 of the lookup result. It is analogous to the "-z" option
 of "ldapsearch".
index 9654a36ce287ef61b7add943e1249fbd3c91af7d..4ccb097ba598e8af0802b1ce8cf30d664c245967 100644 (file)
@@ -27,7 +27,7 @@ alias_maps = mysql:/etc/mysql-aliases.cf
 The file /etc/postfix/mysql-aliases.cf has the same format as
 the Postfix main.cf file, and can specify the parameters
 described below.
-.SH "ALTERNATIVE CONFIGURATION"
+.SH "BACKWARDS COMPATIBILITY"
 .na
 .nf
 .ad
@@ -44,6 +44,30 @@ below would be defined in main.cf as "\fImysqlname\fR_hosts".
 Note: with this form, the passwords for the MySQL sources are
 written in main.cf, which is normally world-readable.  Support
 for this form will be removed in a future Postfix version.
+
+Postfix 2.2 has enhanced query interfaces for MySQL and PostreSQL,
+these include features previously available only in the Postfix
+LDAP client. In the new interface the SQL query is specified via
+a single \fBquery\fR parameter (described in more detail below).
+When the new \fBquery\fR parameter is not specified in the map
+definition, Postfix reverts to the old interface, with the SQL
+query constructed from the \fBselect_field\fR, \fBtable\fR,
+\fBwhere_field\fR and \fBadditional_conditions\fR parameters.
+The old interface will be gradually phased out. To migrate to
+the new interface set:
+
+.ti +4
+\fBquery\fR = SELECT [\fIselect_field\fR]
+.ti +8
+FROM [\fItable\fR]
+.ti +8
+WHERE [\fIwhere_field\fR] = '%s'
+.ti +12
+[\fIadditional_conditions\fR]
+
+Insert the value, not the name, of each legacy parameter. Note
+that the \fBadditional_conditions\fR parameter is optional
+and if not empty, will always start with \fBAND\fR.
 .SH "LIST MEMBERSHIP"
 .na
 .nf
@@ -102,33 +126,176 @@ password = some_password
 The database name on the servers. Example:
 .ti +4
 dbname = customer_database
+.IP "\fBquery\fR"
+The SQL query template used to search the database, where \fB%s\fR
+is a substitute for the address Postfix is trying to resolve,
+e.g.
+.ti +4
+query = SELECT replacement FROM aliases WHERE mailbox = '%s'
+
+This parameter supports the following '%' expansions:
+.RS
+.IP "\fB\fB%%\fR\fR"
+This is replaced by a literal '%' character.
+.IP "\fB\fB%s\fR\fR"
+This is replaced by the input key.
+SQL quoting is used to make sure that the input key does not
+add unexpected metacharacters.
+.IP "\fB\fB%u\fR\fR"
+When the input key is an address of the form user@domain, \fB%u\fR
+is replaced by the SQL quoted local part of the address.
+Otherwise, \fB%u\fR is replaced by the entire search string.
+If the localpart is empty, the query is suppressed and returns
+no results.
+.IP "\fB\fB%d\fR\fR"
+When the input key is an address of the form user@domain, \fB%d\fR
+is replaced by the SQL quoted domain part of the address.
+Otherwise, the query is suppressed and returns no results.
+.IP "\fB\fB%[SUD]\fR\fR"
+The upper-case equivalents of the above expansions behave in the
+\fBquery\fR parameter identically to their lower-case counter-parts.
+With the \fBresult_format\fR parameter (see below), they expand the
+input key rather than the result value.
+.IP "\fB\fB%[1-9]\fR\fR"
+The patterns %1, %2, ... %9 are replaced by the corresponding
+most significant component of the input key's domain. If the
+input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
+%2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
+unqualified or does not have enough domain components to satisfy
+all the specified patterns, the query is suppressed and returns
+no results.
+.RE
+.IP
+The \fBdomain\fR parameter described below limits the input
+keys to addresses in matching domains. When the \fBdomain\fR
+parameter is non-empty, SQL queries for unqualified addresses
+or addresses in non-matching domains are suppressed
+and return no results.
+
+This parameter is available with Postfix 2.2. In prior releases
+the SQL query was built from the separate parameters:
+\fBselect_field\fR, \fBtable\fR, \fBwhere_field\fR and
+\fBadditional_conditions\fR. The mapping from the old parameters
+to the equivalent query is:
+
+.ti +4
+SELECT [\fBselect_field\fR]
+.ti +4
+FROM [\fBtable\fR]
+.ti +4
+WHERE [\fBwhere_field\fR] = '%s'
+.ti +10
+[\fBadditional_conditions\fR]
+
+The '%s' in the \fBWHERE\fR clause expands to the escaped search string.
+With Postfix 2.2 these legacy parameters are used if the \fBquery\fR
+parameter is not specified.
+
+NOTE: DO NOT put quotes around the query parameter.
+.IP "\fBresult_format (default: \fB%s\fR)\fR"
+Format template applied to result attributes. Most commonly used
+to append (or prepend) text to the result. This parameter supports
+the following '%' expansions:
+.RS
+.IP "\fB\fB%%\fR\fR"
+This is replaced by a literal '%' character.
+.IP "\fB\fB%s\fR\fR"
+This is replaced by the value of the result attribute. When
+result is empty it is skipped.
+.IP "\fB%u\fR
+When the result attribute value is an address of the form
+user@domain, \fB%u\fR is replaced by the local part of the
+address. When the result has an empty localpart it is skipped.
+.IP "\fB\fB%d\fR\fR"
+When a result attribute value is an address of the form
+user@domain, \fB%d\fR is replaced by the domain part of
+the attribute value. When the result is unqualified it
+is skipped.
+.IP "\fB\fB%[SUD1-9]\fR\fB"
+The upper-case and decimal digit expansions interpolate
+the parts of the input key rather than the result. Their
+behaviour is identical to that described with \fBquery\fR,
+and in fact because the input key is known in advance, queries
+whose key does not contain all the information specified in
+the result template are suppressed and return no results.
+.RE
+.IP
+For example, using "result_format = smtp:[%s]" allows one
+to use a mailHost attribute as the basis of a transport(5)
+table. After applying the result format, multiple values
+are concatenated as comma separated strings. The expansion_limit
+and parameter explained below allows one to restrict the number
+of values in the result, which is especially useful for maps that
+must return at most one value.
+
+The default value \fB%s\fR specifies that each result value should
+be used as is.
+
+This parameter is available with Postfix 2.2 and later.
+
+NOTE: DO NOT put quotes around the result format!
+.IP "\fBdomain (default: no domain list)\fR"
+This is a list of domain names, paths to files, or
+dictionaries. When specified, only fully qualified search
+keys with a *non-empty* localpart and a matching domain
+are eligible for lookup: 'user' lookups, bare domain lookups
+and "@domain" lookups are not performed. This can significantly
+reduce the query load on the MySQL server.
+.ti +4
+domain = postfix.org, hash:/etc/postfix/searchdomains
+
+It is best not to use SQL to store the domains eligible
+for SQL lookups.
+
+This parameter is available with Postfix 2.2 and later.
+
+NOTE: DO NOT define this parameter for local(8) aliases,
+because the input keys are always unqualified.
+.IP "\fBexpansion_limit (default: 0)\fR"
+A limit on the total number of result elements returned
+(as a comma separated list) by a lookup against the map.
+A setting of zero disables the limit. Lookups fail with a
+temporary error if the limit is exceeded.  Setting the
+limit to 1 ensures that lookups do not return multiple
+values.
 .PP
-The following parameters are used to fill in a SELECT
-query template of the form:
+The following parameters can be used to fill in a
+SELECT template statement of the form:
+
 .ti +4
-select [\fBselect_field\fR] from [\fBtable\fR] where
-.ti +8
-[\fBwhere_field\fR] = '$lookup' [\fBadditional_conditions\fR]
+SELECT [\fBselect_field\fR]
+.ti +4
+FROM [\fBtable\fR]
+.ti +4
+WHERE [\fBwhere_field\fR] = '%s'
+.ti +10
+[\fBadditional_conditions\fR]
+
+The specifier %s is replaced by the search string, and is
+escaped so if it contains single quotes or other odd characters,
+it will not cause a parse error, or worse, a security problem.
 
-$lookup contains the search string, and is escaped so if
-it contains single quotes or other odd characters, it will
-not cause a parse error, or worse, a security problem.
+As of Postfix 2.2 this interface is obsolete, it is replaced
+by the more general \fBquery\fR interface described above.
+If the \fBquery\fR parameter is defined, the legacy parameters
+are ignored. Please migrate to the new interface as the legacy
+interface may be removed in a future release.
 .IP "\fBselect_field\fR"
 The SQL "select" parameter. Example:
 .ti +4
-select_field = forw_addr
+\fBselect_field\fR = forw_addr
 .IP "\fBtable\fR"
 The SQL "select .. from" table name. Example:
 .ti +4
-table = mxaliases
+\fBtable\fR = mxaliases
 .IP "\fBwhere_field\fR
 The SQL "select .. where" parameter. Example:
 .ti +4
-where_field = alias
+\fBwhere_field\fR = alias
 .IP "\fBadditional_conditions\fR
 Additional conditions to the SQL query. Example:
 .ti +4
-additional_conditions = and status = 'paid'
+\fBadditional_conditions\fR = AND status = 'paid'
 .SH "SEE ALSO"
 .na
 .nf
index 2428e84eee04b8649b60cd63beb45097c7f01a1e..6a3925c47af0f8a6cce3c50a2f0feb5deea62408 100644 (file)
@@ -27,7 +27,7 @@ alias_maps = pgsql:/etc/pgsql-aliases.cf
 The file /etc/postfix/pgsql-aliases.cf has the same format as
 the Postfix main.cf file, and can specify the parameters
 described below.
-.SH "ALTERNATIVE CONFIGURATION"
+.SH "BACKWARDS COMPATIBILITY"
 .na
 .nf
 .ad
@@ -46,6 +46,37 @@ Note: with this form, the passwords for the PostgreSQL sources
 are written in main.cf, which is normally world-readable.
 Support for this form will be removed in a future Postfix
 version.
+
+Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL,
+these include features previously available only in the Postfix
+LDAP client. In the new interface the SQL query is specified via
+a single \fBquery\fR parameter (described in more detail below).
+In Postfix 2.1 the parameter precedence was, from highest to lowest,
+\fBselect_function\fR, \fBquery\fR and finally \fBselect_field\fR, ...
+
+With Postfix 2.2 the \fBquery\fR parameter has highest precedence,
+and is used in preference to the still supported, but slated to be
+phased out, \fBselect_function\fR, \fBselect_field\fR, \fBtable\fR,
+\fBwhere_field\fR and \fBadditional_conditions\fR parameters. To
+migrate to the new interface set:
+
+.ti +4
+\fBquery\fR = SELECT \fIselect_function\fR('%s')
+
+or in the absense of \fBselection_function\fR, the lower precedence:
+
+.ti +4
+\fBquery\fR = SELECT \fIselect_field\fR
+.ti +8
+FROM \fItable\fR
+.ti +8
+WHERE \fIwhere_field\fR = '%s'
+.ti +12
+\fIadditional_conditions\fR
+
+Use the value, not the name, of each legacy parameter. Note
+that the \fBadditional_conditions\fR parameter is optional
+and if not empty, will always start with \fBAND\fR.
 .SH "LIST MEMBERSHIP"
 .na
 .nf
@@ -102,61 +133,134 @@ password = some_password
 The database name on the servers. Example:
 .ti +4
 dbname = customer_database
-.PP
-The following parameters can be used to fill in a SELECT
-template statement of the form:
-.ti +4
-select [\fBselect_field\fR] from [\fBtable\fR] where
-.ti +8
-[\fBwhere_field\fR] = '$lookup' [\fBadditional_conditions\fR]
-
-$lookup contains the search string, and is escaped so if
-it contains single quotes or other odd characters, it will
-not cause a parse error, or worse, a security problem.
-.IP "\fBselect_field\fR"
-The SQL "select" parameter. Example:
-.ti +4
-select_field = forw_addr
-.IP "\fBtable\fR"
-The SQL "select .. from" table name. Example:
-.ti +4
-table = mxaliases
-.IP "\fBwhere_field\fR
-The SQL "select .. where" parameter. Example:
-.ti +4
-where_field = alias
-.IP "\fBadditional_conditions\fR
-Additional conditions to the SQL query. Example:
-.ti +4
-additional_conditions = and status = 'paid'
-.PP
-The following parameters provide ways to override the default
-SELECT statement.  Setting them will instruct Postfix to ignore
-the above \fBtable\fR, \fBselect_field\fR, \fBwhere_field\fR and
-\fBadditional_conditions\fR parameters:
 .IP "\fBquery\fR"
-This parameter specifies a complete SQL query. Example:
+The SQL query template used to search the database, where \fB%s\fR
+is a substitute for the address Postfix is trying to resolve,
+e.g.
 .ti +4
-query = select forw_addr from mxaliases where
-.ti +8
-alias = '%s' and status = 'paid'
+query = SELECT replacement FROM aliases WHERE mailbox = '%s'
 
 This parameter supports the following '%' expansions:
 .RS
+.IP "\fB\fB%%\fR\fR"
+This is replaced by a literal '%' character. (Postfix 2.2 and later)
 .IP "\fB\fB%s\fR\fR"
-This is replaced by the input key. Quoting is used to make sure
-that the input key does not add unexpected metacharacters.
+This is replaced by the input key.
+SQL quoting is used to make sure that the input key does not
+add unexpected metacharacters.
 .IP "\fB\fB%u\fR\fR"
-When the input key is an address of the form user@domain,
-\fB%u\fR is replaced by the quoted local part of the address.
-If no domain is specified, \fB%u\fR is replaced by the entire
-search string.
+When the input key is an address of the form user@domain, \fB%u\fR
+is replaced by the SQL quoted local part of the address.
+Otherwise, \fB%u\fR is replaced by the entire search string.
+If the localpart is empty, the query is suppressed and returns
+no results.
+.IP "\fB\fB%d\fR\fR"
+When the input key is an address of the form user@domain, \fB%d\fR
+is replaced by the SQL quoted domain part of the address.
+Otherwise, the query is suppressed and returns no results.
+.IP "\fB\fB%[SUD]\fR\fR"
+The upper-case equivalents of the above expansions behave in the
+\fBquery\fR parameter identically to their lower-case counter-parts.
+With the \fBresult_format\fR parameter (see below), they expand the
+input key rather than the result value.
+.IP
+The above %S, %U and %D expansions are available with Postfix 2.2
+and later
+.IP "\fB\fB%[1-9]\fR\fR"
+The patterns %1, %2, ... %9 are replaced by the corresponding
+most significant component of the input key's domain. If the
+input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
+%2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
+unqualified or does not have enough domain components to satisfy
+all the specified patterns, the query is suppressed and returns
+no results.
+.IP
+The above %1, ... %9 expansions are available with Postfix 2.2
+and later
+.RE
+.IP
+The \fBdomain\fR parameter described below limits the input
+keys to addresses in matching domains. When the \fBdomain\fR
+parameter is non-empty, SQL queries for unqualified addresses
+or addresses in non-matching domains are suppressed
+and return no results.
+
+The precedence of this parameter has changed with Postfix 2.2,
+in prior releases the precedence was, from highest to lowest,
+\fBselect_function\fR, \fBquery\fR, \fBselect_field\fR, ...
+
+With Postfix 2.2 the \fBquery\fR parameter has highest precedence,
+see COMPATIBILITY above.
+
+NOTE: DO NOT put quotes around the \fBquery\fR parameter.
+.IP "\fBresult_format (default: \fB%s\fR)\fR"
+Format template applied to result attributes. Most commonly used
+to append (or prepend) text to the result. This parameter supports
+the following '%' expansions:
+.RS
+.IP "\fB\fB%%\fR\fR"
+This is replaced by a literal '%' character.
+.IP "\fB\fB%s\fR\fR"
+This is replaced by the value of the result attribute. When
+result is empty it is skipped.
+.IP "\fB%u\fR
+When the result attribute value is an address of the form
+user@domain, \fB%u\fR is replaced by the local part of the
+address. When the result has an empty localpart it is skipped.
 .IP "\fB\fB%d\fR\fR"
-When the input key is an address of the form user@domain,
-\fB%d\fR is replaced by the quoted domain part of the address.
-When the input key has no domain qualifier, \fB%d\fR is replaced
-by the entire search string.
+When a result attribute value is an address of the form
+user@domain, \fB%d\fR is replaced by the domain part of
+the attribute value. When the result is unqualified it
+is skipped.
+.IP "\fB\fB%[SUD1-9]\fR\fB"
+The upper-case and decimal digit expansions interpolate
+the parts of the input key rather than the result. Their
+behaviour is identical to that described with \fBquery\fR,
+and in fact because the input key is known in advance, queries
+whose key does not contain all the information specified in
+the result template are suppressed and return no results.
 .RE
+.IP
+For example, using "result_format = smtp:[%s]" allows one
+to use a mailHost attribute as the basis of a transport(5)
+table. After applying the result format, multiple values
+are concatenated as comma separated strings. The expansion_limit
+and parameter explained below allows one to restrict the number
+of values in the result, which is especially useful for maps that
+must return at most one value.
+
+The default value \fB%s\fR specifies that each result value should
+be used as is.
+
+This parameter is available with Postfix 2.2 and later.
+
+NOTE: DO NOT put quotes around the result format!
+.IP "\fBdomain (default: no domain list)\fR"
+This is a list of domain names, paths to files, or
+dictionaries. When specified, only fully qualified search
+keys with a *non-empty* localpart and a matching domain
+are eligible for lookup: 'user' lookups, bare domain lookups
+and "@domain" lookups are not performed. This can significantly
+reduce the query load on the PostgreSQL server.
+.ti +4
+domain = postfix.org, hash:/etc/postfix/searchdomains
+
+It is best not to use SQL to store the domains eligible
+for SQL lookups.
+
+This parameter is available with Postfix 2.2 and later.
+
+NOTE: DO NOT define this parameter for local(8) aliases,
+because the input keys are always unqualified.
+.IP "\fBexpansion_limit (default: 0)\fR"
+A limit on the total number of result elements returned
+(as a comma separated list) by a lookup against the map.
+A setting of zero disables the limit. Lookups fail with a
+temporary error if the limit is exceeded.  Setting the
+limit to 1 ensures that lookups do not return multiple
+values.
+.PP
+Pre-Postfix 2.2 legacy interfaces:
 .IP "\fBselect_function\fR"
 This parameter specifies a database function name. Example:
 .ti +4
@@ -164,16 +268,54 @@ select_function = my_lookup_user_alias
 
 This is equivalent to:
 .ti +4
-query = select my_lookup_user_alias('%s')
+query = SELECT my_lookup_user_alias('%s')
+
+This parameter overrides the legacy table-related fields (described
+below). With Postfix versions prior to 2.2, it also overrides the
+\fBquery\fR parameter. Starting with Postfix 2.2, the \fBquery\fR
+parameter has highest precedence, and this parameter is deprecated.
+Please migrate to the new \fBquery\fR interface as this interface
+is slated to be phased out.
+.PP
+The following parameters (with lower precedence than the
+\fBselect_function\fR interface described above) can be used to
+build the SQL select statement as follows:
 
-and overrides both the \fBquery\fR parameter and the table-related
-fields above.
+.ti +4
+SELECT [\fBselect_field\fR]
+.ti +4
+FROM [\fBtable\fR]
+.ti +4
+WHERE [\fBwhere_field\fR] = '%s'
+.ti +10
+[\fBadditional_conditions\fR]
 
-As of June 2002, if the function returns a single row and
-a single column AND that value is NULL, then the result
-will be treated as if the key was not in the dictionary.
+The specifier %s is replaced with each lookup by the lookup key
+and is escaped so if it contains single quotes or other odd
+characters, it will not cause a parse error, or worse, a security
+problem.
 
-Future versions will allow functions to return result sets.
+Starting with Postfix 2.2, this interface is obsoleted by the more
+general \fBquery\fR interface described above. If higher precedence
+the \fBquery\fR or \fBselect_function\fR parameters described above
+are defined, these parameters are ignored. Please migrate to the new
+\fBquery\fR interface as this interface is slated to be phased out.
+.IP "\fBselect_field\fR"
+The SQL "select" parameter. Example:
+.ti +4
+\fBselect_field\fR = forw_addr
+.IP "\fBtable\fR"
+The SQL "select .. from" table name. Example:
+.ti +4
+\fBtable\fR = mxaliases
+.IP "\fBwhere_field\fR
+The SQL "select .. where" parameter. Example:
+.ti +4
+\fBwhere_field\fR = alias
+.IP "\fBadditional_conditions\fR
+Additional conditions to the SQL query. Example:
+.ti +4
+\fBadditional_conditions\fR = AND status = 'paid'
 .SH "SEE ALSO"
 .na
 .nf
index 4cf5ee78a42bb29f29a1a8c59c422cd669c8e047..3e60aa7fc826d33ee68f1d583563b6198d835b38 100644 (file)
@@ -306,6 +306,20 @@ difficult to enforce consistently and globally.
 Enable the rewriting of the form "user%domain" to "user@domain".
 This is enabled by default.
 .PP
+Note: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true:
+.IP \(bu
+The message is received with the Postfix \fBsendmail\fR(1) command,
+.IP \(bu
+The message is received from a network client that matches
+$local_header_rewrite_clients,
+.IP \(bu
+The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+.PP
+To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".
+.PP
 Example:
 .PP
 .nf
@@ -371,17 +385,45 @@ With locally submitted mail, append the string "@$myorigin" to mail
 addresses without domain information. With remotely submitted mail,
 append the string "@$remote_header_rewrite_domain" instead.
 .PP
-This feature is enabled by default and must not be turned off.
+Note 1: This feature is enabled by default and must not be turned off.
 Postfix does not support domain-less addresses.
+.PP
+Note 2: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true:
+.IP \(bu
+The message is received with the Postfix \fBsendmail\fR(1) command,
+.IP \(bu
+The message is received from a network client that matches
+$local_header_rewrite_clients,
+.IP \(bu
+The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+.PP
+To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".
 .SH append_dot_mydomain (default: yes)
 With locally submitted mail, append the string ".$mydomain" to
 addresses that have no ".domain" information. With remotely submitted
 mail, append the string ".$remote_header_rewrite_domain"
 instead.
 .PP
-This feature is enabled by default. If disabled, users will not be
+Note 1: This feature is enabled by default. If disabled, users will not be
 able to send mail to "user@partialdomainname" but will have to
 specify full domain names instead.
+.PP
+Note 2: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true:
+.IP \(bu
+The message is received with the Postfix \fBsendmail\fR(1) command,
+.IP \(bu
+The message is received from a network client that matches
+$local_header_rewrite_clients,
+.IP \(bu
+The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+.PP
+To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".
 .SH application_event_drain_time (default: 100s)
 How long the \fBpostkick\fR(1) command waits for a request to enter the
 server's input buffer before giving up.
@@ -590,6 +632,20 @@ build the necessary DBM or DB file after every change. The changes
 will become visible after a minute or so.  Use "\fBpostfix reload\fR"
 to eliminate the delay.
 .PP
+Note: with Postfix version 2.2, message header address mapping
+happens only when message header address rewriting is enabled:
+.IP \(bu
+The message is received with the Postfix \fBsendmail\fR(1) command,
+.IP \(bu
+The message is received from a network client that matches
+$local_header_rewrite_clients,
+.IP \(bu
+The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+.PP
+To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".
+.PP
 Examples:
 .PP
 .nf
@@ -1382,7 +1438,8 @@ inet_interfaces = 192.168.1.2, 127.0.0.1
 The Internet protocols Postfix will attempt to use when making
 or accepting connections. Specify one or more of "ipv4"
 or "ipv6", separated by whitespace or commas. The form
-"all" is equivalent to "ipv4, ipv6".
+"all" is equivalent to "ipv4, ipv6" or "ipv4", depending
+on whether the operating system implements IPv6.
 .PP
 This feature is available in Postfix version 2.2 and later.
 .PP
@@ -2077,6 +2134,20 @@ or its subdomains. Thus,
 does not change "user@any.thing.foo.example.com" or "user@foo.example.com",
 but strips "user@any.thing.else.example.com" to "user@example.com".
 .PP
+Note: with Postfix version 2.2, message header address masquerading
+happens only when message header address rewriting is enabled:
+.IP \(bu
+The message is received with the Postfix \fBsendmail\fR(1) command,
+.IP \(bu
+The message is received from a network client that matches
+$local_header_rewrite_clients,
+.IP \(bu
+The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+.PP
+To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".
+.PP
 Example:
 .PP
 .nf
@@ -5205,6 +5276,20 @@ Enable the rewriting of "site!user" into "user@site".  This is
 necessary if your machine is connected to UUCP networks.  It is
 enabled by default.
 .PP
+Note: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true:
+.IP \(bu
+The message is received with the Postfix \fBsendmail\fR(1) command,
+.IP \(bu
+The message is received from a network client that matches
+$local_header_rewrite_clients,
+.IP \(bu
+The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+.PP
+To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".
+.PP
 Example:
 .PP
 .nf
index b681db6243accd758382b68651f247f79a703229..a8ac3d1bd148cd45105916865170e410cb453558 100644 (file)
@@ -317,15 +317,18 @@ turn-off control </th> </tr>
 <tr> <td> <a href="#standard"> Rewrite addresses to standard form</a>
 </td> <td nowrap> all mail </td> <td> trivial-<br>rewrite(8) </td>
 <td> append_at_myorigin, append_dot_mydomain, swap_bangpath,
-allow_percent_hack </td> <td> none </td> </tr>
+allow_percent_hack </td> <td> local_header_rewrite_clients,
+remote_header_rewrite_domain </td> </tr>
 
-<tr> <td> <a href="#canonical"> Canonical address mapping </a>
-</td> <td nowrap> all mail </td> <td> cleanup(8) </td> <td>
-canonical_maps </td> <td> receive_override_options </td> </tr>
+<tr> <td> <a href="#canonical"> Canonical address mapping </a> </td>
+<td nowrap> all mail </td> <td> cleanup(8) </td> <td> canonical_maps
+</td> <td> receive_override_options, local_header_rewrite_clients,
+remote_header_rewrite_domain </td> </tr>
 
-<tr> <td> <a href="#canonical"> Address masquerading </a> </td>
-<td nowrap> all mail </td> <td> cleanup(8) </td> <td> masquerade_domains
-</td> <td> receive_override_options </td> </tr>
+<tr> <td> <a href="#masquerade"> Address masquerading </a> </td> <td
+nowrap> all mail </td> <td> cleanup(8) </td> <td> masquerade_domains
+</td> <td> receive_override_options, local_header_rewrite_clients,
+remote_header_rewrite_domain </td> </tr>
 
 <tr> <td> <a href="#auto_bcc"> Automatic BCC recipients </a> </td>
 <td nowrap> new mail </td> <td> cleanup(8) </td> <td> always_bcc,
@@ -411,11 +414,12 @@ trivial-rewrite(8) daemon.  The purpose of rewriting to standard
 form is to reduce the number of entries needed in lookup tables.
 </p>
 
-<p> Postfix versions 2.2 and later do not rewrite message headers
-from remote SMTP clients at all, unless a non-empty domain name is
-specified with the remote_header_rewrite_domain configuration
-parameter.  The local_header_rewrite_clients parameter controls
-what SMTP clients Postfix considers local. </p>
+<p> NOTE: Postfix versions 2.2 and later rewrite message headers
+from remote SMTP clients only if the client matches the
+local_header_rewrite_clients parameter, or if the
+remote_header_rewrite_domain configuration parameter specifies a
+non-empty value. To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".  </p>
 
 <p> The Postfix trivial-rewrite(8) daemon implements the following
 hard-coded address manipulations: </p>
@@ -455,10 +459,13 @@ parameter (default: yes).  You should never turn off this feature,
 because a lot of Postfix components expect that all addresses have
 the form "user@domain".  </p>
 
-<p> Postfix versions 2.2 and later either do not rewrite message
-headers from remote SMTP clients at all, or they append the domain
-name specified with the remote_header_rewrite_domain configuration
-parameter.  </p>
+<p> NOTE: Postfix versions 2.2 and later rewrite message headers
+from remote SMTP clients only if the client matches the
+local_header_rewrite_clients parameter; otherwise they append the
+domain name specified with the remote_header_rewrite_domain
+configuration parameter, if one is specified. To get the behavior
+before Postfix 2.2, specify "local_header_rewrite_clients =
+static:all". </p>
 
 <p> If your machine is not the main machine for $myorigin and you
 wish to have some users delivered locally without going via that
@@ -476,10 +483,13 @@ Rewrite "user@host" to "user@host.$mydomain" </dt>
 parameter (default: yes).  The purpose is to get consistent treatment
 of different forms of the same hostname. </p>
 
-<p> Postfix versions 2.2 and later either do not rewrite message
-headers from remote clients at all, or they append the domain name
-specified with the remote_header_rewrite_domain configuration
-parameter. </p>
+<p> NOTE: Postfix versions 2.2 and later rewrite message headers
+from remote SMTP clients only if the client matches the
+local_header_rewrite_clients parameter; otherwise they append the
+domain name specified with the remote_header_rewrite_domain
+configuration parameter, if one is specified. To get the behavior
+before Postfix 2.2, specify "local_header_rewrite_clients =
+static:all". </p>
 
 <p> Some will argue that rewriting "host" to "host.domain"
 is bad. That is why it can be turned off. Others like the convenience
@@ -502,11 +512,12 @@ addresses in message envelopes and in message headers.  By default
 all header and envelope addresses are rewritten; this is controlled
 with the canonical_classes configuration parameter.  </p>
 
-<p> Postfix versions 2.2 and later do not rewrite message headers
-from remote clients at all, unless a non-empty domain name is
-specified with the remote_header_rewrite_domain configuration
-parameter.  The local_header_rewrite_clients parameter controls
-what SMTP clients Postfix considers local. </p>
+<p> NOTE: Postfix versions 2.2 and later rewrite message headers
+from remote SMTP clients only if the client matches the
+local_header_rewrite_clients parameter, or if the
+remote_header_rewrite_domain configuration parameter specifies a
+non-empty value. To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".  </p>
 
 <p> Address rewriting is 
 done for local and remote addresses.  The mapping is useful to
@@ -585,11 +596,12 @@ behind their mail gateway, and to make it appear as if the mail
 comes from the gateway itself, instead of from individual machines.
 </p>
 
-<p> Postfix versions 2.2 and later do not rewrite message headers
-from remote SMTP clients at all, unless a non-empty domain name is
-specified with the remote_header_rewrite_domain configuration
-parameter.  The local_header_rewrite_clients parameter controls
-what SMTP clients Postfix considers local. </p>
+<p> NOTE: Postfix versions 2.2 and later rewrite message headers
+from remote SMTP clients only if the client matches the
+local_header_rewrite_clients parameter, or if the
+remote_header_rewrite_domain configuration parameter specifies a
+non-empty value. To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all".  </p>
 
 <p> Address masquerading is disabled by default, and is implemented
 by the cleanup(8) server. To enable, edit the masquerade_domains
index 740989067900bcbba37a706ad11f87902621ce8c..f9573a897cad1c002e35bd96a0e5a1286e601cb5 100644 (file)
@@ -257,7 +257,7 @@ maildrop: this, that, theother
   make sure the lookup makes sense. In the case of virtual lookups,
   maildrops other than mail addresses are pretty useless, because
   Postfix can't know how to set the ownership for program or file
-  delivery. Your query_filter should probably look something like this: </p>
+  delivery. Your <b>query_filter</b> should probably look something like this: </p>
 
 <blockquote>
 <pre>
@@ -276,7 +276,7 @@ query_filter = (&amp;(mailacceptinggeneralid=%s)(!(|(maildrop="*|*")(maildrop="*
   require some thought on your part to implement safely, considering the
   ramifications of this type of delivery. You may decide it's not worth
   the bother to allow any of that nonsense in LDAP lookups, ban it in
-  the query_filter, and keep things like majordomo lists in local alias
+  the <b>query_filter</b>, and keep things like majordomo lists in local alias
   databases. </p>
 
 <blockquote>
@@ -334,13 +334,20 @@ contents, please include the applicable bits of some directory entries. </p>
 
 <li>Victor Duchovni: ldap_bind() timeout. With fixes from LaMont Jones:
                  OpenLDAP cache deprecation. Limits on recursion, expansion
-                 and query results size. LDAP connection sharing for maps
+                 and search results size. LDAP connection sharing for maps
                  differing only in the query parameters.
 
 <li>Liviu Daia: Support for SSL/STARTTLS. Support for storing map definitions in
             external files (ldap:/path/ldap.cf) needed to securely store
             passwords for plain auth.
 
+<li>Liviu Daia revised the configuration interface and added the main.cf
+    configuration feature.</li>
+    
+<li>Liviu Daia with further refinements from Jose Luis Tallon and
+Victor Duchovni developed the common query, result_format, domain and
+expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
+
 </ul>
 
 And of course Wietse.
index 0e3d3961dcf8475e9720cc31f235b9bea3b203fc..9672f5a9d4adbfbfc6e24d4e589994251c84c6e1 100644 (file)
@@ -89,22 +89,16 @@ password = some_password
 # The database name on the servers.
 dbname = customer_database
 
-# The table name.
-table = mxaliases
+# For Postfix 2.2 and later The SQL query template.
+# See mysql_table(5) for details.
+query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
 
-# Query components, see below.
+# For Postfix releases prior to 2.2. See mysql_table(5) for details.
 select_field = forw_addr
+table = mxaliases
 where_field = alias
-
-# You may specify additional_conditions or leave this empty.
-additional_conditions = and status = 'paid'
-
-# The above variables will result in a query of the form:
-#
-# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
-#
-# ($lookup is escaped so if it contains single quotes or other odd
-# characters, it will not cause trouble).
+# Don't forget the leading "AND"!
+additional_conditions = AND status = 'paid'
 </pre>
 
 <h2>Additional notes</h2>
@@ -129,10 +123,14 @@ will be deferred until at least one of those hosts is reachable.
 <ul>
 
 <li> The initial version was contributed by Scott Cotton and Joshua
-Marcus, IC Group, Inc.
+Marcus, IC Group, Inc.</li>
+
+<li> Liviu Daia revised the configuration interface and added the
+main.cf configuration feature.</li>
 
-<li>Liviu Daia revised the configuration interface and added the
-main.cf configuration feature.
+<li> Liviu Daia with further refinements from Jose Luis Tallon and
+Victor Duchovni developed the common query, result_format, domain and
+expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
 
 </ul>
 
index a819394dd248095d20693b5731dfc674d3ec9f14..167c45b9520ad35d9cc1f0bd7f8a961fa0b2d780 100644 (file)
@@ -88,25 +88,15 @@ password = some_password
 # The database name on the servers.
 dbname = customer_database
 
-# The table name.
-table = mxaliases
+# Postfix 2.2 and later The SQL query template. See pgsql_table(5).
+query = SELECT forw_addr FROM mxaliases WHERE alias='%s' AND status='paid'
 
-# Query components, see below.
+# For Postfix releases prior to 2.2. See pgsql_table(5) for details.
 select_field = forw_addr
+table = mxaliases
 where_field = alias
-
-# You may specify additional_conditions or leave this empty.
-additional_conditions = and status = 'paid'
-
-# The above variables will result in a query of the form:
-#
-# select forw_addr from mxaliases where alias = '$lookup' and status = 'paid'
-#
-# ($lookup is escaped so if it contains single quotes or other odd
-# characters, it will not cause problems).
-#
-# You may also override the built-in SELECT template. See pgsql_table(5)
-# for details.
+# Don't forget the leading "AND"!
+additional_conditions = AND status = 'paid'
 </pre>
 
 <h2>Using mirrored databases</h2>
@@ -130,17 +120,24 @@ those hosts is reachable. </p>
 <ul>
 
 <li> This code is based upon the Postfix mysql map by Scott Cotton
-and Joshua Marcus, IC Group, Inc.
+and Joshua Marcus, IC Group, Inc.</li>
 
-<li> The PostgreSQL changes were done by Aaron Sethman.
+<li> The PostgreSQL changes were done by Aaron Sethman.</li>
 
 <li> Updates for Postfix 1.1.x and PostgreSQL 7.1+ and support for
-calling stored procedures were added by Philip Warner.
+calling stored procedures were added by Philip Warner.</li>
 
-<li> LaMont Jones was the initial Postfix pgsql maintainer.
+<li> LaMont Jones was the initial Postfix pgsql maintainer.</li>
 
 <li> Liviu Daia revised the configuration interface and added the
-main.cf configuration feature.
+main.cf configuration feature.</li>
+
+<li> Liviu Daia revised the configuration interface and added the main.cf
+configuration feature.</li>
+
+<li> Liviu Daia with further refinements from Jose Luis Tallon and
+Victor Duchovni developed the common query, result_format, domain and
+expansion_limit interface for LDAP, MySQL and PosgreSQL.</li>
 
 </ul>
 
index 3c840c309e34f738b5a014256cde4235ad2c8f62..c3a0fdf13886a539c95d592fd242648fe6e36ece 100644 (file)
 #      Note: with this form, the passwords for the LDAP sources are
 #      written in main.cf, which is normally world-readable.  Support
 #      for this form will be removed in a future Postfix version.
+#
+#      Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL,
+#      these now include features previously available only in the
+#      Postfix LDAP client. This work also created an opportunity for
+#      improvements in the LDAP interface. The primary compatibility
+#      issue is that \fBresult_filter\fR (a name that has caused some
+#      confusion as to its meaning in the past) has been renamed to
+#      \fBresult_format\fR.  For backwards compatibility with the pre
+#      2.2 LDAP client, \fBresult_filter\fR can for now be used instead
+#      of \fBresult_format\fR, when the latter parameter is not also set.
+#      The new name better reflects the function of the parameter. This
+#      compatibility interface may be removed in a future release.
 # LIST MEMBERSHIP
 # .ad
 # .fi
 #      The port the LDAP server listens on, e.g.
 # .ti +4
 #      server_port = 778
-# .IP "\fBsearch_base (No default; you must configure this)\fR"
-#      The RFC2253 base DN at which to conduct the search, e.g.
-# .ti +4
-#      search_base = dc=your, dc=com
 # .IP "\fBtimeout (default: 10 seconds)\fR"
 #      The number of seconds a search can take before timing out, e.g.
 # .ti +4
 #      timeout = 5
+# .IP "\fBsearch_base (No default; you must configure this)\fR"
+#      The RFC2253 base DN at which to conduct the search, e.g.
+# .ti +4
+#      search_base = dc=your, dc=com
+# .IP
+#      With Postfix 2.2 and later this parameter supports the
+#      following '%' expansions:
+# .RS
+# .IP "\fB\fB%%\fR\fR"
+#      This is replaced by a literal '%' character.
+# .IP "\fB\fB%s\fR\fR"
+#      This is replaced by the input key.
+#      RFC 2253 quoting is used to make sure that the input key
+#      does not add unexpected metacharacters.
+# .IP "\fB\fB%u\fR\fR"
+#      When the input key is an address of the form user@domain, \fB%u\fR
+#      is replaced by the (RFC 2253) quoted local part of the address.
+#      Otherwise, \fB%u\fR is replaced by the entire search string.
+#      If the localpart is empty, the search is suppressed and returns
+#      no results.
+# .IP "\fB\fB%d\fR\fR"
+#      When the input key is an address of the form user@domain, \fB%d\fR
+#      is replaced by the (RFC 2253) quoted domain part of the address.
+#      Otherwise, the search is suppressed and returns no results.
+# .IP "\fB\fB%[SUD]\fR\fR"
+#      For the \fBsearch_base\fR parameter, the upper-case equivalents
+#      of the above expansions behave identically to their lower-case
+#      counter-parts. With the \fBresult_format\fR parameter (previously
+#      called \fBresult_filter\fR see the COMPATIBILITY section and below),
+#      they expand to the corresponding components of input key rather
+#      than the result value.
+# .IP "\fB\fB%[1-9]\fR\fR"
+#      The patterns %1, %2, ... %9 are replaced by the corresponding
+#      most significant component of the input key's domain. If the
+#      input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
+#      %2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
+#      unqualified or does not have enough domain components to satisfy
+#      all the specified patterns, the search is suppressed and returns
+#      no results.
+# .RE
 # .IP "\fBquery_filter (default: mailacceptinggeneralid=%s)\fR"
 #      The RFC2254 filter used to search the directory, where \fB%s\fR
 #      is a substitute for the address Postfix is trying to resolve,
 #
 #      This parameter supports the following '%' expansions:
 # .RS
+# .IP "\fB\fB%%\fR\fR"
+#      This is replaced by a literal '%' character. (Postfix 2.2 and later).
 # .IP "\fB\fB%s\fR\fR"
-#      This is replaced by the input key. RFC 2254 quoting is used
-#      to make sure that the input key does not add unexpected
-#      metacharacters.
+#      This is replaced by the input key.
+#      RFC 2254 quoting is used to make sure that the input key
+#      does not add unexpected metacharacters.
 # .IP "\fB\fB%u\fR\fR"
-#      When the input key is an address of the form user@domain,
-#      \fB%u\fR is replaced by the (RFC 2254) quoted local part of the
-#      address. Otherwise, \fB%u\fR is replaced by the entire
-#      search string.
+#      When the input key is an address of the form user@domain, \fB%u\fR
+#      is replaced by the (RFC 2254) quoted local part of the address.
+#      Otherwise, \fB%u\fR is replaced by the entire search string.
+#      If the localpart is empty, the search is suppressed and returns
+#      no results.
 # .IP "\fB\fB%d\fR\fR"
-#      When the input key is an address of the form user@domain,
-#      \fB%d\fR is replaced by the (RFC 2254) quoted domain part of the
-#      address. Otherwise, \fB%d\fR is replaced by the entire
-#      search string.
+#      When the input key is an address of the form user@domain, \fB%d\fR
+#      is replaced by the (RFC 2254) quoted domain part of the address.
+#      Otherwise, the search is suppressed and returns no results.
+# .IP "\fB\fB%[SUD]\fR\fR"
+#      The upper-case equivalents of the above expansions behave in the
+#      \fBquery_filter\fR parameter identically to their lower-case
+#      counter-parts. With the \fBresult_format\fR parameter (previously
+#      called \fBresult_filter\fR see the COMPATIBILITY section and below),
+#      they expand to the corresponding components of input key rather
+#      than the result value.
+# .IP
+#      The above %S, %U and %D expansions are available with Postfix 2.2
+#      and later.
+# .IP "\fB\fB%[1-9]\fR\fR"
+#      The patterns %1, %2, ... %9 are replaced by the corresponding
+#      most significant component of the input key's domain. If the
+#      input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
+#      %2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
+#      unqualified or does not have enough domain components to satisfy
+#      all the specified patterns, the saerch is suppressed and returns
+#      no results.
+# .IP
+#      The above %1, ..., %9 expansions are available with Postfix 2.2
+#      and later.
 # .RE
 # .IP
 #      The "domain" parameter described below limits the input
 #      addresses or addresses in non-matching domains are suppressed
 #      and return no results.
 #
-#      NOTE: DO NOT put quotes around the query filter.
-# .IP "\fBresult_filter (default: \fB%s\fR)\fR"
-#      Format template applied to result attributes. Supports the
-#      same expansions as the query_filter, and can be easily used
-#      to append (or prepend) text.  This parameter supports the
-#      following '%' expansions:
+#      NOTE: DO NOT put quotes around the \fBquery_filter\fR parameter.
+# .IP "\fBresult_format (default: \fB%s\fR)\fR"
+#      Called \fBresult_filter\fR in Postfix releases prior to 2.2.
+#      Format template applied to result attributes. Most commonly used
+#      to append (or prepend) text to the result. This parameter supports
+#      the following '%' expansions:
 # .RS
+# .IP "\fB\fB%%\fR\fR"
+#      This is replaced by a literal '%' character. (Postfix 2.2 and later).
 # .IP "\fB\fB%s\fR\fR"
-#      This is replaced by the value of the result attribute.
+#      This is replaced by the value of the result attribute. When
+#      result is empty it is skipped.
 # .IP "\fB%u\fR
 #      When the result attribute value is an address of the form
 #      user@domain, \fB%u\fR is replaced by the local part of the
-#      address.  Otherwise, \fB%u\fR is replaced by the entire
-#      attribute value.
+#      address. When the result has an empty localpart it is skipped.
 # .IP "\fB\fB%d\fR\fR"
 #      When a result attribute value is an address of the form
 #      user@domain, \fB%d\fR is replaced by the domain part of
-#      the attribute value.  Otherwise, \fB%d\fR is replaced by
-#      the entire attribute value.
+#      the attribute value. When the result is unqualified it
+#      is skipped.
+# .IP "\fB\fB%[SUD1-9]\fR\fB"
+#      The upper-case and decimal digit expansions interpolate
+#      the parts of the input key rather than the result. Their
+#      behaviour is identical to that described with \fBquery_filter\fR,
+#      and in fact because the input key is known in advance, lookups
+#      whose key does not contain all the information specified in
+#      the result template are suppressed and return no results.
+# .IP
+#      The above %S, %U, %D and %1, ..., %9 expansions are available with
+#      Postfix 2.2 and later.
 # .RE
 # .IP
-#      For example, using "result_filter = smtp:[%s]" allows one
+#      For example, using "result_format = smtp:[%s]" allows one
 #      to use a mailHost attribute as the basis of a transport(5)
-#      table. After applying the result filter, multiple values
+#      table. After applying the result format, multiple values
 #      are concatenated as comma separated strings. The expansion_limit
 #      and size_limit parameters explained below allow one to
 #      restrict the number of values in the result, which is
 #      The default value \fB%s\fR specifies that each
 #      attribute value should be used as is.
 #
-#      NOTE: DO NOT put quotes around the result filter!
+#      This parameter was called \fBresult_filter\fR in Postfix
+#      releases prior to 2.2. If no "result_format" is specified,
+#      the value of "result_filter" will be used instead before
+#      resorting to the default value. This provides compatibility
+#      with old configuration files.
+#
+#      NOTE: DO NOT put quotes around the result format!
 # .IP "\fBdomain (default: no domain list)\fR"
 #      This is a list of domain names, paths to files, or
 #      dictionaries. When specified, only fully qualified search
 #      entries returned by the lookup, to be resolved to an email
 #      address.
 # .ti +4
-#      result_attribute = mailbox,maildrop
+#      result_attribute = mailbox, maildrop
 # .IP "\fBspecial_result_attribute (No default)\fR"
 #      The attribute(s) of directory entries that can contain DNs
 #      or URLs. If found, a recursive subsequent search is done
 #      values.
 # .IP "\fBsize_limit (default: $expansion_limit)\fR"
 #      A limit on the number of LDAP entries returned by any single
-#      LDAP query performed as part of the lookup. A setting of
+#      LDAP search performed as part of the lookup. A setting of
 #      0 disables the limit.  Expansion of DN and URL references
 #      involves nested LDAP queries, each of which is separately
 #      subjected to this limit.
 #
 #      Note: even a single LDAP entry can generate multiple lookup
 #      results, via multiple result attributes and/or multi-valued
-#      result attributes. This limit caps the per query resource
+#      result attributes. This limit caps the per search resource
 #      utilization on the LDAP server, not the final multiplicity
 #      of the lookup result. It is analogous to the "-z" option
 #      of "ldapsearch".
index c7ae4764bb945094a87b49f50628c18407edd08c..f3b3837ad26fb2909ad14c3053d1022707000621 100644 (file)
@@ -21,7 +21,7 @@
 #      The file /etc/postfix/mysql-aliases.cf has the same format as
 #      the Postfix main.cf file, and can specify the parameters
 #      described below.
-# ALTERNATIVE CONFIGURATION
+# BACKWARDS COMPATIBILITY
 # .ad
 # .fi
 #      For compatibility with other Postfix lookup tables, MySQL
 #      Note: with this form, the passwords for the MySQL sources are
 #      written in main.cf, which is normally world-readable.  Support
 #      for this form will be removed in a future Postfix version.
+#
+#      Postfix 2.2 has enhanced query interfaces for MySQL and PostreSQL,
+#      these include features previously available only in the Postfix
+#      LDAP client. In the new interface the SQL query is specified via
+#      a single \fBquery\fR parameter (described in more detail below).
+#      When the new \fBquery\fR parameter is not specified in the map
+#      definition, Postfix reverts to the old interface, with the SQL
+#      query constructed from the \fBselect_field\fR, \fBtable\fR,
+#      \fBwhere_field\fR and \fBadditional_conditions\fR parameters.
+#      The old interface will be gradually phased out. To migrate to
+#      the new interface set:
+#
+# .ti +4
+#      \fBquery\fR = SELECT [\fIselect_field\fR]
+# .ti +8
+#      FROM [\fItable\fR]
+# .ti +8
+#      WHERE [\fIwhere_field\fR] = '%s'
+# .ti +12
+#      [\fIadditional_conditions\fR]
+#
+#      Insert the value, not the name, of each legacy parameter. Note
+#      that the \fBadditional_conditions\fR parameter is optional
+#      and if not empty, will always start with \fBAND\fR.
 # LIST MEMBERSHIP
 # .ad
 # .fi
 #      The database name on the servers. Example:
 # .ti +4
 #      dbname = customer_database
+# .IP "\fBquery\fR"
+#      The SQL query template used to search the database, where \fB%s\fR
+#      is a substitute for the address Postfix is trying to resolve,
+#      e.g.
+# .ti +4
+#      query = SELECT replacement FROM aliases WHERE mailbox = '%s'
+#
+#      This parameter supports the following '%' expansions:
+# .RS
+# .IP "\fB\fB%%\fR\fR"
+#      This is replaced by a literal '%' character.
+# .IP "\fB\fB%s\fR\fR"
+#      This is replaced by the input key.
+#      SQL quoting is used to make sure that the input key does not
+#      add unexpected metacharacters.
+# .IP "\fB\fB%u\fR\fR"
+#      When the input key is an address of the form user@domain, \fB%u\fR
+#      is replaced by the SQL quoted local part of the address.
+#      Otherwise, \fB%u\fR is replaced by the entire search string.
+#      If the localpart is empty, the query is suppressed and returns
+#      no results.
+# .IP "\fB\fB%d\fR\fR"
+#      When the input key is an address of the form user@domain, \fB%d\fR
+#      is replaced by the SQL quoted domain part of the address.
+#      Otherwise, the query is suppressed and returns no results.
+# .IP "\fB\fB%[SUD]\fR\fR"
+#      The upper-case equivalents of the above expansions behave in the
+#      \fBquery\fR parameter identically to their lower-case counter-parts.
+#      With the \fBresult_format\fR parameter (see below), they expand the
+#      input key rather than the result value.
+# .IP "\fB\fB%[1-9]\fR\fR"
+#      The patterns %1, %2, ... %9 are replaced by the corresponding
+#      most significant component of the input key's domain. If the
+#      input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
+#      %2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
+#      unqualified or does not have enough domain components to satisfy
+#      all the specified patterns, the query is suppressed and returns
+#      no results.
+# .RE
+# .IP
+#      The \fBdomain\fR parameter described below limits the input
+#      keys to addresses in matching domains. When the \fBdomain\fR
+#      parameter is non-empty, SQL queries for unqualified addresses
+#      or addresses in non-matching domains are suppressed
+#      and return no results.
+#
+#      This parameter is available with Postfix 2.2. In prior releases
+#      the SQL query was built from the separate parameters:
+#      \fBselect_field\fR, \fBtable\fR, \fBwhere_field\fR and
+#      \fBadditional_conditions\fR. The mapping from the old parameters
+#      to the equivalent query is:
+#
+# .ti +4
+#      SELECT [\fBselect_field\fR]
+# .ti +4
+#      FROM [\fBtable\fR]
+# .ti +4
+#      WHERE [\fBwhere_field\fR] = '%s'
+# .ti +10
+#      [\fBadditional_conditions\fR]
+#
+#      The '%s' in the \fBWHERE\fR clause expands to the escaped search string.
+#      With Postfix 2.2 these legacy parameters are used if the \fBquery\fR
+#      parameter is not specified.
+#
+#      NOTE: DO NOT put quotes around the query parameter.
+# .IP "\fBresult_format (default: \fB%s\fR)\fR"
+#      Format template applied to result attributes. Most commonly used
+#      to append (or prepend) text to the result. This parameter supports
+#      the following '%' expansions:
+# .RS
+# .IP "\fB\fB%%\fR\fR"
+#      This is replaced by a literal '%' character.
+# .IP "\fB\fB%s\fR\fR"
+#      This is replaced by the value of the result attribute. When
+#      result is empty it is skipped.
+# .IP "\fB%u\fR
+#      When the result attribute value is an address of the form
+#      user@domain, \fB%u\fR is replaced by the local part of the
+#      address. When the result has an empty localpart it is skipped.
+# .IP "\fB\fB%d\fR\fR"
+#      When a result attribute value is an address of the form
+#      user@domain, \fB%d\fR is replaced by the domain part of
+#      the attribute value. When the result is unqualified it
+#      is skipped.
+# .IP "\fB\fB%[SUD1-9]\fR\fB"
+#      The upper-case and decimal digit expansions interpolate
+#      the parts of the input key rather than the result. Their
+#      behaviour is identical to that described with \fBquery\fR,
+#      and in fact because the input key is known in advance, queries
+#      whose key does not contain all the information specified in
+#      the result template are suppressed and return no results.
+# .RE
+# .IP
+#      For example, using "result_format = smtp:[%s]" allows one
+#      to use a mailHost attribute as the basis of a transport(5)
+#      table. After applying the result format, multiple values
+#      are concatenated as comma separated strings. The expansion_limit
+#      and parameter explained below allows one to restrict the number
+#      of values in the result, which is especially useful for maps that
+#      must return at most one value.
+#
+#      The default value \fB%s\fR specifies that each result value should
+#      be used as is.
+#
+#      This parameter is available with Postfix 2.2 and later.
+#
+#      NOTE: DO NOT put quotes around the result format!
+# .IP "\fBdomain (default: no domain list)\fR"
+#      This is a list of domain names, paths to files, or
+#      dictionaries. When specified, only fully qualified search
+#      keys with a *non-empty* localpart and a matching domain
+#      are eligible for lookup: 'user' lookups, bare domain lookups
+#      and "@domain" lookups are not performed. This can significantly
+#      reduce the query load on the MySQL server.
+# .ti +4
+#      domain = postfix.org, hash:/etc/postfix/searchdomains
+#
+#      It is best not to use SQL to store the domains eligible
+#      for SQL lookups.
+#
+#      This parameter is available with Postfix 2.2 and later.
+#
+#      NOTE: DO NOT define this parameter for local(8) aliases,
+#      because the input keys are always unqualified.
+# .IP "\fBexpansion_limit (default: 0)\fR"
+#      A limit on the total number of result elements returned
+#      (as a comma separated list) by a lookup against the map.
+#      A setting of zero disables the limit. Lookups fail with a
+#      temporary error if the limit is exceeded.  Setting the
+#      limit to 1 ensures that lookups do not return multiple
+#      values.
 # .PP
-#      The following parameters are used to fill in a SELECT
-#      query template of the form:
+#      The following parameters can be used to fill in a
+#      SELECT template statement of the form:
+#
 # .ti +4
-#      select [\fBselect_field\fR] from [\fBtable\fR] where 
-# .ti +8
-#      [\fBwhere_field\fR] = '$lookup' [\fBadditional_conditions\fR]
+#      SELECT [\fBselect_field\fR]
+# .ti +4
+#      FROM [\fBtable\fR]
+# .ti +4
+#      WHERE [\fBwhere_field\fR] = '%s'
+# .ti +10
+#      [\fBadditional_conditions\fR]
+#
+#      The specifier %s is replaced by the search string, and is
+#      escaped so if it contains single quotes or other odd characters,
+#      it will not cause a parse error, or worse, a security problem.
 #
-#      $lookup contains the search string, and is escaped so if
-#      it contains single quotes or other odd characters, it will
-#      not cause a parse error, or worse, a security problem.
+#      As of Postfix 2.2 this interface is obsolete, it is replaced
+#      by the more general \fBquery\fR interface described above.
+#      If the \fBquery\fR parameter is defined, the legacy parameters
+#      are ignored. Please migrate to the new interface as the legacy
+#      interface may be removed in a future release.
 # .IP "\fBselect_field\fR"
 #      The SQL "select" parameter. Example:
 # .ti +4
-#      select_field = forw_addr
+#      \fBselect_field\fR = forw_addr
 # .IP "\fBtable\fR"
 #      The SQL "select .. from" table name. Example:
 # .ti +4
-#      table = mxaliases
+#      \fBtable\fR = mxaliases
 # .IP "\fBwhere_field\fR
 #      The SQL "select .. where" parameter. Example:
 # .ti +4
-#      where_field = alias
+#      \fBwhere_field\fR = alias
 # .IP "\fBadditional_conditions\fR
 #      Additional conditions to the SQL query. Example:
 # .ti +4
-#      additional_conditions = and status = 'paid'
+#      \fBadditional_conditions\fR = AND status = 'paid'
 # SEE ALSO
 #      postmap(1), Postfix lookup table maintenance
 #      postconf(5), configuration parameters
index 6b9702018161d236a5e08701e218f5f72e25a847..2a3be10c31c42473cc89fb5352d3a517c3913742 100644 (file)
@@ -21,7 +21,7 @@
 #      The file /etc/postfix/pgsql-aliases.cf has the same format as
 #      the Postfix main.cf file, and can specify the parameters
 #      described below.
-# ALTERNATIVE CONFIGURATION
+# BACKWARDS COMPATIBILITY
 # .ad
 # .fi
 #      For compatibility with other Postfix lookup tables, PostgreSQL
 #      are written in main.cf, which is normally world-readable.
 #      Support for this form will be removed in a future Postfix
 #      version.
+#
+#      Postfix 2.2 has enhanced query interfaces for MySQL and PostgreSQL,
+#      these include features previously available only in the Postfix
+#      LDAP client. In the new interface the SQL query is specified via
+#      a single \fBquery\fR parameter (described in more detail below).
+#      In Postfix 2.1 the parameter precedence was, from highest to lowest,
+#      \fBselect_function\fR, \fBquery\fR and finally \fBselect_field\fR, ...
+#
+#      With Postfix 2.2 the \fBquery\fR parameter has highest precedence,
+#      and is used in preference to the still supported, but slated to be
+#      phased out, \fBselect_function\fR, \fBselect_field\fR, \fBtable\fR,
+#      \fBwhere_field\fR and \fBadditional_conditions\fR parameters. To
+#      migrate to the new interface set:
+#
+# .ti +4
+#      \fBquery\fR = SELECT \fIselect_function\fR('%s')
+#
+#      or in the absense of \fBselection_function\fR, the lower precedence:
+#
+# .ti +4
+#      \fBquery\fR = SELECT \fIselect_field\fR
+# .ti +8
+#      FROM \fItable\fR
+# .ti +8
+#      WHERE \fIwhere_field\fR = '%s'
+# .ti +12
+#      \fIadditional_conditions\fR
+#
+#      Use the value, not the name, of each legacy parameter. Note
+#      that the \fBadditional_conditions\fR parameter is optional
+#      and if not empty, will always start with \fBAND\fR.
 # LIST MEMBERSHIP
 # .ad
 # .fi
 #      The database name on the servers. Example:
 # .ti +4
 #      dbname = customer_database
-# .PP
-#      The following parameters can be used to fill in a SELECT
-#      template statement of the form:
-# .ti +4
-#      select [\fBselect_field\fR] from [\fBtable\fR] where 
-# .ti +8
-#      [\fBwhere_field\fR] = '$lookup' [\fBadditional_conditions\fR]
-#
-#      $lookup contains the search string, and is escaped so if
-#      it contains single quotes or other odd characters, it will
-#      not cause a parse error, or worse, a security problem.
-# .IP "\fBselect_field\fR"
-#      The SQL "select" parameter. Example:
-# .ti +4
-#      select_field = forw_addr
-# .IP "\fBtable\fR"
-#      The SQL "select .. from" table name. Example:
-# .ti +4
-#      table = mxaliases
-# .IP "\fBwhere_field\fR
-#      The SQL "select .. where" parameter. Example:
-# .ti +4
-#      where_field = alias
-# .IP "\fBadditional_conditions\fR
-#      Additional conditions to the SQL query. Example:
-# .ti +4
-#      additional_conditions = and status = 'paid'
-# .PP
-#      The following parameters provide ways to override the default
-#      SELECT statement.  Setting them will instruct Postfix to ignore
-#      the above \fBtable\fR, \fBselect_field\fR, \fBwhere_field\fR and
-#      \fBadditional_conditions\fR parameters:
 # .IP "\fBquery\fR"
-#      This parameter specifies a complete SQL query. Example:
+#      The SQL query template used to search the database, where \fB%s\fR
+#      is a substitute for the address Postfix is trying to resolve,
+#      e.g.
 # .ti +4
-#      query = select forw_addr from mxaliases where 
-# .ti +8
-#      alias = '%s' and status = 'paid'
+#      query = SELECT replacement FROM aliases WHERE mailbox = '%s'
 #
 #      This parameter supports the following '%' expansions:
 # .RS
+# .IP "\fB\fB%%\fR\fR"
+#      This is replaced by a literal '%' character. (Postfix 2.2 and later)
 # .IP "\fB\fB%s\fR\fR"
-#      This is replaced by the input key. Quoting is used to make sure
-#      that the input key does not add unexpected metacharacters.
+#      This is replaced by the input key.
+#      SQL quoting is used to make sure that the input key does not
+#      add unexpected metacharacters.
 # .IP "\fB\fB%u\fR\fR"
-#      When the input key is an address of the form user@domain,
-#      \fB%u\fR is replaced by the quoted local part of the address.
-#      If no domain is specified, \fB%u\fR is replaced by the entire
-#      search string.
+#      When the input key is an address of the form user@domain, \fB%u\fR
+#      is replaced by the SQL quoted local part of the address.
+#      Otherwise, \fB%u\fR is replaced by the entire search string.
+#      If the localpart is empty, the query is suppressed and returns
+#      no results.
+# .IP "\fB\fB%d\fR\fR"
+#      When the input key is an address of the form user@domain, \fB%d\fR
+#      is replaced by the SQL quoted domain part of the address.
+#      Otherwise, the query is suppressed and returns no results.
+# .IP "\fB\fB%[SUD]\fR\fR"
+#      The upper-case equivalents of the above expansions behave in the
+#      \fBquery\fR parameter identically to their lower-case counter-parts.
+#      With the \fBresult_format\fR parameter (see below), they expand the
+#      input key rather than the result value.
+# .IP
+#      The above %S, %U and %D expansions are available with Postfix 2.2
+#      and later
+# .IP "\fB\fB%[1-9]\fR\fR"
+#      The patterns %1, %2, ... %9 are replaced by the corresponding
+#      most significant component of the input key's domain. If the
+#      input key is \fIuser@mail.example.com\fR, then %1 is \fBcom\fR,
+#      %2 is \fBexample\fR and %3 is \fBmail\fR. If the input key is
+#      unqualified or does not have enough domain components to satisfy
+#      all the specified patterns, the query is suppressed and returns
+#      no results.
+# .IP
+#      The above %1, ... %9 expansions are available with Postfix 2.2
+#      and later
+# .RE
+# .IP
+#      The \fBdomain\fR parameter described below limits the input
+#      keys to addresses in matching domains. When the \fBdomain\fR
+#      parameter is non-empty, SQL queries for unqualified addresses
+#      or addresses in non-matching domains are suppressed
+#      and return no results.
+#
+#      The precedence of this parameter has changed with Postfix 2.2,
+#      in prior releases the precedence was, from highest to lowest,
+#      \fBselect_function\fR, \fBquery\fR, \fBselect_field\fR, ...
+#
+#      With Postfix 2.2 the \fBquery\fR parameter has highest precedence,
+#      see COMPATIBILITY above.
+#
+#      NOTE: DO NOT put quotes around the \fBquery\fR parameter.
+# .IP "\fBresult_format (default: \fB%s\fR)\fR"
+#      Format template applied to result attributes. Most commonly used
+#      to append (or prepend) text to the result. This parameter supports
+#      the following '%' expansions:
+# .RS
+# .IP "\fB\fB%%\fR\fR"
+#      This is replaced by a literal '%' character.
+# .IP "\fB\fB%s\fR\fR"
+#      This is replaced by the value of the result attribute. When
+#      result is empty it is skipped.
+# .IP "\fB%u\fR
+#      When the result attribute value is an address of the form
+#      user@domain, \fB%u\fR is replaced by the local part of the
+#      address. When the result has an empty localpart it is skipped.
 # .IP "\fB\fB%d\fR\fR"
-#      When the input key is an address of the form user@domain,
-#      \fB%d\fR is replaced by the quoted domain part of the address.
-#      When the input key has no domain qualifier, \fB%d\fR is replaced
-#      by the entire search string.
+#      When a result attribute value is an address of the form
+#      user@domain, \fB%d\fR is replaced by the domain part of
+#      the attribute value. When the result is unqualified it
+#      is skipped.
+# .IP "\fB\fB%[SUD1-9]\fR\fB"
+#      The upper-case and decimal digit expansions interpolate
+#      the parts of the input key rather than the result. Their
+#      behaviour is identical to that described with \fBquery\fR,
+#      and in fact because the input key is known in advance, queries
+#      whose key does not contain all the information specified in
+#      the result template are suppressed and return no results.
 # .RE
+# .IP
+#      For example, using "result_format = smtp:[%s]" allows one
+#      to use a mailHost attribute as the basis of a transport(5)
+#      table. After applying the result format, multiple values
+#      are concatenated as comma separated strings. The expansion_limit
+#      and parameter explained below allows one to restrict the number
+#      of values in the result, which is especially useful for maps that
+#      must return at most one value.
+#
+#      The default value \fB%s\fR specifies that each result value should
+#      be used as is.
+#
+#      This parameter is available with Postfix 2.2 and later.
+#
+#      NOTE: DO NOT put quotes around the result format!
+# .IP "\fBdomain (default: no domain list)\fR"
+#      This is a list of domain names, paths to files, or
+#      dictionaries. When specified, only fully qualified search
+#      keys with a *non-empty* localpart and a matching domain
+#      are eligible for lookup: 'user' lookups, bare domain lookups
+#      and "@domain" lookups are not performed. This can significantly
+#      reduce the query load on the PostgreSQL server.
+# .ti +4
+#      domain = postfix.org, hash:/etc/postfix/searchdomains
+#
+#      It is best not to use SQL to store the domains eligible
+#      for SQL lookups.
+#
+#      This parameter is available with Postfix 2.2 and later.
+#
+#      NOTE: DO NOT define this parameter for local(8) aliases,
+#      because the input keys are always unqualified.
+# .IP "\fBexpansion_limit (default: 0)\fR"
+#     A limit on the total number of result elements returned
+#     (as a comma separated list) by a lookup against the map.
+#     A setting of zero disables the limit. Lookups fail with a
+#     temporary error if the limit is exceeded.  Setting the
+#     limit to 1 ensures that lookups do not return multiple
+#     values.
+# .PP
+#      Pre-Postfix 2.2 legacy interfaces:
 # .IP "\fBselect_function\fR"
 #      This parameter specifies a database function name. Example:
 # .ti +4
 #
 #      This is equivalent to:
 # .ti +4
-#      query = select my_lookup_user_alias('%s')
+#      query = SELECT my_lookup_user_alias('%s')
+#
+#      This parameter overrides the legacy table-related fields (described
+#      below). With Postfix versions prior to 2.2, it also overrides the
+#      \fBquery\fR parameter. Starting with Postfix 2.2, the \fBquery\fR
+#      parameter has highest precedence, and this parameter is deprecated.
+#      Please migrate to the new \fBquery\fR interface as this interface
+#      is slated to be phased out.
+# .PP
+#      The following parameters (with lower precedence than the
+#      \fBselect_function\fR interface described above) can be used to
+#      build the SQL select statement as follows:
 #
-#      and overrides both the \fBquery\fR parameter and the table-related
-#      fields above.
+# .ti +4
+#      SELECT [\fBselect_field\fR]
+# .ti +4
+#      FROM [\fBtable\fR]
+# .ti +4
+#      WHERE [\fBwhere_field\fR] = '%s'
+# .ti +10
+#      [\fBadditional_conditions\fR]
 #
-#      As of June 2002, if the function returns a single row and
-#      a single column AND that value is NULL, then the result
-#      will be treated as if the key was not in the dictionary.
+#      The specifier %s is replaced with each lookup by the lookup key
+#      and is escaped so if it contains single quotes or other odd
+#      characters, it will not cause a parse error, or worse, a security
+#      problem.
 #
-#      Future versions will allow functions to return result sets.
+#      Starting with Postfix 2.2, this interface is obsoleted by the more
+#      general \fBquery\fR interface described above. If higher precedence
+#      the \fBquery\fR or \fBselect_function\fR parameters described above
+#      are defined, these parameters are ignored. Please migrate to the new
+#      \fBquery\fR interface as this interface is slated to be phased out.
+# .IP "\fBselect_field\fR"
+#      The SQL "select" parameter. Example:
+# .ti +4
+#      \fBselect_field\fR = forw_addr
+# .IP "\fBtable\fR"
+#      The SQL "select .. from" table name. Example:
+# .ti +4
+#      \fBtable\fR = mxaliases
+# .IP "\fBwhere_field\fR
+#      The SQL "select .. where" parameter. Example:
+# .ti +4
+#      \fBwhere_field\fR = alias
+# .IP "\fBadditional_conditions\fR
+#      Additional conditions to the SQL query. Example:
+# .ti +4
+#      \fBadditional_conditions\fR = AND status = 'paid'
 # SEE ALSO
 #      postmap(1), Postfix lookup table manager
 #      postconf(5), configuration parameters
index 2dd4587ba828e0aac6ec81a31b17890db414d561..d97a5c478c20b323b94a123a2b7c9c437ac3e29b 100644 (file)
@@ -511,6 +511,24 @@ Enable the rewriting of the form "user%domain" to "user@domain".
 This is enabled by default.
 </p>
 
+<p> Note: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true: </p>
+<ul>
+<li> The message is received with the Postfix sendmail(1) command,
+<li> The message is received from a network client that matches
+$local_header_rewrite_clients,
+<li> The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all". </p>
+
 <p>
 Example:
 </p>
@@ -685,6 +703,24 @@ will become visible after a minute or so.  Use "<b>postfix reload</b>"
 to eliminate the delay.
 </p>
 
+<p> Note: with Postfix version 2.2, message header address mapping
+happens only when message header address rewriting is enabled: </p>
+
+<ul>
+
+<li> The message is received with the Postfix sendmail(1) command,
+
+<li> The message is received from a network client that matches
+$local_header_rewrite_clients,
+
+<li> The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all". </p>
+
 <p>
 Examples:
 </p>
@@ -1638,7 +1674,8 @@ inet_interfaces = 192.168.1.2, 127.0.0.1
 <p> The Internet protocols Postfix will attempt to use when making
 or accepting connections. Specify one or more of "ipv4"
 or "ipv6", separated by whitespace or commas. The form
-"all" is equivalent to "ipv4, ipv6". </p>
+"all" is equivalent to "ipv4, ipv6" or "ipv4", depending
+on whether the operating system implements IPv6. </p>
 
 <p> This feature is available in Postfix version 2.2 and later. </p>
 
@@ -2385,6 +2422,25 @@ does not change "user@any.thing.foo.example.com" or "user@foo.example.com",
 but strips "user@any.thing.else.example.com" to "user@example.com".
 </p>
 
+<p> Note: with Postfix version 2.2, message header address masquerading
+happens only when message header address rewriting is enabled: </p>
+
+<ul>
+
+<li> The message is received with the Postfix sendmail(1) command,
+
+<li> The message is received from a network client that matches
+$local_header_rewrite_clients,
+
+<li> The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all". </p>
+
+
 <p>
 Example:
 </p>
@@ -5478,6 +5534,24 @@ necessary if your machine is connected to UUCP networks.  It is
 enabled by default.
 </p>
 
+<p> Note: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true: </p>
+<ul>
+<li> The message is received with the Postfix sendmail(1) command,
+<li> The message is received from a network client that matches
+$local_header_rewrite_clients,
+<li> The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all". </p>
+
 <p>
 Example:
 </p>
@@ -5834,10 +5908,28 @@ append the string "@$remote_header_rewrite_domain" instead.
 </p>
 
 <p>
-This feature is enabled by default and must not be turned off.
+Note 1: This feature is enabled by default and must not be turned off.
 Postfix does not support domain-less addresses.
 </p>
 
+<p> Note 2: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true: </p>
+<ul>
+<li> The message is received with the Postfix sendmail(1) command,
+<li> The message is received from a network client that matches
+$local_header_rewrite_clients,
+<li> The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all". </p>
+
 %PARAM append_dot_mydomain yes
 
 <p>
@@ -5848,11 +5940,29 @@ instead.
 </p>
 
 <p>
-This feature is enabled by default. If disabled, users will not be
+Note 1: This feature is enabled by default. If disabled, users will not be
 able to send mail to "user@partialdomainname" but will have to
 specify full domain names instead.
 </p>
 
+<p> Note 2: With Postfix version 2.2, message header address rewriting
+happens only when one of the following conditions is true: </p>
+<ul>
+<li> The message is received with the Postfix sendmail(1) command,
+<li> The message is received from a network client that matches
+$local_header_rewrite_clients,
+<li> The message is received from the network, and the
+remote_header_rewrite_domain parameter specifies a non-empty value.
+</ul>
+
+<p> To get the behavior before Postfix 2.2, specify
+"local_header_rewrite_clients = static:all". </p>
+
 %PARAM application_event_drain_time 100s
 
 <p>
index 0e7ec68f94984e4d60749065d27de65e0f205bd0..1ff165321835086fa74a0dfbc350da0b37cb75b5 100644 (file)
@@ -1,7 +1,7 @@
 SHELL  = /bin/sh
 SRCS   = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
        canon_addr.c cfg_parser.c cleanup_strerror.c cleanup_strflags.c \
-       clnt_stream.c debug_peer.c debug_process.c defer.c \
+       clnt_stream.c debug_peer.c debug_process.c defer.c db_common.c \
        deliver_completed.c deliver_flock.c deliver_pass.c deliver_request.c \
        dict_ldap.c dict_mysql.c dict_pgsql.c dict_proxy.c domain_list.c \
        dot_lockfile.c dot_lockfile_as.c ext_prop.c file_id.c flush_clnt.c \
@@ -28,7 +28,7 @@ SRCS  = abounce.c anvil_clnt.c been_here.c bounce.c bounce_log.c \
        wildcard_inet_addr.c valid_mailhost_addr.c
 OBJS   = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \
        canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \
-       clnt_stream.o debug_peer.o debug_process.o defer.o \
+       clnt_stream.o debug_peer.o debug_process.o defer.o db_common.o \
        deliver_completed.o deliver_flock.o deliver_pass.o deliver_request.o \
        dict_ldap.o dict_mysql.o dict_pgsql.o dict_proxy.o domain_list.o \
        dot_lockfile.o dot_lockfile_as.o ext_prop.o file_id.o flush_clnt.o \
@@ -73,7 +73,7 @@ HDRS  = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \
        resolve_local.h rewrite_clnt.h sent.h smtp_stream.h split_addr.h \
        string_list.h strip_addr.h sys_exits.h timed_ipc.h tok822.h \
        trace.h verify.h verify_clnt.h verp_sender.h virtual8_maps.h \
-       xtext.h scache.h user_acl.h ehlo_mask.h \
+       xtext.h scache.h user_acl.h ehlo_mask.h db_common.h \
        wildcard_inet_addr.h valid_mailhost_addr.h
 TESTSRC        = rec2stream.c stream2rec.c recdump.c
 DEFS   = -I. -I$(INC_DIR) -D$(SYSTYPE)
@@ -520,6 +520,20 @@ clnt_stream.o: mail_proto.h
 clnt_stream.o: ../../include/attr.h
 clnt_stream.o: mail_params.h
 clnt_stream.o: clnt_stream.h
+db_common.o: db_common.c
+db_common.o: ../../include/sys_defs.h
+db_common.o: cfg_parser.h
+db_common.o: ../../include/mymalloc.h
+db_common.o: ../../include/vstring.h
+db_common.o: ../../include/vbuf.h
+db_common.o: ../../include/msg.h
+db_common.o: ../../include/dict.h
+db_common.o: ../../include/vstream.h
+db_common.o: ../../include/argv.h
+db_common.o: db_common.h
+db_common.o: string_list.h
+db_common.o: ../../include/match_list.h
+db_common.o: ../../include/match_ops.h
 debug_peer.o: debug_peer.c
 debug_peer.o: ../../include/sys_defs.h
 debug_peer.o: ../../include/msg.h
@@ -603,10 +617,59 @@ deliver_request.o: recipient_list.h
 deliver_request.o: deliver_request.h
 dict_ldap.o: dict_ldap.c
 dict_ldap.o: ../../include/sys_defs.h
+dict_ldap.o: ../../include/msg.h
+dict_ldap.o: ../../include/mymalloc.h
+dict_ldap.o: ../../include/vstring.h
+dict_ldap.o: ../../include/vbuf.h
+dict_ldap.o: ../../include/dict.h
+dict_ldap.o: ../../include/vstream.h
+dict_ldap.o: ../../include/argv.h
+dict_ldap.o: ../../include/stringops.h
+dict_ldap.o: ../../include/binhash.h
+dict_ldap.o: cfg_parser.h
+dict_ldap.o: db_common.h
+dict_ldap.o: string_list.h
+dict_ldap.o: ../../include/match_list.h
+dict_ldap.o: ../../include/match_ops.h
+dict_ldap.o: dict_ldap.h
 dict_mysql.o: dict_mysql.c
 dict_mysql.o: ../../include/sys_defs.h
+dict_mysql.o: ../../include/dict.h
+dict_mysql.o: ../../include/vstream.h
+dict_mysql.o: ../../include/vbuf.h
+dict_mysql.o: ../../include/argv.h
+dict_mysql.o: ../../include/msg.h
+dict_mysql.o: ../../include/mymalloc.h
+dict_mysql.o: ../../include/vstring.h
+dict_mysql.o: ../../include/split_at.h
+dict_mysql.o: ../../include/find_inet.h
+dict_mysql.o: ../../include/myrand.h
+dict_mysql.o: ../../include/events.h
+dict_mysql.o: cfg_parser.h
+dict_mysql.o: db_common.h
+dict_mysql.o: string_list.h
+dict_mysql.o: ../../include/match_list.h
+dict_mysql.o: ../../include/match_ops.h
+dict_mysql.o: dict_mysql.h
 dict_pgsql.o: dict_pgsql.c
 dict_pgsql.o: ../../include/sys_defs.h
+dict_pgsql.o: ../../include/dict.h
+dict_pgsql.o: ../../include/vstream.h
+dict_pgsql.o: ../../include/vbuf.h
+dict_pgsql.o: ../../include/argv.h
+dict_pgsql.o: ../../include/msg.h
+dict_pgsql.o: ../../include/mymalloc.h
+dict_pgsql.o: ../../include/vstring.h
+dict_pgsql.o: ../../include/split_at.h
+dict_pgsql.o: ../../include/find_inet.h
+dict_pgsql.o: ../../include/myrand.h
+dict_pgsql.o: ../../include/events.h
+dict_pgsql.o: cfg_parser.h
+dict_pgsql.o: db_common.h
+dict_pgsql.o: string_list.h
+dict_pgsql.o: ../../include/match_list.h
+dict_pgsql.o: ../../include/match_ops.h
+dict_pgsql.o: dict_pgsql.h
 dict_proxy.o: dict_proxy.c
 dict_proxy.o: ../../include/sys_defs.h
 dict_proxy.o: ../../include/msg.h
diff --git a/postfix/src/global/db_common.c b/postfix/src/global/db_common.c
new file mode 100644 (file)
index 0000000..c694caa
--- /dev/null
@@ -0,0 +1,448 @@
+/*++
+/* NAME
+/*     db_common 3
+/* SUMMARY
+/*     utilities common to network based dictionaries
+/* SYNOPSIS
+/*     #include "db_common.h"
+/*
+/*     int     db_common_parse(dict, ctx, format, query)
+/*     DICT    *dict;
+/*     void    **ctx;
+/*     const char *format;
+/*     int     query;
+/*
+/*     void    db_common_free_context(ctx)
+/*     void    *ctx;
+/*
+/*     int     db_common_expand(ctx, format, value, key, buf, quote_func);
+/*     void    *ctx;
+/*     const char *format;
+/*     const char *value;
+/*     const char *key;
+/*     VSTRING *buf;
+/*     void    (*quote_func)(DICT *, const char *, VSTRING *);
+/*
+/*     int     db_common_check_domain(domain_list, addr);
+/*     STRING_LIST *domain_list;
+/*     const char *addr;
+/*
+/*     void    db_common_sql_build_query(query,parser);
+/*     VSTRING *query;
+/*     CFG_PARSER *parser;
+/*
+/* DESCRIPTION
+/*     This module implements utilities common to network based dictionaries.
+/*
+/*     \fIdb_common_parse\fR parses query and result substitution templates.
+/*     It must be called for each template before any calls to
+/*     \fIdb_common_expand\fR. The \fIctx\fB argument must be initialized to
+/*     a reference to a (void *)0 before the first template is parsed, this
+/*     causes memory for the context to be allocated and the new pointer is
+/*     stored in *ctx. When the dictionary is closed, this memory must be
+/*     freed with a final call to \fBdb_common_free_context\fR.
+/*
+/*     Calls for additional templates associated with the same map must use the
+/*     same ctx argument. The context accumulates run-time lookup key and result
+/*     validation information (inapplicable keys or results are skipped) and is
+/*     needed later in each call of \fIdb_common_expand\fR. A non-zero return
+/*     value indicates that data-depedent '%' expansions were found in the input
+/*     template.
+/*     
+/*     \fIdb_common_expand\fR expands the specifiers in \fIformat\fR.
+/*     When the input data lacks all fields needed for the expansion, zero
+/*     is returned and the query or result should be skipped. Otherwise
+/*     the expansion is appended to the result buffer (after a comma if the
+/*     the result buffer is not empty).
+/*
+/*     If not NULL, the \fBquote_func\fR callback performs database-specific
+/*     quoting of each variable before expansion.
+/*     \fBvalue\fR is the lookup key for query expansion and result for result
+/*     expansion. \fBkey\fR is NULL for query expansion and the lookup key for
+/*     result expansion.
+/* .PP
+/*     The following '%' expansions are performed on \fBvalue\fR:
+/* .IP %%
+/*     A literal percent character.
+/* .IP %s
+/*     The entire lookup key \fIaddr\fR.
+/* .IP %u
+/*     If \fBaddr\fR is a fully qualified address, the local part of the
+/*     address.  Otherwise \fIaddr\fR.
+/* .IP %d
+/*     If \fIaddr\fR is a fully qualified address, the domain part of the
+/*     address.  Otherwise the query against the database is suppressed and
+/*     the lookup returns no results.
+/*
+/*     The following '%' expansions are performed on the lookup \fBkey\fR:
+/* .IP %S
+/*     The entire lookup key \fIkey\fR.
+/* .IP %U
+/*     If \fBkey\fR is a fully qualified address, the local part of the
+/*     address.  Otherwise \fIkey\fR.
+/* .IP %D
+/*     If \fIkey\fR is a fully qualified address, the domain part of the
+/*     address.  Otherwise the query against the database is suppressed and
+/*     the lookup returns no results.
+/*
+/* .PP
+/*     \fIdb_common_check_domain\fR checks domain list so that query optimization
+/*     can be performed
+/*
+/* .PP
+/*     \fIdb_common_sql_build_query\fR builds the "default"(backwards compatible)
+/*     query from the 'table', 'select_field', 'where_field' and
+/*     'additional_conditions' parameters, checking for errors.
+/*
+/* DIAGNOSTICS
+/*     Fatal errors: invalid substitution format, invalid string_list pattern,
+/*     insufficient parameters.
+/* SEE ALSO
+/*     dict(3) dictionary manager
+/*     string_list(3) string list pattern matching
+/*     match_ops(3) simple string or host pattern matching
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*
+/*     Liviu Daia
+/*     Institute of Mathematics of the Romanian Academy
+/*     P.O. BOX 1-764
+/*     RO-014700 Bucharest, ROMANIA
+/*
+/*     Jose Luis Tallon
+/*     G4 J.E. - F.I. - U.P.M.
+/*     Campus de Montegancedo, S/N
+/*     E-28660 Madrid, SPAIN
+/*
+/*     Victor Duchovni
+/*     Morgan Stanley
+/*--*/
+
+ /*
+  * System library.
+  */
+#include "sys_defs.h"
+#include <stddef.h>
+#include <string.h>
+
+ /*
+  * Global library.
+  */
+#include "cfg_parser.h"
+
+ /*
+  * Utility library.
+  */
+#include <mymalloc.h>
+#include <vstring.h>
+#include <msg.h>
+#include <dict.h>
+
+ /*
+  * Application specific
+  */
+#include "db_common.h"
+
+#define        DB_COMMON_KEY_DOMAIN    (1 << 0)        /* Need lookup key domain */
+#define        DB_COMMON_KEY_USER      (1 << 1)        /* Need lookup key localpart */
+#define        DB_COMMON_VALUE_DOMAIN  (1 << 2)        /* Need result domain */
+#define        DB_COMMON_VALUE_USER    (1 << 3)        /* Need result localpart */
+
+typedef struct {
+    DICT    *dict;
+    int      flags;
+    int      nparts;
+} DB_COMMON_CTX;
+
+/* db_common_parse - validate query or result template */
+
+int db_common_parse(DICT *dict, void **ctxPtr, const char *format, int query)
+{
+    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *)*ctxPtr;
+    const char *cp;
+    int     dynamic = 0;
+
+    if (ctx == 0) {
+       ctx = (DB_COMMON_CTX *)(*ctxPtr = mymalloc(sizeof *ctx));
+       ctx->dict = dict;
+       ctx->flags = 0;
+       ctx->nparts = 0;
+    }
+
+    for (cp = format; *cp; ++cp)
+       if (*cp == '%')
+           switch (*++cp) {
+           case '%':
+               break;
+           case 'u':
+               ctx->flags |=
+                   query ? DB_COMMON_KEY_USER : DB_COMMON_VALUE_USER;
+               dynamic = 1;
+               break;
+           case 'd':
+               ctx->flags |=
+                   query ? DB_COMMON_KEY_DOMAIN : DB_COMMON_VALUE_DOMAIN;
+               dynamic = 1;
+               break;
+           case 's': case 'S':
+               dynamic = 1;
+               break;
+           case 'U':
+               ctx->flags |= DB_COMMON_KEY_USER;
+               dynamic = 1;
+               break;
+           case '1': case '2': case '3': case '4': case '5':
+           case '6': case '7': case '8': case '9':
+               if (ctx->nparts < *cp - '0')
+                   ctx->nparts = *cp - '0';
+               /* FALLTHROUGH */
+           case 'D':
+               ctx->flags |= DB_COMMON_KEY_DOMAIN;
+               dynamic = 1;
+               break;
+           default:
+               msg_fatal("db_common_parse: %s: Invalid %s template: %s",
+                         dict->name, query ? "query" : "result", format);
+           }
+    return dynamic;
+}
+
+/* db_common_free_ctx - free parse context */
+
+void db_common_free_ctx(void *ctxPtr)
+{
+    myfree((char *)ctxPtr);
+}
+
+/* db_common_expand - expand query and result templates */
+
+int db_common_expand(void *ctxArg, const char *format, const char *value,
+                    const char *key, VSTRING *result,
+                    db_quote_callback_t quote_func)
+{
+    char   *myname = "db_common_expand";
+    DB_COMMON_CTX *ctx = (DB_COMMON_CTX *)ctxArg;
+    const char *vdomain = 0;
+    const char *kdomain = 0;
+    char   *vuser = 0;
+    char   *kuser = 0;
+    ARGV   *parts = 0;
+    int     i;
+    const char *cp;
+
+    /* Skip NULL or empty values */
+    if (value == 0 || *value == 0)
+       return (0);
+
+    if (key) {
+       /* This is a result template and the input value is the result */
+       if (ctx->flags & (DB_COMMON_VALUE_DOMAIN | DB_COMMON_VALUE_USER))
+           if ((vdomain = strrchr(value, '@')) != 0)
+               ++vdomain;
+
+       if ((!vdomain || !*vdomain) && (ctx->flags&DB_COMMON_VALUE_DOMAIN) != 0
+           || vdomain == value + 1 && (ctx->flags&DB_COMMON_VALUE_USER) != 0)
+           return (0);
+
+       /* The result format may use the local or domain part of the key */
+       if (ctx->flags & (DB_COMMON_KEY_DOMAIN | DB_COMMON_KEY_USER))
+           if ((kdomain = strrchr(key, '@')) != 0)
+               ++kdomain;
+
+       /*
+        * The key should already be checked before the query. No harm if
+        * the query did not get optimized out, so we just issue a warning.
+        */
+       if ((!kdomain || !*kdomain) && (ctx->flags&DB_COMMON_KEY_DOMAIN) != 0
+           || kdomain == key + 1 && (ctx->flags & DB_COMMON_KEY_USER) != 0) {
+           msg_warn("%s: %s: lookup key '%s' skipped after query", myname,
+                     ctx->dict->name, value);
+           return (0);
+       }
+    } else {
+       /* This is a query template and the input value is the lookup key */
+       if (ctx->flags & (DB_COMMON_KEY_DOMAIN | DB_COMMON_KEY_USER))
+           if ((vdomain = strrchr(value, '@')) != 0)
+               ++vdomain;
+
+       if ((!vdomain || !*vdomain) && (ctx->flags&DB_COMMON_KEY_DOMAIN) != 0
+           || vdomain == value + 1 && (ctx->flags & DB_COMMON_KEY_USER) != 0)
+           return (0);
+    }
+
+    if (ctx->nparts > 0) {
+       parts = argv_split(key ? kdomain : vdomain, ".");
+       /*
+        * Skip domains that lack enough labels to fill-in the template.
+        */
+       if (parts->argc < ctx->nparts) {
+           argv_free(parts);
+           return (0);
+       }
+       /*
+        * Skip domains with leading, consecutive or trailing '.'
+        * separators among the required labels.
+        */
+       for (i = 0; i < ctx->nparts; i++)
+           if (*parts->argv[parts->argc-i-1] == 0) {
+               argv_free(parts);
+               return (0);
+           }
+    }
+
+    if (VSTRING_LEN(result) > 0)
+       VSTRING_ADDCH(result, ',');
+
+#define QUOTE_VAL(d, q, v, buf) \
+    (q ? q(d, v, buf) : vstring_strcat(buf, v))
+
+    /*
+     * Replace all instances of %s with the address to look up. Replace
+     * %u with the user portion, and %d with the domain portion. "%%"
+     * expands to "%".  lowercase -> addr, uppercase -> key
+     */
+    for (cp = format; *cp; cp++) {
+       if (*cp == '%') {
+           switch (*++cp) {
+
+           case '%':
+               VSTRING_ADDCH(result, '%');
+               break;
+
+           case 's':
+               QUOTE_VAL(ctx->dict, quote_func, value, result);
+               break;
+
+           case 'u':
+               if (vdomain) {
+                   if (vuser == 0)
+                       vuser = mystrndup(value, vdomain - value - 1);
+                   QUOTE_VAL(ctx->dict, quote_func, vuser, result);
+               }
+               else
+                   QUOTE_VAL(ctx->dict, quote_func, value, result);
+               break;
+
+           case 'd':
+               QUOTE_VAL(ctx->dict, quote_func, vdomain, result);
+               break;
+
+           case 'S':
+               if (key)
+                   QUOTE_VAL(ctx->dict, quote_func, key, result);
+               else
+                   QUOTE_VAL(ctx->dict, quote_func, value, result);
+               break;
+
+           case 'U':
+               if (key) {
+                   if (kdomain) {
+                       if (kuser == 0)
+                           kuser = mystrndup(key, kdomain - key - 1);
+                       QUOTE_VAL(ctx->dict, quote_func, kuser, result);
+                   }
+                   else
+                       QUOTE_VAL(ctx->dict, quote_func, key, result);
+               } else {
+                   if (vdomain) {
+                       if (vuser == 0)
+                           vuser = mystrndup(value, vdomain - value - 1);
+                       QUOTE_VAL(ctx->dict, quote_func, vuser, result);
+                   }
+                   else
+                       QUOTE_VAL(ctx->dict, quote_func, value, result);
+               }
+               break;
+
+           case 'D':
+               if (key)
+                   QUOTE_VAL(ctx->dict, quote_func, kdomain, result);
+               else
+                   QUOTE_VAL(ctx->dict, quote_func, vdomain, result);
+               break;
+
+           case '1': case '2': case '3': case '4': case '5':
+           case '6': case '7': case '8': case '9':
+               QUOTE_VAL(ctx->dict, quote_func,
+                         parts->argv[parts->argc-(*cp-'0')], result);
+               break;
+
+           default:
+               msg_fatal("%s: %s: invalid %s template '%s'", myname,
+                         ctx->dict->name, key ? "result" : "query",
+                         format);
+           }
+       } else
+           VSTRING_ADDCH(result, *cp);
+    }
+    VSTRING_TERMINATE(result);
+
+    if (vuser)
+       myfree(vuser);
+    if (kuser)
+       myfree(kuser);
+    if (parts)
+       argv_free(parts);
+
+    return (1);
+}
+
+
+/* db_common_check_domain - check domain list */
+
+int db_common_check_domain(STRING_LIST *domain_list, const char *addr)
+{
+    char   *domain;
+
+    if (domain_list) {
+       if ((domain = strrchr(addr, '@')) != NULL)
+           ++domain;
+       if (domain == NULL || domain == addr + 1)
+           return (0);
+       if (match_list_match(domain_list, domain) == 0)
+           return (0);
+    }
+    return (1);
+}
+
+/* db_common_sql_build_query -- build query for SQL maptypes */
+
+void db_common_sql_build_query(VSTRING *query, CFG_PARSER *parser)
+{
+    char   *myname = "db_common_sql_build_query";
+    char   *table;
+    char   *select_field;
+    char   *where_field;
+    char   *additional_conditions;
+
+    /*
+     * Build "old style" query: "select %s from %s where %s"
+     */
+    if ((table = cfg_get_str(parser, "table", NULL, 1, 0)) == 0)
+       msg_fatal("%s: 'table' parameter not defined", myname);
+    
+    if ((select_field = cfg_get_str(parser, "select_field", NULL, 1, 0)) == 0)
+       msg_fatal("%s: 'select_field' parameter not defined", myname);
+
+    if ((where_field = cfg_get_str(parser, "where_field", NULL, 1, 0)) == 0)
+            msg_fatal("%s: 'where_field' parameter not defined", myname);
+
+    additional_conditions = cfg_get_str(parser, "additional_conditions",
+                                       "", 0, 0);
+
+    vstring_sprintf(query, "SELECT %s FROM %s WHERE %s='%%s' %s",
+                    select_field, table, where_field,
+                    additional_conditions);
+    
+    myfree(table);
+    myfree(select_field);
+    myfree(where_field);
+    myfree(additional_conditions); 
+}
diff --git a/postfix/src/global/db_common.h b/postfix/src/global/db_common.h
new file mode 100644 (file)
index 0000000..73c4661
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef _DB_COMMON_H_INCLUDED_
+#define _DB_COMMON_H_INCLUDED_
+
+/*++
+/* NAME
+/*     db_common 3h
+/* SUMMARY
+/*     utilities common to network based dictionaries
+/* SYNOPSIS
+/*     #include "db_common.h"
+/* DESCRIPTION
+/* .nf
+ */
+  
+ /*
+  * External interface.
+  */
+#include "dict.h"
+#include "string_list.h"
+
+typedef void (*db_quote_callback_t)(DICT *, const char *, VSTRING *);
+
+extern int db_common_parse(DICT *, void **, const char *, int);
+extern void db_common_free_ctx(void *);
+extern int db_common_expand(void *, const char *, const char *,
+                           const char *, VSTRING *, db_quote_callback_t);
+extern int db_common_check_domain(STRING_LIST *, const char *);
+extern void db_common_sql_build_query(VSTRING *query, CFG_PARSER *parser);
+
+/* LICENSE
+/* .ad
+/* .fi
+/*     The Secure Mailer license must be distributed with this software.
+/* AUTHOR(S)
+/*     Wietse Venema
+/*     IBM T.J. Watson Research
+/*     P.O. Box 704
+/*     Yorktown Heights, NY 10598, USA
+/*
+/*     Liviu Daia
+/*     Institute of Mathematics of the Romanian Academy
+/*     P.O. BOX 1-764
+/*     RO-014700 Bucharest, ROMANIA
+/*
+/*     Jose Luis Tallon
+/*     G4 J.E. - F.I. - U.P.M.
+/*     Campus de Montegancedo, S/N
+/*     E-28660 Madrid, SPAIN
+/*
+/*     Victor Duchovni
+/*     Morgan Stanley
+/*--*/
+
+#endif
+
index d47d2cc82bfc419aae5945d32a15fb0cd0e756e0..6e8a4f752105073399070ac1c003f9c649685aa2 100644 (file)
@@ -7,7 +7,7 @@
 /*     #include <dict_ldap.h>
 /*
 /*     DICT    *dict_ldap_open(attribute, dummy, dict_flags)
-/*     const char *attribute;
+/*     const char *ldapsource;
 /*     int     dummy;
 /*     int     dict_flags;
 /* DESCRIPTION
 /* .IP timeout
 /*     Deadline for LDAP open() and LDAP search() .
 /* .IP query_filter
-/*     The filter used to search for directory entries, for example
-/*     \fI(mailacceptinggeneralid=%s)\fR.
-/* .IP result_filter
-/*     The filter used to expand results from queries.  Default is
-/*     \fI%s\fR.
+/*     The search filter template used to search for directory entries,
+/*     for example \fI(mailacceptinggeneralid=%s)\fR. See ldap_table(5)
+/*     for details.
+/* .IP result_format
+/*     The result template used to expand results from queries. Default
+/*     is \fI%s\fR. See ldap_table(5) for details. Also supported under
+/*     the name \fIresult_filter\fR for compatibility with older releases.
 /* .IP result_attribute
 /*     The attribute(s) returned by the search, in which to find
 /*     RFC822 addresses, for example \fImaildrop\fR.
 
 /* Utility library. */
 
-#include "match_list.h"
-#include "match_ops.h"
 #include "msg.h"
 #include "mymalloc.h"
 #include "vstring.h"
 /* Global library. */
 
 #include "cfg_parser.h"
+#include "db_common.h"
 
 /* Application-specific. */
 
@@ -212,15 +213,17 @@ typedef struct {
  */
 typedef struct {
     DICT    dict;                      /* generic member */
-    CFG_PARSER *parser;
-    char   *ldapsource;
+    CFG_PARSER *parser;                        /* common parameter parser */
+    char   *query;                     /* db_common_expand() query */
+    char   *result_format;             /* db_common_expand() result_format */
+    STRING_LIST *domain;               /* restrict queries to these domains */
+    void   *ctx;                       /* db_common_parse() context */
+    int     dynamic_base;              /* Search base has substitutions? */
+    int     expansion_limit;
     char   *server_host;
     int     server_port;
     int     scope;
     char   *search_base;
-    MATCH_LIST *domain;
-    char   *query_filter;
-    char   *result_filter;
     ARGV   *result_attributes;
     int     num_attributes;            /* rest of list is DN's. */
     int     bind;
@@ -229,7 +232,6 @@ typedef struct {
     int     timeout;
     int     dereference;
     long    recursion_limit;
-    long    expansion_limit;
     long    size_limit;
     int     chase_referrals;
     int     debuglevel;
@@ -251,6 +253,55 @@ typedef struct {
 
 #define DICT_LDAP_CONN(d) ((LDAP_CONN *)((d)->ht->value))
 
+
+
+/*
+ * Quoting rules.
+ */
+
+/* rfc2253_quote - Quote input key for safe inclusion in the search base */
+
+static void rfc2253_quote(DICT *unused, const char *name, VSTRING *result)
+{
+    unsigned char *sub = (unsigned char *)name;
+    size_t len;
+
+    /*
+     * The RFC only requires quoting of a leading or trailing space,
+     * but it is harmless to quote whitespace everywhere. Similarly,
+     * we quote all '#' characters, even though only the leading '#'
+     * character requires quoting per the RFC.
+     */
+    while (*sub)
+       if ((len = strcspn(sub, " \t\"#+,;<>\\")) > 0) {
+           vstring_strncat(result, sub, len);
+           sub += len;
+       } else
+           vstring_sprintf_append(result, "\\%02X", *(sub++));
+}
+
+/* rfc2254_quote - Quote input key for safe inclusion in the query filter */
+
+static void rfc2254_quote(DICT *unused, const char *name, VSTRING *result)
+{
+    unsigned char *sub = (unsigned char *)name;
+    size_t len;
+
+    /*
+     * If any characters in the supplied address should be escaped per RFC
+     * 2254, do so. Thanks to Keith Stevenson and Wietse. And thanks to
+     * Samuel Tardieu for spotting that wildcard searches were being done in
+     * the first place, which prompted the ill-conceived lookup_wildcards
+     * parameter and then this more comprehensive mechanism.
+     */
+    while (*sub)
+       if ((len = strcspn(sub, "*()\\")) > 0) {
+           vstring_strncat(result, sub, len);
+           sub += len;
+       } else
+           vstring_sprintf_append(result, "\\%02X", *(sub++));
+}
+
 static BINHASH *conn_hash = 0;
 
 #if defined(LDAP_API_FEATURE_X_OPENLDAP) || !defined(LDAP_OPT_NETWORK_TIMEOUT)
@@ -404,7 +455,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
 #if defined(LDAP_OPT_DEBUG_LEVEL) && defined(LBER_OPT_LOG_PRINT_FN)
     if (dict_ldap->debuglevel > 0 &&
        ber_set_option(NULL, LBER_OPT_LOG_PRINT_FN,
-                    (LDAP_CONST *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
+                    (LDAP_CONST void *) dict_ldap_logprint) != LBER_OPT_SUCCESS)
        msg_warn("%s: Unable to set ber logprint function.", myname);
 #if defined(LBER_OPT_DEBUG_LEVEL)
     if (ber_set_option(NULL, LBER_OPT_DEBUG_LEVEL,
@@ -497,7 +548,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
        if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT,
                            &dict_ldap->size_limit) != LDAP_OPT_SUCCESS)
            msg_warn("%s: %s: Unable to set query result size limit to %ld.",
-                    myname, dict_ldap->ldapsource, dict_ldap->size_limit);
+                    myname, dict_ldap->parser->name, dict_ldap->size_limit);
     }
 
     /*
@@ -582,7 +633,7 @@ static int dict_ldap_connect(DICT_LDAP *dict_ldap)
 
     if (msg_verbose)
        msg_info("%s: Cached connection handle for LDAP source %s",
-                myname, dict_ldap->ldapsource);
+                myname, dict_ldap->parser->name);
 
     return (0);
 }
@@ -643,59 +694,6 @@ static void dict_ldap_conn_find(DICT_LDAP *dict_ldap)
     vstring_free(keybuf);
 }
 
-/*
- * expand a filter (lookup or result)
- */
-static void dict_ldap_expand_filter(char *ldapsource, char *filter,
-                                           char *value, VSTRING *out)
-{
-    char   *myname = "dict_ldap_expand_filter";
-    char   *sub,
-           *end;
-
-    /*
-     * Yes, replace all instances of %s with the address to look up. Replace
-     * %u with the user portion, and %d with the domain portion.
-     */
-    sub = filter;
-    end = sub + strlen(filter);
-    while (sub < end) {
-
-       /*
-        * Make sure it's %[sud] and not something else.  For backward
-        * compatibilty, treat anything other than %u or %d as %s, with a
-        * warning.
-        */
-       if (*(sub) == '%') {
-           char   *u = value;
-           char   *p = strrchr(u, '@');
-
-           switch (*(sub + 1)) {
-           case 'd':
-               if (p)
-                   vstring_strcat(out, p + 1);
-               break;
-           case 'u':
-               if (p)
-                   vstring_strncat(out, u, p - u);
-               else
-                   vstring_strcat(out, u);
-               break;
-           default:
-               msg_warn("%s: %s: Invalid filter substitution format '%%%c'!",
-                        myname, ldapsource, *(sub + 1));
-               /* fall through */
-           case 's':
-               vstring_strcat(out, u);
-               break;
-           }
-           sub++;
-       } else
-           vstring_strncat(out, sub, 1);
-       sub++;
-    }
-}
-
 /*
  * dict_ldap_get_values: for each entry returned by a search, get the values
  * of all its attributes. Recurses to resolve any DN or URL values found.
@@ -704,7 +702,7 @@ static void dict_ldap_expand_filter(char *ldapsource, char *filter,
  * are thanks to LaMont Jones.
  */
 static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
-                                        VSTRING *result)
+                                        VSTRING *result, const char* name)
 {
     static int recursion = 0;
     static int expansion;
@@ -738,10 +736,12 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
         * LDAP should not, but may produce more than the requested maximum
         * number of entries.
         */
-       if (dict_errno == 0 && ++entries > dict_ldap->size_limit
-           && dict_ldap->size_limit) {
-           msg_warn("%s[%d]: %s: Query size limit (%ld) exceeded", myname,
-                  recursion, dict_ldap->ldapsource, dict_ldap->size_limit);
+       if (dict_errno == 0
+           && dict_ldap->size_limit
+           && ++entries > dict_ldap->size_limit) {
+           msg_warn("%s[%d]: %s: Query size limit (%ld) exceeded",
+                    myname, recursion, dict_ldap->parser->name,
+                    dict_ldap->size_limit);
            dict_errno = DICT_ERR_RETRY;
        }
        for (attr = ldap_first_attribute(dict_ldap->ld, entry, &ber);
@@ -792,22 +792,16 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
            if (i < dict_ldap->num_attributes) {
                /* Ordinary result attribute */
                for (i = 0; vals[i] != NULL; i++) {
-                   if (++expansion > dict_ldap->expansion_limit &&
-                       dict_ldap->expansion_limit) {
-                       msg_warn("%s[%d]: %s: Expansion limit exceeded at"
-                              " result attribute %s=%s", myname, recursion,
-                                dict_ldap->ldapsource, attr, vals[i]);
+                   if (db_common_expand(dict_ldap->ctx,
+                                        dict_ldap->result_format, vals[i],
+                                        name, result, 0)
+                       && dict_ldap->expansion_limit > 0
+                       && ++expansion > dict_ldap->expansion_limit) {
+                       msg_warn("%s[%d]: %s: Expansion limit exceeded for key: '%s'",
+                                myname, recursion, dict_ldap->parser->name, name);
                        dict_errno = DICT_ERR_RETRY;
                        break;
                    }
-                   if (VSTRING_LEN(result) > 0)
-                       vstring_strcat(result, ",");
-                   if (dict_ldap->result_filter == NULL)
-                       vstring_strcat(result, vals[i]);
-                   else
-                       dict_ldap_expand_filter(dict_ldap->ldapsource,
-                                               dict_ldap->result_filter,
-                                               vals[i], result);
                }
                if (dict_errno != 0)
                    continue;
@@ -842,7 +836,7 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
                    }
                    switch (rc) {
                    case LDAP_SUCCESS:
-                       dict_ldap_get_values(dict_ldap, resloop, result);
+                       dict_ldap_get_values(dict_ldap, resloop, result, name);
                        break;
                    case LDAP_NO_SUCH_OBJECT:
 
@@ -875,8 +869,8 @@ static void dict_ldap_get_values(DICT_LDAP *dict_ldap, LDAPMessage * res,
            } else if (recursion >= dict_ldap->recursion_limit
                       && dict_ldap->result_attributes->argv[i]) {
                msg_warn("%s[%d]: %s: Recursion limit exceeded"
-                        " for special attribute %s=%s",
-                  myname, recursion, dict_ldap->ldapsource, attr, vals[0]);
+                        " for special attribute %s=%s", myname, recursion,
+                        dict_ldap->parser->name, attr, vals[0]);
                dict_errno = DICT_ERR_RETRY;
            }
            ldap_value_free(vals);
@@ -897,14 +891,12 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
     char   *myname = "dict_ldap_lookup";
     DICT_LDAP *dict_ldap = (DICT_LDAP *) dict;
     LDAPMessage *res = 0;
+    static VSTRING *base;
+    static VSTRING *query;
     static VSTRING *result;
     struct timeval tv;
-    VSTRING *escaped_name = 0,
-           *filter_buf = 0;
     int     rc = 0;
     int     sizelimit;
-    char   *sub,
-           *end;
 
     dict_errno = 0;
 
@@ -916,24 +908,22 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
      * addresses in domains on the list. This can significantly reduce the
      * load on the LDAP server.
      */
-    if (dict_ldap->domain) {
-       const char *p = strrchr(name, '@');
-
-       if (p == 0 || p == name ||
-           match_list_match(dict_ldap->domain, ++p) == 0) {
-           if (msg_verbose)
-               msg_info("%s: domain of %s not found in domain list", myname,
-                        name);
-           return (0);
-       }
+    if (db_common_check_domain(dict_ldap->domain, name) == 0) {
+       if (msg_verbose)
+           msg_info("%s: Skipping lookup of '%s'", myname, name);
+       return (0);
     }
 
-    /*
-     * Initialize the result holder.
-     */
-    if (result == 0)
-       result = vstring_alloc(2);
-    vstring_strcpy(result, "");
+#define INIT_VSTR(buf, len) do { \
+       if (buf == 0) \
+           buf = vstring_alloc(len); \
+       VSTRING_RESET(buf); \
+       VSTRING_TERMINATE(buf); \
+    } while (0)
+
+    INIT_VSTR(base, 10);
+    INIT_VSTR(query, 10);
+    INIT_VSTR(result, 10);
 
     /*
      * Because the connection may be shared and invalidated via queries for
@@ -949,7 +939,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
        if (msg_verbose)
            msg_info
                ("%s: No existing connection for LDAP source %s, reopening",
-                myname, dict_ldap->ldapsource);
+                myname, dict_ldap->parser->name);
 
        dict_ldap_connect(dict_ldap);
 
@@ -960,7 +950,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
            return (0);
     } else if (msg_verbose)
        msg_info("%s: Using existing connection for LDAP source %s",
-                myname, dict_ldap->ldapsource);
+                myname, dict_ldap->parser->name);
 
     /*
      * Connection caching, means that the connection handle may have the
@@ -973,89 +963,55 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
     if (ldap_set_option(dict_ldap->ld, LDAP_OPT_SIZELIMIT, &sizelimit)
        != LDAP_OPT_SUCCESS)
        msg_warn("%s: %s: Unable to set query result size limit to %ld.",
-                myname, dict_ldap->ldapsource, dict_ldap->size_limit);
+                myname, dict_ldap->parser->name, dict_ldap->size_limit);
 
     /*
-     * Prepare the query.
+     * Expand the search base and query. Skip lookup when the
+     * input key lacks sufficient domain components to satisfy
+     * all the requested %-substitutions.
+     *
+     * When the search base is not static, LDAP_NO_SUCH_OBJECT is
+     * expected and is therefore treated as a non-error: the lookup
+     * returns no results rather than a soft error.
      */
-    tv.tv_sec = dict_ldap->timeout;
-    tv.tv_usec = 0;
-    escaped_name = vstring_alloc(20);
-    filter_buf = vstring_alloc(30);
+    if (!db_common_expand(dict_ldap->ctx, dict_ldap->search_base,
+                         name, 0, base, rfc2253_quote)) {
+        if (msg_verbose > 1)
+           msg_info("%s: %s: Empty expansion for %s", myname,
+                    dict_ldap->parser->name, dict_ldap->search_base);
+        return (0);
+    }
 
-    /*
-     * If any characters in the supplied address should be escaped per RFC
-     * 2254, do so. Thanks to Keith Stevenson and Wietse. And thanks to
-     * Samuel Tardieu for spotting that wildcard searches were being done in
-     * the first place, which prompted the ill-conceived lookup_wildcards
-     * parameter and then this more comprehensive mechanism.
-     */
-    end = (char *) name + strlen((char *) name);
-    sub = (char *) strpbrk((char *) name, "*()\\\0");
-    if (sub && sub != end) {
-       if (msg_verbose)
-           msg_info("%s: Found character(s) in %s that must be escaped",
-                    myname, name);
-       for (sub = (char *) name; sub != end; sub++) {
-           switch (*sub) {
-           case '*':
-               vstring_strcat(escaped_name, "\\2a");
-               break;
-           case '(':
-               vstring_strcat(escaped_name, "\\28");
-               break;
-           case ')':
-               vstring_strcat(escaped_name, "\\29");
-               break;
-           case '\\':
-               vstring_strcat(escaped_name, "\\5c");
-               break;
-           case '\0':
-               vstring_strcat(escaped_name, "\\00");
-               break;
-           default:
-               vstring_strncat(escaped_name, sub, 1);
-           }
-       }
-       if (msg_verbose)
-           msg_info("%s: After escaping, it's %s", myname,
-                    vstring_str(escaped_name));
-    } else
-       vstring_strcpy(escaped_name, (char *) name);
+    if (!db_common_expand(dict_ldap->ctx, dict_ldap->query,
+                         name, 0, query, rfc2254_quote)) {
+        if (msg_verbose > 1)
+           msg_info("%s: %s: Empty expansion for %s", myname,
+                    dict_ldap->parser->name, dict_ldap->query);
+        return (0);
+    }
 
     /*
-     * Does the supplied query_filter even include a substitution?
+     * Prepare the query.
      */
-    if ((char *) strchr(dict_ldap->query_filter, '%') == NULL) {
-
-       /*
-        * No, log the fact and continue.
-        */
-       msg_warn("%s: %s: Fixed query_filter %s is probably useless",
-                myname, dict_ldap->ldapsource, dict_ldap->query_filter);
-       vstring_strcpy(filter_buf, dict_ldap->query_filter);
-    } else {
-       dict_ldap_expand_filter(dict_ldap->ldapsource, dict_ldap->query_filter,
-                               vstring_str(escaped_name), filter_buf);
-    }
+    tv.tv_sec = dict_ldap->timeout;
+    tv.tv_usec = 0;
 
     /*
      * On to the search.
      */
     if (msg_verbose)
-       msg_info("%s: Searching with filter %s", myname,
-                vstring_str(filter_buf));
+       msg_info("%s: %s: Searching with filter %s", myname,
+                dict_ldap->parser->name, vstring_str(query));
 
-    rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
-                       dict_ldap->scope,
-                       vstring_str(filter_buf),
+    rc = ldap_search_st(dict_ldap->ld, vstring_str(base),
+                       dict_ldap->scope, vstring_str(query),
                        dict_ldap->result_attributes->argv,
                        0, &tv, &res);
 
     if (rc == LDAP_SERVER_DOWN) {
        if (msg_verbose)
            msg_info("%s: Lost connection for LDAP source %s, reopening",
-                    myname, dict_ldap->ldapsource);
+                    myname, dict_ldap->parser->name);
 
        ldap_unbind(dict_ldap->ld);
        dict_ldap->ld = DICT_LDAP_CONN(dict_ldap)->conn_ld = 0;
@@ -1067,20 +1023,21 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
        if (dict_errno)
            return (0);
 
-       rc = ldap_search_st(dict_ldap->ld, dict_ldap->search_base,
-                           dict_ldap->scope,
-                           vstring_str(filter_buf),
+       rc = ldap_search_st(dict_ldap->ld, vstring_str(base),
+                           dict_ldap->scope, vstring_str(query),
                            dict_ldap->result_attributes->argv,
                            0, &tv, &res);
 
     }
-    if (rc == LDAP_SUCCESS) {
 
+    switch (rc) {
+
+    case LDAP_SUCCESS:
        /*
         * Search worked; extract the requested result_attribute.
         */
 
-       dict_ldap_get_values(dict_ldap, res, result);
+       dict_ldap_get_values(dict_ldap, res, result, name);
 
        /*
         * OpenLDAP's ldap_next_attribute returns a bogus
@@ -1097,8 +1054,24 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
            msg_info("%s: Search returned %s", myname,
                     VSTRING_LEN(result) >
                     0 ? vstring_str(result) : "nothing");
-    } else {
+       break;
 
+    case LDAP_NO_SUCH_OBJECT:
+        /*
+        * If the search base is input key dependent, then not finding it,
+        * is equivalent to not finding the input key. Sadly, we cannot
+        * detect misconfiguration in this case.
+        */
+       if (dict_ldap->dynamic_base)
+           break;
+
+       msg_warn("%s: %s: Search base '%s' not found: %d: %s",
+                myname, dict_ldap->parser->name,
+                vstring_str(base), rc, ldap_err2string(rc));
+       dict_errno = DICT_ERR_RETRY;
+       break;
+
+    default:
        /*
         * Rats. The search didn't work.
         */
@@ -1116,6 +1089,7 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
         * And tell the caller to try again later.
         */
        dict_errno = DICT_ERR_RETRY;
+       break;
     }
 
     /*
@@ -1123,10 +1097,6 @@ static const char *dict_ldap_lookup(DICT *dict, const char *name)
      */
     if (res != 0)
        ldap_msgfree(res);
-    if (filter_buf != 0)
-       vstring_free(filter_buf);
-    if (escaped_name != 0)
-       vstring_free(escaped_name);
 
     /*
      * If we had an error, return nothing, Otherwise, return the result, if
@@ -1148,23 +1118,24 @@ static void dict_ldap_close(DICT *dict)
        if (conn->conn_ld) {
            if (msg_verbose)
                msg_info("%s: Closed connection handle for LDAP source %s",
-                        myname, dict_ldap->ldapsource);
+                        myname, dict_ldap->parser->name);
            ldap_unbind(conn->conn_ld);
        }
        binhash_delete(conn_hash, ht->key, ht->key_len, myfree);
     }
     cfg_parser_free(dict_ldap->parser);
-    myfree(dict_ldap->ldapsource);
     myfree(dict_ldap->server_host);
     myfree(dict_ldap->search_base);
     if (dict_ldap->domain)
-       match_list_free(dict_ldap->domain);
-    myfree(dict_ldap->query_filter);
-    if (dict_ldap->result_filter)
-       myfree(dict_ldap->result_filter);
+       string_list_free(dict_ldap->domain);
+    myfree(dict_ldap->query);
+    if (dict_ldap->result_format)
+        myfree(dict_ldap->result_format);
     argv_free(dict_ldap->result_attributes);
     myfree(dict_ldap->bind_dn);
     myfree(dict_ldap->bind_pw);
+    if (dict_ldap->ctx)
+       db_common_free_ctx(dict_ldap->ctx);
 #ifdef LDAP_API_FEATURE_X_OPENLDAP
     myfree(dict_ldap->tls_ca_cert_file);
     myfree(dict_ldap->tls_ca_cert_dir);
@@ -1202,7 +1173,6 @@ DICT   *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
 
     dict_ldap->ld = NULL;
     dict_ldap->parser = cfg_parser_alloc(ldapsource);
-    dict_ldap->ldapsource = mystrdup(ldapsource);
 
     server_host = cfg_get_str(dict_ldap->parser, "server_host",
                              "localhost", 1, 0);
@@ -1260,20 +1230,26 @@ DICT   *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
            if (strcasecmp(url_desc->lud_scheme, "ldaps") == 0)
                dict_ldap->ldap_ssl = 1;
            ldap_free_urldesc(url_desc);
-           vstring_sprintf_append(url_list, " %s", h);
+           if (VSTRING_LEN(url_list) > 0)
+               VSTRING_ADDCH(url_list, ' ');
+           vstring_strcat(url_list, h);
        } else {
+           if (VSTRING_LEN(url_list) > 0)
+               VSTRING_ADDCH(url_list, ' ');
            if (strrchr(h, ':'))
-               vstring_sprintf_append(url_list, " ldap://%s", h);
+               vstring_sprintf_append(url_list, "ldap://%s", h);
            else
-               vstring_sprintf_append(url_list, " ldap://%s:%d", h,
+               vstring_sprintf_append(url_list, "ldap://%s:%d", h,
                                       dict_ldap->server_port);
        }
 #else
-       vstring_sprintf_append(url_list, " %s", h);
+       if (VSTRING_LEN(url_list) > 0)
+           VSTRING_ADDCH(url_list, ' ');
+       vstring_strcat(url_list, h);
 #endif
     }
-    dict_ldap->server_host =
-       mystrdup(VSTRING_LEN(url_list) > 0 ? vstring_str(url_list) + 1 : "");
+    VSTRING_TERMINATE(url_list);
+    dict_ldap->server_host = vstring_export(url_list);
 
 #if defined(LDAP_API_FEATURE_X_OPENLDAP)
 
@@ -1286,7 +1262,6 @@ DICT   *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
                 dict_ldap->server_host);
 #endif
     myfree(server_host);
-    vstring_free(url_list);
 
     /*
      * Scope handling thanks to Carsten Hoeger of SuSE.
@@ -1312,18 +1287,15 @@ DICT   *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
 
     domainlist = cfg_get_str(dict_ldap->parser, "domain", "", 0, 0);
     if (*domainlist) {
-#ifdef MATCH_FLAG_NONE
-       dict_ldap->domain = match_list_init(MATCH_FLAG_NONE,
-                                           domainlist, 1, match_string);
-#else
-       dict_ldap->domain = match_list_init(domainlist, 1, match_string);
-#endif
+       dict_ldap->domain = string_list_init(MATCH_FLAG_NONE, domainlist);
        if (dict_ldap->domain == NULL)
-           msg_warn("%s: domain match list creation using \"%s\" failed, will continue without it",
-                    myname, domainlist);
-       if (msg_verbose)
-           msg_info("%s: domain list created using \"%s\"", myname,
-                    domainlist);
+           /*
+            * The "domain" optimization skips input keys that may in fact
+            * have unwanted matches in the database, so failure to create
+            * the match list is fatal.
+            */
+           msg_fatal("%s: %s: domain match list creation using '%s' failed",
+                     myname, ldapsource, domainlist);
     } else {
        dict_ldap->domain = NULL;
     }
@@ -1335,20 +1307,37 @@ DICT   *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
      * Thanks to Manuel Guesdon for spotting that this wasn't really getting
      * set.
      */
-    dict_ldap->timeout = cfg_get_int(dict_ldap->parser, "timeout",
-                                    10, 0, 0);
+    dict_ldap->timeout = cfg_get_int(dict_ldap->parser, "timeout", 10, 0, 0);
 
-    dict_ldap->query_filter =
-       cfg_get_str(dict_ldap->parser, "query_filter",
-                   "(mailacceptinggeneralid=%s)", 0, 0);
+#if 0  /* No benefit from changing this to match the MySQL/PGSQL syntax */
+    if ((dict_ldap->query =
+            cfg_get_str(dict_ldap->parser, "query", 0, 0, 0)) == 0)
+#endif
+        dict_ldap->query =
+           cfg_get_str(dict_ldap->parser, "query_filter",
+                       "(mailacceptinggeneralid=%s)", 0, 0);
 
-    dict_ldap->result_filter =
-       cfg_get_str(dict_ldap->parser, "result_filter", "%s", 0, 0);
+    if ((dict_ldap->result_format =
+        cfg_get_str(dict_ldap->parser, "result_format", 0, 0, 0)) == 0)
+        dict_ldap->result_format =
+                cfg_get_str(dict_ldap->parser, "result_filter", "%s", 1, 0);
 
-    if (strcmp(dict_ldap->result_filter, "%s") == 0) {
-       myfree(dict_ldap->result_filter);
-       dict_ldap->result_filter = NULL;
+    /*
+     * Must parse all templates before we can use db_common_expand()
+     * If data dependent substitutions are found in the search base,
+     * treat NO_SUCH_OBJECT search errors as a non-matching key, rather
+     * than a fatal run-time error.
+     */
+    dict_ldap->ctx = 0;
+    dict_ldap->dynamic_base =
+       db_common_parse(&dict_ldap->dict, &dict_ldap->ctx,
+                       dict_ldap->search_base, 1);
+    if (!db_common_parse(0, &dict_ldap->ctx, dict_ldap->query, 1)) {
+       msg_warn("%s: %s: Fixed query_filter %s is probably useless",
+                myname, ldapsource, dict_ldap->query);
     }
+    (void) db_common_parse(0, &dict_ldap->ctx, dict_ldap->result_format, 0);
+
     attr = cfg_get_str(dict_ldap->parser, "result_attribute",
                       "maildrop", 0, 0);
     dict_ldap->result_attributes = argv_split(attr, " ,\t\r\n");
@@ -1357,9 +1346,8 @@ DICT   *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
 
     attr = cfg_get_str(dict_ldap->parser, "special_result_attribute",
                       "", 0, 0);
-    if (*attr) {
+    if (*attr)
        argv_split_append(dict_ldap->result_attributes, attr, " ,\t\r\n");
-    }
     myfree(attr);
 
     /*
@@ -1378,44 +1366,32 @@ DICT   *dict_ldap_open(const char *ldapsource, int dummy, int dict_flags)
     dict_ldap->bind_pw = cfg_get_str(dict_ldap->parser, "bind_pw", "", 0, 0);
 
     /*
-     * get configured value of "cache"; default to false
+     * LDAP message caching never worked and is no longer supported.
      */
     tmp = cfg_get_bool(dict_ldap->parser, "cache", 0);
     if (tmp)
        msg_warn("%s: %s ignoring cache", myname, ldapsource);
 
-    /*
-     * get configured value of "cache_expiry"; default to 30 seconds
-     */
     tmp = cfg_get_int(dict_ldap->parser, "cache_expiry", -1, 0, 0);
     if (tmp >= 0)
        msg_warn("%s: %s ignoring cache_expiry", myname, ldapsource);
 
-    /*
-     * get configured value of "cache_size"; default to 32k
-     */
     tmp = cfg_get_int(dict_ldap->parser, "cache_size", -1, 0, 0);
     if (tmp >= 0)
        msg_warn("%s: %s ignoring cache_size", myname, ldapsource);
 
-    /*
-     * get configured value of "recursion_limit"; default to 1000
-     */
     dict_ldap->recursion_limit = cfg_get_int(dict_ldap->parser,
                                             "recursion_limit", 1000, 1, 0);
 
     /*
-     * get configured value of "expansion_limit"; default to 0
+     * XXX: The default should be non-zero for safety, but that is not
+     * backwards compatible.
      */
     dict_ldap->expansion_limit = cfg_get_int(dict_ldap->parser,
                                             "expansion_limit", 0, 0, 0);
 
-    /*
-     * get configured value of "size_limit"; default to expansion_limit
-     */
     dict_ldap->size_limit = cfg_get_int(dict_ldap->parser, "size_limit",
-                                       dict_ldap->expansion_limit,
-                                       0, 0);
+                                       dict_ldap->expansion_limit, 0, 0);
 
     /*
      * Alias dereferencing suggested by Mike Mattice.
index f1abc847cffe64e244261f6459ffcc15e8fc9060..e16d22933e46148c0de92400c7d86049341535d5 100644 (file)
 /*     Password for the above.
 /* .IP \fIdbname\fR
 /*     Name of the database.
+/* .IP \fIdomain\fR
+/*      List of domains the queries should be restricted to.  If
+/*      specified, only FQDN addresses whose domain parts matching this
+/*      list will be queried against the SQL database.  Lookups for
+/*      partial addresses are also supressed.  This can significantly
+/*      reduce the query load on the server.
+/* .IP \fIquery\fR
+/*      Query template, before the query is actually issued, variable
+/*     substitutions are performed. See mysql_table(5) for details. If
+/*     No query is specified, the legacy variables \fItable\fR,
+/*     \fIselect_field\fR, \fIwhere_field\fR and \fIadditional_conditions\fR
+/*     are used to construct the query template.
+/* .IP \fIresult_format\fR
+/*      The format used to expand results from queries.  Substitutions
+/*      are performed as described in mysql_table(5). Defaults to returning
+/*     the lookup result unchanged.
+/* .IP expansion_limit
+/*     Limit (if any) on the total number of lookup result values. Lookups which
+/*     exceed the limit fail with dict_errno=DICT_ERR_RETRY. Note that each
+/*     non-empty (and non-NULL) column of a multi-column result row counts as
+/*     one result.
 /* .IP \fItable\fR
-/*     Name of the table.
+/*     When \fIquery\fR is not set, name of the table used to construct the
+/*     query string. This provides compatibility with older releases.
 /* .IP \fIselect_field\fR
-/*     Name of the result field.
+/*     When \fIquery\fR is not set, name of the result field used to
+/*     construct the query string. This provides compatibility with older
+/*     releases.
 /* .IP \fIwhere_field\fR
-/*     Field used in the WHERE clause.
+/*     When \fIquery\fR is not set, name of the where clause field used to
+/*     construct the query string. This provides compatibility with older
+/*     releases.
 /* .IP \fIadditional_conditions\fR
-/*     Additional conditions to the WHERE clause.
+/*     When \fIquery\fR is not set, additional where clause conditions used
+/*     to construct the query string. This provides compatibility with older
+/*     releases.
+/* .IP \fIhosts\fR
+/*     List of hosts to connect to.
+/* .PP
+/*     For example, if you want the map to reference databases of
+/*     the name "your_db" and execute a query like this: select
+/*     forw_addr from aliases where alias like '<some username>'
+/*     against any database called "vmailer_info" located on hosts
+/*     host1.some.domain and host2.some.domain, logging in as user
+/*     "vmailer" and password "passwd" then the configuration file
+/*     should read:
+/* .PP
+/*     \fIuser\fR = \fBvmailer\fR
+/* .br
+/*     \fIpassword\fR = \fBpasswd\fR
+/* .br
+/*     \fIdbname\fR = \fBvmailer_info\fR
+/* .br
+/*     \fItable\fR = \fBaliases\fR
+/* .br
+/*     \fIselect_field\fR = \fBforw_addr\fR
+/* .br
+/*     \fIwhere_field\fR = \fBalias\fR
+/* .br
+/*     \fIhosts\fR = \fBhost1.some.domain\fR \fBhost2.some.domain\fR
+/* .IP \fIadditional_conditions\fR
+/*      Backward compatibility when \fIquery\fR is not set, additional
+/*     conditions to the WHERE clause.
 /* .IP \fIhosts\fR
 /*     List of hosts to connect to.
 /* .PP
 /* Global library. */
 
 #include "cfg_parser.h"
+#include "db_common.h"
 
 /* Application-specific. */
 
@@ -156,22 +212,18 @@ typedef struct {
 } PLMYSQL;
 
 typedef struct {
+    DICT    dict;
     CFG_PARSER *parser;
+    char   *query;
+    char   *result_format;
+    STRING_LIST *domain;   
+    void   *ctx;
+    int     expansion_limit;
     char   *username;
     char   *password;
     char   *dbname;
-    char   *table;
-    char   *select_field;
-    char   *where_field;
-    char   *additional_conditions;
-    char  **hostnames;
-    int     len_hosts;
-} MYSQL_NAME;
-
-typedef struct {
-    DICT    dict;
+    ARGV   *hosts;
     PLMYSQL *pldb;
-    MYSQL_NAME *name;
 } DICT_MYSQL;
 
 #define STATACTIVE                     (1<<0)
@@ -186,7 +238,7 @@ typedef struct {
 #define IDLE_CONN_INTV                 60              /* 1 minute */
 
 /* internal function declarations */
-static PLMYSQL *plmysql_init(char *hostnames[], int);
+static PLMYSQL *plmysql_init(ARGV *);
 static MYSQL_RES *plmysql_query(PLMYSQL *, const char *, char *, char *, char *);
 static void plmysql_dealloc(PLMYSQL *);
 static void plmysql_close_host(HOST *);
@@ -195,89 +247,120 @@ static void plmysql_connect_single(HOST *, char *, char *, char *);
 static const char *dict_mysql_lookup(DICT *, const char *);
 DICT   *dict_mysql_open(const char *, int, int);
 static void dict_mysql_close(DICT *);
-static MYSQL_NAME *mysqlname_parse(const char *);
+static void mysql_parse_config(DICT_MYSQL *, const char *);
 static HOST *host_init(const char *);
 
+/* dict_mysql_quote - escape SQL metacharacters in input string */
+
+static void dict_mysql_quote(DICT *dict, const char *name, VSTRING *result)
+{
+    DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
+    int len = strlen(name);
+    int buflen = 2*len + 1;
+
+    /*
+     * We won't get integer overflows in 2*len + 1, because Postfix
+     * input keys have reasonable size limits, better safe than sorry.
+     */
+    if (buflen < len)
+       msg_panic("dict_mysql_quote: integer overflow in 2*%d+1", len);
+    VSTRING_SPACE(result, buflen);
+
+    /*
+     * XXX Too expensive to find out which connection is still open at
+     * this point. Grrr!
+     */
+#if 0 && defined(MYSQL_VERSION_ID) && MYSQL_VERSION_ID >= 40000
+    mysql_real_escape_string(dict_mysql->pldb->db_hosts[i].db,
+                            vstring_end(result), name, len);
+#else
+    mysql_escape_string(vstring_end(result), name, len);
+#endif
+    VSTRING_SKIP(result);
+}
 
+/* dict_mysql_lookup - find database entry */
 
-/**********************************************************************
- * public interface dict_mysql_lookup
- * find database entry return 0 if no alias found, set dict_errno
- * on errors to DICT_ERRBO_RETRY and set dict_errno to 0 on success
- *********************************************************************/
 static const char *dict_mysql_lookup(DICT *dict, const char *name)
 {
+    char   *myname = "dict_mysql_lookup";
+    DICT_MYSQL *dict_mysql = (DICT_MYSQL *)dict;
+    PLMYSQL *pldb = dict_mysql->pldb;
     MYSQL_RES *query_res;
     MYSQL_ROW row;
-    DICT_MYSQL *dict_mysql;
-    PLMYSQL *pldb;
     static VSTRING *result;
-    static VSTRING *query = 0;
-    int     i,
-            j,
-            numrows;
-    char   *name_escaped = 0;
-
-    dict_mysql = (DICT_MYSQL *) dict;
-    pldb = dict_mysql->pldb;
-    /* initialization  for query */
-    query = vstring_alloc(24);
-    vstring_strcpy(query, "");
-    if ((name_escaped = (char *) mymalloc((sizeof(char) * (strlen(name) * 2) +1))) == NULL) {
-       msg_fatal("dict_mysql_lookup: out of memory.");
+    static VSTRING *query;
+    int     i;
+    int     j;
+    int     numrows;
+    int     expansion;
+    const char *r;
+
+    dict_errno = 0;
+    
+    /*
+     * If there is a domain list for this map, then only search for
+     * addresses in domains on the list. This can significantly reduce
+     * the load on the server. Do not try "@domain" keys.
+     */
+    if (db_common_check_domain(dict_mysql->domain, name) == 0) {
+        if (msg_verbose)
+           msg_info("%s: Skipping lookup of '%s'", myname, name);
+        return (0);
     }
-    /* prepare the query */
-    mysql_escape_string(name_escaped, name, (unsigned int) strlen(name));
-    vstring_sprintf(query, "select %s from %s where %s = '%s' %s", dict_mysql->name->select_field,
-       dict_mysql->name->table, dict_mysql->name->where_field, name_escaped,
-                   dict_mysql->name->additional_conditions);
-    if (msg_verbose)
-       msg_info("dict_mysql_lookup using sql query: %s", vstring_str(query));
-    /* free mem associated with preparing the query */
-    myfree(name_escaped);
+
+#define INIT_VSTR(buf, len) do { \
+       if (buf == 0) \
+           buf = vstring_alloc(len); \
+       VSTRING_RESET(buf); \
+       VSTRING_TERMINATE(buf); \
+    } while (0)
+
+    INIT_VSTR(query, 10);
+
+    /*
+     * Suppress the lookup if the query expansion is empty
+     */
+    if (!db_common_expand(dict_mysql->ctx, dict_mysql->query,
+                         name, 0, query, dict_mysql_quote))
+        return (0);
+    
     /* do the query - set dict_errno & cleanup if there's an error */
-    if ((query_res = plmysql_query(pldb,
-                                  vstring_str(query),
-                                  dict_mysql->name->dbname,
-                                  dict_mysql->name->username,
-                                  dict_mysql->name->password)) == 0) {
+    if ((query_res = plmysql_query(pldb, vstring_str(query),
+                                  dict_mysql->dbname,
+                                  dict_mysql->username,
+                                  dict_mysql->password)) == 0) {
        dict_errno = DICT_ERR_RETRY;
-       vstring_free(query);
-       return 0;
+       return (0);
     }
-    dict_errno = 0;
-    /* free the vstring query */
-    vstring_free(query);
+
     numrows = mysql_num_rows(query_res);
     if (msg_verbose)
-       msg_info("dict_mysql_lookup: retrieved %d rows", numrows);
+       msg_info("%s: retrieved %d rows", myname, numrows);
     if (numrows == 0) {
        mysql_free_result(query_res);
        return 0;
     }
-    if (result == 0)
-       result = vstring_alloc(10);
-    vstring_strcpy(result, "");
-    for (i = 0; i < numrows; i++) {
+
+    INIT_VSTR(result, 10);
+
+    for (expansion = i = 0; i < numrows && dict_errno == 0; i++) {
        row = mysql_fetch_row(query_res);
-       if (i > 0)
-           vstring_strcat(result, ",");
        for (j = 0; j < mysql_num_fields(query_res); j++) {
-           if (row[j] == 0) {
-               if (msg_verbose > 1)
-                   msg_info("dict_mysql_lookup: null field #%d row #%d", j, i);
-               mysql_free_result(query_res);
-               return (0);
+           if (db_common_expand(dict_mysql->ctx, dict_mysql->result_format,
+                                row[j], name, result, 0)
+               && dict_mysql->expansion_limit > 0
+               && ++expansion > dict_mysql->expansion_limit) {
+               msg_warn("%s: %s: Expansion limit exceeded for key: '%s'",
+                        myname, dict_mysql->parser->name, name);
+               dict_errno = DICT_ERR_RETRY;
+               break;
            }
-           if (j > 0)
-               vstring_strcat(result, ",");
-           vstring_strcat(result, row[j]);
-           if (msg_verbose > 1)
-               msg_info("dict_mysql_lookup: retrieved field: %d: %s", j, row[j]);
        }
     }
     mysql_free_result(query_res);
-    return vstring_str(result);
+    r = vstring_str(result);
+    return ((dict_errno == 0 && *r) ? r : 0);
 }
 
 /* dict_mysql_check_stat - check the status of a host */
@@ -461,12 +544,77 @@ static void plmysql_down_host(HOST *host)
     event_cancel_timer(dict_mysql_event, (char *) host);
 }
 
-/**********************************************************************
- * public interface dict_mysql_open
- *    create association with database with appropriate values
- *    parse the map's config file
- *    allocate memory
- **********************************************************************/
+/* mysql_parse_config - parse mysql configuration file */
+
+static void mysql_parse_config(DICT_MYSQL *dict_mysql, const char *mysqlcf)
+{
+    const char *myname = "mysqlname_parse";
+    CFG_PARSER *p;
+    VSTRING *buf;
+    int     i;
+    char   *hosts;
+    char   *domain;
+    
+    p = dict_mysql->parser = cfg_parser_alloc(mysqlcf);
+    dict_mysql->username = cfg_get_str(p, "user", "", 0, 0);
+    dict_mysql->password = cfg_get_str(p, "password", "", 0, 0);
+    dict_mysql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
+    dict_mysql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
+    /*
+     * XXX: The default should be non-zero for safety, but that is not
+     * backwards compatible.
+     */
+    dict_mysql->expansion_limit = cfg_get_int(dict_mysql->parser,
+                                             "expansion_limit", 0, 0, 0);
+
+    if ((dict_mysql->query = cfg_get_str(p, "query", NULL, 0, 0)) == 0) {
+        /*
+         * No query specified -- fallback to building it from components
+         * (old style "select %s from %s where %s")
+         */                 
+       buf = vstring_alloc(64);
+       db_common_sql_build_query(buf, p);
+       dict_mysql->query = vstring_export(buf);
+    }
+
+    /*
+     * Must parse all templates before we can use db_common_expand()
+     */
+    dict_mysql->ctx = 0;
+    (void) db_common_parse(&dict_mysql->dict, &dict_mysql->ctx,
+                          dict_mysql->query, 1);
+    (void) db_common_parse(0, &dict_mysql->ctx, dict_mysql->result_format, 0);
+
+    domain = cfg_get_str(p, "domain", "", 0, 0);
+    if (*domain) {
+        if (!(dict_mysql->domain = string_list_init(MATCH_FLAG_NONE, domain)))
+           /*
+            * The "domain" optimization skips input keys that may in fact
+            * have unwanted matches in the database, so failure to create
+            * the match list is fatal.
+            */
+           msg_fatal("%s: %s: domain match list creation using '%s' failed",
+                     myname, mysqlcf, domain);
+    }
+    else
+        dict_mysql->domain = 0;
+    myfree(domain);
+
+    hosts = cfg_get_str(p, "hosts", "", 0, 0);
+
+    dict_mysql->hosts = argv_split(hosts, " ,\t\r\n");
+    if (dict_mysql->hosts->argc == 0) {
+       argv_add(dict_mysql->hosts, "localhost", ARGV_END);
+       argv_terminate(dict_mysql->hosts);
+       if (msg_verbose)
+           msg_info("%s: %s: no hostnames specified, defaulting to '%s'",
+                    myname, mysqlcf, dict_mysql->hosts->argv[0]);
+    }
+    myfree(hosts);
+}
+
+/* dict_mysql_open - open MYSQL data base */
+
 DICT   *dict_mysql_open(const char *name, int open_flags, int dict_flags)
 {
     DICT_MYSQL *dict_mysql;
@@ -483,95 +631,31 @@ DICT   *dict_mysql_open(const char *name, int open_flags, int dict_flags)
     dict_mysql->dict.lookup = dict_mysql_lookup;
     dict_mysql->dict.close = dict_mysql_close;
     dict_mysql->dict.flags = dict_flags | DICT_FLAG_FIXED;
-    dict_mysql->name = mysqlname_parse(name);
-    dict_mysql->pldb = plmysql_init(dict_mysql->name->hostnames,
-                                   dict_mysql->name->len_hosts);
+    mysql_parse_config(dict_mysql, name);
+    dict_mysql->pldb = plmysql_init(dict_mysql->hosts);
     if (dict_mysql->pldb == NULL)
        msg_fatal("couldn't intialize pldb!\n");
     return (DICT_DEBUG (&dict_mysql->dict));
 }
 
-/* mysqlname_parse - parse mysql configuration file */
-static MYSQL_NAME *mysqlname_parse(const char *mysqlcf)
-{
-    const char *myname = "mysqlname_parse";
-    int     i;
-    char   *hosts;
-    MYSQL_NAME *name = (MYSQL_NAME *) mymalloc(sizeof(MYSQL_NAME));
-    ARGV   *hosts_argv;
-
-    /* parser */
-    name->parser = cfg_parser_alloc(mysqlcf);
-
-    /* username */
-    name->username = cfg_get_str(name->parser, "user", "", 0, 0);
-
-    /* password */
-    name->password = cfg_get_str(name->parser, "password", "", 0, 0);
-
-    /* database name */
-    name->dbname = cfg_get_str(name->parser, "dbname", "", 1, 0);
-
-    /* table name */
-    name->table = cfg_get_str(name->parser, "table", "", 1, 0);
-
-    /* select field */
-    name->select_field = cfg_get_str(name->parser, "select_field", "", 1, 0);
-
-    /* where field */
-    name->where_field = cfg_get_str(name->parser, "where_field", "", 1, 0);
-
-    /* additional conditions */
-    name->additional_conditions = cfg_get_str(name->parser,
-                                             "additional_conditions",
-                                             "", 0, 0);
-
-    /* mysql server hosts */
-    hosts = cfg_get_str(name->parser, "hosts", "", 0, 0);
-
-    /* coo argv interface */
-    hosts_argv = argv_split(hosts, " ,\t\r\n");
-    if (hosts_argv->argc == 0) {               /* no hosts specified,
-                                                * default to 'localhost' */
-       if (msg_verbose)
-           msg_info("%s: %s: no hostnames specified, defaulting to 'localhost'",
-                    myname, mysqlcf);
-       argv_add(hosts_argv, "localhost", ARGV_END);
-       argv_terminate(hosts_argv);
-    }
-    name->len_hosts = hosts_argv->argc;
-    name->hostnames = (char **) mymalloc((sizeof(char *)) * name->len_hosts);
-    i = 0;
-    for (i = 0; hosts_argv->argv[i] != NULL; i++) {
-       name->hostnames[i] = mystrdup(hosts_argv->argv[i]);
-       if (msg_verbose)
-           msg_info("%s: %s: adding host '%s' to list of mysql server hosts",
-                    myname, mysqlcf, name->hostnames[i]);
-    }
-    myfree(hosts);
-    argv_free(hosts_argv);
-    return name;
-}
-
-
 /*
  * plmysql_init - initalize a MYSQL database.
  *                 Return NULL on failure, or a PLMYSQL * on success.
  */
-static PLMYSQL *plmysql_init(char *hostnames[], int len_hosts)
+static PLMYSQL *plmysql_init(ARGV *hosts)
 {
     PLMYSQL *PLDB;
     int     i;
 
-    if ((PLDB = (PLMYSQL *) mymalloc(sizeof(PLMYSQL))) == NULL) {
+    if ((PLDB = (PLMYSQL *) mymalloc(sizeof(PLMYSQL))) == 0)
        msg_fatal("mymalloc of pldb failed");
-    }
-    PLDB->len_hosts = len_hosts;
-    if ((PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * len_hosts)) == NULL)
-       return NULL;
-    for (i = 0; i < len_hosts; i++) {
-       PLDB->db_hosts[i] = host_init(hostnames[i]);
-    }
+
+    PLDB->len_hosts = hosts->argc;
+    if ((PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * hosts->argc)) == 0)
+       return (0);
+    for (i = 0; i < hosts->argc; i++)
+       PLDB->db_hosts[i] = host_init(hosts->argv[i]);
+
     return PLDB;
 }
 
@@ -619,29 +703,26 @@ static HOST *host_init(const char *hostname)
     return host;
 }
 
-/**********************************************************************
- * public interface dict_mysql_close
- * unregister, disassociate from database, freeing appropriate memory
- **********************************************************************/
+/* dict_mysql_close - close MYSQL database */
+
 static void dict_mysql_close(DICT *dict)
 {
     int     i;
     DICT_MYSQL *dict_mysql = (DICT_MYSQL *) dict;
 
     plmysql_dealloc(dict_mysql->pldb);
-    cfg_parser_free(dict_mysql->name->parser);
-    myfree(dict_mysql->name->username);
-    myfree(dict_mysql->name->password);
-    myfree(dict_mysql->name->dbname);
-    myfree(dict_mysql->name->table);
-    myfree(dict_mysql->name->select_field);
-    myfree(dict_mysql->name->where_field);
-    myfree(dict_mysql->name->additional_conditions);
-    for (i = 0; i < dict_mysql->name->len_hosts; i++) {
-       myfree(dict_mysql->name->hostnames[i]);
-    }
-    myfree((char *) dict_mysql->name->hostnames);
-    myfree((char *) dict_mysql->name);
+    cfg_parser_free(dict_mysql->parser);
+    myfree(dict_mysql->username);
+    myfree(dict_mysql->password);
+    myfree(dict_mysql->dbname);
+    myfree(dict_mysql->query);
+    myfree(dict_mysql->result_format);
+    if (dict_mysql->domain)
+        string_list_free(dict_mysql->domain);
+    if (dict_mysql->hosts)
+       argv_free(dict_mysql->hosts);
+    if (dict_mysql->ctx)
+       db_common_free_ctx(dict_mysql->ctx);
     dict_free(dict);
 }
 
index 1657d65e7274cefd8308efa5e86cd0bf54620e8c..78ae4b28adf938adca8c1146445f353e1a9cc580 100644 (file)
 /*     Password for the above.
 /* .IP \fIdbname\fR
 /*     Name of the database.
+/* .IP \fIquery\fR
+/*     Query template. If not defined a default query template is constructed
+/*     from the legacy \fIselect_function\fR or failing that the \fItable\fR,
+/*     \fIselect_field\fR, \fIwhere_field\fR, and \fIadditional_conditions\fR
+/*     parameters. Before the query is issues, variable substitutions are
+/*     performed. See pgsql_table(5).
+/* .IP \fIdomain\fR
+/*     List of domains the queries should be restricted to.  If
+/*     specified, only FQDN addresses whose domain parts matching this
+/*     list will be queried against the SQL database.  Lookups for
+/*     partial addresses are also supressed.  This can significantly
+/*     reduce the query load on the server.
+/* .IP \fIresult_format\fR
+/*     The format used to expand results from queries.  Substitutions
+/*     are performed as described in pgsql_table(5). Defaults to returning
+/*     the lookup result unchanged.
+/* .IP expansion_limit
+/*     Limit (if any) on the total number of lookup result values. Lookups which
+/*     exceed the limit fail with dict_errno=DICT_ERR_RETRY. Note that each
+/*     non-empty (and non-NULL) column of a multi-column result row counts as
+/*     one result.
+/* .IP \fIselect_function\fR
+/*     When \fIquery\fR is not defined, the function to be used instead of
+/*     the default query based on the legacy \fItable\fR, \fIselect_field\fR,
+/*     \fIwhere_field\fR, and \fIadditional_conditions\fR parameters.
 /* .IP \fItable\fR
-/*     Name of the table.
+/*     When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
+/*     FROM table used to construct the default query template, see pgsql_table(5).
 /* .IP \fIselect_field\fR
-/*     Name of the result field.
+/*     When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
+/*     SELECT field used to construct the default query template, see pgsql_table(5).
 /* .IP \fIwhere_field\fR
-/*     Field used in the WHERE clause.
+/*     When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
+/*     WHERE field used to construct the default query template, see pgsql_table(5).
 /* .IP \fIadditional_conditions\fR
-/*     Additional conditions to the WHERE clause.
-/* .IP \fIquery\fR
-/*     Query overriding \fItable\fR, \fIselect_field\fR,
-/*     \fIwhere_field\fR, and \fIadditional_conditions\fR.  Before the
-/*     query is actually issued, all occurrences of %s are replaced
-/*     with the address to look up, %u are replaced with the user
-/*     portion, and %d with the domain portion.
-/* .IP \fIselect_function\fR
-/*     Function to be used instead of the SELECT statement.  Overrides
-/*     both \fIquery\fR and \fItable\fR, \fIselect_field\fR,
-/*     \fIwhere_field\fR, and \fIadditional_conditions\fR settings.
+/*     When \fIquery\fR and \fIselect_function\fR are not defined, the name of the
+/*     additional text to add to the WHERE field in the default query template (this
+/*     usually begins with "and") see pgsql_table(5).
 /* .IP \fIhosts\fR
 /*     List of hosts to connect to.
 /* .PP
 /* Global library. */
 
 #include "cfg_parser.h"
+#include "db_common.h"
 
 /* Application-specific. */
 
@@ -183,24 +204,19 @@ typedef struct {
 } PLPGSQL;
 
 typedef struct {
+    DICT    dict;
     CFG_PARSER *parser;
+    char   *query;
+    char   *result_format;
+    STRING_LIST *domain;
+    void   *ctx;
+    int     expansion_limit;
     char   *username;
     char   *password;
     char   *dbname;
     char   *table;
-    char   *query;                     /* if set, overrides fields, etc */
-    char   *select_function;
-    char   *select_field;
-    char   *where_field;
-    char   *additional_conditions;
-    char  **hostnames;
-    int     len_hosts;
-} PGSQL_NAME;
-
-typedef struct {
-    DICT    dict;
+    ARGV   *hosts;
     PLPGSQL *pldb;
-    PGSQL_NAME *name;
 } DICT_PGSQL;
 
 
@@ -208,7 +224,7 @@ typedef struct {
 #define PGSQL_RES PGresult
 
 /* internal function declarations */
-static PLPGSQL *plpgsql_init(char *hostnames[], int);
+static PLPGSQL *plpgsql_init(ARGV *);
 static PGSQL_RES *plpgsql_query(PLPGSQL *, const char *, char *, char *, char *);
 static void plpgsql_dealloc(PLPGSQL *);
 static void plpgsql_close_host(HOST *);
@@ -217,213 +233,129 @@ static void plpgsql_connect_single(HOST *, char *, char *, char *);
 static const char *dict_pgsql_lookup(DICT *, const char *);
 DICT   *dict_pgsql_open(const char *, int, int);
 static void dict_pgsql_close(DICT *);
-static PGSQL_NAME *pgsqlname_parse(const char *);
 static HOST *host_init(const char *);
 
 
+/* dict_pgsql_quote - escape SQL metacharacters in input string */
 
-/**********************************************************************
- * public interface dict_pgsql_lookup
- * find database entry return 0 if no alias found, set dict_errno
- * on errors to DICT_ERROR_RETRY and set dict_errno to 0 on success
- *********************************************************************/
-static void pgsql_escape_string(char *new, const char *old, unsigned int len)
+static void dict_pgsql_quote(DICT *unused, const char *name, VSTRING *result)
 {
-    unsigned int x,
-            y;
+    const char *sub;
 
     /*
      * XXX We really should be using an escaper that is provided by the PGSQL
      * library. The code below seems to be over-kill (see RUS-CERT Advisory
      * 2001-08:01), but it's better to be safe than to be sorry -- Wietse
      */
-    for (x = 0, y = 0; x < len; x++, y++) {
-       switch (old[x]) {
+     for (sub = name; *sub; sub++) {
+        switch(*sub) {
        case '\n':
-           new[y++] = '\\';
-           new[y] = 'n';
+            vstring_strcat(result, "\\n");
            break;
        case '\r':
-           new[y++] = '\\';
-           new[y] = 'r';
+           vstring_strcat(result, "\\r");
            break;
        case '\'':
-           new[y++] = '\\';
-           new[y] = '\'';
+           vstring_strcat(result, "\\'");
            break;
        case '"':
-           new[y++] = '\\';
-           new[y] = '"';
+           vstring_strcat(result, "\\\"");
            break;
        case 0:
-           new[y++] = '\\';
-           new[y] = '0';
+           vstring_strcat(result, "\\0");
            break;
        default:
-           new[y] = old[x];
+           VSTRING_ADDCH(result, *sub);
            break;
        }
     }
-    new[y] = 0;
+    VSTRING_TERMINATE(result);
 }
 
-/*
- * expand a filter (lookup or result)
- */
-static void dict_pgsql_expand_filter(char *filter, char *value, VSTRING *out)
-{
-    const char *myname = "dict_pgsql_expand_filter";
-    char   *sub,
-           *end;
-
-    /*
-     * Yes, replace all instances of %s with the address to look up. Replace
-     * %u with the user portion, and %d with the domain portion.
-     */
-    sub = filter;
-    end = sub + strlen(filter);
-    while (sub < end) {
-
-       /*
-        * Make sure it's %[sud] and not something else.  For backward
-        * compatibilty, treat anything other than %u or %d as %s, with a
-        * warning.
-        */
-       if (*(sub) == '%') {
-           char   *u = value;
-           char   *p = strrchr(u, '@');
-
-           switch (*(sub + 1)) {
-           case 'd':
-               if (p)
-                   vstring_strcat(out, p + 1);
-               break;
-           case 'u':
-               if (p)
-                   vstring_strncat(out, u, p - u);
-               else
-                   vstring_strcat(out, u);
-               break;
-           default:
-               msg_warn
-                   ("%s: Invalid filter substitution format '%%%c'!",
-                    myname, *(sub + 1));
-               break;
-           case 's':
-               vstring_strcat(out, u);
-               break;
-           }
-           sub++;
-       } else
-           vstring_strncat(out, sub, 1);
-       sub++;
-    }
-}
+/* dict_pgsql_lookup - find database entry */
 
 static const char *dict_pgsql_lookup(DICT *dict, const char *name)
 {
+    char   *myname = "dict_pgsql_lookup";
     PGSQL_RES *query_res;
     DICT_PGSQL *dict_pgsql;
     PLPGSQL *pldb;
+    static VSTRING *query;
     static VSTRING *result;
-    static VSTRING *query = 0;
-    int     i,
-            j,
-            numrows;
-    char   *name_escaped = 0;
-    int     isFunctionCall;
+    int     i;
+    int     j;
+    int     numrows;
     int     numcols;
-
+    int     expansion;
+    const char *r;
+   
     dict_pgsql = (DICT_PGSQL *) dict;
     pldb = dict_pgsql->pldb;
-    /* initialization  for query */
-    query = vstring_alloc(24);
-    vstring_strcpy(query, "");
-    if ((name_escaped = (char *) mymalloc((sizeof(char) * (strlen(name) * 2) +1))) == NULL) {
-       msg_fatal("dict_pgsql_lookup: out of memory.");
-    }
-    /* prepare the query */
-    pgsql_escape_string(name_escaped, name, (unsigned int) strlen(name));
-
-    /* Build SQL - either a select from table or select a function */
-
-    isFunctionCall = (dict_pgsql->name->select_function != NULL);
-    if (isFunctionCall) {
-       vstring_sprintf(query, "select %s('%s')",
-                       dict_pgsql->name->select_function,
-                       name_escaped);
-    } else if (dict_pgsql->name->query) {
-       dict_pgsql_expand_filter(dict_pgsql->name->query, name_escaped, query);
-    } else {
-       vstring_sprintf(query, "select %s from %s where %s = '%s' %s",
-                       dict_pgsql->name->select_field,
-                       dict_pgsql->name->table,
-                       dict_pgsql->name->where_field,
-                       name_escaped,
-                       dict_pgsql->name->additional_conditions);
-    }
 
-    if (msg_verbose)
-       msg_info("dict_pgsql_lookup using sql query: %s", vstring_str(query));
+#define INIT_VSTR(buf, len) do { \
+       if (buf == 0) \
+           buf = vstring_alloc(len); \
+       VSTRING_RESET(buf); \
+       VSTRING_TERMINATE(buf); \
+    } while (0)
 
-    /* free mem associated with preparing the query */
-    myfree(name_escaped);
+    INIT_VSTR(query, 10);
+    INIT_VSTR(result, 10);
+
+    dict_errno = 0;
+    /*
+     * If there is a domain list for this map, then only search for
+     * addresses in domains on the list. This can significantly reduce
+     * the load on the server. Do not try "@domain" keys.
+     */
+    if (db_common_check_domain(dict_pgsql->domain, name) == 0) {
+        if (msg_verbose)
+           msg_info("%s: Skipping lookup of '%s'", myname, name);
+        return (0);
+    }
 
+    /*
+     * Suppress the actual lookup if the expansion is empty
+     */
+    if (!db_common_expand(dict_pgsql->ctx, dict_pgsql->query,
+                         name, 0, query, dict_pgsql_quote))
+       return (0);
+    
     /* do the query - set dict_errno & cleanup if there's an error */
-    if ((query_res = plpgsql_query(pldb,
-                                  vstring_str(query),
-                                  dict_pgsql->name->dbname,
-                                  dict_pgsql->name->username,
-                                  dict_pgsql->name->password)) == 0) {
+    if ((query_res = plpgsql_query(pldb, vstring_str(query),
+                                  dict_pgsql->dbname,
+                                  dict_pgsql->username,
+                                  dict_pgsql->password)) == 0) {
        dict_errno = DICT_ERR_RETRY;
-       vstring_free(query);
        return 0;
     }
-    dict_errno = 0;
-    /* free the vstring query */
-    vstring_free(query);
+    
     numrows = PQntuples(query_res);
     if (msg_verbose)
-       msg_info("dict_pgsql_lookup: retrieved %d rows", numrows);
+       msg_info("%s: retrieved %d rows", myname, numrows);
     if (numrows == 0) {
        PQclear(query_res);
        return 0;
     }
     numcols = PQnfields(query_res);
 
-    if (numcols == 1 && numrows == 1 && isFunctionCall) {
-
-       /*
-        * We do the above check because PostgreSQL 7.3 will allow functions
-        * to return result sets
-        */
-       if (PQgetisnull(query_res, 0, 0) == 1) {
-
-           /*
-            * Functions returning a single row & column that is null are
-            * deemed to have not found the key.
-            */
-           PQclear(query_res);
-           return 0;
-       }
-    }
-    if (result == 0)
-       result = vstring_alloc(10);
-
-    vstring_strcpy(result, "");
-    for (i = 0; i < numrows; i++) {
-       if (i > 0)
-           vstring_strcat(result, ",");
+    for (expansion = i = 0; i < numrows && dict_errno == 0; i++) {
        for (j = 0; j < numcols; j++) {
-           if (j > 0)
-               vstring_strcat(result, ",");
-           vstring_strcat(result, PQgetvalue(query_res, i, j));
-           if (msg_verbose > 1)
-               msg_info("dict_pgsql_lookup: retrieved field: %d: %s", j, PQgetvalue(query_res, i, j));
+           r = PQgetvalue(query_res, i, j);
+           if (db_common_expand(dict_pgsql->ctx, dict_pgsql->result_format,
+                                r, name, result, 0)
+               && dict_pgsql->expansion_limit > 0
+               && ++expansion > dict_pgsql->expansion_limit) {
+               msg_warn("%s: %s: Expansion limit exceeded for key: '%s'",
+                        myname, dict_pgsql->parser->name, name);
+               dict_errno = DICT_ERR_RETRY;
+               break;
+           }
        }
     }
     PQclear(query_res);
-    return vstring_str(result);
+    r = vstring_str(result);
+    return ((dict_errno == 0 && *r) ? r : 0);
 }
 
 /* dict_pgsql_check_stat - check the status of a host */
@@ -603,19 +535,87 @@ static void plpgsql_down_host(HOST *host)
     event_cancel_timer(dict_pgsql_event, (char *) host);
 }
 
-/**********************************************************************
- * public interface dict_pgsql_open
- *    create association with database with appropriate values
- *    parse the map's config file
- *    allocate memory
- **********************************************************************/
-DICT   *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
+/* pgsql_parse_config - parse pgsql configuration file */
+
+static void pgsql_parse_config(DICT_PGSQL *dict_pgsql, const char *pgsqlcf)
 {
-    DICT_PGSQL *dict_pgsql;
+    const char *myname = "pgsql_parse_config";
+    CFG_PARSER *p;
+    int     i;
+    char   *hosts;
+    VSTRING *query;
+    char   *select_function;
+    char   *domain;
 
+    p = dict_pgsql->parser = cfg_parser_alloc(pgsqlcf);
+    dict_pgsql->username = cfg_get_str(p, "user", "", 0, 0);
+    dict_pgsql->password = cfg_get_str(p, "password", "", 0, 0);
+    dict_pgsql->dbname = cfg_get_str(p, "dbname", "", 1, 0);
+    dict_pgsql->result_format = cfg_get_str(p, "result_format", "%s", 1, 0);
     /*
-     * Sanity checks.
+     * XXX: The default should be non-zero for safety, but that is not
+     * backwards compatible.
      */
+    dict_pgsql->expansion_limit = cfg_get_int(dict_pgsql->parser,
+                                             "expansion_limit", 0, 0, 0);
+
+    if ((dict_pgsql->query = cfg_get_str(p, "query", 0, 0, 0)) == 0) {
+         /*
+          * No query specified -- fallback to building it from components
+          * ( old style "select %s from %s where %s" )
+          */
+       query = vstring_alloc(64);
+       select_function = cfg_get_str(p, "select_function", 0, 0, 0);
+       if (select_function != 0) {
+           vstring_sprintf(query, "SELECT %s('%%s')", select_function);
+           myfree(select_function);
+       } else
+            db_common_sql_build_query(query, p);
+       dict_pgsql->query = vstring_export(query);
+    }
+
+    /*
+     * Must parse all templates before we can use db_common_expand()
+     */
+    dict_pgsql->ctx = 0;
+    (void) db_common_parse(&dict_pgsql->dict, &dict_pgsql->ctx,
+                          dict_pgsql->query, 1);
+    (void) db_common_parse(0, &dict_pgsql->ctx, dict_pgsql->result_format, 0);
+
+    domain = cfg_get_str(p, "domain", "", 0, 0);
+    if (*domain) {
+        if (!(dict_pgsql->domain = string_list_init(MATCH_FLAG_NONE, domain)))
+           /*
+            * The "domain" optimization skips input keys that may in fact
+            * have unwanted matches in the database, so failure to create
+            * the match list is fatal.
+            */
+           msg_fatal("%s: %s: domain match list creation using '%s' failed",
+                     myname, pgsqlcf, domain);
+    }
+    else
+        dict_pgsql->domain = 0;
+    myfree(domain);
+
+    hosts = cfg_get_str(p, "hosts", "", 0, 0);
+
+    dict_pgsql->hosts = argv_split(hosts, " ,\t\r\n");
+    if (dict_pgsql->hosts == 0) {
+       argv_add(dict_pgsql->hosts, "localhost", ARGV_END);
+       argv_terminate(dict_pgsql->hosts);
+       if (msg_verbose)
+           msg_info("%s: %s: no hostnames specified, defaulting to '%s'",
+                    myname, pgsqlcf, dict_pgsql->hosts->argv[0]);
+    }
+    myfree(hosts);
+}
+
+/* dict_pgsql_open - open PGSQL data base */
+
+DICT   *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
+{
+    DICT_PGSQL *dict_pgsql;
+
     if (open_flags != O_RDONLY)
        msg_fatal("%s:%s map requires O_RDONLY access mode",
                  DICT_TYPE_PGSQL, name);
@@ -624,122 +624,33 @@ DICT   *dict_pgsql_open(const char *name, int open_flags, int dict_flags)
                                           sizeof(DICT_PGSQL));
     dict_pgsql->dict.lookup = dict_pgsql_lookup;
     dict_pgsql->dict.close = dict_pgsql_close;
-    dict_pgsql->name = pgsqlname_parse(name);
-    dict_pgsql->pldb = plpgsql_init(dict_pgsql->name->hostnames,
-                                   dict_pgsql->name->len_hosts);
+    pgsql_parse_config(dict_pgsql, name);
+    dict_pgsql->pldb = plpgsql_init(dict_pgsql->hosts);
     dict_pgsql->dict.flags = dict_flags | DICT_FLAG_FIXED;
     if (dict_pgsql->pldb == NULL)
        msg_fatal("couldn't intialize pldb!\n");
     return &dict_pgsql->dict;
 }
 
-/* pgsqlname_parse - parse pgsql configuration file */
-static PGSQL_NAME *pgsqlname_parse(const char *pgsqlcf)
-{
-    const char *myname = "pgsqlname_parse";
-    int     i;
-    char   *hosts;
-    PGSQL_NAME *name = (PGSQL_NAME *) mymalloc(sizeof(PGSQL_NAME));
-    ARGV   *hosts_argv;
-
-    name->parser = cfg_parser_alloc(pgsqlcf);
-
-    /* username */
-    name->username = cfg_get_str(name->parser, "user", "", 0, 0);
-
-    /* password */
-    name->password = cfg_get_str(name->parser, "password", "", 0, 0);
-
-    /* database name lookup */
-    name->dbname = cfg_get_str(name->parser, "dbname", "", 1, 0);
-
-    /*
-     * See what kind of lookup we have - a traditional 'select' or a function
-     * call
-     */
-    name->select_function = cfg_get_str(name->parser, "select_function",
-                                       NULL, 0, 0);
-    name->query = cfg_get_str(name->parser, "query", NULL, 0, 0);
-
-    if (name->select_function == 0 && name->query == 0) {
-
-       /*
-        * We have an old style 'select %s from %s...' call
-        */
-
-       /* table name */
-       name->table = cfg_get_str(name->parser, "table", "", 1, 0);
-
-       /* select field */
-       name->select_field = cfg_get_str(name->parser, "select_field",
-                                        "", 1, 0);
-
-       /* where field */
-       name->where_field = cfg_get_str(name->parser, "where_field",
-                                       "", 1, 0);
-
-       /* additional conditions */
-       name->additional_conditions = cfg_get_str(name->parser,
-                                                 "additional_conditions",
-                                                 "", 0, 0);
-    } else {
-       name->table = 0;
-       name->select_field = 0;
-       name->where_field = 0;
-       name->additional_conditions = 0;
-    }
-
-    /* server hosts */
-    hosts = cfg_get_str(name->parser, "hosts", "", 0, 0);
-
-    /* coo argv interface */
-    hosts_argv = argv_split(hosts, " ,\t\r\n");
-    if (hosts_argv->argc == 0) {               /* no hosts specified,
-                                                * default to 'localhost' */
-       if (msg_verbose)
-           msg_info("%s: %s: no hostnames specified, defaulting to 'localhost'",
-                    myname, pgsqlcf);
-       argv_add(hosts_argv, "localhost", ARGV_END);
-       argv_terminate(hosts_argv);
-    }
-    name->len_hosts = hosts_argv->argc;
-    name->hostnames = (char **) mymalloc((sizeof(char *)) * name->len_hosts);
-    i = 0;
-    for (i = 0; hosts_argv->argv[i] != NULL; i++) {
-       name->hostnames[i] = mystrdup(hosts_argv->argv[i]);
-       if (msg_verbose)
-           msg_info("%s: %s: adding host '%s' to list of pgsql server hosts",
-                    myname, pgsqlcf, name->hostnames[i]);
-    }
-    myfree(hosts);
-    argv_free(hosts_argv);
-    return name;
-}
-
+/* plpgsql_init - initalize a PGSQL database */
 
-/*
- * plpgsql_init - initalize a PGSQL database.
- *                 Return NULL on failure, or a PLPGSQL * on success.
- */
-static PLPGSQL *plpgsql_init(char *hostnames[], int len_hosts)
+static PLPGSQL *plpgsql_init(ARGV *hosts)
 {
     PLPGSQL *PLDB;
     int     i;
 
-    if ((PLDB = (PLPGSQL *) mymalloc(sizeof(PLPGSQL))) == NULL) {
-       msg_fatal("mymalloc of pldb failed");
-    }
-    PLDB->len_hosts = len_hosts;
-    if ((PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * len_hosts)) == NULL)
-       return NULL;
-    for (i = 0; i < len_hosts; i++) {
-       PLDB->db_hosts[i] = host_init(hostnames[i]);
-    }
+    PLDB = (PLPGSQL *) mymalloc(sizeof(PLPGSQL));
+    PLDB->len_hosts = hosts->argc;
+    PLDB->db_hosts = (HOST **) mymalloc(sizeof(HOST *) * hosts->argc);
+    for (i = 0; i < hosts->argc; i++)
+       PLDB->db_hosts[i] = host_init(hosts->argv[i]);
+
     return PLDB;
 }
 
 
 /* host_init - initialize HOST structure */
+
 static HOST *host_init(const char *hostname)
 {
     const char *myname = "pgsql host_init";
@@ -773,41 +684,31 @@ static HOST *host_init(const char *hostname)
     return host;
 }
 
-/**********************************************************************
- * public interface dict_pgsql_close
- * unregister, disassociate from database, freeing appropriate memory
- **********************************************************************/
+/* dict_pgsql_close - close PGSQL data base */
+
 static void dict_pgsql_close(DICT *dict)
 {
     int     i;
     DICT_PGSQL *dict_pgsql = (DICT_PGSQL *) dict;
 
     plpgsql_dealloc(dict_pgsql->pldb);
-    cfg_parser_free(dict_pgsql->name->parser);
-    myfree(dict_pgsql->name->username);
-    myfree(dict_pgsql->name->password);
-    myfree(dict_pgsql->name->dbname);
-    if (dict_pgsql->name->table)
-       myfree(dict_pgsql->name->table);
-    if (dict_pgsql->name->query)
-       myfree(dict_pgsql->name->query);
-    if (dict_pgsql->name->select_function)
-       myfree(dict_pgsql->name->select_function);
-    if (dict_pgsql->name->select_field)
-       myfree(dict_pgsql->name->select_field);
-    if (dict_pgsql->name->where_field)
-       myfree(dict_pgsql->name->where_field);
-    if (dict_pgsql->name->additional_conditions)
-       myfree(dict_pgsql->name->additional_conditions);
-    for (i = 0; i < dict_pgsql->name->len_hosts; i++) {
-       myfree(dict_pgsql->name->hostnames[i]);
-    }
-    myfree((char *) dict_pgsql->name->hostnames);
-    myfree((char *) dict_pgsql->name);
+    cfg_parser_free(dict_pgsql->parser);
+    myfree(dict_pgsql->username);
+    myfree(dict_pgsql->password);
+    myfree(dict_pgsql->dbname);
+    myfree(dict_pgsql->query);
+    myfree(dict_pgsql->result_format);
+    if (dict_pgsql->domain)
+        string_list_free(dict_pgsql->domain);
+    if (dict_pgsql->hosts)
+       argv_free(dict_pgsql->hosts);
+    if (dict_pgsql->ctx)
+       db_common_free_ctx(dict_pgsql->ctx);
     dict_free(dict);
 }
 
 /* plpgsql_dealloc - free memory associated with PLPGSQL close databases */
+
 static void plpgsql_dealloc(PLPGSQL *PLDB)
 {
     int     i;
index 1767812914ce475bff42375a98ea4833e5d6f4f6..7a911e3cd7c2534e7b7ee33a2dcea3bd2f7120dd 100644 (file)
@@ -20,8 +20,8 @@
   * Patches change the patchlevel and the release date. Snapshots change the
   * release date only.
   */
-#define MAIL_RELEASE_DATE      "20050304"
-#define MAIL_VERSION_NUMBER    "2.2"
+#define MAIL_RELEASE_DATE      "20050308"
+#define MAIL_VERSION_NUMBER    "2.3"
 
 #define VAR_MAIL_VERSION       "mail_version"
 #ifdef SNAPSHOT
index 5bdc493d7d4902260ed665f7061ea90b3b9d6768..24f08902a79bce034e0cd8453bca18d6be370859 100644 (file)
@@ -416,7 +416,7 @@ DNS_RR *smtp_domain_addr(char *name, int misc_flags, VSTRING *why,
        if (addr_list == 0) {
            if (var_smtp_defer_mxaddr)
                smtp_errno = SMTP_ERR_RETRY;
-           msg_warn("no MX host for %s has a valid A record", name);
+           msg_warn("no MX host for %s has a valid address record", name);
            break;
        }
        best_found = (addr_list ? addr_list->pref : IMPOSSIBLE_PREFERENCE);
index 786df955efcaadc207067a9b0480542a37a65330..5e59e2b9cbf8a9d32b782c947ff53be28d645896 100644 (file)
@@ -2260,6 +2260,33 @@ static int check_server_access(SMTPD_STATE *state, const char *table,
     else
        domain = name;
 
+    /*
+     * Treat an address literal as its own MX server, just like we treat a
+     * name without MX record as its own MX server. There is, however, no
+     * applicable NS server equivalent.
+     */
+    if (*domain == '[') {
+       char   *saved_addr;
+       const char *bare_addr;
+       int     len;
+
+       if (type != T_MX)
+           return (SMTPD_CHECK_DUNNO);
+       len = strlen(domain);
+       if (domain[len - 1] != ']')
+           return (SMTPD_CHECK_DUNNO);
+       /* Memory leak alert: no early returns after this point. */
+       saved_addr = mystrndup(domain + 1, len - 2);
+       if ((bare_addr = valid_mailhost_addr(saved_addr, DONT_GRIPE)) == 0)
+           status = SMTPD_CHECK_DUNNO;
+       else
+           status = check_addr_access(state, table, bare_addr, FULL,
+                                      &found, reply_name, reply_class,
+                                      def_acl);
+       myfree(saved_addr);
+       return (status);
+    }
+
     /*
      * If the domain name does not exist then we apply no restriction.
      *