]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.12-20140921
authorWietse Venema <wietse@porcupine.org>
Sun, 21 Sep 2014 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sun, 21 Sep 2014 20:49:25 +0000 (16:49 -0400)
54 files changed:
postfix/.indent.pro
postfix/HISTORY
postfix/README_FILES/DATABASE_README
postfix/RELEASE_NOTES
postfix/WISHLIST
postfix/html/DATABASE_README.html
postfix/html/postconf.1.html
postfix/html/postconf.5.html
postfix/html/proxymap.8.html
postfix/html/qmqp-sink.1.html
postfix/html/smtp-sink.1.html
postfix/man/man1/postconf.1
postfix/man/man5/postconf.5
postfix/proto/DATABASE_README.html
postfix/proto/postconf.html.prolog
postfix/proto/postconf.man.prolog
postfix/src/bounce/bounce_template.h
postfix/src/cleanup/cleanup_message.c
postfix/src/global/data_redirect.c
postfix/src/global/dict_memcache.c
postfix/src/global/mail_version.h
postfix/src/global/maps.c
postfix/src/global/memcache_proto.h
postfix/src/global/server_acl.c
postfix/src/postconf/Makefile.in
postfix/src/postconf/postconf.c
postfix/src/postconf/postconf.h
postfix/src/postconf/postconf_builtin.c
postfix/src/postconf/postconf_dbms.c
postfix/src/postconf/test57.ref [new file with mode: 0644]
postfix/src/postconf/test58.ref [new file with mode: 0644]
postfix/src/proxymap/proxymap.c
postfix/src/smtpd/smtpd_check.c
postfix/src/smtpstone/qmqp-sink.c
postfix/src/smtpstone/smtp-sink.c
postfix/src/util/Makefile.in
postfix/src/util/argv.h
postfix/src/util/argv_splitq.c [new file with mode: 0644]
postfix/src/util/balpar.c [new file with mode: 0644]
postfix/src/util/dict.h
postfix/src/util/dict_open.c
postfix/src/util/dict_pipe.c
postfix/src/util/dict_pipe.h
postfix/src/util/dict_random.c
postfix/src/util/dict_random.h
postfix/src/util/dict_test.c
postfix/src/util/mac_expand.c
postfix/src/util/mac_expand.in
postfix/src/util/mac_expand.ref
postfix/src/util/match_list.c
postfix/src/util/msg.h
postfix/src/util/mystrtok.c
postfix/src/util/stringops.h
postfix/src/util/vbuf_print.c

index 37dcdeaef67cfd4f2e7f07a9349e712924b537ea..92d8d25a297cd73be93bfe7b6a47866ad3601012 100644 (file)
 -TLOCAL_EXP
 -TLOCAL_STATE
 -TLONG_NAME_MASK
--TMAC_EXP
+-TMAC_EXP_CONTEXT
+-TMAC_EXP_OP_INFO
 -TMAC_HEAD
 -TMAC_PARSE
 -TMAIL_PRINT
index b0e7a500dc578467ca106538ed027ba468b0ed95..3921bf0f2f1c1e5d0357147fcdf8783d31075b64 100644 (file)
@@ -20345,3 +20345,68 @@ Apologies for any names omitted.
        global/deliver_request.h, global/mail_params.h, global/sent.c,
        *qmgr/qmgr.c, *qmgr/qmgr_active.c, *qmgr/qmgr_message.c.
 
+20140908-14
+
+       Feature: for the first time in 17 years, support for
+       ${name?if-nonempty:if-empty} macro expressions, and for
+       logical expressions ${logical-expr?if-true:if-false}. In
+       preparation for configurable message headers and logging.
+       Files: util/mac_expand.c.
+
+20140914
+
+       Bugfix (introduced: 19971026): a zero precision value in
+       %.*s and $.<digits>s was implemented as if no precision
+       value was specified, i.e. print the entire string. This was
+       not harmful, it just looked weird.  File: util/vbuf_print.c.
+
+20120917
+
+       Feature: RFC 7372 enhanced status code for unknown SMTP
+       client hostnames.  File: smtpd/smtpd_check.c
+
+       Bugfix: the accept() calls in test progams escaped attention
+       when Postfix 2.2 was ported to IPv6. Problem found by Mark
+       Martinec. Files: smtpstone/smtp-sink.c, smtpstone/qmqp-sink.c.
+
+20140918
+
+       Cleanup: log a warning when the cleanup server detects too
+       many hops. smtpd(8) does not log any of the CLEANUP_STAT_XXX
+       results. The pickup server logs some because there is no
+       client to send the problem description to. This logic of
+       who logs what needs to be revisited. File:
+       cleanup/cleanup_message.c.
+
+20140919
+
+       Usability: randmap and pipemap syntax, for example,
+       pipemap:{type_1:name_1, ..., type_n:name_n}.  This required
+       small updates to code that parses input into lookup table
+       names. Files: global/data_redirect.c, global/maps.c,
+       global/server_acl.c, postconf/postconf.c, postconf/postconf_dbms.c,
+       postconf/test58.ref, proto/DATABASE_README.html,
+       proxymap/proxymap.c, smtpd/smtpd_check.c, util/argv.h,
+       util/balpar.c, util/dict_pipe.c, util/dict_random.c,
+       util/match_list.c, util/mystrtok.c, util/argv_splitq.c,
+       util/stringops.h.
+
+       Cleanup: added PRINTFLIKE() to enable missing format string
+       checks. Files: bounce/bounce_template.h, global/memcache_proto.h,
+       global/dict_memcache, postconf/postconf.h, util/dict.h,
+       util/msg.h.
+
+20140920
+
+       Bugfix (introduced: 20080212): incorrect client name in
+       reject messages from check_reverse_client_hostname_access
+       and check_reverse_client_hostname_{a,mx,ns}_access.  They
+       replied with the verified client name, instead of the name
+       that was rejected.  Problem reported by Reindl Harald. File:
+       smtpd/smtpd_check.c.
+
+20140921
+
+       Cleanup: postconf code to determine the default mydomain
+       value had not evolved since 1997, while the rest of Postfix
+       changed in 2000.  File: postconf/postconf-dbms.c.
index bb361307e36d331c75460cd1f6c2c5f2c4a8ddf9..8e0bd8829601f346b96bbf24e63394487c9aefae 100644 (file)
@@ -243,13 +243,13 @@ To find out what database types your Postfix system supports, use the "p\bpo\bos\bs
         format is described in pcre_table(5). The lookup table name as used in
         "pcre:table" is the name of the regular expression file.
     p\bpi\bip\bpe\bem\bma\bap\bp (read-only)
-        A pipeline of lookup tables. Example: "p\bpi\bip\bpe\bem\bma\bap\bp:\b:!type1:name1! ...
-        !typen:namen". Each "pipemap:" query is given to the first table. Each
+        A pipeline of lookup tables. Example: "pipemap:{type1:name1, ...,
+        typen:namen}". Each "pipemap:" query is given to the first table. Each
         lookup result becomes the query for the next table in the pipeline, and
         the last table produces the final result. When any table lookup
-        produces no result, the pipeline produces no result. The first ASCII
-        character after "pipemap:" will be used as the separator between the
-        lookup tables that follow (do not use space, ",", ":" or non-ASCII).
+        produces no result, the pipeline produces no result. The first and last
+        characters of the "pipemap:" table name must be "{" and "}". Within
+        these, individual maps are separated with comma or whitespace.
     p\bpg\bgs\bsq\bql\bl (read-only)
         PostgreSQL database client. Configuration details are given in
         pgsql_table(5).
@@ -257,11 +257,11 @@ To find out what database types your Postfix system supports, use the "p\bpo\bos\bs
         Postfix proxymap(8) client for shared access to Postfix databases. The
         lookup table name syntax is "proxy:type:table".
     r\bra\ban\bnd\bdm\bma\bap\bp (read-only)
-        An in-memory table that performs random selection. Example: "r\bra\ban\bnd\bdm\bma\bap\bp:\b:
-        !result1! ... !resultn". Each table query returns a random choice from
-        the specified results. The first ASCII character after "randmap:" will
-        be used as the separator between the results that follow (do not use
-        space, ",", ":" or non-ASCII).
+        An in-memory table that performs random selection. Example: "randmap:
+        {result1. ..., resultn}". Each table query returns a random choice from
+        the specified results. The first and last characters of the "randmap:
+        " table name must be "{" and "}". Within these, individual maps are
+        separated with comma or whitespace.
     r\bre\beg\bge\bex\bxp\bp (read-only)
         A lookup table based on regular expressions. The file format is
         described in regexp_table(5). The lookup table name as used in "regexp:
index c49daae86a2bee7132f9e82b5d825c4bed8e6818..45b23efb75e3eabb855821ce84d5dc789eba9ca3 100644 (file)
@@ -41,6 +41,36 @@ Maintainers may also benefit from the makedefs documentation
 (mantools/srctoman - makedefs | nroff -man | less) with information
 about build options that are not described in the INSTALL instructions.
 
+Major changes with snapshot 20140921
+====================================
+
+In preparation for configurable mail headers and logging, new main.cf
+support for if-then-else expressions:
+
+    ${name?{text1}:{text2}}
+
+and for logical expressions: 
+
+    ${{text1}=={text2}?{text3}:{text4}}
+    ${{text1}!={text2}?{text3}:{text4}}
+
+Whitespace before and after {text} is ignored. This can help to
+make complex expressions more readable.  See the postconf(5) manpage
+for further details.
+
+The syntax of pipemap and randmap has improved. Postfix now uses
+pipemap:{map1, ..., mapN} and randmap:{result1, ..., resultN}.
+The old syntax was just too ugly.
+
+It is expected that usability can be improved elsewhere in Postfix
+in a similar manner.  For example, 
+
+- Milter clients and policy clients with non-default settings:
+  smtpd_milters = {inet:host:port, timeout=xxx, default_action=yyy}, ...
+
+- Parameter overrides in master.cf with commas and spaces:
+  -o { parameter = value ... }
+
 Major changes with snapshot 20140801
 ====================================
 
index a9a08d656f41a9a8eda76c02b3d92075f00e3e29..bd1bbf5bf2f331f44b355a0a1e4ce21782936ce4 100644 (file)
@@ -8,8 +8,41 @@ Wish list:
 
        Things to do after the stable release:
 
+       The pickup daemon logs warnings only when the cleanup daemon
+       dit not provide a "reason" attribute. Is this logic right?
+
+       Make the "relayed after delay" notification conditional on
+       the presence of the DSN_NOTIFY_DELAY flag.
+
        up-convert myhostname to UTF-8 in MIME boundary strings?
 
+       Update postconf to recursively parse legacy-style mapnames
+       in random:, pipe:, and other multimaps.
+
+       Introduce constants to replace all the ad-hoc ", \t\r\n"
+       etc. for tokenization. That will have to go into an "util"
+       file because match_strings(3), dict_pipe(3) and dict_random(3)
+       depend on these definitions.
+
+       Make sure that proxy: can handle random:, pipe:, and other
+       multimaps.
+
+       Add a switch to consider postscreen deep protocol tests as
+       "completed" when receiving "RSET" after "RCPT TO" and the
+       session has passed all tests up to that point. RSET becomes
+       like QUIT except perhaps that it does not hang up.
+
+       apipe: map, splits results into address lists and performs
+       lookups for the invidual addresses, converting back and
+       forth between external and internal forms.
+
+       union: map, concatenates results, default separator is ','.
+
+       Include <3htPpS5B6bzbcpM@spike.porcupine.org> example with
+       filter policies for different mail streams. Correction:
+       filter should be content_filter. Posted Wed, 10 Sep 2014
+       09:53:52 -0400 (EDT).
+
        Clarify that receive_override_options should not be used
        with smtpd_proxy_filter.
 
index 3f0d9ec4e2526e41adf562a2fc0e06b8ed8b8823..830fb4fabd122fccd32699f981f7252629d56746 100644 (file)
@@ -365,14 +365,14 @@ file.  </dd>
 <dt> <b>pipemap</b> (read-only) </dt>
 
 <dd> A pipeline of lookup tables. Example:
-"<b><a href="DATABASE_README.html#types">pipemap</a>:</b><i>!type<sub>1</sub>:name<sub>1</sub>! ...
-!type<sub>n</sub>:name<sub>n</sub></i>".  Each "<a href="DATABASE_README.html#types">pipemap</a>:" query is
+"<a href="DATABASE_README.html#types">pipemap</a>:{<i>type<sub>1</sub>:name<sub>1</sub>, ...,
+type<sub>n</sub>:name<sub>n</sub></i>}".  Each "<a href="DATABASE_README.html#types">pipemap</a>:" query is
 given to the first table.  Each lookup result becomes the query for
 the next table in the pipeline, and the last table produces the
 final result.  When any table lookup produces no result, the pipeline
-produces no result.  The first ASCII character after "<a href="DATABASE_README.html#types">pipemap</a>:"
-will be used as the separator between the lookup tables that follow
-(do not use space, ",", ":" or non-ASCII).  </dd>
+produces no result.  The first and last characters of the "<a href="DATABASE_README.html#types">pipemap</a>:"
+table name must be "{" and "}". Within these, individual maps are
+separated with comma or whitespace.  </dd>
 
 <dt> <b>pgsql</b> (read-only) </dt>
 
@@ -388,11 +388,11 @@ databases. The lookup table name syntax is "<a href="proxymap.8.html">proxy</a>:
 <dt> <b>randmap</b> (read-only) </dt>
 
 <dd> An in-memory table that performs random selection.  Example:
-"<b><a href="DATABASE_README.html#types">randmap</a>:</b><i>!result<sub>1</sub>! ... !result<sub>n</sub></i>".
+"<a href="DATABASE_README.html#types">randmap</a>:{<i>result<sub>1</sub>. ..., result<sub>n</sub></i>}".
 Each table query returns a random choice from the specified results.
-The first ASCII character after "<a href="DATABASE_README.html#types">randmap</a>:" will be used as the
-separator between the results that follow (do not use space, ",",
-":" or non-ASCII).  </dd>
+The first and last characters of the "<a href="DATABASE_README.html#types">randmap</a>:" table name must be
+"{" and "}".  Within these, individual maps are separated with comma
+or whitespace.  </dd>
 
 <dt> <b>regexp</b> (read-only) </dt>
 
index bf2ddd9eb637347c40089cf8f4f89d9574742a65..bb64bd3af7cafc80be800b3bed630487cd304312 100644 (file)
@@ -55,16 +55,16 @@ POSTCONF(1)                                                        POSTCONF(1)
        <b>postconf -a</b>|<b>-A</b>|<b>-l</b>|<b>-m</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>]
 
 <b>DESCRIPTION</b>
-       By default, the <a href="postconf.1.html"><b>postconf</b>(1)</a> command displays the values of <a href="postconf.5.html"><b>main.cf</b></a> con-
+       By default, the <a href="postconf.1.html"><b>postconf</b>(1)</a> command displays the values of <a href="postconf.5.html"><b>main.cf</b></a> con
        figuration parameters, and warns  about  possible  mis-typed  parameter
-       names  (Postfix  2.9 and later).  It can also change <a href="postconf.5.html"><b>main.cf</b></a> configura-
+       names  (Postfix  2.9 and later).  It can also change <a href="postconf.5.html"><b>main.cf</b></a> configura
        tion parameter values, or display other configuration information about
        the Postfix mail system.
 
        Options:
 
        <b>-a</b>     List  the available SASL server plug-in types.  The SASL plug-in
-              type is selected with the <b><a href="postconf.5.html#smtpd_sasl_type">smtpd_sasl_type</a></b> configuration  parame-
+              type is selected with the <b><a href="postconf.5.html#smtpd_sasl_type">smtpd_sasl_type</a></b> configuration  parame
               ter by specifying one of the names listed below.
 
               <b>cyrus</b>  This  server  plug-in  is available when Postfix is built
@@ -78,7 +78,7 @@ POSTCONF(1)                                                        POSTCONF(1)
               This feature is available with Postfix 2.3 and later.
 
        <b>-A</b>     List the available SASL client plug-in types.  The SASL  plug-in
-              type  is selected with the <b><a href="postconf.5.html#smtp_sasl_type">smtp_sasl_type</a></b> or <b><a href="postconf.5.html#lmtp_sasl_type">lmtp_sasl_type</a></b> con-
+              type  is selected with the <b><a href="postconf.5.html#smtp_sasl_type">smtp_sasl_type</a></b> or <b><a href="postconf.5.html#lmtp_sasl_type">lmtp_sasl_type</a></b> con
               figuration parameters by specifying  one  of  the  names  listed
               below.
 
@@ -88,8 +88,8 @@ POSTCONF(1)                                                        POSTCONF(1)
               This feature is available with Postfix 2.3 and later.
 
        <b>-b</b> [<i>template</i><b>_</b><i>file</i>]
-              Display the message text that appears at the beginning of deliv-
-              ery  status notification (DSN) messages, replacing $<b>name</b> expres-
+              Display the message text that appears at the beginning of deliv
+              ery  status notification (DSN) messages, replacing $<b>name</b> expres
               sions with actual values as described in <a href="bounce.5.html"><b>bounce</b>(5)</a>.
 
               To override the built-in templates, specify a template file name
@@ -125,29 +125,29 @@ POSTCONF(1)                                                        POSTCONF(1)
 
               This feature is available with Postfix 2.9 and later.
 
-       <b>-d</b>     Print  <a href="postconf.5.html"><b>main.cf</b></a> default parameter settings instead of actual set-
+       <b>-d</b>     Print  <a href="postconf.5.html"><b>main.cf</b></a> default parameter settings instead of actual set
               tings.  Specify <b>-df</b> to fold long  lines  for  human  readability
               (Postfix 2.9 and later).
 
-       <b>-e</b>     Edit  the  <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and update parameter set-
+       <b>-e</b>     Edit  the  <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and update parameter set
               tings with the "<i>name=value</i>" pairs  on  the  <a href="postconf.1.html"><b>postconf</b>(1)</a>  command
               line.
 
               With  <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and replace one
-              or more service entries with new values as specified with  "<i>ser-</i>
+              or more service entries with new values as specified with  "<i>ser</i>
               <i>vice/type=value</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
 
               With  <b>-F</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and replace one
-              or more service fields with new values  as  specied  with  "<i>ser-</i>
-              <i>vice/type/field=value</i>"  on  the  <a href="postconf.1.html"><b>postconf</b>(1)</a>  command line. Cur-
-              rently, the "command" field contains the command name  and  com-
+              or more service fields with new values  as  specied  with  "<i>ser</i>
+              <i>vice/type/field=value</i>"  on  the  <a href="postconf.1.html"><b>postconf</b>(1)</a>  command line. Cur
+              rently, the "command" field contains the command name  and  com
               mand arguments.  this may change in the near future, so that the
-              "command" field contains only the command name, and a new "argu-
+              "command" field contains only the command name, and a new "argu
               ments" pseudofield contains the command arguments.
 
               With  <b>-P</b>,  edit  the  <a href="master.5.html"><b>master.cf</b></a>  configuration  file, and add or
-              update one  or  more  service  parameter  settings  (-o  parame-
-              ter=value  settings)  with  new  values  as  specied  with "<i>ser-</i>
+              update one  or  more  service  parameter  settings  (-o  parame
+              ter=value  settings)  with  new  values  as  specied  with "<i>ser</i>
               <i>vice/type/parameter=value</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
 
               In all cases the file is copied to a temporary file then renamed
@@ -166,8 +166,8 @@ POSTCONF(1)                                                        POSTCONF(1)
               and all fields), formatted as one "<i>service/type/field=value</i>" per
               line. Specify <b>-Ff</b> to fold long lines.
 
-              Specify one or more "<i>service/type/field</i>" instances on the  <a href="postconf.1.html"><b>post-</b></a>
-              <a href="postconf.1.html"><b>conf</b>(1)</a>  command line to limit the output to fields of interest.
+              Specify one or more "<i>service/type/field</i>" instances on the  <b>post</b>‐\b
+              <b>conf</b>(1)  command line to limit the output to fields of interest.
               Trailing parameter name or service type fields that are  omitted
               will be handled as "*" wildcard fields.
 
@@ -176,7 +176,7 @@ POSTCONF(1)                                                        POSTCONF(1)
        <b>-h</b>     Show  parameter  or attribute values without the "<i>name</i> = " label
               that normally precedes the value.
 
-       <b>-l</b>     List the names of all supported mailbox locking methods.   Post-
+       <b>-l</b>     List the names of all supported mailbox locking methods.   Post
               fix supports the following methods:
 
               <b>flock</b>  A  kernel-based  advisory  locking method for local files
@@ -188,21 +188,21 @@ POSTCONF(1)                                                        POSTCONF(1)
 
               <b>dotlock</b>
                      An application-level locking method. An application locks
-                     a  file  named  <i>filename</i>  by  creating a file named <i>file-</i>
+                     a  file  named  <i>filename</i>  by  creating a file named <i>file</i>
                      <i>name</i><b>.lock</b>.  The application is expected to remove its own
                      lock  file,  as  well  as stale lock files that were left
                      behind after abnormal program termination.
 
        <b>-m</b>     List the names of all supported lookup table types.  In  Postfix
               configuration  files,  lookup tables are specified as <i>type</i><b>:</b><i>name</i>,
-              where <i>type</i> is one of the types listed below. The table <i>name</i> syn-
-              tax  depends  on the lookup table type as described in the <a href="DATABASE_README.html">DATA</a>-
-              <a href="DATABASE_README.html">BASE_README</a> document.
+              where <i>type</i> is one of the types listed below. The table <i>name</i> syn
+              tax  depends  on the lookup table type as described in the DATA‐
+              <a href="BASE_README.html">BASE_README</a> document.
 
               <b>btree</b>  A sorted, balanced tree structure.  Available on  systems
                      with support for Berkeley DB databases.
 
-              <b>cdb</b>    A  read-optimized structure with no support for incremen-
+              <b>cdb</b>    A  read-optimized structure with no support for incremen
                      tal updates.  Available on systems with support  for  CDB
                      databases.
 
