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/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);