From 098feb387ea06c15d432a961f3d4b722c2885e6d Mon Sep 17 00:00:00 2001 From: Wietse Venema Date: Sun, 21 Sep 2014 00:00:00 -0500 Subject: [PATCH] postfix-2.12-20140921 --- postfix/.indent.pro | 3 +- postfix/HISTORY | 65 ++++ postfix/README_FILES/DATABASE_README | 20 +- postfix/RELEASE_NOTES | 30 ++ postfix/WISHLIST | 33 ++ postfix/html/DATABASE_README.html | 18 +- postfix/html/postconf.1.html | 211 ++++++------ postfix/html/postconf.5.html | 37 +- postfix/html/proxymap.8.html | 18 +- postfix/html/qmqp-sink.1.html | 6 +- postfix/html/smtp-sink.1.html | 38 +- postfix/man/man1/postconf.1 | 19 +- postfix/man/man5/postconf.5 | 34 +- postfix/proto/DATABASE_README.html | 18 +- postfix/proto/postconf.html.prolog | 37 +- postfix/proto/postconf.man.prolog | 34 +- postfix/src/bounce/bounce_template.h | 2 +- postfix/src/cleanup/cleanup_message.c | 5 +- postfix/src/global/data_redirect.c | 2 +- postfix/src/global/dict_memcache.c | 5 +- postfix/src/global/mail_version.h | 2 +- postfix/src/global/maps.c | 3 +- postfix/src/global/memcache_proto.h | 2 +- postfix/src/global/server_acl.c | 2 +- postfix/src/postconf/Makefile.in | 92 ++++- postfix/src/postconf/postconf.c | 19 +- postfix/src/postconf/postconf.h | 2 +- postfix/src/postconf/postconf_builtin.c | 5 +- postfix/src/postconf/postconf_dbms.c | 93 +++-- postfix/src/postconf/test57.ref | 10 + postfix/src/postconf/test58.ref | 7 + postfix/src/proxymap/proxymap.c | 3 +- postfix/src/smtpd/smtpd_check.c | 15 +- postfix/src/smtpstone/qmqp-sink.c | 15 +- postfix/src/smtpstone/smtp-sink.c | 23 +- postfix/src/util/Makefile.in | 18 +- postfix/src/util/argv.h | 4 + postfix/src/util/argv_splitq.c | 118 +++++++ postfix/src/util/balpar.c | 56 +++ postfix/src/util/dict.h | 2 +- postfix/src/util/dict_open.c | 9 +- postfix/src/util/dict_pipe.c | 34 +- postfix/src/util/dict_pipe.h | 4 - postfix/src/util/dict_random.c | 28 +- postfix/src/util/dict_random.h | 4 - postfix/src/util/dict_test.c | 3 - postfix/src/util/mac_expand.c | 440 ++++++++++++++++++++---- postfix/src/util/mac_expand.in | 56 ++- postfix/src/util/mac_expand.ref | 141 +++++++- postfix/src/util/match_list.c | 2 +- postfix/src/util/msg.h | 2 +- postfix/src/util/mystrtok.c | 56 ++- postfix/src/util/stringops.h | 2 + postfix/src/util/vbuf_print.c | 33 +- 54 files changed, 1482 insertions(+), 458 deletions(-) create mode 100644 postfix/src/postconf/test57.ref create mode 100644 postfix/src/postconf/test58.ref create mode 100644 postfix/src/util/argv_splitq.c create mode 100644 postfix/src/util/balpar.c diff --git a/postfix/.indent.pro b/postfix/.indent.pro index 37dcdeaef..92d8d25a2 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -168,7 +168,8 @@ -TLOCAL_EXP -TLOCAL_STATE -TLONG_NAME_MASK --TMAC_EXP +-TMAC_EXP_CONTEXT +-TMAC_EXP_OP_INFO -TMAC_HEAD -TMAC_PARSE -TMAIL_PRINT diff --git a/postfix/HISTORY b/postfix/HISTORY index b0e7a500d..3921bf0f2 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -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 $.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. diff --git a/postfix/README_FILES/DATABASE_README b/postfix/README_FILES/DATABASE_README index bb361307e..8e0bd8829 100644 --- a/postfix/README_FILES/DATABASE_README +++ b/postfix/README_FILES/DATABASE_README @@ -243,13 +243,13 @@ To find out what database types your Postfix system supports, use the "ppooss format is described in pcre_table(5). The lookup table name as used in "pcre:table" is the name of the regular expression file. ppiippeemmaapp (read-only) - A pipeline of lookup tables. Example: "ppiippeemmaapp::!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. ppggssqqll (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 "ppooss Postfix proxymap(8) client for shared access to Postfix databases. The lookup table name syntax is "proxy:type:table". rraannddmmaapp (read-only) - An in-memory table that performs random selection. Example: "rraannddmmaapp:: - !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. rreeggeexxpp (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: diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index c49daae86..45b23efb7 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -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 ==================================== diff --git a/postfix/WISHLIST b/postfix/WISHLIST index a9a08d656..bd1bbf5bf 100644 --- a/postfix/WISHLIST +++ b/postfix/WISHLIST @@ -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. diff --git a/postfix/html/DATABASE_README.html b/postfix/html/DATABASE_README.html index 3f0d9ec4e..830fb4fab 100644 --- a/postfix/html/DATABASE_README.html +++ b/postfix/html/DATABASE_README.html @@ -365,14 +365,14 @@ file.
pipemap (read-only)
A pipeline of lookup tables. Example: -"pipemap:!type1:name1! ... -!typen:namen". Each "pipemap:" query is +"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 first and last characters of the "pipemap:" +table name must be "{" and "}". Within these, individual maps are +separated with comma or whitespace.
pgsql (read-only)
@@ -388,11 +388,11 @@ databases. The lookup table name syntax is "proxy:
randmap (read-only)
An in-memory table that performs random selection. Example: -"randmap:!result1! ... !resultn". +"randmap:{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).
+The first and last characters of the "randmap:" table name must be +"{" and "}". Within these, individual maps are separated with comma +or whitespace.
regexp (read-only)
diff --git a/postfix/html/postconf.1.html b/postfix/html/postconf.1.html index bf2ddd9eb..bb64bd3af 100644 --- a/postfix/html/postconf.1.html +++ b/postfix/html/postconf.1.html @@ -55,16 +55,16 @@ POSTCONF(1) POSTCONF(1) postconf -a|-A|-l|-m [-v] [-c config_dir] DESCRIPTION - By default, the postconf(1) command displays the values of main.cf con- + By default, the postconf(1) command displays the values of main.cf con‐ figuration parameters, and warns about possible mis-typed parameter - names (Postfix 2.9 and later). It can also change main.cf configura- + names (Postfix 2.9 and later). It can also change main.cf configura‐ tion parameter values, or display other configuration information about the Postfix mail system. Options: -a List the available SASL server plug-in types. The SASL plug-in - type is selected with the smtpd_sasl_type configuration parame- + type is selected with the smtpd_sasl_type configuration parame‐ ter by specifying one of the names listed below. cyrus 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. -A List the available SASL client plug-in types. The SASL plug-in - type is selected with the smtp_sasl_type or lmtp_sasl_type con- + type is selected with the smtp_sasl_type or lmtp_sasl_type 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 [template_file] - Display the message text that appears at the beginning of deliv- - ery status notification (DSN) messages, replacing $name expres- + Display the message text that appears at the beginning of deliv‐ + ery status notification (DSN) messages, replacing $name expres‐ sions with actual values as described in bounce(5). 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. - -d Print main.cf default parameter settings instead of actual set- + -d Print main.cf default parameter settings instead of actual set‐ tings. Specify -df to fold long lines for human readability (Postfix 2.9 and later). - -e Edit the main.cf configuration file, and update parameter set- + -e Edit the main.cf configuration file, and update parameter set‐ tings with the "name=value" pairs on the postconf(1) command line. With -M, edit the master.cf configuration file, and replace one - or more service entries with new values as specified with "ser- + or more service entries with new values as specified with "ser‐ vice/type=value" on the postconf(1) command line. With -F, edit the master.cf configuration file, and replace one - or more service fields with new values as specied with "ser- - vice/type/field=value" on the postconf(1) command line. Cur- - rently, the "command" field contains the command name and com- + or more service fields with new values as specied with "ser‐ + vice/type/field=value" on the postconf(1) 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 -P, edit the master.cf configuration file, and add or - update one or more service parameter settings (-o parame- - ter=value settings) with new values as specied with "ser- + update one or more service parameter settings (-o parame‐ + ter=value settings) with new values as specied with "ser‐ vice/type/parameter=value" on the postconf(1) 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 "service/type/field=value" per line. Specify -Ff to fold long lines. - Specify one or more "service/type/field" instances on the post- - conf(1) command line to limit the output to fields of interest. + Specify one or more "service/type/field" instances on the post‐‐ + conf(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) -h Show parameter or attribute values without the "name = " label that normally precedes the value. - -l List the names of all supported mailbox locking methods. Post- + -l List the names of all supported mailbox locking methods. Post‐ fix supports the following methods: flock A kernel-based advisory locking method for local files @@ -188,21 +188,21 @@ POSTCONF(1) POSTCONF(1) dotlock An application-level locking method. An application locks - a file named filename by creating a file named file- + a file named filename by creating a file named file‐ name.lock. The application is expected to remove its own lock file, as well as stale lock files that were left behind after abnormal program termination. -m List the names of all supported lookup table types. In Postfix configuration files, lookup tables are specified as type:name, - where type is one of the types listed below. The table name syn- - tax depends on the lookup table type as described in the DATA- - BASE_README document. + where type is one of the types listed below. The table name syn‐ + tax depends on the lookup table type as described in the DATA‐ + BASE_README document. btree A sorted, balanced tree structure. Available on systems with support for Berkeley DB databases. - cdb A read-optimized structure with no support for incremen- + cdb 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 cidr_table(5). - dbm An indexed file type based on hashing. Available on sys- + dbm An indexed file type based on hashing. Available on sys‐ tems with support for DBM databases. environ 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. - fail A table that reliably fails all requests. The lookup ta- - ble name is used for logging. This table exists to sim- + fail 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. - hash An indexed file type based on hashing. Available on sys- + hash An indexed file type based on hashing. Available on sys‐ tems with support for Berkeley DB databases. internal @@ -230,23 +230,23 @@ POSTCONF(1) POSTCONF(1) when a process terminates. lmdb 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 lmdb_table(5). ldap (read-only) LDAP database client. This is described in ldap_table(5). memcache - Memcache database client. This is described in mem- - cache_table(5). + Memcache database client. This is described in mem‐‐ + cache_table(5). mysql (read-only) MySQL database client. Available on systems with support - for MySQL databases. This is described in mysql_ta- - ble(5). + for MySQL databases. This is described in mysql_ta‐‐ + ble(5). pcre (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 pcre_table(5). pgsql (read-only) @@ -254,95 +254,94 @@ POSTCONF(1) POSTCONF(1) pgsql_table(5). pipemap (read-only) - A pipeline of lookup tables. Example: - "pipemap:!type_1:name_1! ... !type_n:name_n". Each - "pipemap:" query is given to the first table. Each + A lookup table that constructs a pipeline of tables. + Example: "pipemap:{type_1:name_1, ..., type_n:name_n}". + 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 "{" and "}". Within these, + individual maps are separated with comma or whitespace. - proxy Postfix proxymap(8) client for shared access to Postfix + proxy Postfix proxymap(8) client for shared access to Postfix databases. The table name syntax is type:name. randmap (read-only) - An in-memory table that performs random selection. Exam- - ple: "randmap:!result_1! ... !result_n". 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. Exam‐ + ple: "randmap:{result_1, ..., result_n}". 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. regexp (read-only) - A lookup table based on regular expressions. The file + A lookup table based on regular expressions. The file format is described in regexp_table(5). - sdbm An indexed file type based on hashing. Available on sys- + sdbm An indexed file type based on hashing. Available on sys‐ tems with support for SDBM databases. socketmap (read-only) - Sendmail-style socketmap client. The table name is - inet:host:port:name for a TCP/IP server, or unix:path- - name:name for a UNIX-domain server. This is described in + Sendmail-style socketmap client. The table name is + inet:host:port:name for a TCP/IP server, or unix:path‐ + name:name for a UNIX-domain server. This is described in socketmap_table(5). sqlite (read-only) SQLite database. This is described in sqlite_table(5). static (read-only) - A table that always returns its name as lookup result. - For example, static:foobar always returns the string foo- + A table that always returns its name as lookup result. + For example, static:foobar always returns the string foo‐‐ bar as lookup result. tcp (read-only) TCP/IP client. The protocol is described in tcp_table(5). texthash (read-only) - Produces similar results as hash: files, except that you - don't need to run the postmap(1) command before you can - use the file, and that it does not detect changes after + Produces similar results as hash: files, except that you + don't need to run the postmap(1) command before you can + use the file, and that it does not detect changes after the file is read. unix (read-only) - A limited view of the UNIX authentication database. The + A limited view of the UNIX authentication database. The following tables are implemented: unix:passwd.byname - 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 passwd(5) format. unix:group.byname 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 group(5) format. - Other table types may exist depending on how Postfix was built. + Other table types may exist depending on how Postfix was built. - -M Show master.cf file contents instead of main.cf file contents. + -M Show master.cf file contents instead of main.cf file contents. Specify -Mf to fold long lines for human readability. - Specify zero or more arguments, each with a service-name or ser- - vice-name/service-type pair, where service-name is the first - field of a master.cf entry and service-type is one of (inet, + Specify zero or more arguments, each with a service-name or ser‐ + vice-name/service-type pair, where service-name is the first + field of a master.cf entry and service-type is one of (inet, unix, fifo, or pass). - If service-name or service-name/service-type is specified, only - the matching master.cf entries will be output. For example, - "postconf -Mf smtp" will output all services named "smtp", and - "postconf -Mf smtp/inet" will output only the smtp service that - listens on the network. Trailing service type fields that are + If service-name or service-name/service-type is specified, only + the matching master.cf entries will be output. For example, + "postconf -Mf smtp" will output all services named "smtp", and + "postconf -Mf smtp/inet" 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 "name.type" to "name/type", and "*" wildcard + was changed from "name.type" to "name/type", and "*" wildcard support was added with Postfix 2.11. -n Show only configuration parameters that have explicit name=value - settings in main.cf. Specify -nf to fold long lines for human + settings in main.cf. Specify -nf to fold long lines for human readability (Postfix 2.9 and later). -o name=value @@ -354,81 +353,81 @@ POSTCONF(1) POSTCONF(1) This feature is available with Postfix 2.11 and later. - -P Show master.cf service parameter settings (by default all ser- - vices and all parameters). formatted as one "ser- - vice/type/parameter=value" per line. Specify -Pf to fold long + -P Show master.cf service parameter settings (by default all ser‐ + vices and all parameters). formatted as one "ser‐ + vice/type/parameter=value" per line. Specify -Pf to fold long lines. - Specify one or more "service/type/parameter" instances on the - postconf(1) command line to limit the output to parameters of - interest. Trailing parameter name or service type fields that + Specify one or more "service/type/parameter" instances on the + postconf(1) 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. -t [template_file] - 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 $name expressions. To override the built-in templates, specify a template file name - at the end of the postconf(1) command line, or specify a file + at the end of the postconf(1) command line, or specify a file name in main.cf with the bounce_template_file parameter. - To force selection of the built-in templates, specify an empty - template file name on the postconf(1) command line (in shell + To force selection of the built-in templates, specify an empty + template file name on the postconf(1) command line (in shell language: ""). This feature is available with Postfix 2.3 and later. - -v Enable verbose logging for debugging purposes. Multiple -v + -v Enable verbose logging for debugging purposes. Multiple -v options make the software increasingly verbose. - -x Expand $name in main.cf or master.cf parameter values. The + -x Expand $name in main.cf or master.cf parameter values. The expansion is recursive. This feature is available with Postfix 2.10 and later. - -X Edit the main.cf configuration file, and remove the parameters - named on the postconf(1) command line. Specify a list of param- + -X Edit the main.cf configuration file, and remove the parameters + named on the postconf(1) command line. Specify a list of param‐ eter names, not "name=value" pairs. - With -M, edit the master.cf configuration file, and remove one - or more service entries as specified with "service/type" on the + With -M, edit the master.cf configuration file, and remove one + or more service entries as specified with "service/type" on the postconf(1) command line. - With -P, edit the master.cf configuration file, and remove one + With -P, edit the master.cf configuration file, and remove one or more service parameter settings (-o parameter=value settings) - as specied with "service/type/parameter" on the postconf(1) com- + as specied with "service/type/parameter" on the postconf(1) 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 postconf(1) command line. - There is no postconf(1) command to perform the reverse opera- + There is no postconf(1) 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. - -# Edit the main.cf configuration file, and comment out the parame- - ters named on the postconf(1) command line, so that those param- - eters revert to their default values. Specify a list of parame- + -# Edit the main.cf configuration file, and comment out the parame‐ + ters named on the postconf(1) command line, so that those param‐ + eters revert to their default values. Specify a list of parame‐ ter names, not "name=value" pairs. - With -M, edit the master.cf configuration file, and comment out - one or more service entries as specified with "service/type" on + With -M, edit the master.cf configuration file, and comment out + one or more service entries as specified with "service/type" on the postconf(1) 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 postconf(1) command line. - There is no postconf(1) command to perform the reverse opera- + There is no postconf(1) 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. DIAGNOSTICS @@ -439,18 +438,18 @@ POSTCONF(1) POSTCONF(1) Directory with Postfix configuration files. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant to this pro- + The following main.cf parameters are especially relevant to this pro‐ gram. - The text below provides only a parameter summary. See postconf(5) for + The text below provides only a parameter summary. See postconf(5) for more details including examples. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and master.cf con- + The default location of the Postfix main.cf and master.cf con‐ figuration files. bounce_template_file (empty) - Pathname of a configuration file with bounce message templates. + Pathname of a configuration file with bounce message templates. FILES /etc/postfix/main.cf, Postfix configuration parameters diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index a09a1cc39..c5d32e726 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -42,19 +42,40 @@ that starts with whitespace continues a logical line.

  • When the same parameter is defined multiple times, only diff --git a/postfix/html/proxymap.8.html b/postfix/html/proxymap.8.html index ebe62e993..ceb127769 100644 --- a/postfix/html/proxymap.8.html +++ b/postfix/html/proxymap.8.html @@ -14,11 +14,11 @@ PROXYMAP(8) PROXYMAP(8) DESCRIPTION The proxymap(8) 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: proxymap and proxywrite, respectively. The purpose of these services is: - o 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) local_recipient_maps = proxy:unix:passwd.byname $alias_maps - o 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. - o 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) open maptype:mapname flags Open the table with type maptype and name mapname, as controlled by flags. The reply includes the maptype dependent flags (to - distinguish a fixed string table from a regular expression ta- + distinguish a fixed string table from a regular expression ta‐ ble). lookup maptype:mapname flags key @@ -101,7 +101,7 @@ PROXYMAP(8) PROXYMAP(8) The proxymap(8) server opens only tables that are approved via the proxy_read_maps or proxy_write_maps 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 proxymap(8) 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 main.cf 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 data_directory. @@ -131,7 +131,7 @@ PROXYMAP(8) PROXYMAP(8) The proxymap(8) 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. config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and master.cf con- + The default location of the Postfix main.cf and master.cf con‐ figuration files. data_directory (see 'postconf -d' output) diff --git a/postfix/html/qmqp-sink.1.html b/postfix/html/qmqp-sink.1.html index 17077e496..a72ead8ed 100644 --- a/postfix/html/qmqp-sink.1.html +++ b/postfix/html/qmqp-sink.1.html @@ -16,13 +16,13 @@ QMQP-SINK(1) QMQP-SINK(1) DESCRIPTION qmqp-sink 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 qmqp- source(1) 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) -c Display a running counter that is updated whenever a delivery is completed. - -v Increase verbosity. Specify -v -v to see some of the QMQP con- + -v Increase verbosity. Specify -v -v to see some of the QMQP con‐ versation. -x time diff --git a/postfix/html/smtp-sink.1.html b/postfix/html/smtp-sink.1.html index 8c676de7c..cf6fa6ebc 100644 --- a/postfix/html/smtp-sink.1.html +++ b/postfix/html/smtp-sink.1.html @@ -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. - smtp-sink may also be configured to capture each mail delivery transac- + smtp-sink 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 smtp-source(1) 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) -a Do not announce SASL authentication support. -A delay - Wait delay seconds after responding to DATA, then abort prema- + Wait delay 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 hard-bounce-reply for hard reject responses. The default reply is "500 5.3.0 Error: command failed". - -c Display running counters that are updated whenever an SMTP ses- + -c Display running counters that are updated whenever an SMTP ses‐ sion ends, a QUIT command is executed, or when "." is received. -C Disable XCLIENT support. @@ -67,7 +67,7 @@ SMTP-SINK(1) SMTP-SINK(1) is created by expanding the dump-template 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) -m count (default: 256) An upper bound on the maximal number of simultaneous connections - that smtp-sink will handle. This prevents the process from run- + that smtp-sink 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 -e. -q command,command,... - 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. -Q command,command,... - 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), \ddd (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 > 0 and < 65536. -u username - 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 -R 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: - o The optional string specified with the -S option. + · The optional string specified with the -S option. - o The smtp-sink generated headers as documented below. + · The smtp-sink generated headers as documented below. - o The message header and body as received from the SMTP client. + · The message header and body as received from the SMTP client. - o An empty line. + · An empty line. The format of the smtp-sink generated headers is as follows: @@ -233,11 +233,11 @@ SMTP-SINK(1) SMTP-SINK(1) X-Helo-Args: text 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. X-Mail-Args: text - 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. X-Rcpt-Args: text @@ -246,8 +246,8 @@ SMTP-SINK(1) SMTP-SINK(1) are in the order as sent by the client. Received: text - 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 smtp-sink, and is formatted as follows: from helo ([addr]) @@ -257,7 +257,7 @@ SMTP-SINK(1) SMTP-SINK(1) by host (smtp-sink) with proto id random; The hostname specified with the -h option, the client - protocol (see X-Client-Proto above), and the pseudo-ran- + protocol (see X-Client-Proto above), and the pseudo-ran‐ dom portion of the per-message capture file name. time-stamp diff --git a/postfix/man/man1/postconf.1 b/postfix/man/man1/postconf.1 index 68f9bab24..a2db7755a 100644 --- a/postfix/man/man1/postconf.1 +++ b/postfix/man/man1/postconf.1 @@ -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). diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index d19ce122d..0cb0a6344 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -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 diff --git a/postfix/proto/DATABASE_README.html b/postfix/proto/DATABASE_README.html index 3ac27ec3d..847ad01b9 100644 --- a/postfix/proto/DATABASE_README.html +++ b/postfix/proto/DATABASE_README.html @@ -365,14 +365,14 @@ file.

    pipemap (read-only)
    A pipeline of lookup tables. Example: -"pipemap:!type1:name1! ... -!typen:namen". Each "pipemap:" query is +"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 first and last characters of the "pipemap:" +table name must be "{" and "}". Within these, individual maps are +separated with comma or whitespace.
    pgsql (read-only)
    @@ -388,11 +388,11 @@ databases. The lookup table name syntax is "proxy:type:table".
    randmap (read-only)
    An in-memory table that performs random selection. Example: -"randmap:!result1! ... !resultn". +"randmap:{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).
    +The first and last characters of the "randmap:" table name must be +"{" and "}". Within these, individual maps are separated with comma +or whitespace.
    regexp (read-only)
    diff --git a/postfix/proto/postconf.html.prolog b/postfix/proto/postconf.html.prolog index 3963e7e6d..a1661f4cd 100644 --- a/postfix/proto/postconf.html.prolog +++ b/postfix/proto/postconf.html.prolog @@ -42,19 +42,40 @@ that starts with whitespace continues a logical line.

      -
    • 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, except where noted. +An undefined parameter value is replaced with the empty value.

      -
    • 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. +

      -
    • 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.

      + +
    • 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. +

      + +
    • 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.

      + +
    • Each "value" is subject to recursive named parameter and +logical expression evaluation, except where noted.

      + +
    • Whitespace before or after each "{value}" is ignored.

    • Specify "$$" to produce a single "$" character.

      +
    • The legacy form "$(...)" is equivalent to the preferred +form "${...}".

      +
  • When the same parameter is defined multiple times, only diff --git a/postfix/proto/postconf.man.prolog b/postfix/proto/postconf.man.prolog index 170838fe6..ac915824c 100644 --- a/postfix/proto/postconf.man.prolog +++ b/postfix/proto/postconf.man.prolog @@ -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 diff --git a/postfix/src/bounce/bounce_template.h b/postfix/src/bounce/bounce_template.h index fe6c65524..2927d1328 100644 --- a/postfix/src/bounce/bounce_template.h +++ b/postfix/src/bounce/bounce_template.h @@ -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 *); diff --git a/postfix/src/cleanup/cleanup_message.c b/postfix/src/cleanup/cleanup_message.c index aa1fe6c5b..7860df213 100644 --- a/postfix/src/cleanup/cleanup_message.c +++ b/postfix/src/cleanup/cleanup_message.c @@ -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-"; diff --git a/postfix/src/global/data_redirect.c b/postfix/src/global/data_redirect.c index 7f497ca49..1a8fe0f9c 100644 --- a/postfix/src/global/data_redirect.c +++ b/postfix/src/global/data_redirect.c @@ -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); diff --git a/postfix/src/global/dict_memcache.c b/postfix/src/global/dict_memcache.c index 161e0c880..0b87dfb0d 100644 --- a/postfix/src/global/dict_memcache.c +++ b/postfix/src/global/dict_memcache.c @@ -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) { diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index adb59662a..c4bfa30d8 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -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 diff --git a/postfix/src/global/maps.c b/postfix/src/global/maps.c index 391a80019..b0e0b9b5b 100644 --- a/postfix/src/global/maps.c +++ b/postfix/src/global/maps.c @@ -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)); diff --git a/postfix/src/global/memcache_proto.h b/postfix/src/global/memcache_proto.h index 88a1d191f..e3f850188 100644 --- a/postfix/src/global/memcache_proto.h +++ b/postfix/src/global/memcache_proto.h @@ -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); diff --git a/postfix/src/global/server_acl.c b/postfix/src/global/server_acl.c index 3855c6fe0..7717f02d0 100644 --- a/postfix/src/global/server_acl.c +++ b/postfix/src/global/server_acl.c @@ -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" diff --git a/postfix/src/postconf/Makefile.in b/postfix/src/postconf/Makefile.in index 45d894760..b7eefb516 100644 --- a/postfix/src/postconf/Makefile.in +++ b/postfix/src/postconf/Makefile.in @@ -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 diff --git a/postfix/src/postconf/postconf.c b/postfix/src/postconf/postconf.c index c5251b8fc..016304f1a 100644 --- a/postfix/src/postconf/postconf.c +++ b/postfix/src/postconf/postconf.c @@ -268,25 +268,26 @@ /* 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). diff --git a/postfix/src/postconf/postconf.h b/postfix/src/postconf/postconf.h index f174d164c..178a237f9 100644 --- a/postfix/src/postconf/postconf.h +++ b/postfix/src/postconf/postconf.h @@ -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. diff --git a/postfix/src/postconf/postconf_builtin.c b/postfix/src/postconf/postconf_builtin.c index ba53d050d..b07f5bb64 100644 --- a/postfix/src/postconf/postconf_builtin.c +++ b/postfix/src/postconf/postconf_builtin.c @@ -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)); } diff --git a/postfix/src/postconf/postconf_dbms.c b/postfix/src/postconf/postconf_dbms.c index 883d4368c..a618fdac7 100644 --- a/postfix/src/postconf/postconf_dbms.c +++ b/postfix/src/postconf/postconf_dbms.c @@ -54,10 +54,12 @@ #include #include #include +#include /* Global library. */ #include +#include #include #include #include @@ -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. + * Naive parsing. We don't really know if this substring specifies a + * database or some other text. */ - 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. - */ - 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), - PCF_PARAM_FLAG_DBMS | PCF_PARAM_FLAG_USER, - local_scope); + 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); + } + 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 index 000000000..362fd167a --- /dev/null +++ b/postfix/src/postconf/test57.ref @@ -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 index 000000000..8de9edc81 --- /dev/null +++ b/postfix/src/postconf/test58.ref @@ -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 diff --git a/postfix/src/proxymap/proxymap.c b/postfix/src/proxymap/proxymap.c index 036a494ab..ec745e094 100644 --- a/postfix/src/proxymap/proxymap.c +++ b/postfix/src/proxymap/proxymap.c @@ -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 { diff --git a/postfix/src/smtpd/smtpd_check.c b/postfix/src/smtpd/smtpd_check.c index 302015e70..42db60571 100644 --- a/postfix/src/smtpd/smtpd_check.c +++ b/postfix/src/smtpd/smtpd_check.c @@ -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); } diff --git a/postfix/src/smtpstone/qmqp-sink.c b/postfix/src/smtpstone/qmqp-sink.c index 5dac7bca3..e65490e45 100644 --- a/postfix/src/smtpstone/qmqp-sink.c +++ b/postfix/src/smtpstone/qmqp-sink.c @@ -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); diff --git a/postfix/src/smtpstone/smtp-sink.c b/postfix/src/smtpstone/smtp-sink.c index 617fbf915..120cef10a 100644 --- a/postfix/src/smtpstone/smtp-sink.c +++ b/postfix/src/smtpstone/smtp-sink.c @@ -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 diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index 4dbe47db4..12e829b7c 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -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 diff --git a/postfix/src/util/argv.h b/postfix/src/util/argv.h index fda35c311..f7ce69956 100644 --- a/postfix/src/util/argv.h +++ b/postfix/src/util/argv.h @@ -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 index 000000000..3900ee117 --- /dev/null +++ b/postfix/src/util/argv_splitq.c @@ -0,0 +1,118 @@ +/*++ +/* NAME +/* argv_splitq 3 +/* SUMMARY +/* string array utilities +/* SYNOPSIS +/* #include +/* +/* 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 +#include + +/* 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 index 000000000..6ff97eb72 --- /dev/null +++ b/postfix/src/util/balpar.c @@ -0,0 +1,56 @@ +/*++ +/* NAME +/* balpar 3 +/* SUMMARY +/* determine length of string in parentheses +/* SYNOPSIS +/* #include +/* +/* 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 + +/* Utility library. */ + +#include + +/* 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); +} diff --git a/postfix/src/util/dict.h b/postfix/src/util/dict.h index 70b7be5ff..353df5baf 100644 --- a/postfix/src/util/dict.h +++ b/postfix/src/util/dict.h @@ -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. diff --git a/postfix/src/util/dict_open.c b/postfix/src/util/dict_open.c index 48ae556d8..b50d49dd5 100644 --- a/postfix/src/util/dict_open.c +++ b/postfix/src/util/dict_open.c @@ -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. */ diff --git a/postfix/src/util/dict_pipe.c b/postfix/src/util/dict_pipe.c index 8102cc936..e7e56787e 100644 --- a/postfix/src/util/dict_pipe.c +++ b/postfix/src/util/dict_pipe.c @@ -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); diff --git a/postfix/src/util/dict_pipe.h b/postfix/src/util/dict_pipe.h index fb0578e1f..8d03009d5 100644 --- a/postfix/src/util/dict_pipe.h +++ b/postfix/src/util/dict_pipe.h @@ -21,10 +21,6 @@ */ #define DICT_TYPE_PIPE "pipemap" -#ifdef SNAPSHOT -#define DICT_TYPE_PIPE_LEGACY "pipeline" -#endif - extern DICT *dict_pipe_open(const char *, int, int); /* LICENSE diff --git a/postfix/src/util/dict_random.c b/postfix/src/util/dict_random.c index af975bd34..0ee4a8365 100644 --- a/postfix/src/util/dict_random.c +++ b/postfix/src/util/dict_random.c @@ -12,14 +12,14 @@ /* 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 #include #include +#include #include /* 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)); } diff --git a/postfix/src/util/dict_random.h b/postfix/src/util/dict_random.h index fd16e9570..4aa08b140 100644 --- a/postfix/src/util/dict_random.h +++ b/postfix/src/util/dict_random.h @@ -21,10 +21,6 @@ */ #define DICT_TYPE_RANDOM "randmap" -#ifdef SNAPSHOT -#define DICT_TYPE_RANDOM_LEGACY "random" -#endif - extern DICT *dict_random_open(const char *, int, int); /* LICENSE diff --git a/postfix/src/util/dict_test.c b/postfix/src/util/dict_test.c index f988f3276..2fedcb60f 100644 --- a/postfix/src/util/dict_test.c +++ b/postfix/src/util/dict_test.c @@ -24,9 +24,6 @@ #include #include -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); diff --git a/postfix/src/util/mac_expand.c b/postfix/src/util/mac_expand.c index f1142d889..f0934f94d 100644 --- a/postfix/src/util/mac_expand.c +++ b/postfix/src/util/mac_expand.c @@ -14,46 +14,70 @@ /* 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 @@ -73,14 +97,14 @@ /* 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 @@ -106,6 +130,7 @@ #include #include #include +#include #include #include @@ -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; /* diff --git a/postfix/src/util/mac_expand.in b/postfix/src/util/mac_expand.in index f92ad2c89..deff16b7c 100644 --- a/postfix/src/util/mac_expand.in +++ b/postfix/src/util/mac_expand.in @@ -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} == {}} diff --git a/postfix/src/util/mac_expand.ref b/postfix/src/util/mac_expand.ref index d76d3877a..1489a1616 100644 --- a/postfix/src/util/mac_expand.ref +++ b/postfix/src/util/mac_expand.ref @@ -1,5 +1,84 @@ << name1 = name1-value -<< name2 = +<< +<< $(name1 +unknown: warning: truncated macro reference: "$(name1" +stat=1 result= +<< $(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= +<< + +<< name1 = name1-value << << ${name1?name 1 defined, |$name1|$name2|} stat=2 result=name 1 defined, |name1-value|| @@ -11,23 +90,47 @@ stat=0 result= 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 " -stat=1 result= -<< - -<< name2 = name2-value -<< -<< ${name1?name 1 defined, |$name1|$name2|} +<< ${{$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=2 result=name 1 undefined, ||name2-value| -<< ${name2?name 2 defined, |$name1|$name2|} -stat=2 result=name 2 defined, ||name2-value| -<< ${name2:name 2 undefined, |$name1|$name2|} +<< ${{$name1} == {}?{name 1 undefined, |$name1|$name2|}} stat=0 result= -<< |$name1|$name2| -stat=2 result=||name2-value| +<< ${{$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 diff --git a/postfix/src/util/match_list.c b/postfix/src/util/match_list.c index ca7091efb..cfd93e655 100644 --- a/postfix/src/util/match_list.c +++ b/postfix/src/util/match_list.c @@ -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); diff --git a/postfix/src/util/msg.h b/postfix/src/util/msg.h index 599c69a57..c69dce14c 100644 --- a/postfix/src/util/msg.h +++ b/postfix/src/util/msg.h @@ -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 diff --git a/postfix/src/util/mystrtok.c b/postfix/src/util/mystrtok.c index a87a58cba..0056f836a 100644 --- a/postfix/src/util/mystrtok.c +++ b/postfix/src/util/mystrtok.c @@ -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); diff --git a/postfix/src/util/stringops.h b/postfix/src/util/stringops.h index 5b30bdaa4..93647a3d2 100644 --- a/postfix/src/util/stringops.h +++ b/postfix/src/util/stringops.h @@ -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 diff --git a/postfix/src/util/vbuf_print.c b/postfix/src/util/vbuf_print.c index 1acda0f89..044fe3371 100644 --- a/postfix/src/util/vbuf_print.c +++ b/postfix/src/util/vbuf_print.c @@ -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);