@@ -210,19 +210,19 @@ POSTCONF(1)                                                        POSTCONF(1)
                      Domain Routing (CIDR)  patterns.  This  is  described  in
                      <a href="cidr_table.5.html"><b>cidr_table</b>(5)</a>.
 
-              <b>dbm</b>    An indexed file type based on hashing.  Available on sys-
+              <b>dbm</b>    An indexed file type based on hashing.  Available on sys
                      tems with support for DBM databases.
 
               <b>environ</b>
                      The UNIX process environment array. The lookup key is the
-                     variable  name. Originally implemented for testing, some-
+                     variable  name. Originally implemented for testing, some
                      one may find this useful someday.
 
-              <b>fail</b>   A table that reliably fails all requests. The lookup  ta-
-                     ble  name  is used for logging. This table exists to sim-
+              <b>fail</b>   A table that reliably fails all requests. The lookup  ta
+                     ble  name  is used for logging. This table exists to sim
                      plify Postfix error tests.
 
-              <b>hash</b>   An indexed file type based on hashing.  Available on sys-
+              <b>hash</b>   An indexed file type based on hashing.  Available on sys
                      tems with support for Berkeley DB databases.
 
               <b>internal</b>
@@ -230,23 +230,23 @@ POSTCONF(1)                                                        POSTCONF(1)
                      when a process terminates.
 
               <b>lmdb</b>   OpenLDAP  LMDB  database  (a  memory-mapped,   persistent
-                     file).   Available on systems with support for LMDB data-
+                     file).   Available on systems with support for LMDB data
                      bases.  This is described in <a href="lmdb_table.5.html"><b>lmdb_table</b>(5)</a>.
 
               <b>ldap</b> (read-only)
                      LDAP database client. This is described in <a href="ldap_table.5.html"><b>ldap_table</b>(5)</a>.
 
               <b>memcache</b>
-                     Memcache  database  client.  This  is  described  in <a href="memcache_table.5.html"><b>mem-</b></a>
-                     <a href="memcache_table.5.html"><b>cache_table</b>(5)</a>.
+                     Memcache  database  client.  This  is  described  in <b>mem</b>‐\b
+                     <b>cache_table</b>(5).
 
               <b>mysql</b> (read-only)
                      MySQL database client.  Available on systems with support
-                     for  MySQL  databases.   This  is  described in <a href="mysql_table.5.html"><b>mysql_ta-</b></a>
-                     <a href="mysql_table.5.html"><b>ble</b>(5)</a>.
+                     for  MySQL  databases.   This  is  described in <b>mysql_ta</b>‐\b
+                     <b>ble</b>(5).
 
               <b>pcre</b> (read-only)
-                     A lookup table based on Perl Compatible  Regular  Expres-
+                     A lookup table based on Perl Compatible  Regular  Expres
                      sions.  The file format is described in <a href="pcre_table.5.html"><b>pcre_table</b>(5)</a>.
 
               <b>pgsql</b> (read-only)
@@ -254,95 +254,94 @@ POSTCONF(1)                                                        POSTCONF(1)
                      <a href="pgsql_table.5.html"><b>pgsql_table</b>(5)</a>.
 
               <b>pipemap</b> (read-only)
-                     A    pipeline     of     lookup     tables.      Example:
-                     "<b><a href="DATABASE_README.html#types">pipemap</a>:</b><i>!type</i><b>_</b><i>1:name</i><b>_</b><i>1!    ...   !type</i><b>_</b><i>n:name</i><b>_</b><i>n</i>".   Each
-                     "<a href="DATABASE_README.html#types">pipemap</a>:" query is  given  to  the  first  table.   Each
+                     A lookup table that  constructs  a  pipeline  of  tables.
+                     Example:  "<b><a href="DATABASE_README.html#types">pipemap</a>:{</b><i>type</i><b>_</b><i>1:name</i><b>_</b><i>1,  ..., type</i><b>_</b><i>n:name</i><b>_</b><i>n</i><b>}</b>".
+                     Each "<a href="DATABASE_README.html#types">pipemap</a>:" query is given to the first table.   Each
                      lookup result becomes the query for the next table in the
                      pipeline, and the last table produces the  final  result.
                      When  any  table  lookup produces no result, the pipeline
-                     produces no  result.  The  first  ASCII  character  after
-                     "<a href="DATABASE_README.html#types">pipemap</a>:"  will  be  used  as  the separator between the
-                     lookup tables that follow (do not use space, ",", ":"  or
-                     non-ASCII).
+                     produces no result. The first and last characters of  the
+                     "<a href="DATABASE_README.html#types">pipemap</a>:" table name must be "<b>{</b>" and "<b>}</b>".  Within these,
+                     individual maps are separated with comma or whitespace.
 
-              <b>proxy</b>  Postfix  <a href="proxymap.8.html"><b>proxymap</b>(8)</a>  client for shared access to Postfix
+              <b>proxy</b>  Postfix <a href="proxymap.8.html"><b>proxymap</b>(8)</a> client for shared access  to  Postfix
                      databases. The table name syntax is <i>type</i><b>:</b><i>name</i>.
 
               <b>randmap</b> (read-only)
-                     An in-memory table that performs random selection.  Exam-
-                     ple: "<b><a href="DATABASE_README.html#types">randmap</a>:</b><i>!result</i><b>_</b><i>1! ... !result</i><b>_</b><i>n</i>". Each table query
-                     returns a random choice from the specified  results.  The
-                     first  ASCII  character  after "<a href="DATABASE_README.html#types">randmap</a>:" will be used as
-                     the separator between the results that follow (do not use
-                     space, ",", ":" or non-ASCII).
+                     An  in-memory table that performs random selection. Exam‐
+                     ple:  "<b><a href="DATABASE_README.html#types">randmap</a>:{</b><i>result</i><b>_</b><i>1,  ...,  result</i><b>_</b><i>n</i><b>}</b>".  Each  table
+                     query returns a random choice from the specified results.
+                     The first and last characters  of  the  "<a href="DATABASE_README.html#types">randmap</a>:"  table
+                     name  must be "<b>{</b>" and "<b>}</b>".  Within these, individual maps
+                     are separated with comma or whitespace.
 
               <b>regexp</b> (read-only)
-                     A  lookup  table  based  on regular expressions. The file
+                     A lookup table based on  regular  expressions.  The  file
                      format is described in <a href="regexp_table.5.html"><b>regexp_table</b>(5)</a>.
 
-              <b>sdbm</b>   An indexed file type based on hashing.  Available on sys-
+              <b>sdbm</b>   An indexed file type based on hashing.  Available on sys
                      tems with support for SDBM databases.
 
               <b>socketmap</b> (read-only)
-                     Sendmail-style   socketmap  client.  The  table  name  is
-                     <b>inet</b>:<i>host</i>:<i>port</i>:<i>name</i> for a TCP/IP  server,  or  <b>unix</b>:<i>path-</i>
-                     <i>name</i>:<i>name</i>  for a UNIX-domain server. This is described in
+                     Sendmail-style  socketmap  client.  The  table  name   is
+                     <b>inet</b>:<i>host</i>:<i>port</i>:<i>name</i>  for  a  TCP/IP server, or <b>unix</b>:<i>path‐</i>
+                     <i>name</i>:<i>name</i> for a UNIX-domain server. This is described  in
                      <a href="socketmap_table.5.html"><b>socketmap_table</b>(5)</a>.
 
               <b>sqlite</b> (read-only)
                      SQLite database. This is described in <a href="sqlite_table.5.html"><b>sqlite_table</b>(5)</a>.
 
               <b>static</b> (read-only)
-                     A table that always returns its name  as  lookup  result.
-                     For example, <b><a href="DATABASE_README.html#types">static</a>:foobar</b> always returns the string <b>foo-</b>
+                     A  table  that  always returns its name as lookup result.
+                     For example, <b><a href="DATABASE_README.html#types">static</a>:foobar</b> always returns the string <b>foo</b>‐\b
                      <b>bar</b> as lookup result.
 
               <b>tcp</b> (read-only)
                      TCP/IP client. The protocol is described in <a href="tcp_table.5.html"><b>tcp_table</b>(5)</a>.
 
               <b>texthash</b> (read-only)
-                     Produces  similar results as <a href="DATABASE_README.html#types">hash</a>: files, except that you
-                     don't need to run the <a href="postmap.1.html"><b>postmap</b>(1)</a> command before  you  can
-                     use  the  file, and that it does not detect changes after
+                     Produces similar results as <a href="DATABASE_README.html#types">hash</a>: files, except that  you
+                     don't  need  to run the <a href="postmap.1.html"><b>postmap</b>(1)</a> command before you can
+                     use the file, and that it does not detect  changes  after
                      the file is read.
 
               <b>unix</b> (read-only)
-                     A limited view of the UNIX authentication  database.  The
+                     A  limited  view of the UNIX authentication database. The
                      following tables are implemented:
 
                      <b>unix:passwd.byname</b>
-                            The  table  is the UNIX password database. The key
-                            is a login name.  The result is  a  password  file
+                            The table is the UNIX password database.  The  key
+                            is  a  login  name.  The result is a password file
                             entry in <b>passwd</b>(5) format.
 
                      <b>unix:group.byname</b>
                             The table is the UNIX group database. The key is a
-                            group name.  The result is a group file  entry  in
+                            group  name.   The result is a group file entry in
                             <b>group</b>(5) format.
 
-              Other  table types may exist depending on how Postfix was built.
+              Other table types may exist depending on how Postfix was  built.
 
-       <b>-M</b>     Show <a href="master.5.html"><b>master.cf</b></a> file contents instead of <a href="postconf.5.html"><b>main.cf</b></a>  file  contents.
+       <b>-M</b>     Show  <a href="master.5.html"><b>master.cf</b></a>  file contents instead of <a href="postconf.5.html"><b>main.cf</b></a> file contents.
               Specify <b>-Mf</b> to fold long lines for human readability.
 
-              Specify zero or more arguments, each with a <i>service-name</i> or <i>ser-</i>
-              <i>vice-name/service-type</i> pair, where  <i>service-name</i>  is  the  first
-              field  of  a  <a href="master.5.html">master.cf</a>  entry and <i>service-type</i> is one of (<b>inet</b>,
+              Specify zero or more arguments, each with a <i>service-name</i> or <i>ser</i>
+              <i>vice-name/service-type</i>  pair,  where  <i>service-name</i>  is the first
+              field of a <a href="master.5.html">master.cf</a> entry and <i>service-type</i>  is  one  of  (<b>inet</b>,
               <b>unix</b>, <b>fifo</b>, or <b>pass</b>).
 
-              If <i>service-name</i> or <i>service-name/service-type</i> is specified,  only
-              the  matching  <a href="master.5.html">master.cf</a>  entries  will  be output. For example,
-              "<b>postconf -Mf smtp</b>" will output all services named  "smtp",  and
-              "<b>postconf  -Mf smtp/inet</b>" will output only the smtp service that
-              listens on the network.  Trailing service type fields  that  are
+              If  <i>service-name</i> or <i>service-name/service-type</i> is specified, only
+              the matching <a href="master.5.html">master.cf</a> entries  will  be  output.  For  example,
+              "<b>postconf  -Mf  smtp</b>" will output all services named "smtp", and
+              "<b>postconf -Mf smtp/inet</b>" will output only the smtp service  that
+              listens  on  the network.  Trailing service type fields that are
               omitted will be handled as "*" wildcard fields.
 
               This feature is available with Postfix 2.9 and later. The syntax
-              was changed from "<i>name.type</i>" to "<i>name/type</i>",  and  "*"  wildcard
+              was  changed  from  "<i>name.type</i>" to "<i>name/type</i>", and "*" wildcard
               support was added with Postfix 2.11.
 
        <b>-n</b>     Show only configuration parameters that have explicit <i>name=value</i>
-              settings in <a href="postconf.5.html"><b>main.cf</b></a>.  Specify <b>-nf</b> to fold long lines  for  human
+              settings  in  <a href="postconf.5.html"><b>main.cf</b></a>.  Specify <b>-nf</b> to fold long lines for human
               readability (Postfix 2.9 and later).
 
        <b>-o</b> <i>name=value</i>
@@ -354,81 +353,81 @@ POSTCONF(1)                                                        POSTCONF(1)
 
               This feature is available with Postfix 2.11 and later.
 
-       <b>-P</b>     Show  <a href="master.5.html"><b>master.cf</b></a>  service parameter settings (by default all ser-
-              vices   and   all   parameters).    formatted   as   one   "<i>ser-</i>
-              <i>vice/type/parameter=value</i>"  per  line.  Specify <b>-Pf</b> to fold long
+       <b>-P</b>     Show <a href="master.5.html"><b>master.cf</b></a> service parameter settings (by default  all  ser‐
+              vices   and   all   parameters).    formatted   as   one   "<i>ser</i>
+              <i>vice/type/parameter=value</i>" per line.  Specify <b>-Pf</b> to  fold  long
               lines.
 
-              Specify one or more "<i>service/type/parameter</i>"  instances  on  the
-              <a href="postconf.1.html"><b>postconf</b>(1)</a>  command  line  to limit the output to parameters of
-              interest.  Trailing parameter name or service type  fields  that
+              Specify  one  or  more "<i>service/type/parameter</i>" instances on the
+              <a href="postconf.1.html"><b>postconf</b>(1)</a> command line to limit the output  to  parameters  of
+              interest.   Trailing  parameter name or service type fields that
               are omitted will be handled as "*" wildcard fields.
 
               This feature is available with Postfix 2.11 and later.
 
        <b>-t</b> [<i>template</i><b>_</b><i>file</i>]
-              Display  the templates for text that appears at the beginning of
-              delivery status notification (DSN) messages,  without  expanding
+              Display the templates for text that appears at the beginning  of
+              delivery  status  notification (DSN) messages, without expanding
               $<b>name</b> expressions.
 
               To override the built-in templates, specify a template file name
-              at the end of the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line, or  specify  a  file
+              at  the  end  of the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line, or specify a file
               name in <a href="postconf.5.html"><b>main.cf</b></a> with the <b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a></b> parameter.
 
-              To  force  selection of the built-in templates, specify an empty
-              template file name on the <a href="postconf.1.html"><b>postconf</b>(1)</a>  command  line  (in  shell
+              To force selection of the built-in templates, specify  an  empty
+              template  file  name  on  the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line (in shell
               language: "").
 
               This feature is available with Postfix 2.3 and later.
 
-       <b>-v</b>     Enable  verbose  logging  for  debugging  purposes.  Multiple <b>-v</b>
+       <b>-v</b>     Enable verbose  logging  for  debugging  purposes.  Multiple  <b>-v</b>
               options make the software increasingly verbose.
 
-       <b>-x</b>     Expand <i>$name</i> in  <a href="postconf.5.html"><b>main.cf</b></a>  or  <a href="master.5.html"><b>master.cf</b></a>  parameter  values.  The
+       <b>-x</b>     Expand  <i>$name</i>  in  <a href="postconf.5.html"><b>main.cf</b></a>  or  <a href="master.5.html"><b>master.cf</b></a>  parameter values. The
               expansion is recursive.
 
               This feature is available with Postfix 2.10 and later.
 
-       <b>-X</b>     Edit  the  <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and remove the parameters
-              named on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.  Specify a list of param-
+       <b>-X</b>     Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and remove  the  parameters
+              named on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.  Specify a list of param
               eter names, not "<i>name=value</i>" pairs.
 
-              With  <b>-M</b>,  edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and remove one
-              or more service entries as specified with "<i>service/type</i>" on  the
+              With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and  remove  one
+              or  more service entries as specified with "<i>service/type</i>" on the
               <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
 
-              With  <b>-P</b>,  edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and remove one
+              With <b>-P</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and  remove  one
               or more service parameter settings (-o parameter=value settings)
-              as specied with "<i>service/type/parameter</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> com-
+              as specied with "<i>service/type/parameter</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> com
               mand line.
 
               In all cases the file is copied to a temporary file then renamed
               into place.  Specify quotes to protect special characters on the
               <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
 
-              There is no <a href="postconf.1.html"><b>postconf</b>(1)</a> command to perform  the  reverse  opera-
+              There  is  no  <a href="postconf.1.html"><b>postconf</b>(1)</a> command to perform the reverse opera‐
               tion.
 
-              This  feature is available with Postfix 2.10 and later.  Support
+              This feature is available with Postfix 2.10 and later.   Support
               for -M and -P was added with Postfix 2.11.
 
-       <b>-#</b>     Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and comment out the parame-
-              ters named on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line, so that those param-
-              eters revert to their default values.  Specify a list of parame-
+       <b>-#</b>     Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and comment out the parame
+              ters named on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line, so that those param
+              eters revert to their default values.  Specify a list of parame
               ter names, not "<i>name=value</i>" pairs.
 
-              With  <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and comment out
-              one or more service entries as specified with "<i>service/type</i>"  on
+              With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and comment  out
+              one  or more service entries as specified with "<i>service/type</i>" on
               the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
 
               In all cases the file is copied to a temporary file then renamed
               into place.  Specify quotes to protect special characters on the
               <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
 
-              There  is  no  <a href="postconf.1.html"><b>postconf</b>(1)</a> command to perform the reverse opera-
+              There is no <a href="postconf.1.html"><b>postconf</b>(1)</a> command to perform  the  reverse  opera‐
               tion.
 
-              This feature is available with Postfix 2.6  and  later.  Support
+              This  feature  is  available with Postfix 2.6 and later. Support
               for -M was added with Postfix 2.11.
 
 <b>DIAGNOSTICS</b>
@@ -439,18 +438,18 @@ POSTCONF(1)                                                        POSTCONF(1)
               Directory with Postfix configuration files.
 
 <b>CONFIGURATION PARAMETERS</b>
-       The  following  <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
+       The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to  this  pro‐
        gram.
 
-       The text below provides only a parameter summary. See  <a href="postconf.5.html"><b>postconf</b>(5)</a>  for
+       The  text  below provides only a parameter summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for
        more details including examples.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The  default  location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+              The default location of the Postfix <a href="postconf.5.html">main.cf</a> and  <a href="master.5.html">master.cf</a>  con‐
               figuration files.
 
        <b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a> (empty)</b>
-              Pathname of a configuration file with bounce message  templates.
+              Pathname  of a configuration file with bounce message templates.
 
 <b>FILES</b>
        /etc/postfix/<a href="postconf.5.html">main.cf</a>, Postfix configuration parameters
index a09a1cc39e86b1df7f7593a2a498e8fbc31bc152..c5d32e7264c2631df94616354c414af23371d0de 100644 (file)
@@ -42,19 +42,40 @@ that starts with whitespace continues a logical line. </p>
 
 <ul>
 
-<li> <p> The expressions "$name", "${name}" or "$(name)" are
-recursively replaced by the value of the named parameter. </p>
+<li> <p> The expressions "$name" and "${name}" are recursively
+replaced with the value of the named parameter, except where noted.
+An undefined parameter value is replaced with the empty value.  </p>
 
-<li> <p> The expression "${name?value}" expands to "value" when
-"$name" is non-empty. This form is supported with Postfix version
-2.2 and later. </p>
+<li> <p> The expressions "${name?value}" and "${name?{value}}" are
+replaced with "value" when "$name" is non-empty. These forms are
+supported with Postfix versions &ge; 2.2 and &ge; 2.12, respectively.
+</p>
 
-<li> <p> The expression "${name:value}" expands to "value" when
-"$name" is empty. This form is supported with Postfix version 2.2
-and later.  </p>
+<li> <p> The expressions "${name:value}" and "${name?{value}}" are
+replaced with "value" when "$name" is empty. These forms are supported
+with Postfix versions &ge; 2.2 and &ge; 2.12, respectively.  </p>
+
+<li> <p> The expression "${name?{value1}:{value2}}" is replaced
+with "value1" when "$name" is non-empty, and with "value2" when
+"$name" is empty.  The "{}" is required for "value1", optional for
+"value2".  This form is supported with Postfix versions &ge; 2.12.
+</p>
+
+<li> <p> Instead of a parameter name, the first item inside "${...}"
+may be a logical expression of the form: "{value3} == {value4}"
+(equality) or "{value3} != {value4}" (inequality). This form is
+supported with Postfix versions &ge; 2.12.  </p>
+
+<li> <p> Each "value" is subject to recursive named parameter and
+logical expression evaluation, except where noted.  </p>
+
+<li> <p> Whitespace before or after each "{value}" is ignored. </p>
 
 <li> <p> Specify "$$" to produce a single "$" character. </p>
 
+<li> <p> The legacy form "$(...)" is equivalent to the preferred
+form "${...}". </p>
+
 </ul>
 
 <li> <p> When the same parameter is defined multiple times, only
index ebe62e9934fad1365c4b36609583c19c2d1213f4..ceb1277690329680e9afac5fc071bbd14166486b 100644 (file)
@@ -14,11 +14,11 @@ PROXYMAP(8)                                                        PROXYMAP(8)
 
 <b>DESCRIPTION</b>
        The  <a href="proxymap.8.html"><b>proxymap</b>(8)</a>  server  provides read-only or read-write table lookup
-       service to Postfix processes. These services are implemented with  dis-
+       service to Postfix processes. These services are implemented with  dis
        tinct service names: <b>proxymap</b> and <b>proxywrite</b>, respectively. The purpose
        of these services is:
 
-       <b>o</b>      To overcome chroot restrictions. For example,  a  chrooted  SMTP
+       ·      To overcome chroot restrictions. For example,  a  chrooted  SMTP
               server needs access to the system passwd file in order to reject
               mail for non-existent local addresses, but it is  not  practical
               to  maintain  a copy of the passwd file in the chroot jail.  The
@@ -27,7 +27,7 @@ PROXYMAP(8)                                                        PROXYMAP(8)
               <a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a> =
                   <a href="proxymap.8.html">proxy</a>:unix:passwd.byname $<a href="postconf.5.html#alias_maps">alias_maps</a>
 
-       <b>o</b>      To consolidate the number of open lookup tables by  sharing  one
+       ·      To consolidate the number of open lookup tables by  sharing  one
               open  table  among multiple processes. For example, making mysql
               connections from every Postfix daemon process  results  in  "too
               many connections" errors. The solution:
@@ -38,7 +38,7 @@ PROXYMAP(8)                                                        PROXYMAP(8)
               The  total  number  of  connections  is limited by the number of
               proxymap server processes.
 
-       <b>o</b>      To provide single-updater functionality for lookup  tables  that
+       ·      To provide single-updater functionality for lookup  tables  that
               do  not  reliably  support multiple writers (i.e. all file-based
               tables).
 
@@ -47,7 +47,7 @@ PROXYMAP(8)                                                        PROXYMAP(8)
        <b>open</b> <i>maptype:mapname flags</i>
               Open the table with type <i>maptype</i> and name <i>mapname</i>, as controlled
               by  <i>flags</i>.  The  reply  includes the <i>maptype</i> dependent flags (to
-              distinguish a fixed string table from a regular  expression  ta-
+              distinguish a fixed string table from a regular  expression  ta
               ble).
 
        <b>lookup</b> <i>maptype:mapname flags key</i>
@@ -101,7 +101,7 @@ PROXYMAP(8)                                                        PROXYMAP(8)
        The  <a href="proxymap.8.html"><b>proxymap</b>(8)</a>  server  opens  only  tables that are approved via the
        <b><a href="postconf.5.html#proxy_read_maps">proxy_read_maps</a></b> or <b><a href="postconf.5.html#proxy_write_maps">proxy_write_maps</a></b> configuration parameters, does  not
        talk  to  users,  and  can run at fixed low privilege, chrooted or not.
-       However, running the proxymap server chrooted severely  limits  usabil-
+       However, running the proxymap server chrooted severely  limits  usabil
        ity, because it can open only chrooted tables.
 
        The <a href="proxymap.8.html"><b>proxymap</b>(8)</a> server is not a trusted daemon process, and must not be
@@ -113,7 +113,7 @@ PROXYMAP(8)                                                        PROXYMAP(8)
        the  table directly. This allows the same <a href="postconf.5.html">main.cf</a> setting to be used by
        sensitive and non-sensitive processes.
 
-       Postfix-writable data files should be stored under a  dedicated  direc-
+       Postfix-writable data files should be stored under a  dedicated  direc
        tory  that  is  writable  only  by the Postfix mail system, such as the
        Postfix-owned <b><a href="postconf.5.html#data_directory">data_directory</a></b>.
 
@@ -131,7 +131,7 @@ PROXYMAP(8)                                                        PROXYMAP(8)
 
        The <a href="proxymap.8.html"><b>proxymap</b>(8)</a> read-write service does  not  explicitly  close  lookup
        tables  (even  if  it  did,  this  could  not be relied on, because the
-       process may be terminated between table updates).  The read-write  ser-
+       process may be terminated between table updates).  The read-write  ser
        vice  should  therefore  not  be used with tables that leave persistent
        storage in an inconsistent state between updates  (for  example,  CDB).
        Tables  that  support  "sync  on  update"  should be safe (for example,
@@ -146,7 +146,7 @@ PROXYMAP(8)                                                        PROXYMAP(8)
        more details including examples.
 
        <b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
-              The  default  location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
+              The  default  location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con
               figuration files.
 
        <b><a href="postconf.5.html#data_directory">data_directory</a> (see 'postconf -d' output)</b>
index 17077e496b5cf1179de947890db597b6a05e6f91..a72ead8ed56df9d4a4e34d61485a3a21dd8c2c88 100644 (file)
@@ -16,13 +16,13 @@ QMQP-SINK(1)                                                      QMQP-SINK(1)
 
 <b>DESCRIPTION</b>
        <b>qmqp-sink</b> listens on the named host (or address) and port.  It receives
-       messages from the network and throws them away.  The purpose is to mea-
+       messages from the network and throws them away.  The purpose is to mea
        sure QMQP client performance, not protocol compliance.  Connections can
        be accepted on IPv4 or IPv6 endpoints, or on UNIX-domain sockets.  IPv4
        and  IPv6 are the default.  This program is the complement of the <a href="qmqp-source.1.html"><b>qmqp-</b></a>
        <a href="qmqp-source.1.html"><b>source</b>(1)</a> program.
 
-       Note: this is an unsupported test program. No attempt is made to  main-
+       Note: this is an unsupported test program. No attempt is made to  main
        tain compatibility between successive versions.
 
        Arguments:
@@ -36,7 +36,7 @@ QMQP-SINK(1)                                                      QMQP-SINK(1)
        <b>-c</b>     Display a running counter that is updated whenever a delivery is
               completed.
 
-       <b>-v</b>     Increase verbosity. Specify <b>-v -v</b> to see some of the  QMQP  con-
+       <b>-v</b>     Increase verbosity. Specify <b>-v -v</b> to see some of the  QMQP  con
               versation.
 
        <b>-x</b> <i>time</i>
index 8c676de7cd95269e2f1068126d941aa04cd1e496..cf6fa6ebc573bafeb153ee6a36e3429b23cf7f20 100644 (file)
@@ -19,7 +19,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
        SMTP messages from the network and throws them away.  The purpose is to
        measure client performance, not protocol compliance.
 
-       <b>smtp-sink</b> may also be configured to capture each mail delivery transac-
+       <b>smtp-sink</b> may also be configured to capture each mail delivery transac
        tion to file. Since  disk  latencies  are  large  compared  to  network
        delays,  this  mode  of operation can reduce the maximal performance by
        several orders of magnitude.
@@ -28,7 +28,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
        domain  sockets.   IPv4  and IPv6 are the default.  This program is the
        complement of the <a href="smtp-source.1.html"><b>smtp-source</b>(1)</a> program.
 
-       Note: this is an unsupported test program. No attempt is made to  main-
+       Note: this is an unsupported test program. No attempt is made to  main
        tain compatibility between successive versions.
 
        Arguments:
@@ -44,7 +44,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
        <b>-a</b>     Do not announce SASL authentication support.
 
        <b>-A</b> <i>delay</i>
-              Wait  <i>delay</i>  seconds after responding to DATA, then abort prema-
+              Wait  <i>delay</i>  seconds after responding to DATA, then abort prema
               turely with a 550 reply status.  Do not read further input  from
               the  client;  this  is  an attempt to block the client before it
               sends ".".  Specify a zero delay value to abort immediately.
@@ -57,7 +57,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
               Use  <i>hard-bounce-reply</i>  for  hard reject responses.  The default
               reply is "500 5.3.0 Error: command failed".
 
-       <b>-c</b>     Display running counters that are updated whenever an SMTP  ses-
+       <b>-c</b>     Display running counters that are updated whenever an SMTP  ses
               sion  ends, a QUIT command is executed, or when "." is received.
 
        <b>-C</b>     Disable XCLIENT support.
@@ -67,7 +67,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
               is  created  by  expanding the <i>dump-template</i> via strftime(3) and
               appending   a   pseudo-random   hexadecimal   number   (example:
               "%Y%m%d%H/%M."  expands  into "2006081203/05.809a62e3").  If the
-              template contains "/" characters, missing directories  are  cre-
+              template contains "/" characters, missing directories  are  cre
               ated automatically.  The message dump format is described below.
 
               Note: this option keeps one capture file  open  for  every  mail
@@ -106,7 +106,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
 
        <b>-m</b> <i>count</i> (default: 256)
               An upper bound on the maximal number of simultaneous connections
-              that <b>smtp-sink</b> will handle. This prevents the process from  run-
+              that <b>smtp-sink</b> will handle. This prevents the process from  run
               ning  out  of  file  descriptors.  Excess  connections will stay
               queued in the TCP/IP stack.
 
@@ -122,7 +122,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
               CISCO PIX system. Implies <b>-e</b>.
 
        <b>-q</b> <i>command,command,...</i>
-              Disconnect  (without replying) after receiving one of the speci-
+              Disconnect  (without replying) after receiving one of the speci
               fied commands.
 
               Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL,  RCPT,
@@ -131,7 +131,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
               from the shell. Command names are case-insensitive.
 
        <b>-Q</b> <i>command,command,...</i>
-              Send a 421 reply and disconnect after receiving one of the spec-
+              Send a 421 reply and disconnect after receiving one of the spec
               ified commands.
 
               Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL,  RCPT,
@@ -165,7 +165,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
               An optional string that is prepended to  each  message  that  is
               written  to  a  dump  file (see the dump file format description
               below). The following  C  escape  sequences  are  supported:  \a
-              (bell),  \b  (backslace),  \f (formfeed), \n (newline), \r (car-
+              (bell),  \b  (backslace),  \f (formfeed), \n (newline), \r (car
               riage return), \t (horizontal tab), \v (vertical tab), \<i>ddd</i>  (up
               to three octal digits) and \\ (the backslash character).
 
@@ -178,7 +178,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
               window scaling implementations, specify a value &gt; 0 and &lt; 65536.
 
        <b>-u</b> <i>username</i>
-              Switch to the specified user privileges after opening  the  net-
+              Switch to the specified user privileges after opening  the  net
               work  socket and optionally changing the process root directory.
               This option is required when the process  runs  with  super-user
               privileges. See also the <b>-R</b> option.
@@ -212,13 +212,13 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
        Each  dumped message contains a sequence of text lines, terminated with
        the newline character. The sequence of information is as follows:
 
-       <b>o</b>      The optional string specified with the <b>-S</b> option.
+       ·      The optional string specified with the <b>-S</b> option.
 
-       <b>o</b>      The <b>smtp-sink</b> generated headers as documented below.
+       ·      The <b>smtp-sink</b> generated headers as documented below.
 
-       <b>o</b>      The message header and body as received from the SMTP client.
+       ·      The message header and body as received from the SMTP client.
 
-       <b>o</b>      An empty line.
+       ·      An empty line.
 
        The format of the <b>smtp-sink</b> generated headers is as follows:
 
@@ -233,11 +233,11 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
        <b>X-Helo-Args:</b> <i>text</i>
               The arguments of the last HELO or EHLO command before this  mail
               delivery  transaction. This record is present only if the client
-              sent a recognizable HELO or EHLO command before  the  DATA  com-
+              sent a recognizable HELO or EHLO command before  the  DATA  com
               mand.
 
        <b>X-Mail-Args:</b> <i>text</i>
-              The  arguments of the MAIL command that started this mail deliv-
+              The  arguments of the MAIL command that started this mail deliv
               ery transaction. This record is present exactly once.
 
        <b>X-Rcpt-Args:</b> <i>text</i>
@@ -246,8 +246,8 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
               are in the order as sent by the client.
 
        <b>Received:</b> <i>text</i>
-              A message header for compatibility with  mail  processing  soft-
-              ware.  This  three-line header marks the end of the headers pro-
+              A message header for compatibility with  mail  processing  soft
+              ware.  This  three-line header marks the end of the headers pro
               vided by <b>smtp-sink</b>, and is formatted as follows:
 
               <b>from</b> <i>helo</i> <b>([</b><i>addr</i><b>])</b>
@@ -257,7 +257,7 @@ SMTP-SINK(1)                                                      SMTP-SINK(1)
 
               <b>by</b> <i>host</i> <b>(smtp-sink) with</b> <i>proto</i> <b>id</b> <i>random</i><b>;</b>
                      The hostname specified with the  <b>-h</b>  option,  the  client
-                     protocol  (see <b>X-Client-Proto</b> above), and the pseudo-ran-
+                     protocol  (see <b>X-Client-Proto</b> above), and the pseudo-ran
                      dom portion of the per-message capture file name.
 
               <i>time-stamp</i>
index 68f9bab24722b3f78bee97d1a698a2b1884ff4e1..a2db7755ade852b2d3bcd812f8607dc02605bc0f 100644 (file)
@@ -274,25 +274,26 @@ The file format is described in \fBpcre_table\fR(5).
 PostgreSQL database client. This is described in
 \fBpgsql_table\fR(5).
 .IP "\fBpipemap\fR (read-only)"
-A pipeline of lookup tables.  Example:
-"\fBpipemap:\fI!type_1:name_1!  ... !type_n:name_n\fR".
+A lookup table that constructs a pipeline of tables.  Example:
+"\fBpipemap:{\fItype_1:name_1,  ..., type_n:name_n\fB}\fR".
 Each "pipemap:" query is given to the first table.  Each
 lookup result becomes the query for the next table in the
 pipeline, and the last table produces the final result.
 When any table lookup produces no result, the pipeline
-produces no result. The first ASCII character after "pipemap:"
-will be used as the separator between the lookup tables
-that follow (do not use space, ",", ":" or non-ASCII).
+produces no result. The first and last characters of the
+"pipemap:" table name must be "\fB{\fR" and "\fB}\fR".
+Within these, individual maps are separated with comma or
+whitespace.
 .IP "\fBproxy\fR"
 Postfix \fBproxymap\fR(8) client for shared access to Postfix
 databases. The table name syntax is \fItype\fB:\fIname\fR.
 .IP "\fBrandmap\fR (read-only)"
 An in-memory table that performs random selection. Example:
-"\fBrandmap:\fI!result_1! ... !result_n\fR". Each table query
+"\fBrandmap:{\fIresult_1, ..., result_n\fB}\fR". Each table query
 returns a random choice from the specified results. The first
-ASCII character after "randmap:" will be used as the separator
-between the results that follow (do not use space, ",", ":"
-or non-ASCII).
+and last characters of the "randmap:" table name must be
+"\fB{\fR" and "\fB}\fR".  Within these, individual maps are
+separated with comma or whitespace.
 .IP "\fBregexp\fR (read-only)"
 A lookup table based on regular expressions. The file format
 is described in \fBregexp_table\fR(5).
index d19ce122d19b1fe9db6fb6490893f597150378f8..0cb0a6344843f45c9a7d69cca5bbb27e3025d49e 100644 (file)
@@ -32,18 +32,36 @@ with whitespace continues a logical line.
 A parameter value may refer to other parameters.
 .RS
 .IP \(bu 
-The expressions "$name", "${name}" or "$(name)" are
-recursively replaced by the value of the named parameter.
+The expressions "$name" and "${name}" are recursively replaced with
+the value of the named parameter. An undefined parameter value is
+replaced with the empty value.
 .IP \(bu
-The expression "${name?value}" expands to "value" when
-"$name" is non-empty. This form is supported with Postfix
-version 2.2 and later.
+The expressions "${name?value}" and "${name?{value}}" are replaced
+with "value" when "$name" is non-empty. These forms are supported
+with Postfix versions >= 2.2 and >= 2.12, respectively.
 .IP \(bu
-The expression "${name:value}" expands to "value" when
-"$name" is empty. This form is supported with Postfix
-version 2.2 and later.
+The expressions "${name:value}" and "${name:{value}}" are replaced
+with "value" when "$name" is empty. These forms are supported with
+Postfix versions >= 2.2 and >= 2.12, respectively.
+.IP \(bu
+The expression "${name?{value1}:{value2}}" is replaced with "value1"
+when "$name" is non-empty, and with "value2" when "$name" is empty.
+The "{}" is required for "value1", optional for "value2". This form
+is supported with Postfix versions >= 2.12.
+.IP \(bu
+Instead of a parameter name, the first item inside "${...}" may be
+a logical expression of the form: "{value3} == {value4}" (equality)
+or "{value3} != {value4}" (inequality).  This form is supported
+with Postfix versions >= 2.12.
+.IP \(bu
+Each "value" is subject to recursive named parameter and logical
+expression evaluation, except where noted.
+.IP \(bu
+Whitespace before or after each "{value}" is ignored.
 .IP \(bu
 Specify "$$" to produce a single "$" character.
+.IP \(bu
+The legacy form "$(...)" is equivalent to the preferred form "${...}".
 .RE
 .IP \(bu
 When the same parameter is defined multiple times, only the last
index 3ac27ec3d4f783283bcd6af414c1f159b4c6e987..847ad01b9b4089fca5c5f06ebe216adbafb71371 100644 (file)
@@ -365,14 +365,14 @@ file.  </dd>
 <dt> <b>pipemap</b> (read-only) </dt>
 
 <dd> A pipeline of lookup tables. Example:
-"<b>pipemap:</b><i>!type<sub>1</sub>:name<sub>1</sub>! ...
-!type<sub>n</sub>:name<sub>n</sub></i>".  Each "pipemap:" query is
+"pipemap:{<i>type<sub>1</sub>:name<sub>1</sub>, ...,
+type<sub>n</sub>:name<sub>n</sub></i>}".  Each "pipemap:" query is
 given to the first table.  Each lookup result becomes the query for
 the next table in the pipeline, and the last table produces the
 final result.  When any table lookup produces no result, the pipeline
-produces no result.  The first ASCII character after "pipemap:"
-will be used as the separator between the lookup tables that follow
-(do not use space, ",", ":" or non-ASCII).  </dd>
+produces no result.  The first and last characters of the "pipemap:"
+table name must be "{" and "}". Within these, individual maps are
+separated with comma or whitespace.  </dd>
 
 <dt> <b>pgsql</b> (read-only) </dt>
 
@@ -388,11 +388,11 @@ databases. The lookup table name syntax is "proxy:type:table".
 <dt> <b>randmap</b> (read-only) </dt>
 
 <dd> An in-memory table that performs random selection.  Example:
-"<b>randmap:</b><i>!result<sub>1</sub>! ... !result<sub>n</sub></i>".
+"randmap:{<i>result<sub>1</sub>. ..., result<sub>n</sub></i>}".
 Each table query returns a random choice from the specified results.
-The first ASCII character after "randmap:" will be used as the
-separator between the results that follow (do not use space, ",",
-":" or non-ASCII).  </dd>
+The first and last characters of the "randmap:" table name must be
+"{" and "}".  Within these, individual maps are separated with comma
+or whitespace.  </dd>
 
 <dt> <b>regexp</b> (read-only) </dt>
 
index 3963e7e6d5a3e2c5388640a99ff3afad2e20f644..a1661f4cd1f21d0f0f6d2b797866402e10033e57 100644 (file)
@@ -42,19 +42,40 @@ that starts with whitespace continues a logical line. </p>
 
 <ul>
 
-<li> <p> The expressions "$name", "${name}" or "$(name)" are
-recursively replaced by the value of the named parameter. </p>
+<li> <p> The expressions "$name" and "${name}" are recursively
+replaced with the value of the named parameter, except where noted.
+An undefined parameter value is replaced with the empty value.  </p>
 
-<li> <p> The expression "${name?value}" expands to "value" when
-"$name" is non-empty. This form is supported with Postfix version
-2.2 and later. </p>
+<li> <p> The expressions "${name?value}" and "${name?{value}}" are
+replaced with "value" when "$name" is non-empty. These forms are
+supported with Postfix versions &ge; 2.2 and &ge; 2.12, respectively.
+</p>
 
-<li> <p> The expression "${name:value}" expands to "value" when
-"$name" is empty. This form is supported with Postfix version 2.2
-and later.  </p>
+<li> <p> The expressions "${name:value}" and "${name?{value}}" are
+replaced with "value" when "$name" is empty. These forms are supported
+with Postfix versions &ge; 2.2 and &ge; 2.12, respectively.  </p>
+
+<li> <p> The expression "${name?{value1}:{value2}}" is replaced
+with "value1" when "$name" is non-empty, and with "value2" when
+"$name" is empty.  The "{}" is required for "value1", optional for
+"value2".  This form is supported with Postfix versions &ge; 2.12.
+</p>
+
+<li> <p> Instead of a parameter name, the first item inside "${...}"
+may be a logical expression of the form: "{value3} == {value4}"
+(equality) or "{value3} != {value4}" (inequality). This form is
+supported with Postfix versions &ge; 2.12.  </p>
+
+<li> <p> Each "value" is subject to recursive named parameter and
+logical expression evaluation, except where noted.  </p>
+
+<li> <p> Whitespace before or after each "{value}" is ignored. </p>
 
 <li> <p> Specify "$$" to produce a single "$" character. </p>
 
+<li> <p> The legacy form "$(...)" is equivalent to the preferred
+form "${...}". </p>
+
 </ul>
 
 <li> <p> When the same parameter is defined multiple times, only
index 170838fe64ab1e78724f86853d48712a75ae0b3c..ac915824c84775b042f2d2c641040926c02140d4 100644 (file)
@@ -32,18 +32,36 @@ with whitespace continues a logical line.
 A parameter value may refer to other parameters.
 .RS
 .IP \(bu 
-The expressions "$name", "${name}" or "$(name)" are
-recursively replaced by the value of the named parameter.
+The expressions "$name" and "${name}" are recursively replaced with
+the value of the named parameter. An undefined parameter value is
+replaced with the empty value.
 .IP \(bu
-The expression "${name?value}" expands to "value" when
-"$name" is non-empty. This form is supported with Postfix
-version 2.2 and later.
+The expressions "${name?value}" and "${name?{value}}" are replaced
+with "value" when "$name" is non-empty. These forms are supported
+with Postfix versions >= 2.2 and >= 2.12, respectively.
 .IP \(bu
-The expression "${name:value}" expands to "value" when
-"$name" is empty. This form is supported with Postfix
-version 2.2 and later.
+The expressions "${name:value}" and "${name:{value}}" are replaced
+with "value" when "$name" is empty. These forms are supported with
+Postfix versions >= 2.2 and >= 2.12, respectively.
+.IP \(bu
+The expression "${name?{value1}:{value2}}" is replaced with "value1"
+when "$name" is non-empty, and with "value2" when "$name" is empty.
+The "{}" is required for "value1", optional for "value2". This form
+is supported with Postfix versions >= 2.12.
+.IP \(bu
+Instead of a parameter name, the first item inside "${...}" may be
+a logical expression of the form: "{value3} == {value4}" (equality)
+or "{value3} != {value4}" (inequality).  This form is supported
+with Postfix versions >= 2.12.
+.IP \(bu
+Each "value" is subject to recursive named parameter and logical
+expression evaluation, except where noted.
+.IP \(bu
+Whitespace before or after each "{value}" is ignored.
 .IP \(bu
 Specify "$$" to produce a single "$" character.
+.IP \(bu
+The legacy form "$(...)" is equivalent to the preferred form "${...}".
 .RE
 .IP \(bu
 When the same parameter is defined multiple times, only the last
index fe6c65524a940bc2a1dd05c131c09162e82e1437..2927d13285be849b92b2864a108910cc8644cb73 100644 (file)
@@ -50,7 +50,7 @@ typedef struct BOUNCE_TEMPLATE {
 #define bounce_template_encoding(t)    ((t)->mime_encoding)
 #define bounce_template_charset(t)     ((t)->mime_charset)
 
-typedef int (*BOUNCE_XP_PRN_FN) (VSTREAM *, const char *, ...);
+typedef int PRINTFLIKE(2, 3) (*BOUNCE_XP_PRN_FN) (VSTREAM *, const char *,...);
 typedef int (*BOUNCE_XP_PUT_FN) (VSTREAM *, const char *);
 
 extern BOUNCE_TEMPLATE *bounce_template_create(const BOUNCE_TEMPLATE *);
index aa1fe6c5bbb36f180d19339867258c1674cdd3d2..7860df213b006a994f4ffd1129d9e6d54c0b9592 100644 (file)
@@ -580,8 +580,11 @@ static void cleanup_header_callback(void *context, int header_class,
        if (hdr_opts->type == HDR_RESENT_MESSAGE_ID)
            msg_info("%s: resent-message-id=%s", state->queue_id, hdrval);
        if (hdr_opts->type == HDR_RECEIVED)
-           if (++state->hop_count >= var_hopcount_limit)
+           if (++state->hop_count >= var_hopcount_limit) {
+               msg_warn("%s: message rejected: hopcount exceeded",
+                        state->queue_id);
                state->errs |= CLEANUP_STAT_HOPS;
+           }
        if (CLEANUP_OUT_OK(state)) {
            if (hdr_opts->flags & HDR_OPT_RR)
                state->resent = "Resent-";
index 7f497ca494573a2bd1c2a9d7528ac7a054f7d313..1a8fe0f9cb3ed8d2771c34fdef6cb2069a6132ec 100644 (file)
@@ -227,7 +227,7 @@ int     main(int argc, char **argv)
            vstream_fflush(VSTREAM_OUT);
            continue;
        }
-       target = mystrtok(&bufp, " \t");
+       target = mystrtokq(&bufp, " \t");
        junk = mystrtok(&bufp, " \t");
        if (strcmp(cmd, "file") == 0 && target && !junk) {
            data_redirect_file(result, target);
index 161e0c880be8ce33769b8592c65742dce8a367b4..0b87dfb0d7a877caa99564b165016e5fd76a0873 100644 (file)
@@ -133,7 +133,7 @@ static int dict_memcache_set(DICT_MC *dict_mc, const char *value, int ttl)
 {
     VSTREAM *fp;
     int     count;
-    int     data_len = strlen(value);
+    size_t  data_len = strlen(value);
 
     /*
      * Return a permanent error if we can't store this data. This results in
@@ -153,7 +153,8 @@ static int dict_memcache_set(DICT_MC *dict_mc, const char *value, int ttl)
        if ((fp = auto_clnt_access(dict_mc->clnt)) == 0) {
            break;
        } else if (memcache_printf(fp, "set %s %d %d %ld",
-               STR(dict_mc->key_buf), dict_mc->mc_flags, ttl, data_len) < 0
+                                  STR(dict_mc->key_buf), dict_mc->mc_flags,
+                                  ttl, (long) data_len) < 0
                   || memcache_fwrite(fp, value, strlen(value)) < 0
                   || memcache_get(fp, dict_mc->clnt_buf,
                                   dict_mc->max_line) < 0) {
index adb59662a4a1707f4802d3fdfc21f14c3dc732e1..c4bfa30d8e41a657b74cef054a80aef4d9ddf550 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20140907"
+#define MAIL_RELEASE_DATE      "20140921"
 #define MAIL_VERSION_NUMBER    "2.12"
 
 #ifdef SNAPSHOT
index 391a800190a1d21183493213837190c7c3400300..b0e0b9b5b73d3e56f352d3545dd73ef5a2a95f0d 100644 (file)
@@ -115,6 +115,7 @@ MAPS   *maps_create(const char *title, const char *map_names, int dict_flags)
     char   *temp;
     char   *bufp;
     static char sep[] = " \t,\r\n";
+    static char parens[] = "{}";
     MAPS   *maps;
     char   *map_type_name;
     VSTRING *map_type_name_flags;
@@ -138,7 +139,7 @@ MAPS   *maps_create(const char *title, const char *map_names, int dict_flags)
 
 #define OPEN_FLAGS     O_RDONLY
 
-       while ((map_type_name = mystrtok(&bufp, sep)) != 0) {
+       while ((map_type_name = mystrtokq(&bufp, sep, parens)) != 0) {
            vstring_sprintf(map_type_name_flags, "%s(%o,%s)",
                            map_type_name, OPEN_FLAGS,
                            dict_flags_str(dict_flags));
index 88a1d191f4e9b1786df1fe109afe5f67fec20d93..e3f8501888bce8f176d00d84b6d0e574dcaff7f1 100644 (file)
@@ -16,7 +16,7 @@
   */
 extern int memcache_get(VSTREAM *, VSTRING *, ssize_t);
 extern int memcache_vprintf(VSTREAM *, const char *, va_list);
-extern int memcache_printf(VSTREAM *, const char *fmt,...);
+extern int PRINTFLIKE(2, 3) memcache_printf(VSTREAM *, const char *fmt,...);
 extern int memcache_fread(VSTREAM *, VSTRING *, ssize_t);
 extern int memcache_fwrite(VSTREAM *, const char *, ssize_t);
 
index 3855c6fe037db4b69fcc0dd5d24aceeb38f9c940..7717f02d0184b00f6b2f868d6f3b47ae78f4bc45 100644 (file)
@@ -120,7 +120,7 @@ SERVER_ACL *server_acl_parse(const char *extern_acl, const char *origin)
      * chroot jail, while access lists are evaluated after entering the
      * chroot jail.
      */
-    while ((acl = mystrtok(&bp, SERVER_ACL_SEPARATORS)) != 0) {
+    while ((acl = mystrtokq(&bp, SERVER_ACL_SEPARATORS, "{}")) != 0) {
        if (strchr(acl, ':') != 0) {
            if (strchr(origin, ':') != 0) {
                msg_warn("table %s: lookup result \"%s\" is not allowed"
index 45d894760e6140d62c01702db03b28193ad0e421..b7eefb516aa09f05bfad15701cff3f2dd62c973d 100644 (file)
@@ -49,7 +49,7 @@ tests: test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 \
        test22 test23 test24 test25 test26 test27 test28 test29 test30 test4b \
        test31 test32 test33 test34 test35 test36 test37 test39 test40 test41 \
        test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 \
-       test52 test53 test54 test55 test56
+       test52 test53 test54 test55 test56 test57 test58
 
 root_tests:
 
@@ -72,6 +72,7 @@ test1:        $(PROG) test1.ref
        touch main.cf master.cf
        echo smtpd_restriction_classes = foo bar >> main.cf
        echo foo = yes >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test1.tmp 2>&1
        diff test1.ref test1.tmp
        rm -f main.cf master.cf test1.tmp
@@ -83,6 +84,7 @@ test2:        $(PROG) test2.ref
        touch main.cf master.cf
        echo restriction_classes = foo bar >> main.cf
        echo foo = yes >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test2.tmp 2>&1
        diff test2.ref test2.tmp
        rm -f main.cf master.cf test2.tmp
@@ -95,6 +97,7 @@ test3:        $(PROG) test3.ref
        echo foo = yes >> main.cf
        echo 'bar = $$foo' >> main.cf
        echo 'always_bcc = $$bar' >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test3.tmp 2>&1
        diff test3.ref test3.tmp
        rm -f main.cf master.cf test3.tmp
@@ -108,6 +111,7 @@ test4:      $(PROG) test4.ref
        echo 'bar = $$foo' >> main.cf
        echo smtpd unix - n n - 0 smtpd >> master.cf
        echo ' -o always_bcc=$$bar' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test4.tmp 2>&1
        diff test4.ref test4.tmp
        rm -f main.cf master.cf test4.tmp
@@ -123,6 +127,7 @@ test4b:     $(PROG) test4b.ref
        echo smtpd1 unix - n n - 0 smtpd >> master.cf
        echo ' -o foo=xxx -o bar=yyy -o baz=zzz' >> master.cf
        echo '#smtpd2 unix - n n - 0 smtpd' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test4b.tmp 2>&1
        diff test4b.ref test4b.tmp
        rm -f main.cf master.cf test4b.tmp
@@ -135,6 +140,7 @@ test5:      $(PROG) test5.ref
        touch main.cf master.cf
        echo smtpd unix - n n - 0 smtpd >> master.cf
        echo ' -o bar=yes -o always_bcc=$$bar -o' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test5.tmp 2>&1
        diff test5.ref test5.tmp
        rm -f main.cf master.cf test5.tmp
@@ -145,6 +151,7 @@ test6:      $(PROG) test6.ref
        rm -f main.cf master.cf
        touch main.cf master.cf
        echo whatevershebrings unix - n n - 0 pipe >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . 2>&1 | grep whatevershebrings >test6.tmp
        diff test6.ref test6.tmp
        rm -f main.cf master.cf test6.tmp
@@ -155,6 +162,7 @@ test7:      $(PROG) test7.ref
        rm -f main.cf master.cf
        touch main.cf master.cf
        echo whatevershebrings unix - n n - 0 spawn >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . 2>&1 | grep whatevershebrings >test7.tmp
        diff test7.ref test7.tmp
        rm -f main.cf master.cf test7.tmp
@@ -164,6 +172,7 @@ test8:      $(PROG) test8.ref
        touch main.cf master.cf
        echo whatevershebrings inet - n n - 0 spawn >> master.cf
        echo whatevershebrings_time_limit=1 >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . 2>&1 | grep whatevershebrings >test8.tmp
        diff test8.ref test8.tmp
        rm -f main.cf master.cf test8.tmp
@@ -173,6 +182,7 @@ test9:      $(PROG) test9.ref
        touch main.cf master.cf
        echo foo inet - n n - 0 spawn >> master.cf
        echo bar unix - n n - 0 spawn >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . -M '*'/inet >test9.tmp 2>&1
        diff test9.ref test9.tmp
        rm -f main.cf master.cf test9.tmp
@@ -182,6 +192,7 @@ test10:     $(PROG) test10.ref
        touch main.cf master.cf
        echo foo inet - n n - 0 spawn >> master.cf
        echo bar unix - n n - 0 spawn >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . -M bar/inet foo/unix >test10.tmp 2>&1
        diff test10.ref test10.tmp
        rm -f main.cf master.cf test10.tmp
@@ -191,6 +202,7 @@ test11:     $(PROG) test11.ref
        touch main.cf master.cf
        echo foo inet - n n - 0 spawn >> master.cf
        echo bar unix - n n - 0 spawn >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . -M >test11.tmp 2>&1
        diff test11.ref test11.tmp
        rm -f main.cf master.cf test11.tmp
@@ -205,6 +217,7 @@ test12:     $(PROG) test12.ref
        echo ' -o always_bcc=$$bar -o' >> master.cf
        echo foo inet - n n - 0 spawn >> master.cf
        echo ' -o always_bcc=$$bar -o' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . -M >test12.tmp 2>&1
        diff test12.ref test12.tmp
        rm -f main.cf master.cf test12.tmp
@@ -218,6 +231,7 @@ test13:     $(PROG) test13.ref
        echo baz=xx >> main.cf
        echo foo inet - n n - 0 spawn >> master.cf
        echo ' -o smtpd_restriction_classes=bar' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test13.tmp 2>&1
        diff test13.ref test13.tmp
        rm -f main.cf master.cf test13.tmp
@@ -230,6 +244,7 @@ test14:     $(PROG) test14.ref
        echo smtpd_restriction_classes=bar >> main.cf
        echo foo inet - n n - 0 spawn >> master.cf
        echo ' -o bar=yes -o baz=xx' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test14.tmp 2>&1
        diff test14.ref test14.tmp
        rm -f main.cf master.cf test14.tmp
@@ -243,6 +258,7 @@ test15:     $(PROG) test15.ref
        echo baz=yy >> main.cf
        echo foo inet - n n - 0 spawn >> master.cf
        echo ' -o bar=yes -o always_bcc=$$bar$$baz' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test15.tmp 2>&1
        diff test15.ref test15.tmp
        rm -f main.cf master.cf test15.tmp
@@ -251,14 +267,14 @@ test15:   $(PROG) test15.ref
 
 test16:        $(PROG) test16.ref
        rm -f main.cf master.cf
-       touch main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test16.tmp 2>&1
        diff test16.ref test16.tmp
        rm -f main.cf master.cf test16.tmp
 
 test17:        $(PROG) test17.ref
        rm -f main.cf master.cf
-       touch main.cf
+       touch -t 197101010000 main.cf
        -./$(PROG) -Mc . >test17.tmp 2>&1; exit 0
        diff test17.ref test17.tmp
        rm -f main.cf master.cf test17.tmp
@@ -270,6 +286,7 @@ test18:     $(PROG) test18.ref
        touch main.cf master.cf
        echo virtual_maps=xxx >> main.cf
        echo smtpd_client_connection_limit_exceptions=yyy >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test18.tmp 2>&1
        diff test18.ref test18.tmp
        rm -f main.cf master.cf test18.tmp
@@ -281,6 +298,7 @@ test19:     $(PROG) test19.ref
        touch main.cf master.cf
        echo forward_path='$$'aaaa >> main.cf
        echo default_rbl_reply='$$'bbbb >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test19.tmp 2>&1
        diff test19.ref test19.tmp
        rm -f main.cf master.cf test19.tmp
@@ -292,6 +310,7 @@ test20:     $(PROG) test20.ref
        touch main.cf master.cf
        echo foo inet - n n - 0 spawn >> master.cf
        echo ' -o always_bcc=$$bar$$baz' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mfc . >test20.tmp 2>&1
        diff test20.ref test20.tmp
        rm -f main.cf master.cf test20.tmp
@@ -303,6 +322,7 @@ test21:     $(PROG) test21.ref
        touch main.cf master.cf
        echo forward_path = xxxxxxxxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxx \
            xxxxxxxxxxxxx xxxxxxxxxxxxxx >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nfc . >test21.tmp 2>&1
        diff test21.ref test21.tmp
        rm -f main.cf master.cf test21.tmp
@@ -313,6 +333,7 @@ test22:     $(PROG) test22.ref
        rm -f main.cf master.cf
        touch main.cf master.cf
        echo whatevershebrings unix - n n - 0 smtp >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . 2>&1 | grep whatevershebrings >test22.tmp
        diff test22.ref test22.tmp
        rm -f main.cf master.cf test22.tmp
@@ -326,6 +347,7 @@ test23:     $(PROG) test23.ref
        echo name = value >> main.cf
        echo whatevershebrings unix - n n - 0 smtp >> master.cf
        echo ' -o always_bcc=$$name' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . -nC builtin >test23.tmp 2>&1
        diff test23.ref test23.tmp
        rm -f main.cf master.cf test23.tmp
@@ -337,6 +359,7 @@ test24:     $(PROG) test24.ref
        echo name = value >> main.cf
        echo whatevershebrings unix - n n - 0 smtp >> master.cf
        echo ' -o always_bcc=$$name' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . -nC user >test24.tmp 2>&1
        diff test24.ref test24.tmp
        rm -f main.cf master.cf test24.tmp
@@ -348,6 +371,7 @@ test25:     $(PROG) test25.ref
        echo name = value >> main.cf
        echo whatevershebrings unix - n n - 0 smtp >> master.cf
        echo ' -o always_bcc=$$name' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . -C service 2>&1 | grep whatevershebrings >test25.tmp
        diff test25.ref test25.tmp
        rm -f main.cf master.cf test25.tmp
@@ -361,6 +385,7 @@ test26:     $(PROG) test26.ref
        echo name = value >> main.cf
        echo whatevershebrings unix - n n - 0 smtp >> master.cf
        echo ' -o always_bcc=$$name' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . -C all >test26.tmp 2>&1
        diff test26.ref test26.tmp
        rm -f main.cf master.cf test26.tmp
@@ -372,6 +397,7 @@ test27:     $(PROG) test27.ref
        echo name = value >> main.cf
        echo whatevershebrings unix - n n - 0 smtp >> master.cf
        echo ' -o always_bcc=$$name' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -c . -C all 2>&1 | grep whatevershebrings >test27.tmp
        diff test27.ref test27.tmp
        rm -f main.cf master.cf test27.tmp
@@ -392,6 +418,7 @@ test28:     $(PROG) test28.ref
        echo ' -o body_checks=$$db:zz' >> master.cf
        echo 'zz_domain = whatever' >> main.cf
        echo 'aa_domain = whatever' >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test28.tmp 2>&1
        diff test28.ref test28.tmp
        rm -f main.cf master.cf test28.tmp
@@ -416,6 +443,7 @@ test29:     $(PROG) test29.ref
        echo 'memcachexx = proxy:memcache:memcachefoo' >> main.cf
        echo 'memcachefoo_domain = bar' >> main.cf
        echo 'memcachefoo_domainx = bar' >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test29.tmp 2>&1
        diff test29.ref test29.tmp
        rm -f main.cf master.cf test29.tmp
@@ -432,6 +460,7 @@ test30:     $(PROG) test30.ref
        echo ' -o bodyx_checks=$$p2' >> master.cf
        echo ' -oheader_checks=$$p3' >> master.cf
        echo ' -oheaderx_checks=$$p4' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nc . >test30.tmp 2>&1
        diff test30.ref test30.tmp
        rm -f main.cf master.cf test30.tmp
@@ -443,6 +472,7 @@ test31:     $(PROG) test31.ref
        touch main.cf master.cf
        echo 'smtpd_helo_restrictions=whatever' >> main.cf
        echo 'smtpd_sender_restrictions=$$smtpd_helo_restrictions' >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nxc . >test31.tmp 2>&1
        diff test31.ref test31.tmp
        rm -f main.cf master.cf test31.tmp
@@ -453,6 +483,7 @@ test32:     $(PROG) test32.ref
        rm -f main.cf master.cf
        touch main.cf master.cf
        echo 'relay_domains=whatever' >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -xc . fast_flush_domains >test32.tmp 2>&1
        diff test32.ref test32.tmp
        rm -f main.cf master.cf test32.tmp
@@ -464,6 +495,7 @@ test33:     $(PROG) test33.ref
        touch main.cf master.cf
        echo 'mydestination=whatever' >> main.cf
        echo 'always_bcc=$$relay_domains' >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -xc . always_bcc >test33.tmp 2>&1
        diff test33.ref test33.tmp
        rm -f main.cf master.cf test33.tmp
@@ -474,6 +506,7 @@ test34:     $(PROG) test34.ref
        echo 'mydestination=whatever' >> main.cf
        echo 'process_name=xxx' >> main.cf
        echo 'process_id=yyy' >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -xc . mydestination process_name >test34.tmp 2>&1
        diff test34.ref test34.tmp
        rm -f main.cf master.cf test34.tmp
@@ -485,6 +518,7 @@ test35:     $(PROG) test35.ref
        echo ' -o body_checks=whatever' >> master.cf
        echo ' -o process_name=aaa' >> master.cf
        echo ' -o process_id=bbb' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -xc . process_name >test35.tmp 2>&1
        diff test35.ref test35.tmp
        rm -f main.cf master.cf test35.tmp
@@ -494,6 +528,7 @@ test36:     $(PROG) test36.ref
        touch main.cf master.cf
        echo 'mydestination=$$virtual_mapx' >> main.cf
        echo 'virtual_alias_maps=$$virtual_maps' >> main.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -nxc . >test36.tmp 2>&1
        diff test36.ref test36.tmp
        rm -f main.cf master.cf test36.tmp
@@ -507,6 +542,7 @@ test37: $(PROG) test37.ref
        echo ' -o mydestination=$$xxx' >> master.cf
        echo ' -o always_bcc=$$aaa' >> master.cf
        echo ' -o aaa=ccc' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mfxc . >test37.tmp 2>&1
        diff test37.ref test37.tmp
        rm -f main.cf master.cf test37.tmp
@@ -517,6 +553,7 @@ test39: $(PROG) test39.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar inet - n n - 0 other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mfc . '*'/unix >test39.tmp 2>&1
        diff test39.ref test39.tmp
        rm -f main.cf master.cf test39.tmp
@@ -528,6 +565,7 @@ test40: $(PROG) test40.ref
        echo ' -voaaa=bbb' >> master.cf
        echo ' -vo ccc=$$aaa' >> master.cf
        echo ' -v -oddd=$$ccc' >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mfxc . '*'/unix >test40.tmp 2>&1
        diff test40.ref test40.tmp
        rm -f main.cf master.cf test40.tmp
@@ -538,6 +576,7 @@ test41: $(PROG) test41.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar unix - n n - 0 other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Pc . bar/unix/xxx=yyy bar/unix/aaa=bbb >test41.tmp 2>&1
        ./$(PROG) -Mfc. >>test41.tmp 2>&1
        ./$(PROG) -Pc . bar/unix/xxx=YYY bar/unix/aaa=BBB >>test41.tmp 2>&1
@@ -552,6 +591,7 @@ test42: $(PROG) test42.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar unix - n n - 0 other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Pc . bar/unix/xxx=yyy bar/unix/aaa=bbb >test42.tmp 2>&1
        ./$(PROG) -Mfc. >>test42.tmp 2>&1
        ./$(PROG) -Pc . >>test42.tmp 2>&1
@@ -566,6 +606,7 @@ test43: $(PROG) test43.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar unix - n n - 0 other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Fc . bar/unix/chroot=y bar/unix/command='aa -stuffobb=cc dd' >test43.tmp 2>&1
        ./$(PROG) -Mfc. >>test43.tmp 2>&1
        diff test43.ref test43.tmp
@@ -577,6 +618,7 @@ test44: $(PROG) test44.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar unix - n n - 0 other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mc . bar/unix='xx inet - n n - 0 aa -stuffobb=cc dd' >test44.tmp 2>&1
        ./$(PROG) -Mfc. >>test44.tmp 2>&1
        diff test44.ref test44.tmp
@@ -588,6 +630,7 @@ test45: $(PROG) test45.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar xxxx - n n - 0 other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mfc. >test45.tmp 2>&1 || true
        diff test45.ref test45.tmp
        rm -f main.cf master.cf test45.tmp
@@ -598,6 +641,7 @@ test46: $(PROG) test46.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar inet X n n - 0 other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mfc. >test46.tmp 2>&1 || true
        diff test46.ref test46.tmp
        rm -f main.cf master.cf test46.tmp
@@ -608,6 +652,7 @@ test47: $(PROG) test47.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar inet - X n - 0 other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mfc. >test47.tmp 2>&1 || true
        diff test47.ref test47.tmp
        rm -f main.cf master.cf test47.tmp
@@ -618,6 +663,7 @@ test48: $(PROG) test48.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar inet - n X - 0 other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mfc. >test48.tmp 2>&1 || true
        diff test48.ref test48.tmp
        rm -f main.cf master.cf test48.tmp
@@ -628,6 +674,7 @@ test49: $(PROG) test49.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar inet - n n X 0 other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mfc. >test49.tmp 2>&1 || true
        diff test49.ref test49.tmp
        rm -f main.cf master.cf test49.tmp
@@ -638,6 +685,7 @@ test50: $(PROG) test50.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar inet - n n - X other >> master.cf
        echo baz unix - n n - 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mfc. >test50.tmp 2>&1 || true
        diff test50.ref test50.tmp
        rm -f main.cf master.cf test50.tmp
@@ -648,6 +696,7 @@ test51: $(PROG) test51.ref
        echo foo unix - n n -? 0 other >> master.cf
        echo bar inet - n n X? 0 other >> master.cf
        echo baz unix - n n 0? 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -Mfc. >test51.tmp 2>&1 || true
        diff test51.ref test51.tmp
        rm -f main.cf master.cf test51.tmp
@@ -658,6 +707,7 @@ test52: $(PROG) test52.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar inet - n n 0 0 other >> master.cf
        echo baz unix - n n 0 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -MXc. bar/inet foo/unix xxx/yyy
        ./$(PROG) -Mfc. >test52.tmp 2>&1 || true
        diff test52.ref test52.tmp
@@ -669,6 +719,7 @@ test53: $(PROG) test53.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar inet - n n 0 0 other >> master.cf
        echo baz unix - n n 0 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -M#c. bar/inet xxx/yyy
        diff test53.ref master.cf
        rm -f main.cf master.cf test53.tmp
@@ -679,6 +730,7 @@ test54: $(PROG) test54.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar inet - n n 0 0 other >> master.cf
        echo baz unix - n n 0 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -M#c. bar/inet foo/unix
        diff test54.ref master.cf
        rm -f main.cf master.cf test54.tmp
@@ -689,6 +741,7 @@ test55: $(PROG) test55.ref
        echo foo unix - n n - 0 other >> master.cf
        echo bar inet - n n 0 0 other >> master.cf
        echo baz unix - n n 0 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -M#c. bar/inet baz/unix
        diff test55.ref master.cf
        rm -f main.cf master.cf test55.tmp
@@ -701,10 +754,41 @@ test56: $(PROG) test56.ref
        echo " -o first" >> master.cf
        echo " -o second" >> master.cf
        echo baz unix - n n 0 0 other >> master.cf
+       touch -t 197101010000 main.cf
        ./$(PROG) -M#c. bar/inet xxx/yyy
        diff test56.ref master.cf
        rm -f main.cf master.cf test56.tmp
 
+# Many more tests in util/mac_expand.in.
+
+test57: $(PROG) test57.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo 'x = $${{1} == {2}?{error}:x-value}' >> main.cf
+       echo 'y = y-value' >> main.cf
+       echo 'bar = $${x?{$$y}:$$z}' >> main.cf
+       echo 'baz = $${x?{$$z}:$$y}' >> main.cf
+       echo 'foo = $$bar$$baz' >> main.cf
+       echo 't1 = Postfix 2.11 $${{$${x?bug:x}} == {bug}?in}compatible' >> main.cf
+       echo 't2 = $$t1' >> main.cf
+       touch -t 197101010000 main.cf
+       ./$(PROG) -nxc. >test57.tmp 2>&1
+       diff test57.ref test57.tmp
+       rm -f main.cf master.cf test57.tmp
+
+test58: $(PROG) test58.ref
+       rm -f main.cf master.cf
+       touch main.cf master.cf
+       echo 'mydestination = foo bar pipemap:{ldap:xxx, memcache:yy} randmap:{xx' >> main.cf
+       echo 'xxx_domain = foo' >> main.cf
+       echo 'xxx_bogus = foo' >> main.cf
+       echo 'yy_backup = bbb' >> main.cf
+       echo 'yy_bogus = bbb' >> main.cf
+       touch -t 197101010000 main.cf
+       $(SHLIB_ENV) ./postconf -nc. >test58.tmp 2>&1 || true
+       diff test58.ref test58.tmp
+       rm -f main.cf master.cf test58.tmp
+
 printfck: $(OBJS) $(PROG)
        rm -rf printfck
        mkdir printfck
@@ -812,6 +896,8 @@ postconf_dbms.o: ../../include/htable.h
 postconf_dbms.o: ../../include/mac_expand.h
 postconf_dbms.o: ../../include/mac_parse.h
 postconf_dbms.o: ../../include/mail_conf.h
+postconf_dbms.o: ../../include/mail_params.h
+postconf_dbms.o: ../../include/msg.h
 postconf_dbms.o: ../../include/myflock.h
 postconf_dbms.o: ../../include/name_code.h
 postconf_dbms.o: ../../include/split_at.h
index c5251b8fcb3faf350670b1982e1a9c1f67e3a8fd..016304f1ad71af26c5b90c1b37e88ac82cd7b76b 100644 (file)
 /*     PostgreSQL database client. This is described in
 /*     \fBpgsql_table\fR(5).
 /* .IP "\fBpipemap\fR (read-only)"
-/*     A pipeline of lookup tables.  Example:
-/*     "\fBpipemap:\fI!type_1:name_1!  ... !type_n:name_n\fR".
+/*     A lookup table that constructs a pipeline of tables.  Example:
+/*     "\fBpipemap:{\fItype_1:name_1,  ..., type_n:name_n\fB}\fR".
 /*     Each "pipemap:" query is given to the first table.  Each
 /*     lookup result becomes the query for the next table in the
 /*     pipeline, and the last table produces the final result.
 /*     When any table lookup produces no result, the pipeline
-/*     produces no result. The first ASCII character after "pipemap:"
-/*     will be used as the separator between the lookup tables
-/*     that follow (do not use space, ",", ":" or non-ASCII).
+/*     produces no result. The first and last characters of the
+/*     "pipemap:" table name must be "\fB{\fR" and "\fB}\fR".
+/*     Within these, individual maps are separated with comma or
+/*     whitespace.
 /* .IP "\fBproxy\fR"
 /*     Postfix \fBproxymap\fR(8) client for shared access to Postfix
 /*     databases. The table name syntax is \fItype\fB:\fIname\fR.
 /* .IP "\fBrandmap\fR (read-only)"
 /*     An in-memory table that performs random selection. Example:
-/*     "\fBrandmap:\fI!result_1! ... !result_n\fR". Each table query
+/*     "\fBrandmap:{\fIresult_1, ..., result_n\fB}\fR". Each table query
 /*     returns a random choice from the specified results. The first
-/*     ASCII character after "randmap:" will be used as the separator
-/*     between the results that follow (do not use space, ",", ":"
-/*     or non-ASCII).
+/*     and last characters of the "randmap:" table name must be
+/*     "\fB{\fR" and "\fB}\fR".  Within these, individual maps are
+/*     separated with comma or whitespace.
 /* .IP "\fBregexp\fR (read-only)"
 /*     A lookup table based on regular expressions. The file format
 /*     is described in \fBregexp_table\fR(5).
index f174d164c530105e3a561fe1acbb634dff9ce387..178a237f9f19aa4c3b27caaafe4c997602aa7b11 100644 (file)
@@ -288,7 +288,7 @@ extern char *pcf_expand_parameter_value(VSTRING *, int, const char *,
  /*
   * postconf_print.c.
   */
-extern void pcf_print_line(VSTREAM *, int, const char *,...);
+extern void PRINTFLIKE(3, 4) pcf_print_line(VSTREAM *, int, const char *,...);
 
  /*
   * postconf_unused.c.
index ba53d050d2fc8b3798621ec47304c506499284ab..b07f5bb6408787c604915660a1701c37825b1a6c 100644 (file)
@@ -223,12 +223,11 @@ static const char *pcf_check_mydomainname(void)
        return (domain);
 
     /*
-     * Use the hostname when it is not a FQDN ("foo"), or when the hostname
-     * actually is a domain name ("foo.com").
+     * Use a default domain when the hostname is not a FQDN ("foo").
      */
     if (var_myhostname == 0)
        pcf_get_myhostname();
-    if ((dot = strchr(var_myhostname, '.')) == 0 || strchr(dot + 1, '.') == 0)
+    if ((dot = strchr(var_myhostname, '.')) == 0)
        return (domain = DEF_MYDOMAIN);
     return (domain = mystrdup(dot + 1));
 }
index 883d4368c060bc2227cdf36815350237d150ae7f..a618fdac755406495f3d61e34cfd86651227cf25 100644 (file)
 #include <split_at.h>
 #include <mac_expand.h>
 #include <dict.h>
+#include <msg.h>
 
 /* Global library. */
 
 #include <mail_conf.h>
+#include <mail_params.h>
 #include <dict_proxy.h>
 #include <dict_ldap.h>
 #include <dict_mysql.h>
@@ -142,35 +144,31 @@ static const PCF_DBMS_INFO pcf_dbms_info[] = {
     0,
 };
 
-/* pcf_register_dbms_parameters - look for database_type:prefix_name */
+ /*
+  * Pseudo-databases that wrap around other databases.
+  */
+static const char *pcf_multi_dbms_names[] = {
+    "pipemap", "addr_pipemap", "unionmap", 0,
+};
 
-void    pcf_register_dbms_parameters(const char *param_value,
+/* pcf_register_dbms_helper - parse one possible database type:name */
+
+static void pcf_register_dbms_helper(char *str_value,
          const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *),
                                             PCF_MASTER_ENT *local_scope)
 {
     const PCF_DBMS_INFO *dp;
-    char   *bufp;
+    size_t  len;
     char   *db_type;
     char   *prefix;
-    static VSTRING *buffer = 0;
     static VSTRING *candidate = 0;
     const char **cpp;
 
     /*
-     * XXX This does not examine both sides of conditional macro expansion,
-     * and may expand the "wrong" conditional macros. This is the best we can
-     * do for legacy database configuration support.
-     */
-    if (buffer == 0)
-       buffer = vstring_alloc(100);
-    bufp = pcf_expand_parameter_value(buffer, PCF_SHOW_EVAL, param_value,
-                                     local_scope);
-
-    /*
-     * Naive parsing. We don't really know if the parameter specifies free
-     * text or a list of databases.
+     * Naive parsing. We don't really know if this substring specifies a
+     * database or some other text.
      */
-    while ((db_type = mystrtok(&bufp, " ,\t\r\n")) != 0) {
+    while ((db_type = mystrtokq(&str_value, " ,\t\r\n", "{}")) != 0) {
 
        /*
         * Skip over "proxy:" maptypes, to emulate the proxymap(8) server's
@@ -188,21 +186,62 @@ void    pcf_register_dbms_parameters(const char *param_value,
         * local or global namespace.
         */
        if (prefix != 0 && *prefix != '/' && *prefix != '.') {
-           for (dp = pcf_dbms_info; dp->db_type != 0; dp++) {
-               if (strcmp(db_type, dp->db_type) == 0) {
-                   for (cpp = dp->db_suffixes; *cpp; cpp++) {
-                       vstring_sprintf(candidate ? candidate :
-                                       (candidate = vstring_alloc(30)),
-                                       "%s_%s", prefix, *cpp);
-                       flag_parameter(STR(candidate),
+           if (*prefix == '{') {
+               if ((len = balpar(prefix, "{}")) > 0) {
+                   prefix[len - 1] = 0;
+                   for (cpp = pcf_multi_dbms_names; *cpp; cpp++) {
+                       if (strcmp(db_type, *cpp) == 0) {
+                           pcf_register_dbms_helper(prefix + 1, flag_parameter,
+                                                    local_scope);
+                           break;
+                       }
+                   }
+               } else {
+                   if (local_scope)
+                       msg_warn("%s:%s: missing '}' in parameter value: \"%s:%s\"",
+                                MASTER_CONF_FILE, local_scope->name_space);
+                   else
+                       msg_warn("%s: missing '}' in parameter value: \"%s:%s\"",
+                                MAIN_CONF_FILE, db_type, prefix);
+               }
+           } else {
+               for (dp = pcf_dbms_info; dp->db_type != 0; dp++) {
+                   if (strcmp(db_type, dp->db_type) == 0) {
+                       for (cpp = dp->db_suffixes; *cpp; cpp++) {
+                           vstring_sprintf(candidate ? candidate :
+                                           (candidate = vstring_alloc(30)),
+                                           "%s_%s", prefix, *cpp);
+                           flag_parameter(STR(candidate),
                                  PCF_PARAM_FLAG_DBMS | PCF_PARAM_FLAG_USER,
-                                      local_scope);
+                                          local_scope);
+                       }
+                       break;
                    }
-                   break;
                }
            }
        }
     }
 }
 
+/* pcf_register_dbms_parameters - look for database_type:prefix_name */
+
+void    pcf_register_dbms_parameters(const char *param_value,
+         const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *),
+                                            PCF_MASTER_ENT *local_scope)
+{
+    char   *bufp;
+    static VSTRING *buffer = 0;
+
+    /*
+     * XXX This does not examine both sides of conditional macro expansion,
+     * and may expand the "wrong" conditional macros. This is the best we can
+     * do for legacy database configuration support.
+     */
+    if (buffer == 0)
+       buffer = vstring_alloc(100);
+    bufp = pcf_expand_parameter_value(buffer, PCF_SHOW_EVAL, param_value,
+                                     local_scope);
+    pcf_register_dbms_helper(bufp, flag_parameter, local_scope);
+}
+
 #endif
diff --git a/postfix/src/postconf/test57.ref b/postfix/src/postconf/test57.ref
new file mode 100644 (file)
index 0000000..362fd16
--- /dev/null
@@ -0,0 +1,10 @@
+./postconf: warning: ./main.cf: undefined parameter: z
+./postconf: warning: ./main.cf: undefined parameter: z
+bar = y-value
+baz =
+config_directory = .
+t1 = Postfix 2.11 compatible
+x = x-value
+y = y-value
+./postconf: warning: ./main.cf: unused parameter: t2=$t1
+./postconf: warning: ./main.cf: unused parameter: foo=$bar$baz
diff --git a/postfix/src/postconf/test58.ref b/postfix/src/postconf/test58.ref
new file mode 100644 (file)
index 0000000..8de9edc
--- /dev/null
@@ -0,0 +1,7 @@
+./postconf: warning: main.cf: missing '}' in parameter value: "randmap:{xx"
+config_directory = .
+mydestination = foo bar pipemap:{ldap:xxx, memcache:yy} randmap:{xx
+xxx_domain = foo
+yy_backup = bbb
+./postconf: warning: ./main.cf: unused parameter: yy_bogus=bbb
+./postconf: warning: ./main.cf: unused parameter: xxx_bogus=foo
index 036a494ab0c8a283cfadc743a0e00e8ba0c65d85..ec745e0942110c1c80f0f319fe1e02f3d9b93504 100644 (file)
@@ -651,6 +651,7 @@ DICT   *dict_proxy_open(const char *map, int open_flags, int dict_flags)
 static void post_jail_init(char *service_name, char **unused_argv)
 {
     const char *sep = ", \t\r\n";
+    const char *parens = "{}";
     char   *saved_filter;
     char   *bp;
     char   *type_name;
@@ -679,7 +680,7 @@ static void post_jail_init(char *service_name, char **unused_argv)
     saved_filter = bp = mystrdup(proxy_writer ? var_proxy_write_maps :
                                 var_proxy_read_maps);
     proxy_auth_maps = htable_create(13);
-    while ((type_name = mystrtok(&bp, sep)) != 0) {
+    while ((type_name = mystrtokq(&bp, sep, parens)) != 0) {
        if (strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN))
            continue;
        do {
index 302015e706f5d694c492dc7a72720819302f99b7..42db60571cd5fc9fa39ca6da2573a56117ed4da0 100644 (file)
@@ -497,7 +497,7 @@ static ARGV *smtpd_check_parse(int flags, const char *checks)
 #define SMTPD_CHECK_PARSE_MAPS         (1<<1)
 #define SMTPD_CHECK_PARSE_ALL          (~0)
 
-    while ((name = mystrtok(&bp, RESTRICTION_SEPARATORS)) != 0) {
+    while ((name = mystrtokq(&bp, RESTRICTION_SEPARATORS, "{}")) != 0) {
        argv_add(argv, name, (char *) 0);
        if ((flags & SMTPD_CHECK_PARSE_POLICY)
            && last && strcasecmp(last, CHECK_POLICY_SERVICE) == 0)
@@ -1044,10 +1044,11 @@ static int reject_unknown_client(SMTPD_STATE *state)
     if (msg_verbose)
        msg_info("%s: %s %s", myname, state->name, state->addr);
 
+    /* RFC 7372: Email Authentication Status Codes. */
     if (state->name_status != SMTPD_PEER_CODE_OK)
        return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
                                state->name_status >= SMTPD_PEER_CODE_PERM ?
-                                  var_unk_client_code : 450, "4.7.1",
+                                  var_unk_client_code : 450, "4.7.25",
                    "Client host rejected: cannot find your hostname, [%s]",
                                   state->addr));
     return (SMTPD_CHECK_DUNNO);
@@ -2437,7 +2438,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
      */
 #define ADDROF(x) ((char *) &(x))
 
-    restrictions = argv_split(value, RESTRICTION_SEPARATORS);
+    restrictions = argv_splitq(value, RESTRICTION_SEPARATORS, "{}");
     memcpy(ADDROF(savebuf), ADDROF(smtpd_check_buf), sizeof(savebuf));
     status = setjmp(smtpd_check_buf);
     if (status != 0) {
@@ -3940,7 +3941,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
                                         SMTPD_NAME_CLIENT, def_acl);
        } else if (is_map_command(state, name, CHECK_REVERSE_CLIENT_ACL, &cpp)) {
            status = check_namadr_access(state, *cpp, state->reverse_name, state->addr,
-                                        FULL, &found, state->namaddr,
+                                        FULL, &found, state->reverse_name,
                                         SMTPD_NAME_REV_CLIENT, def_acl);
            forbid_whitelist(state, name, status, state->reverse_name);
        } else if (strcasecmp(name, REJECT_MAPS_RBL) == 0) {
@@ -4030,21 +4031,21 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
        } else if (is_map_command(state, name, CHECK_REVERSE_CLIENT_NS_ACL, &cpp)) {
            if (strcasecmp(state->reverse_name, "unknown") != 0) {
                status = check_server_access(state, *cpp, state->reverse_name,
-                                            T_NS, state->namaddr,
+                                            T_NS, state->reverse_name,
                                             SMTPD_NAME_REV_CLIENT, def_acl);
                forbid_whitelist(state, name, status, state->reverse_name);
            }
        } else if (is_map_command(state, name, CHECK_REVERSE_CLIENT_MX_ACL, &cpp)) {
            if (strcasecmp(state->reverse_name, "unknown") != 0) {
                status = check_server_access(state, *cpp, state->reverse_name,
-                                            T_MX, state->namaddr,
+                                            T_MX, state->reverse_name,
                                             SMTPD_NAME_REV_CLIENT, def_acl);
                forbid_whitelist(state, name, status, state->reverse_name);
            }
        } else if (is_map_command(state, name, CHECK_REVERSE_CLIENT_A_ACL, &cpp)) {
            if (strcasecmp(state->reverse_name, "unknown") != 0) {
                status = check_server_access(state, *cpp, state->reverse_name,
-                                            T_A, state->namaddr,
+                                            T_A, state->reverse_name,
                                             SMTPD_NAME_REV_CLIENT, def_acl);
                forbid_whitelist(state, name, status, state->reverse_name);
            }
index 5dac7bca38ae7b4a53c6d8d80c3947aba201761d..e65490e45aa45ea19983bbd4d979bf6917c2ea80 100644 (file)
@@ -200,22 +200,23 @@ static void disconnect(SINK_STATE *state)
 static void connect_event(int unused_event, char *context)
 {
     int     sock = CAST_CHAR_PTR_TO_INT(context);
-    struct sockaddr sa;
-    SOCKADDR_SIZE len = sizeof(sa);
+    struct sockaddr_storage ss;
+    SOCKADDR_SIZE len = sizeof(ss);
+    struct sockaddr *sa = (struct sockaddr *) &ss;
     SINK_STATE *state;
     int     fd;
 
-    if ((fd = accept(sock, &sa, &len)) >= 0) {
+    if ((fd = accept(sock, sa, &len)) >= 0) {
        if (msg_verbose)
            msg_info("connect (%s)",
 #ifdef AF_LOCAL
-                    sa.sa_family == AF_LOCAL ? "AF_LOCAL" :
+                    sa->sa_family == AF_LOCAL ? "AF_LOCAL" :
 #else
-                    sa.sa_family == AF_UNIX ? "AF_UNIX" :
+                    sa->sa_family == AF_UNIX ? "AF_UNIX" :
 #endif
-                    sa.sa_family == AF_INET ? "AF_INET" :
+                    sa->sa_family == AF_INET ? "AF_INET" :
 #ifdef AF_INET6
-                    sa.sa_family == AF_INET6 ? "AF_INET6" :
+                    sa->sa_family == AF_INET6 ? "AF_INET6" :
 #endif
                     "unknown protocol family");
        non_blocking(fd, NON_BLOCKING);
index 617fbf915b3c6561b990be39a65d19c64b36b7f3..120cef10a809e305994fbdf45feb51dbd94da0fb 100644 (file)
@@ -1298,31 +1298,32 @@ static void disconnect(SINK_STATE *state)
 
 static void connect_event(int unused_event, char *unused_context)
 {
-    struct sockaddr sa;
-    SOCKADDR_SIZE len = sizeof(sa);
+    struct sockaddr_storage ss;
+    SOCKADDR_SIZE len = sizeof(ss);
+    struct sockaddr *sa = (struct sockaddr *) &ss;
     SINK_STATE *state;
     int     fd;
 
-    if ((fd = sane_accept(sock, &sa, &len)) >= 0) {
+    if ((fd = sane_accept(sock, sa, &len)) >= 0) {
        /* Safety: limit the number of open sockets and capture files. */
        if (++client_count == max_client_count)
            event_disable_readwrite(sock);
        state = (SINK_STATE *) mymalloc(sizeof(*state));
-       if (strchr((char *) proto_info->sa_family_list, sa.sa_family))
-           SOCKADDR_TO_HOSTADDR(&sa, len, &state->client_addr,
-                                (MAI_SERVPORT_STR *) 0, sa.sa_family);
+       if (strchr((char *) proto_info->sa_family_list, sa->sa_family))
+           SOCKADDR_TO_HOSTADDR(sa, len, &state->client_addr,
+                                (MAI_SERVPORT_STR *) 0, sa->sa_family);
        else
            strncpy(state->client_addr.buf, "local", sizeof("local"));
        if (msg_verbose)
            msg_info("connect (%s %s)",
 #ifdef AF_LOCAL
-                    sa.sa_family == AF_LOCAL ? "AF_LOCAL" :
+                    sa->sa_family == AF_LOCAL ? "AF_LOCAL" :
 #else
-                    sa.sa_family == AF_UNIX ? "AF_UNIX" :
+                    sa->sa_family == AF_UNIX ? "AF_UNIX" :
 #endif
-                    sa.sa_family == AF_INET ? "AF_INET" :
+                    sa->sa_family == AF_INET ? "AF_INET" :
 #ifdef AF_INET6
-                    sa.sa_family == AF_INET6 ? "AF_INET6" :
+                    sa->sa_family == AF_INET6 ? "AF_INET6" :
 #endif
                     "unknown protocol family",
                     state->client_addr.buf);
@@ -1340,7 +1341,7 @@ static void connect_event(int unused_event, char *unused_context)
        state->delayed_args = 0;
        /* Initialize file capture attributes. */
 #ifdef AF_INET6
-       if (sa.sa_family == AF_INET6)
+       if (sa->sa_family == AF_INET6)
            state->addr_prefix = "ipv6:";
        else
 #endif
index 4dbe47db484461c52ffe1da829d5ec9f554ab5ae..12e829b7c0620c559cb08ffdeef1c4840456e5c4 100644 (file)
@@ -38,7 +38,7 @@ SRCS  = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
        dict_fail.c msg_rate_delay.c dict_surrogate.c warn_stat.c \
        dict_sockmap.c line_number.c recv_pass_attr.c pass_accept.c \
        poll_fd.c timecmp.c slmdb.c dict_pipe.c dict_random.c \
-       valid_utf8_hostname.c midna.c
+       valid_utf8_hostname.c midna.c argv_splitq.c balpar.c
 OBJS   = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
        attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@@ -78,7 +78,7 @@ OBJS  = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
        dict_fail.o msg_rate_delay.o dict_surrogate.o warn_stat.o \
        dict_sockmap.o line_number.o recv_pass_attr.o pass_accept.o \
        poll_fd.o timecmp.o $(NON_PLUGIN_MAP_OBJ) dict_pipe.o dict_random.o \
-       valid_utf8_hostname.o midna.o
+       valid_utf8_hostname.o midna.o argv_splitq.o balpar.o
 # MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
 # When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
 # otherwise it sets the PLUGIN_* macros.
@@ -763,6 +763,14 @@ argv_split.o: stringops.h
 argv_split.o: sys_defs.h
 argv_split.o: vbuf.h
 argv_split.o: vstring.h
+argv_splitq.o: argv.h
+argv_splitq.o: argv_splitq.c
+argv_splitq.o: msg.h
+argv_splitq.o: mymalloc.h
+argv_splitq.o: stringops.h
+argv_splitq.o: sys_defs.h
+argv_splitq.o: vbuf.h
+argv_splitq.o: vstring.h
 attr_clnt.o: attr.h
 attr_clnt.o: attr_clnt.c
 attr_clnt.o: attr_clnt.h
@@ -847,6 +855,11 @@ auto_clnt.o: split_at.h
 auto_clnt.o: sys_defs.h
 auto_clnt.o: vbuf.h
 auto_clnt.o: vstream.h
+balpar.o: balpar.c
+balpar.o: stringops.h
+balpar.o: sys_defs.h
+balpar.o: vbuf.h
+balpar.o: vstring.h
 base32_code.o: base32_code.c
 base32_code.o: base32_code.h
 base32_code.o: msg.h
@@ -1156,6 +1169,7 @@ dict_random.o: msg.h
 dict_random.o: myflock.h
 dict_random.o: mymalloc.h
 dict_random.o: myrand.h
+dict_random.o: stringops.h
 dict_random.o: sys_defs.h
 dict_random.o: vbuf.h
 dict_random.o: vstream.h
index fda35c3119e6841309c6a75102e35056d1ab52ee..f7ce699569e79dbccea6f9da779bb7ab3a802dc0 100644 (file)
@@ -35,6 +35,10 @@ extern ARGV *argv_split(const char *, const char *);
 extern ARGV *argv_split_count(const char *, const char *, ssize_t);
 extern ARGV *argv_split_append(ARGV *, const char *, const char *);
 
+extern ARGV *argv_splitq(const char *, const char *, const char *);
+extern ARGV *argv_splitq_count(const char *, const char *, const char *, ssize_t);
+extern ARGV *argv_splitq_append(ARGV *, const char *, const char *, const char *);
+
 #define ARGV_FAKE_BEGIN(fake_argv, arg) { \
        ARGV fake_argv; \
        char *__fake_argv_args__[2]; \
diff --git a/postfix/src/util/argv_splitq.c b/postfix/src/util/argv_splitq.c
new file mode 100644 (file)
index 0000000..3900ee1
--- /dev/null
@@ -0,0 +1,118 @@
+/*++
+/* NAME
+/*     argv_splitq 3
+/* SUMMARY
+/*     string array utilities
+/* SYNOPSIS
+/*     #include <argv.h>
+/*
+/*     ARGV    *argv_splitq(string, delim, parens)
+/*     const char *string;
+/*     const char *delim;
+/*     const char *parens;
+/*
+/*     ARGV    *argv_splitq_count(string, delim, parens, count)
+/*     const char *string;
+/*     const char *delim;
+/*     const char *parens;
+/*     ssize_t count;
+/*
+/*     ARGV    *argv_splitq_append(argv, string, delim, parens)
+/*     ARGV    *argv;
+/*     const char *string;
+/*     const char *delim;
+/*     const char *parens;
+/* DESCRIPTION
+/*     argv_splitq() breaks up \fIstring\fR into tokens according
+/*     to the delimiters specified in \fIdelim\fR, while avoiding
+/*     splitting text between matching parentheses. The result is
+/*     a null-terminated string array.
+/*
+/*     argv_splitq_count() is like argv_splitq() but stops splitting
+/*     input after at most \fIcount\fR -1 times and leaves the
+/*     remainder, if any, in the last array element. It is an error
+/*     to specify a count < 1.
+/*
+/*     argv_splitq_append() performs the same operation as argv_splitq(),
+/*     but appends the result to an existing string array.
+/* SEE ALSO
+/*     mystrtokq(), safe string splitter.
+/* DIAGNOSTICS
+/*     Fatal errors: memory allocation problem.
+/* 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
+/*--*/
+
+/* System libraries. */
+
+#include <sys_defs.h>
+#include <string.h>
+
+/* Application-specific. */
+
+#include "mymalloc.h"
+#include "stringops.h"
+#include "argv.h"
+#include "msg.h"
+
+/* argv_splitq - split string into token array */
+
+ARGV   *argv_splitq(const char *string, const char *delim, const char *parens)
+{
+    ARGV   *argvp = argv_alloc(1);
+    char   *saved_string = mystrdup(string);
+    char   *bp = saved_string;
+    char   *arg;
+
+    while ((arg = mystrtokq(&bp, delim, parens)) != 0)
+       argv_add(argvp, arg, (char *) 0);
+    argv_terminate(argvp);
+    myfree(saved_string);
+    return (argvp);
+}
+
+/* argv_splitq_count - split string into token array */
+
+ARGV   *argv_splitq_count(const char *string, const char *delim,
+                                 const char *parens, ssize_t count)
+{
+    ARGV   *argvp = argv_alloc(1);
+    char   *saved_string = mystrdup(string);
+    char   *bp = saved_string;
+    char   *arg;
+
+    if (count < 1)
+       msg_panic("argv_splitq_count: bad count: %ld", (long) count);
+    while (count-- > 1 && (arg = mystrtokq(&bp, delim, parens)) != 0)
+       argv_add(argvp, arg, (char *) 0);
+    if (*bp)
+       bp += strspn(bp, delim);
+    if (*bp)
+       argv_add(argvp, bp, (char *) 0);
+    argv_terminate(argvp);
+    myfree(saved_string);
+    return (argvp);
+}
+
+/* argv_splitq_append - split string into token array, append to array */
+
+ARGV   *argv_splitq_append(ARGV *argvp, const char *string, const char *delim,
+                                  const char *parens)
+{
+    char   *saved_string = mystrdup(string);
+    char   *bp = saved_string;
+    char   *arg;
+
+    while ((arg = mystrtokq(&bp, delim, parens)) != 0)
+       argv_add(argvp, arg, (char *) 0);
+    argv_terminate(argvp);
+    myfree(saved_string);
+    return (argvp);
+}
diff --git a/postfix/src/util/balpar.c b/postfix/src/util/balpar.c
new file mode 100644 (file)
index 0000000..6ff97eb
--- /dev/null
@@ -0,0 +1,56 @@
+/*++
+/* NAME
+/*     balpar 3
+/* SUMMARY
+/*     determine length of string in parentheses
+/* SYNOPSIS
+/*     #include <stringops.h>
+/*
+/*     size_t  balpar(string, parens)
+/*     const char *string;
+/*     const char *parens;
+/* DESCRIPTION
+/*     balpar() determines the length of a string enclosed in 
+/*     the specified parentheses, zero in case of error.
+/* SEE ALSO
+/*     A balpar() routine appears in Brian W. Kernighan, P.J. Plauger:
+/*     "Software Tools", Addison-Wesley 1976. This function is different.
+/* 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
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+
+/* Utility library. */
+
+#include <stringops.h>
+
+/* balpar - return length of {text} */
+
+size_t  balpar(const char *string, const char *parens)
+{
+    const char *cp;
+    int     level;
+    int     ch;
+
+    if (*string != parens[0])
+       return (0);
+    for (level = 1, cp = string + 1; (ch = *cp) != 0; cp++) {
+       if (ch == parens[1]) {
+           if (--level == 0)
+               return (cp - string + 1);
+       } else if (ch == parens[0]) {
+           level++;
+       }
+    }
+    return (0);
+}
index 70b7be5ff727d657bde63c6adf6aa4c2794eccd7..353df5baf5bb39029cb5ff8c4d3328056eab21a8 100644 (file)
@@ -244,7 +244,7 @@ void    dict_test(int, char **);
   * functionality.
   */
 extern int dict_allow_surrogate;
-extern DICT *dict_surrogate(const char *, const char *, int, int, const char *,...);
+extern DICT *PRINTFLIKE(5, 6) dict_surrogate(const char *, const char *, int, int, const char *,...);
 
  /*
   * This name is reserved for matchlist error handling.
index 48ae556d82ded9be8fdea1601b5a6740edbeb7b6..b50d49dd561e20956245be20d7ab8a62c995cfe6 100644 (file)
@@ -343,13 +343,7 @@ static const DICT_OPEN_INFO dict_open_info[] = {
     DICT_TYPE_SOCKMAP, dict_sockmap_open,
     DICT_TYPE_FAIL, dict_fail_open,
     DICT_TYPE_PIPE, dict_pipe_open,
-#ifdef DICT_TYPE_PIPE_LEGACY
-    DICT_TYPE_PIPE_LEGACY, dict_pipe_open,
-#endif
     DICT_TYPE_RANDOM, dict_random_open,
-#ifdef DICT_TYPE_RANDOM_LEGACY
-    DICT_TYPE_RANDOM_LEGACY, dict_random_open,
-#endif
 #ifndef USE_DYNAMIC_MAPS
 #ifdef HAS_PCRE
     DICT_TYPE_PCRE, dict_pcre_open,
@@ -530,6 +524,9 @@ DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(DICT_MAPNAMES_EXTEND_FN new_cb)
 
 #ifdef TEST
 
+DEFINE_DICT_LMDB_MAP_SIZE;
+DEFINE_DICT_DB_CACHE_SIZE;
+
  /*
   * Proof-of-concept test program.
   */
index 8102cc93611d2337584a92c5a81664ad467c6577..e7e56787e02da26c90c73f30aab479799e752ea1 100644 (file)
@@ -12,7 +12,7 @@
 /*     int     dict_flags;
 /* DESCRIPTION
 /*     dict_pipe_open() opens a pipeline of one or more tables.
-/*     Example: "\fBpipemap:\fI!type_1:name_1! ... !type_n:name_n\fR".
+/*     Example: "\fBpipemap:{\fItype_1:name_1, ... ,type_n:name_n\fR}".
 /*
 /*     Each "pipemap:" query is given to the first table.  Each
 /*     lookup result becomes the query for the next table in the
@@ -20,9 +20,9 @@
 /*     When any table lookup produces no result, the pipeline
 /*     produces no result.
 /*
-/*     The first ASCII character after "pipemap:" will be used as
-/*     the separator between the lookup tables that follow (do not
-/*     use space, ",", ":" or non-ASCII).
+/*     The first and last characters of the "pipemap:" table name
+/*     must be '{' and '}'. Within these, individual maps are
+/*     separated with comma or whitespace.
 /*
 /*     The open_flags and dict_flags arguments are passed on to
 /*     the underlying dictionaries.
@@ -114,7 +114,7 @@ DICT   *dict_pipe_open(const char *name, int open_flags, int dict_flags)
     DICT   *dict;
     int     match_flags = 0;
     struct DICT_OWNER aggr_owner;
-    char    delim[2];
+    size_t  len;
 
     /*
      * Clarity first. Let the optimizer worry about redundant code.
@@ -135,32 +135,24 @@ DICT   *dict_pipe_open(const char *name, int open_flags, int dict_flags)
                                        open_flags, dict_flags,
                                  "%s:%s map requires O_RDONLY access mode",
                                        DICT_TYPE_PIPE, name));
-    if (name[0] == ':')
-       DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name,
-                                       open_flags, dict_flags,
-                              "invalid list delimiter \"%c\" in \"%s:%s\"",
-                                       name[0], DICT_TYPE_PIPE, name));
-
     /*
-     * Split the table name on the user-specified delimiter.
+     * Split the table name into its constituent parts.
      */
-    delim[0] = name[0];                                /* XXX ASCII delimiter */
-    delim[1] = 0;
-    saved_name = mystrdup(name + 1);           /* XXX ASCII delimiter */
-    if (*saved_name == 0)
+    if ((len = balpar(name, "{}")) == 0 || name[len] != 0
+       || *(saved_name = mystrndup(name + 1, len - 2)) == 0)
        DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name,
                                        open_flags, dict_flags,
                                        "bad syntax: \"%s:%s\"; "
-                                       "need \"%s:%stype:name%s...\"",
+                                       "need \"%s:{type:name...}\"",
                                        DICT_TYPE_PIPE, name,
-                                       DICT_TYPE_PIPE, delim, delim));
+                                       DICT_TYPE_PIPE));
 
     /*
      * The least-trusted table in the pipeline determines the over-all trust
      * level. The first table determines the pattern-matching flags.
      */
     DICT_OWNER_AGGREGATE_INIT(aggr_owner);
-    argv = argv_split(saved_name, delim);
+    argv = argv_splitq(saved_name, ", \t\r\n", "{}");
     for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
        if (msg_verbose)
            msg_info("%s: %s", myname, dict_type_name);
@@ -168,9 +160,9 @@ DICT   *dict_pipe_open(const char *name, int open_flags, int dict_flags)
            DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name,
                                            open_flags, dict_flags,
                                            "bad syntax: \"%s:%s\"; "
-                                           "need \"%s:%stype:name%s...\"",
+                                           "need \"%s:{type:name...}\"",
                                            DICT_TYPE_PIPE, name,
-                                           DICT_TYPE_PIPE, delim, delim));
+                                           DICT_TYPE_PIPE));
        if ((dict = dict_handle(dict_type_name)) == 0)
            dict = dict_open(dict_type_name, open_flags, dict_flags);
        dict_register(dict_type_name, dict);
index fb0578e1fec538978800b5407197a1395a216ae8..8d03009d541735dadd2fe46bea505a0397db6dca 100644 (file)
   */
 #define DICT_TYPE_PIPE "pipemap"
 
-#ifdef SNAPSHOT
-#define DICT_TYPE_PIPE_LEGACY  "pipeline"
-#endif
-
 extern DICT *dict_pipe_open(const char *, int, int);
 
 /* LICENSE
index af975bd34aaa2c7aca9f3ebb4cf56a6c7b96bcb9..0ee4a83653306150125ee9bdcef35c6a7c246806 100644 (file)
 /*     int     dict_flags;
 /* DESCRIPTION
 /*     dict_random_open() opens an in-memory, read-only, table.
-/*     Example: "\fBrandmap:\fI!result_1! ... !result_n\fR".
+/*     Example: "\fBrandmap:{\fIresult_1, ... ,result_n}\fR".
 /*
 /*     Each table query returns a random choice from the specified
 /*     results. Other table access methods are not supported.
 /*
-/*     The ASCII character after "randmap:" will be used as the
-/*     separator between the results that follow (do not use space,
-/*     ",", ":" or non-ASCII).
+/*     The first and last characters of the "randmap:" table name
+/*     must be '{' and '}'. Within these, individual maps are
+/*     separated with comma or whitespace.
 /* SEE ALSO
 /*     dict(3) generic dictionary manager
 /* LICENSE
@@ -43,6 +43,7 @@
 #include <msg.h>
 #include <mymalloc.h>
 #include <myrand.h>
+#include <stringops.h>
 #include <dict_random.h>
 
 /* Application-specific. */
@@ -80,7 +81,7 @@ DICT   *dict_random_open(const char *name, int open_flags, int dict_flags)
 {
     DICT_RANDOM *dict_random;
     char   *saved_name = 0;
-    char    delim[2];
+    size_t  len;
 
     /*
      * Clarity first. Let the optimizer worry about redundant code.
@@ -101,17 +102,16 @@ DICT   *dict_random_open(const char *name, int open_flags, int dict_flags)
                                          DICT_TYPE_RANDOM, name));
 
     /*
-     * Split the name on the user-specified delimiter.
+     * Split the name name into its constituent parts.
      */
-    delim[0] = name[0];                                /* XXX ASCII delimiter */
-    delim[1] = 0;
-    saved_name = mystrdup(name + 1);           /* XXX ASCII delimiter */
-    if (*saved_name == 0)
+    if ((len = balpar(name, "{}")) == 0 || name[len] != 0
+       || *(saved_name = mystrndup(name + 1, len - 2)) == 0)
        DICT_RANDOM_RETURN(dict_surrogate(DICT_TYPE_RANDOM, name,
                                          open_flags, dict_flags,
-                         "bad syntax: \"%s:%s\"; need \"%s:%svalue%s...\"",
+                                         "bad syntax: \"%s:%s\"; "
+                                         "need \"%s:{type:name...}\"",
                                          DICT_TYPE_RANDOM, name,
-                                         DICT_TYPE_RANDOM, delim, delim));
+                                         DICT_TYPE_RANDOM));
 
     /*
      * Bundle up the result.
@@ -121,9 +121,9 @@ DICT   *dict_random_open(const char *name, int open_flags, int dict_flags)
     dict_random->dict.lookup = dict_random_lookup;
     dict_random->dict.close = dict_random_close;
     dict_random->dict.flags = dict_flags | DICT_FLAG_PATTERN;
-    dict_random->replies = argv_split(saved_name, delim);
+    dict_random->replies = argv_splitq(saved_name, ", \t\r\n", "{}");
     dict_random->dict.owner.status = DICT_OWNER_TRUSTED;
     dict_random->dict.owner.uid = 0;
 
-    DICT_RANDOM_RETURN(DICT_DEBUG(&dict_random->dict));
+    DICT_RANDOM_RETURN(DICT_DEBUG (&dict_random->dict));
 }
index fd16e9570a7e7fc6a3722c56b696868485d4a472..4aa08b1404f666629489b72ccb2927c29df0e4fd 100644 (file)
   */
 #define DICT_TYPE_RANDOM       "randmap"
 
-#ifdef SNAPSHOT
-#define DICT_TYPE_RANDOM_LEGACY        "random"
-#endif
-
 extern DICT *dict_random_open(const char *, int, int);
 
 /* LICENSE
index f988f32760a5a1de76f8cdb0a081dfb8ae7226a5..2fedcb60fe3902264e64aa7c018d975daf80b921 100644 (file)
@@ -24,9 +24,6 @@
 #include <dict_lmdb.h>
 #include <dict_db.h>
 
-DEFINE_DICT_LMDB_MAP_SIZE;
-DEFINE_DICT_DB_CACHE_SIZE;
-
 static NORETURN usage(char *myname)
 {
     msg_fatal("usage: %s type:file read|write|create [flags...]", myname);
index f1142d889cf58564be3f94551bd7f3ab584504d7..f0934f94d988df2ea356c19bcb4aace1236c578b 100644 (file)
 /*     const char *lookup(const char *key, int mode, char *context)
 /*     char    *context;
 /* DESCRIPTION
-/*     This module implements parameter-less macro expansions, both
-/*     conditional and unconditional, and both recursive and non-recursive.
+/*     This module implements parameter-less named attribute
+/*     expansions, both conditional and unconditional. As of Postfix
+/*     2.12 this code supports logical expression evaluation.
 /*
 /*     In this text, an attribute is considered "undefined" when its value
 /*     is a null pointer.  Otherwise, the attribute is considered "defined"
 /*     and is expected to have as value a null-terminated string.
 /*
-/*     The following expansions are implemented:
-/* .IP "$name, ${name}, $(name)"
-/*     Unconditional expansion. If the named attribute value is non-empty, the
-/*     expansion is the value of the named attribute,  optionally subjected
-/*     to further $name expansions.  Otherwise, the expansion is empty.
-/* .IP "${name?text}, $(name?text)"
-/*     Conditional expansion. If the named attribute value is non-empty, the
-/*     expansion is the given text, subjected to another iteration of
-/*     $name expansion.  Otherwise, the expansion is empty.
-/* .IP "${name:text}, $(name:text)"
-/*     Conditional expansion. If the attribute value is empty or undefined,
-/*     the expansion is the given text, subjected to another iteration
-/*     of $name expansion.  Otherwise, the expansion is empty.
-/* .PP
+/*     In the text below, the legacy form $(...) is equivalent to
+/*     ${...}. The legacy form $(...) may eventually disappear
+/*     from documentation.
+/*
+/*     The following substitutions are supported:
+/* .IP "$name, ${name}"
+/*     Unconditional attribute-based substition. The result is the
+/*     named attribute value (empty if the attribute is not defined)
+/*     after optional further named attribute substitution.
+/* .IP "${name?text}, ${name?{text}}"
+/*     Conditional attribute-based substition. If the named attribute
+/*     value is non-empty, the result is the given text, after
+/*     named attribute expansion and logical expression evaluation.
+/*     Otherwise, the result is empty.  Whitespace before or after
+/*     {text} is ignored.
+/* .IP "${name:text}, ${name:{text}}"
+/*     Conditional attribute-based substition. If the attribute
+/*     value is empty or undefined, the expansion is the given
+/*     text, after named attribute expansion and logical expression
+/*     evaluation.  Otherwise, the result is empty.  Whitespace
+/*     before or after {text} is ignored.
+/* .IP "${name?{text1}:{text2}}, ${name?{text1}:text2}"
+/*     Conditional attribute-based substition. If the named attribute
+/*     value is non-empty, the result is text1.  Otherwise, the
+/*     result is text2. In both cases the result is subject to
+/*     named attribute expansion and logical expression evaluation.
+/*     Whitespace before or after {text1} or {text2} is ignored.
+/* .IP "${{text1} == ${text2} ? {text3} : {text4}}"
+/* .IP "${{text1} != ${text2} ? {text3} : {text4}}"
+/*     Logical expression-based substition.  First, the content
+/*     of {text1} and ${text2} is subjected to named attribute and
+/*     logical expression-based substitution.  Next, the logical
+/*     expression is evaluated. If it evaluates to "true", the
+/*     result is the content of {text3}, otherwise it is the content
+/*     of {text4}, after named attribute and logical expression-based
+/*     substitution.
+/*
 /*     Arguments:
 /* .IP result
-/*     Storage for the result of expansion. The result is truncated
-/*     upon entry.
+/*     Storage for the result of expansion. By default, the result
+/*     is truncated upon entry.
 /* .IP pattern
 /*     The string to be expanded.
 /* .IP flags
 /*     Bit-wise OR of zero or more of the following:
 /* .RS
 /* .IP MAC_EXP_FLAG_RECURSE
-/*     Expand macros in lookup results. This should never be done with
-/*     data whose origin is untrusted.
+/*     Expand attributes in lookup results. This should never be
+/*     done with data whose origin is untrusted.
 /* .IP MAC_EXP_FLAG_APPEND
 /*     Append text to the result buffer without truncating it.
 /* .IP MAC_EXP_FLAG_SCAN
-/*     Invoke the call-back function for each macro name in the input
-/*     string, including macro names in the values of conditional
-/*     expressions.  Do not expand macros, and do not write to the
-/*     result argument.
+/*     Scan the input for named attributes, including named
+/*     attributes in all conditional result values.  Do not expand
+/*     named attributes, and do not truncate or write to the result
+/*     argument.
 /* .IP MAC_EXP_FLAG_PRINTABLE
 /*     Use the printable() function instead of \fIfilter\fR.
 /* .PP
 /*     Caller context that is passed on to the attribute lookup routine.
 /* DIAGNOSTICS
 /*     Fatal errors: out of memory.  Warnings: syntax errors, unreasonable
-/*     macro nesting.
+/*     recursion depth.
 /*
 /*     The result value is the binary OR of zero or more of the following:
 /* .IP MAC_PARSE_ERROR
-/*     A syntax error was found in \fBpattern\fR, or some macro had
+/*     A syntax error was found in \fBpattern\fR, or some attribute had
 /*     an unreasonable nesting depth.
 /* .IP MAC_PARSE_UNDEF
-/*     A macro was expanded but its value not defined.
+/*     An attribute was expanded but its value was not defined.
 /* SEE ALSO
 /*     mac_parse(3) locate macro references in string.
 /* LICENSE
 #include <vstring.h>
 #include <mymalloc.h>
 #include <stringops.h>
+#include <name_code.h>
 #include <mac_parse.h>
 #include <mac_expand.h>
 
@@ -120,95 +145,366 @@ typedef struct {
     char   *context;                   /* caller context */
     int     status;                    /* findings */
     int     level;                     /* nesting level */
-} MAC_EXP;
+} MAC_EXP_CONTEXT;
+
+ /*
+  * Support for logical expressions.
+  * 
+  * As of Postfix 2.2, ${attr-name?result} or ${attr-name:result} return the
+  * result respectively when the parameter value is non-empty, or when the
+  * parameter value is undefined or empty; support for the ternary ?:
+  * operator was anticipated, but not implemented for 10 years.
+  * 
+  * To make ${logical-expr?result} and ${logical-expr:result} work as expected
+  * without breaking the way that ? and : work, logical expressions evaluate
+  * to a non-empty or empty value. It does not matter what non-empty value we
+  * use for TRUE. However we must not use the undefined (null pointer) value
+  * for FALSE - that would raise the MAC_PARSE_UNDEF flag.
+  * 
+  * The value of a logical expression can be exposed with ${logical-expr}, i.e.
+  * a logical expression that is not followed by ? or : conditional
+  * expansion.
+  */
+#define MAC_EXP_BVAL_TRUE      "true"
+#define MAC_EXP_BVAL_FALSE     ""
+
+ /*
+  * Relational operator. For now, we test only for (in)equality.
+  */
+#define MAC_EXP_OP_STR_EQ      "=="
+#define MAC_EXP_OP_STR_NE      "!="
+#define MAC_EXP_OP_STR_ANY     "\"" MAC_EXP_OP_STR_EQ \
+                               "\" or \"" MAC_EXP_OP_STR_NE "\""
+
+#define MAC_EXP_OP_TOK_NONE    0
+#define MAC_EXP_OP_TOK_EQ      1
+#define MAC_EXP_OP_TOK_NE      2
+
+static const NAME_CODE mac_exp_op_table[] =
+{
+    MAC_EXP_OP_STR_EQ, MAC_EXP_OP_TOK_EQ,
+    MAC_EXP_OP_STR_NE, MAC_EXP_OP_TOK_NE,
+    0, MAC_EXP_OP_TOK_NONE,
+};
+
+ /*
+  * The whitespace separator set.
+  */
+#define MAC_EXP_WHITESPACE     " \t\r\n"
+
+/* mac_exp_parse_error - report parse error, set error flag, return status */
+
+static int PRINTFLIKE(2, 3) mac_exp_parse_error(MAC_EXP_CONTEXT *mc,
+                                                       const char *fmt,...)
+{
+    va_list ap;
+
+    va_start(ap, fmt);
+    vmsg_warn(fmt, ap);
+    va_end(ap);
+    return (mc->status |= MAC_PARSE_ERROR);
+};
+
+/* MAC_EXP_ERR_RETURN - report parse error, set error flag, return status */
+
+#define MAC_EXP_ERR_RETURN(mc, fmt, ...) do { \
+       return (mac_exp_parse_error(mc, fmt, __VA_ARGS__)); \
+    } while (0)
+
+ /*
+  * Postfix 2.12 introduces support for {text} operands. Only with these do
+  * we support the ternary ?: operator and logical operators.
+  * 
+  * We cannot support operators in random text, because that would break Postfix
+  * 2.11 compatibility. For example, with the expression "${name?value}", the
+  * value is random text that may contain ':', '?', '{' and '}' characters.
+  * In particular, with Postfix 2.2 .. 2.11, "${name??foo:{b}ar}" evaluates
+  * to "??foo:{b}ar" or empty. There are explicit tests in this directory and
+  * the postconf directory to ensure that Postfix 2.11 compatibility is
+  * maintained.
+  * 
+  * Ideally, future Postfix configurations enclose random text operands inside
+  * {} braces. These allow whitespace around operands, which improves
+  * readability.
+  */
+
+/* MAC_EXP_FIND_LEFT_CURLY - skip over whitespace to '{', advance read ptr */
+
+#define MAC_EXP_FIND_LEFT_CURLY(len, cp) \
+       ((cp[len = strspn(cp, MAC_EXP_WHITESPACE)] == '{') ? \
+        (cp += len) : 0)
+
+/* mac_exp_extract_curly_payload - balance {}, skip whitespace, return payload */
+
+static char *mac_exp_extract_curly_payload(MAC_EXP_CONTEXT *mc, char **bp)
+{
+    char   *payload;
+    char   *cp;
+    int     level;
+    int     ch;
+
+    /*
+     * Extract the payload and balance the {}. The caller is expected to skip
+     * leading whitespace before the {. See MAC_EXP_FIND_LEFT_CURLY().
+     */
+    for (level = 1, cp = *bp, payload = ++cp; /* see below */ ; cp++) {
+       if ((ch = *cp) == 0) {
+           mac_exp_parse_error(mc, "unbalanced {} in attribute expression: "
+                               "\"%s\"",
+                               *bp);
+           return (0);
+       } else if (ch == '{') {
+           level++;
+       } else if (ch == '}') {
+           if (--level <= 0)
+               break;
+       }
+    }
+    *cp++ = 0;
+
+    /*
+     * Skip trailing whitespace after }.
+     */
+    *bp = cp + strspn(cp, MAC_EXP_WHITESPACE);
+    return (payload);
+}
+
+/* mac_exp_parse_logical - parse logical expression, advance read ptr */
+
+static int mac_exp_parse_logical(MAC_EXP_CONTEXT *mc, const char **lookup,
+                                        char **bp)
+{
+    const char myname[] = "mac_exp_parse_logical";
+    char   *cp = *bp;
+    VSTRING *left_op_buf;
+    VSTRING *rite_op_buf;
+    const char *left_op_strval;
+    const char *rite_op_strval;
+    char   *op_strval;
+    size_t  op_len;
+    int     op_tokval;
+    int     op_result;
+    size_t  tmp_len;
+
+    /*
+     * Left operand. The caller is expected to skip leading whitespace before
+     * the {. See MAC_EXP_FIND_LEFT_CURLY().
+     */
+    if ((left_op_strval = mac_exp_extract_curly_payload(mc, &cp)) == 0)
+       return (mc->status);
+
+    /*
+     * Operator. Todo: regexp operator.
+     */
+    op_len = strspn(cp, "<>!=?+-*/~&|%");      /* for better diagnostics. */
+    op_strval = mystrndup(cp, op_len);
+    op_tokval = name_code(mac_exp_op_table, NAME_CODE_FLAG_NONE, op_strval);
+    myfree(op_strval);
+    if (op_tokval == MAC_EXP_OP_TOK_NONE)
+       MAC_EXP_ERR_RETURN(mc, "%s expected at: \"...%s}>>>%.20s\"",
+                          MAC_EXP_OP_STR_ANY, left_op_strval, cp);
+    cp += op_len;
+
+    /*
+     * Right operand. Todo: syntax may depend on operator.
+     */
+    if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp) == 0)
+       MAC_EXP_ERR_RETURN(mc, "\"{expression}\" expected at: "
+                          "\"...{%s} %.*s>>>%.20s\"",
+                          left_op_strval, (int) op_len, op_strval, cp);
+    if ((rite_op_strval = mac_exp_extract_curly_payload(mc, &cp)) == 0)
+       return (mc->status);
+
+    /*
+     * Evaluate the logical expression. Todo: regexp support.
+     */
+    mc->status |=
+       mac_expand(left_op_buf = vstring_alloc(100), left_op_strval,
+                  mc->flags, mc->filter, mc->lookup, mc->context);
+    mc->status |=
+       mac_expand(rite_op_buf = vstring_alloc(100), rite_op_strval,
+                  mc->flags, mc->filter, mc->lookup, mc->context);
+    op_result =
+       strcmp(vstring_str(left_op_buf), vstring_str(rite_op_buf));
+    vstring_free(left_op_buf);
+    vstring_free(rite_op_buf);
+    if (mc->status & MAC_PARSE_ERROR)
+       return (mc->status);
+
+    /*
+     * Here, we fake up a non-empty or empty parameter value lookup result,
+     * for compatibility with the historical code that looks named parameter
+     * values.
+     */
+    switch (op_tokval) {
+    case MAC_EXP_OP_TOK_EQ:
+       *lookup = op_result == 0 ?
+           MAC_EXP_BVAL_TRUE : MAC_EXP_BVAL_FALSE;
+       break;
+    case MAC_EXP_OP_TOK_NE:
+       *lookup = op_result != 0 ?
+           MAC_EXP_BVAL_TRUE : MAC_EXP_BVAL_FALSE;
+       break;
+    default:
+       msg_panic("%s: unknown macro operator code %d",
+                 myname, op_tokval);
+    }
+    *bp = cp;
+    return (0);
+}
 
 /* mac_expand_callback - callback for mac_parse */
 
 static int mac_expand_callback(int type, VSTRING *buf, char *ptr)
 {
-    MAC_EXP *mc = (MAC_EXP *) ptr;
+    const char myname[] = "mac_expand_callback";
+    MAC_EXP_CONTEXT *mc = (MAC_EXP_CONTEXT *) ptr;
     int     lookup_mode;
-    const char *text;
+    const char *lookup;
     char   *cp;
     int     ch;
-    ssize_t len;
+    ssize_t res_len;
+    ssize_t tmp_len;
+    const char *res_iftrue;
+    const char *res_iffalse;
 
     /*
      * Sanity check.
      */
-    if (mc->level++ > 100) {
-       msg_warn("unreasonable macro call nesting: \"%s\"", vstring_str(buf));
-       mc->status |= MAC_PARSE_ERROR;
-    }
+    if (mc->level++ > 100)
+       mac_exp_parse_error(mc, "unreasonable macro call nesting: \"%s\"",
+                           vstring_str(buf));
     if (mc->status & MAC_PARSE_ERROR)
        return (mc->status);
 
     /*
-     * $Name etc. reference.
-     * 
-     * In order to support expansion of lookup results, we must save the lookup
-     * result. We use the input buffer since it will not be needed anymore.
+     * Named parameter or logical expression. In case of a syntax error,
+     * return without doing damage, and issue a warning instead.
      */
     if (type == MAC_PARSE_EXPR) {
 
+       cp = vstring_str(buf);
+
        /*
-        * Look for the ? or : delimiter. In case of a syntax error, return
-        * without doing damage, and issue a warning instead.
+        * Logical expression. If recursion is disabled, perform only one
+        * level of $name expansion.
         */
-       for (cp = vstring_str(buf); /* void */ ; cp++) {
-           if ((ch = *cp) == 0) {
-               lookup_mode = MAC_EXP_MODE_USE;
-               break;
-           }
-           if (ch == '?' || ch == ':') {
-               *cp++ = 0;
-               lookup_mode = MAC_EXP_MODE_TEST;
-               break;
-           }
-           if (!ISALNUM(ch) && ch != '_') {
-               msg_warn("macro name syntax error: \"%s\"", vstring_str(buf));
-               mc->status |= MAC_PARSE_ERROR;
+       if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) {
+           if (mac_exp_parse_logical(mc, &lookup, &cp) != 0)
                return (mc->status);
+
+           /*
+            * Look for the ? or : operator.
+            */
+           if ((ch = *cp) != 0) {
+               if (ch != '?' && ch != ':')
+                   MAC_EXP_ERR_RETURN(mc, "\"?\" or \":\" expected at: "
+                                      "\"...}>>>%.20s\"", cp);
+               cp++;
            }
        }
 
        /*
-        * Look up the named parameter.
+        * Named parameter.
         */
-       text = mc->lookup(vstring_str(buf), lookup_mode, mc->context);
+       else {
+
+           /*
+            * Look for the ? or : operator. In case of a syntax error,
+            * return without doing damage, and issue a warning instead.
+            */
+           for ( /* void */ ; /* void */ ; cp++) {
+               if ((ch = *cp) == 0) {
+                   lookup_mode = MAC_EXP_MODE_USE;
+                   break;
+               }
+               if (ch == '?' || ch == ':') {
+                   *cp++ = 0;
+                   lookup_mode = MAC_EXP_MODE_TEST;
+                   break;
+               }
+               if (!ISALNUM(ch) && ch != '_') {
+                   MAC_EXP_ERR_RETURN(mc, "attribute name syntax error at: "
+                                      "\"...%.*s>>>%.20s\"",
+                                      (int) (cp - vstring_str(buf)),
+                                      vstring_str(buf), cp);
+               }
+           }
+
+           /*
+            * Look up the named parameter. Todo: allow the lookup function
+            * to specify if the result is safe for $name expanson.
+            */
+           lookup = mc->lookup(vstring_str(buf), lookup_mode, mc->context);
+       }
 
        /*
-        * Perform the requested substitution.
+        * Return the requested result. After parsing the result operand
+        * following ?, we fall through to parse the result operand following
+        * :. This is necessary with the ternary ?: operator: first, with
+        * MAC_EXP_FLAG_SCAN to parse both result operands with mac_parse(),
+        * and second, to find garbage after any result operand. Without
+        * MAC_EXP_FLAG_SCAN the content of only one of the ?: result
+        * operands will be parsed with mac_parse(); syntax errors in the
+        * other operand will be missed.
         */
        switch (ch) {
        case '?':
-           if ((text != 0 && *text != 0) || (mc->flags & MAC_EXP_FLAG_SCAN))
-               mac_parse(cp, mac_expand_callback, (char *) mc);
-           break;
+           if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) {
+               if ((res_iftrue = mac_exp_extract_curly_payload(mc, &cp)) == 0)
+                   return (mc->status);
+           } else {
+               res_iftrue = cp;
+               cp = "";                        /* no left-over text */
+           }
+           if ((lookup != 0 && *lookup != 0) || (mc->flags & MAC_EXP_FLAG_SCAN))
+               mc->status |= mac_parse(res_iftrue, mac_expand_callback,
+                                       (char *) mc);
+           if (*cp == 0)                       /* end of input, OK */
+               break;
+           if (*cp != ':')                     /* garbage */
+               MAC_EXP_ERR_RETURN(mc, "\":\" expected at: "
+                                  "\"...%s}>>>%.20s\"", res_iftrue, cp);
+           cp += 1;
+           /* FALLTHROUGH: do not remove, see comment above. */
        case ':':
-           if (text == 0 || *text == 0 || (mc->flags & MAC_EXP_FLAG_SCAN))
-               mac_parse(cp, mac_expand_callback, (char *) mc);
+           if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) {
+               if ((res_iffalse = mac_exp_extract_curly_payload(mc, &cp)) == 0)
+                   return (mc->status);
+           } else {
+               res_iffalse = cp;
+               cp = "";                        /* no left-over text */
+           }
+           if (lookup == 0 || *lookup == 0 || (mc->flags & MAC_EXP_FLAG_SCAN))
+               mc->status |= mac_parse(res_iffalse, mac_expand_callback,
+                                       (char *) mc);
+           if (*cp != 0)                       /* garbage */
+               MAC_EXP_ERR_RETURN(mc, "unexpected input at: "
+                                  "\"...%s}>>>%.20s\"", res_iffalse, cp);
            break;
-       default:
-           if (text == 0) {
+       case 0:
+           if (lookup == 0) {
                mc->status |= MAC_PARSE_UNDEF;
-           } else if (*text == 0 || (mc->flags & MAC_EXP_FLAG_SCAN)) {
+           } else if (*lookup == 0 || (mc->flags & MAC_EXP_FLAG_SCAN)) {
                 /* void */ ;
            } else if (mc->flags & MAC_EXP_FLAG_RECURSE) {
-               vstring_strcpy(buf, text);
-               mac_parse(vstring_str(buf), mac_expand_callback, (char *) mc);
+               vstring_strcpy(buf, lookup);
+               mc->status |= mac_parse(vstring_str(buf), mac_expand_callback,
+                                       (char *) mc);
            } else {
-               len = VSTRING_LEN(mc->result);
-               vstring_strcat(mc->result, text);
+               res_len = VSTRING_LEN(mc->result);
+               vstring_strcat(mc->result, lookup);
                if (mc->flags & MAC_EXP_FLAG_PRINTABLE) {
-                   printable(vstring_str(mc->result) + len, '_');
+                   printable(vstring_str(mc->result) + res_len, '_');
                } else if (mc->filter) {
-                   cp = vstring_str(mc->result) + len;
+                   cp = vstring_str(mc->result) + res_len;
                    while (*(cp += strspn(cp, mc->filter)))
                        *cp++ = '_';
                }
            }
            break;
+       default:
+           msg_panic("%s: unknown operator code %d", myname, ch);
        }
     }
 
@@ -229,7 +525,7 @@ int     mac_expand(VSTRING *result, const char *pattern, int flags,
                           const char *filter,
                           MAC_EXP_LOOKUP_FN lookup, char *context)
 {
-    MAC_EXP mc;
+    MAC_EXP_CONTEXT mc;
     int     status;
 
     /*
index f92ad2c893c0b904500f01e000d0f4ec94a0a31c..deff16b7cf583773275ac96084cc006c93feaf34 100644 (file)
@@ -1,18 +1,60 @@
 name1 = name1-value
-name2 =
 
-${name1?name 1 defined, |$name1|$name2|}
-${name1:name 1 undefined, |$name1|$name2|}
-${name2?name 2 defined, |$name1|$name2|}
-${name2:name 2 undefined, |$name1|$name2|}
-|$name1|$name2|
 $(name1
 $(name )
+${${name1} != {}?name 1 defined, |$name1|$name2|}
+${ ${name1} != {}?name 1 defined, |$name1|$name2|}
+${ ${name1} ?name 1 defined, |$name1|$name2|}
+${{$name1} ? {name 1 defined, |$name1|$name2|} : {name 1 undefined, |$name1|$name2|} }
+${x{$name1} != {}?{name 1 defined, |$name1|$name2|}}
+${{$name1}x?{name 1 defined, |$name1|$name2|}}
+${{$name1} != {}x{name 1 defined, |$name1|$name2|}}
+${{$name1} != {}?x{name 1 defined, |$name1|$name2|}}
+${{$name2} != {}?x{name 2 defined, |$name1|$name2|}:{name 2 undefined, |$name1|$name2|}}
+${{$name1} != {}?{name 1 defined, |$name1|$name2|}x}
+${{$name1} != {}?{name 1 defined, |$name1|$name2|}x:{name 1 undefined, |$name1|$name2|}}
+${{$name1} != {}?{name 1 defined, |$name1|$name2|}:x{name 1 undefined, |$name1|$name2|}}
+${{$name2} != {}?{name 2 defined, |$name1|$name2|}:x{name 2 undefined, |$name1|$name2|}}
+${{text}}
+${{text}?{non-empty}:{empty}}
+${{text} = {}}
+${{${ name1}} == {}}
+${name1?{${ name1}}:{${name2}}}
+${name2?{${ name1}}:{${name2}}}
+${name2?{${name1}}:{${ name2}}}
+${name2:{${name1}}:{${name2}}}
+${name2?{${name1}}?{${name2}}}
+${{${name1?bug:test}} != {bug:test}?{Error: NOT}:{Good:}} Postfix 2.11 compatible
+${{${name1??bug}} != {?bug}?{Error: NOT}:{Good:}} Postfix 2.11 compatible
+${{${name2::bug}} != {:bug}?{Error: NOT}:{Good:}} Postfix 2.11 compatible
+${{xx}==(yy)?{oops}:{phew}}
 
-name2 = name2-value
+name1 = name1-value
 
 ${name1?name 1 defined, |$name1|$name2|}
 ${name1:name 1 undefined, |$name1|$name2|}
 ${name2?name 2 defined, |$name1|$name2|}
 ${name2:name 2 undefined, |$name1|$name2|}
 |$name1|$name2|
+${{$name1} != {}?{name 1 defined, |$name1|$name2|}}
+${{$name1} != {}:{name 1 undefined, |$name1|$name2|}}
+${{$name1} == {}?{name 1 undefined, |$name1|$name2|}}
+${{$name1} == {}:{name 1 defined, |$name1|$name2|}}
+${name1?{name 1 defined, |$name1|$name2|}:{name 1 undefined, |$name1|$name2|}}
+${{$name1} != {}?{name 1 defined, |$name1|$name2|}:{name 1 undefined, |$name1|$name2|}}
+${{$name1} != {} ? {name 1 defined, |$name1|$name2|} : {name 1 undefined, |$name1|$name2|}}
+${{$name1} != {}?{name 1 defined, |$name1|$name2|}:name 1 undefined, |$name1|$name2|}
+${{$name1} != {} ? {name 1 defined, |$name1|$name2|} : name 1 undefined, |$name1|$name2|}
+${{$name1} != {}}
+${{$name1} == {}}
+${{$name2} != {}?{name 2 defined, |$name1|$name2|}}
+${{$name2} != {}:{name 2 undefined, |$name1|$name2|}}
+${{$name2} == {}?{name 2 undefined, |$name1|$name2|}}
+${{$name2} == {}:{name 2 defined, |$name1|$name2|}}
+${name2?{name 2 defined, |$name1|$name2|}:{name 2 undefined, |$name1|$name2|}}
+${{$name2} != {}?{name 2 defined, |$name1|$name2|}:{name 2 undefined, |$name1|$name2|}}
+${{$name2} != {} ? {name 2 defined, |$name1|$name2|} : {name 2 undefined, |$name1|$name2|}}
+${{$name2} != {}?{name 2 defined, |$name1|$name2|}:name 2 undefined, |$name1|$name2|}
+${{$name2} != {} ? {name 2 defined, |$name1|$name2|} : name 2 undefined, |$name1|$name2|}
+${{$name2} != {}}
+${{$name2} == {}}
index d76d3877a6c5d0b7decbf62a0f7476c29eb6413d..1489a16167b34f4483997d5e2b52af27ceb04d1c 100644 (file)
 << name1 = name1-value
-<< name2 =
 << 
-<< ${name1?name 1 defined, |$name1|$name2|}
-stat=2 result=name 1 defined, |name1-value||
-<< ${name1:name 1 undefined, |$name1|$name2|}
-stat=0 result=
-<< ${name2?name 2 defined, |$name1|$name2|}
-stat=0 result=
-<< ${name2:name 2 undefined, |$name1|$name2|}
-stat=2 result=name 2 undefined, |name1-value||
-<< |$name1|$name2|
-stat=2 result=|name1-value||
 << $(name1
 unknown: warning: truncated macro reference: "$(name1"
 stat=1 result=
 << $(name )
-unknown: warning: macro name syntax error: "name "
+unknown: warning: attribute name syntax error at: "...name>>> "
+stat=1 result=
+<< ${${name1} != {}?name 1 defined, |$name1|$name2|}
+unknown: warning: attribute name syntax error at: "...>>>${name1} != {}?name "
+stat=1 result=
+<< ${ ${name1} != {}?name 1 defined, |$name1|$name2|}
+unknown: warning: attribute name syntax error at: "...>>> ${name1} != {}?name"
+stat=1 result=
+<< ${ ${name1} ?name 1 defined, |$name1|$name2|}
+unknown: warning: attribute name syntax error at: "...>>> ${name1} ?name 1 de"
+stat=1 result=
+<< ${{$name1} ? {name 1 defined, |$name1|$name2|} : {name 1 undefined, |$name1|$name2|} }
+unknown: warning: "==" or "!=" expected at: "...$name1}>>>? {name 1 defined, |"
+stat=1 result=
+<< ${x{$name1} != {}?{name 1 defined, |$name1|$name2|}}
+unknown: warning: attribute name syntax error at: "...x>>>{$name1} != {}?{name"
+stat=1 result=
+<< ${{$name1}x?{name 1 defined, |$name1|$name2|}}
+unknown: warning: "==" or "!=" expected at: "...$name1}>>>x?{name 1 defined, |"
+stat=1 result=
+<< ${{$name1} != {}x{name 1 defined, |$name1|$name2|}}
+unknown: warning: "?" or ":" expected at: "...}>>>x{name 1 defined, |$"
+stat=1 result=
+<< ${{$name1} != {}?x{name 1 defined, |$name1|$name2|}}
+stat=2 result=x{name 1 defined, |name1-value||}
+<< ${{$name2} != {}?x{name 2 defined, |$name1|$name2|}:{name 2 undefined, |$name1|$name2|}}
+stat=2 result=
+<< ${{$name1} != {}?{name 1 defined, |$name1|$name2|}x}
+unknown: warning: ":" expected at: "...name 1 defined, |$name1|$name2|}>>>x"
+stat=3 result=name 1 defined, |name1-value||
+<< ${{$name1} != {}?{name 1 defined, |$name1|$name2|}x:{name 1 undefined, |$name1|$name2|}}
+unknown: warning: ":" expected at: "...name 1 defined, |$name1|$name2|}>>>x:{name 1 undefined,"
+stat=3 result=name 1 defined, |name1-value||
+<< ${{$name1} != {}?{name 1 defined, |$name1|$name2|}:x{name 1 undefined, |$name1|$name2|}}
+stat=2 result=name 1 defined, |name1-value||
+<< ${{$name2} != {}?{name 2 defined, |$name1|$name2|}:x{name 2 undefined, |$name1|$name2|}}
+stat=2 result=x{name 2 undefined, |name1-value||}
+<< ${{text}}
+unknown: warning: "==" or "!=" expected at: "...text}>>>"
+stat=1 result=
+<< ${{text}?{non-empty}:{empty}}
+unknown: warning: "==" or "!=" expected at: "...text}>>>?{non-empty}:{empty}"
+stat=1 result=
+<< ${{text} = {}}
+unknown: warning: "==" or "!=" expected at: "...text}>>>= {}"
+stat=1 result=
+<< ${{${ name1}} == {}}
+unknown: warning: attribute name syntax error at: "...>>> name1"
+stat=1 result=
+<< ${name1?{${ name1}}:{${name2}}}
+unknown: warning: attribute name syntax error at: "...>>> name1"
+stat=1 result=
+<< ${name2?{${ name1}}:{${name2}}}
+stat=2 result=
+<< ${name2?{${name1}}:{${ name2}}}
+unknown: warning: attribute name syntax error at: "...>>> name2"
+stat=1 result=
+<< ${name2:{${name1}}:{${name2}}}
+unknown: warning: unexpected input at: "...${name1}}>>>:{${name2}}"
+stat=1 result=name1-value
+<< ${name2?{${name1}}?{${name2}}}
+unknown: warning: ":" expected at: "...${name1}}>>>?{${name2}}"
+stat=1 result=
+<< ${{${name1?bug:test}} != {bug:test}?{Error: NOT}:{Good:}} Postfix 2.11 compatible
+stat=0 result=Good: Postfix 2.11 compatible
+<< ${{${name1??bug}} != {?bug}?{Error: NOT}:{Good:}} Postfix 2.11 compatible
+stat=0 result=Good: Postfix 2.11 compatible
+<< ${{${name2::bug}} != {:bug}?{Error: NOT}:{Good:}} Postfix 2.11 compatible
+stat=0 result=Good: Postfix 2.11 compatible
+<< ${{xx}==(yy)?{oops}:{phew}}
+unknown: warning: "{expression}" expected at: "...{xx} ??>>>(yy)?{oops}:{phew}"
 stat=1 result=
 << 
 
-<< name2 = name2-value
+<< name1 = name1-value
 << 
 << ${name1?name 1 defined, |$name1|$name2|}
-stat=0 result=
+stat=2 result=name 1 defined, |name1-value||
 << ${name1:name 1 undefined, |$name1|$name2|}
-stat=2 result=name 1 undefined, ||name2-value|
+stat=0 result=
 << ${name2?name 2 defined, |$name1|$name2|}
-stat=2 result=name 2 defined, ||name2-value|
-<< ${name2:name 2 undefined, |$name1|$name2|}
 stat=0 result=
+<< ${name2:name 2 undefined, |$name1|$name2|}
+stat=2 result=name 2 undefined, |name1-value||
 << |$name1|$name2|
-stat=2 result=||name2-value|
+stat=2 result=|name1-value||
+<< ${{$name1} != {}?{name 1 defined, |$name1|$name2|}}
+stat=2 result=name 1 defined, |name1-value||
+<< ${{$name1} != {}:{name 1 undefined, |$name1|$name2|}}
+stat=0 result=
+<< ${{$name1} == {}?{name 1 undefined, |$name1|$name2|}}
+stat=0 result=
+<< ${{$name1} == {}:{name 1 defined, |$name1|$name2|}}
+stat=2 result=name 1 defined, |name1-value||
+<< ${name1?{name 1 defined, |$name1|$name2|}:{name 1 undefined, |$name1|$name2|}}
+stat=2 result=name 1 defined, |name1-value||
+<< ${{$name1} != {}?{name 1 defined, |$name1|$name2|}:{name 1 undefined, |$name1|$name2|}}
+stat=2 result=name 1 defined, |name1-value||
+<< ${{$name1} != {} ? {name 1 defined, |$name1|$name2|} : {name 1 undefined, |$name1|$name2|}}
+stat=2 result=name 1 defined, |name1-value||
+<< ${{$name1} != {}?{name 1 defined, |$name1|$name2|}:name 1 undefined, |$name1|$name2|}
+stat=2 result=name 1 defined, |name1-value||
+<< ${{$name1} != {} ? {name 1 defined, |$name1|$name2|} : name 1 undefined, |$name1|$name2|}
+stat=2 result=name 1 defined, |name1-value||
+<< ${{$name1} != {}}
+stat=0 result=true
+<< ${{$name1} == {}}
+stat=0 result=
+<< ${{$name2} != {}?{name 2 defined, |$name1|$name2|}}
+stat=2 result=
+<< ${{$name2} != {}:{name 2 undefined, |$name1|$name2|}}
+stat=2 result=name 2 undefined, |name1-value||
+<< ${{$name2} == {}?{name 2 undefined, |$name1|$name2|}}
+stat=2 result=name 2 undefined, |name1-value||
+<< ${{$name2} == {}:{name 2 defined, |$name1|$name2|}}
+stat=2 result=
+<< ${name2?{name 2 defined, |$name1|$name2|}:{name 2 undefined, |$name1|$name2|}}
+stat=2 result=name 2 undefined, |name1-value||
+<< ${{$name2} != {}?{name 2 defined, |$name1|$name2|}:{name 2 undefined, |$name1|$name2|}}
+stat=2 result=name 2 undefined, |name1-value||
+<< ${{$name2} != {} ? {name 2 defined, |$name1|$name2|} : {name 2 undefined, |$name1|$name2|}}
+stat=2 result=name 2 undefined, |name1-value||
+<< ${{$name2} != {}?{name 2 defined, |$name1|$name2|}:name 2 undefined, |$name1|$name2|}
+stat=2 result=name 2 undefined, |name1-value||
+<< ${{$name2} != {} ? {name 2 defined, |$name1|$name2|} : name 2 undefined, |$name1|$name2|}
+stat=2 result= name 2 undefined, |name1-value||
+<< ${{$name2} != {}}
+stat=2 result=
+<< ${{$name2} == {}}
+stat=2 result=true
index ca7091efb30b89a5d08eb95b108d5537f8f5e8fb..cfd93e655bf79e5da697c1fab0e25aa3ab96c1f7 100644 (file)
@@ -114,7 +114,7 @@ static ARGV *match_list_parse(ARGV *list, char *string, int init_match)
      * /filename contents are expanded in-line. To support !/filename we
      * prepend the negation operator to each item from the file.
      */
-    while ((start = mystrtok(&bp, delim)) != 0) {
+    while ((start = mystrtokq(&bp, delim, "{}")) != 0) {
        if (*start == '#') {
            msg_warn("%s: comment at end of line is not supported: %s %s",
                     myname, start, bp);
index 599c69a57b88a4eb73e6ac9eb3205518fe766c21..c69dce14c63dc68109251b895b590475f0522414 100644 (file)
@@ -43,7 +43,7 @@ extern void msg_error_clear(void);
 extern MSG_CLEANUP_FN msg_cleanup(MSG_CLEANUP_FN);
 
 extern void PRINTFLIKE(4, 5) msg_rate_delay(time_t *, int,
-                                         void (*log_fn) (const char *,...),
+                        void PRINTFLIKE(1, 2) (*log_fn) (const char *,...),
                                                    const char *,...);
 
 /* LICENSE
index a87a58cba97f77805cd871604bb77220d115730b..0056f836ab9b3ef4b806c46827fffb6f1947e166 100644 (file)
@@ -9,11 +9,21 @@
 /*     char    *mystrtok(bufp, delimiters)
 /*     char    **bufp;
 /*     const char *delimiters;
+/*
+/*     char    *mystrtokq(bufp, delimiters, parens)
+/*     char    **bufp;
+/*     const char *delimiters;
+/*     const char *parens;
 /* DESCRIPTION
 /*     mystrtok() splits a buffer on the specified \fIdelimiters\fR.
 /*     Tokens are delimited by runs of delimiters, so this routine
 /*     cannot return zero-length tokens.
 /*
+/*     mystrtokq() is like mystrtok() but will not split text
+/*     between balanced parentheses.  \fIparens\fR specifies the
+/*     opening and closing parenthesis (one of each).  The set of
+/*     \fIparens\fR must be distinct from the set of \fIdelimiters\fR.
+/*
 /*     The \fIbufp\fR argument specifies the start of the search; it
 /*     is updated with each call. The input is destroyed.
 /*
@@ -65,6 +75,41 @@ char   *mystrtok(char **src, const char *sep)
     return (start);
 }
 
+/* mystrtokq - safe tokenizer with quoting support */
+
+char   *mystrtokq(char **src, const char *sep, const char *parens)
+{
+    char   *start = *src;
+    static char   *cp;
+    int     ch;
+    int     level;
+
+    /*
+     * Skip over leading delimiters.
+     */
+    start += strspn(start, sep);
+    if (*start == 0) {
+       *src = start;
+       return (0);
+    }
+
+    /*
+     * Parse out the next token.
+     */
+    for (level = 0, cp = start; (ch = *(unsigned char *) cp) != 0; cp++) {
+       if (ch == parens[0]) {
+           level++;
+        } else if (level > 0 && ch == parens[1]) {
+           level--;
+       } else if (level == 0 && strchr(sep, ch) != 0) {
+           *cp++ = 0;
+           break;
+       }
+    }
+    *src = cp;
+    return (start);
+}
+
 #ifdef TEST
 
  /*
@@ -80,10 +125,15 @@ int     main(void)
     char   *start;
     char   *str;
 
-    while (vstring_fgets(vp, VSTREAM_IN)) {
+    while (vstring_fgets(vp, VSTREAM_IN) && VSTRING_LEN(vp) > 0) {
        start = vstring_str(vp);
-       while ((str = mystrtok(&start, " \t\r\n")) != 0)
-           vstream_printf(">%s<\n", str);
+       if (strchr(start, '{') == 0) {
+           while ((str = mystrtok(&start, " \t\r\n")) != 0)
+               vstream_printf(">%s<\n", str);
+       } else {
+           while ((str = mystrtokq(&start, " \t\r\n", "{}")) != 0)
+               vstream_printf(">%s<\n", str);
+       }
        vstream_fflush(VSTREAM_OUT);
     }
     vstring_free(vp);
index 5b30bdaa4cf21b9ef6a4e485e87b8aedc69784ee..93647a3d24db6740ca51c23eab78ef1c0408c051 100644 (file)
@@ -28,6 +28,7 @@ extern char *skipblanks(const char *);
 extern char *trimblanks(char *, int);
 extern char *concatenate(const char *,...);
 extern char *mystrtok(char **, const char *);
+extern char *mystrtokq(char **, const char *, const char *);
 extern char *translit(char *, const char *, const char *);
 #ifndef HAVE_BASENAME
 #define basename postfix_basename
@@ -43,6 +44,7 @@ extern int allspace(const char *);
 extern int allascii(const char *);
 extern const char *split_nameval(char *, char **, char **);
 extern int valid_utf8_string(const char *, ssize_t);
+extern size_t balpar(const char *, const char *);
 
 /* LICENSE
 /* .ad
index 1acda0f89474e8965fa9d6bf7932b925afe1096b..044fe3371edc6a253b61f230b5d6a8e81312d3e6 100644 (file)
@@ -151,7 +151,7 @@ VBUF   *vbuf_print(VBUF *bp, const char *format, va_list ap)
             * strings, since we are ging to let sprintf() do the hard work.
             * In regular expression notation, we recognize:
             * 
-            * %-?0?([0-9]+|\*)?\.?([0-9]+|\*)?l?[a-zA-Z]
+            * %-?0?([0-9]+|\*)?\.(?[0-9]+|\*)?l?[a-zA-Z]
             * 
             * which includes some combinations that do not make sense. Garbage
             * in, garbage out.
@@ -178,21 +178,24 @@ VBUF   *vbuf_print(VBUF *bp, const char *format, va_list ap)
                msg_warn("%s: bad width %d in %.50s", myname, width, format);
                width = 0;
            }
-           if (*cp == '.')                     /* width/precision separator */
+           if (*cp == '.') {                   /* width/precision separator */
                VSTRING_ADDCH(fmt, *cp++);
-           if (*cp == '*') {                   /* dynamic precision */
-               prec = va_arg(ap, int);
-               VSTRING_ADDNUM(fmt, prec);
-               cp++;
-           } else {                            /* hard-coded precision */
-               for (prec = 0; ch = *cp, ISDIGIT(ch); cp++) {
-                   prec = prec * 10 + ch - '0';
-                   VSTRING_ADDCH(fmt, ch);
+               if (*cp == '*') {               /* dynamic precision */
+                   prec = va_arg(ap, int);
+                   VSTRING_ADDNUM(fmt, prec);
+                   cp++;
+               } else {                        /* hard-coded precision */
+                   for (prec = 0; ch = *cp, ISDIGIT(ch); cp++) {
+                       prec = prec * 10 + ch - '0';
+                       VSTRING_ADDCH(fmt, ch);
+                   }
                }
-           }
-           if (prec < 0) {
-               msg_warn("%s: bad precision %d in %.50s", myname, prec, format);
-               prec = 0;
+               if (prec < 0) {
+                   msg_warn("%s: bad precision %d in %.50s", myname, prec, format);
+                   prec = -1;
+               }
+           } else {
+               prec = -1;
            }
            if ((long_flag = (*cp == 'l')) != 0)/* long whatever */
                VSTRING_ADDCH(fmt, *cp++);
@@ -210,7 +213,7 @@ VBUF   *vbuf_print(VBUF *bp, const char *format, va_list ap)
            switch (*cp) {
            case 's':                           /* string-valued argument */
                s = va_arg(ap, char *);
-               if (prec > 0 || (width > 0 && width > strlen(s))) {
+               if (prec >= 0 || (width > 0 && width > strlen(s))) {
                    if (VBUF_SPACE(bp, (width > prec ? width : prec) + INT_SPACE))
                        return (bp);
                    sprintf((char *) bp->ptr, vstring_str(fmt), s);