2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 05:38:06 +00:00

postfix-2.12-20140921

This commit is contained in:
Wietse Venema 2014-09-21 00:00:00 -05:00 committed by Viktor Dukhovni
parent 558905a662
commit 098feb387e
54 changed files with 1482 additions and 458 deletions

3
postfix/.indent.pro vendored
View File

@ -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

View File

@ -20345,3 +20345,68 @@ Apologies for any names omitted.
global/deliver_request.h, global/mail_params.h, global/sent.c,
*qmgr/qmgr.c, *qmgr/qmgr_active.c, *qmgr/qmgr_message.c.
20140908-14
Feature: for the first time in 17 years, support for
${name?if-nonempty:if-empty} macro expressions, and for
logical expressions ${logical-expr?if-true:if-false}. In
preparation for configurable message headers and logging.
Files: util/mac_expand.c.
20140914
Bugfix (introduced: 19971026): a zero precision value in
%.*s and $.<digits>s was implemented as if no precision
value was specified, i.e. print the entire string. This was
not harmful, it just looked weird. File: util/vbuf_print.c.
20120917
Feature: RFC 7372 enhanced status code for unknown SMTP
client hostnames. File: smtpd/smtpd_check.c
Bugfix: the accept() calls in test progams escaped attention
when Postfix 2.2 was ported to IPv6. Problem found by Mark
Martinec. Files: smtpstone/smtp-sink.c, smtpstone/qmqp-sink.c.
20140918
Cleanup: log a warning when the cleanup server detects too
many hops. smtpd(8) does not log any of the CLEANUP_STAT_XXX
results. The pickup server logs some because there is no
client to send the problem description to. This logic of
who logs what needs to be revisited. File:
cleanup/cleanup_message.c.
20140919
Usability: randmap and pipemap syntax, for example,
pipemap:{type_1:name_1, ..., type_n:name_n}. This required
small updates to code that parses input into lookup table
names. Files: global/data_redirect.c, global/maps.c,
global/server_acl.c, postconf/postconf.c, postconf/postconf_dbms.c,
postconf/test58.ref, proto/DATABASE_README.html,
proxymap/proxymap.c, smtpd/smtpd_check.c, util/argv.h,
util/balpar.c, util/dict_pipe.c, util/dict_random.c,
util/match_list.c, util/mystrtok.c, util/argv_splitq.c,
util/stringops.h.
Cleanup: added PRINTFLIKE() to enable missing format string
checks. Files: bounce/bounce_template.h, global/memcache_proto.h,
global/dict_memcache, postconf/postconf.h, util/dict.h,
util/msg.h.
20140920
Bugfix (introduced: 20080212): incorrect client name in
reject messages from check_reverse_client_hostname_access
and check_reverse_client_hostname_{a,mx,ns}_access. They
replied with the verified client name, instead of the name
that was rejected. Problem reported by Reindl Harald. File:
smtpd/smtpd_check.c.
20140921
Cleanup: postconf code to determine the default mydomain
value had not evolved since 1997, while the rest of Postfix
changed in 2000. File: postconf/postconf-dbms.c.

View File

@ -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:

View File

@ -41,6 +41,36 @@ Maintainers may also benefit from the makedefs documentation
(mantools/srctoman - makedefs | nroff -man | less) with information
about build options that are not described in the INSTALL instructions.
Major changes with snapshot 20140921
====================================
In preparation for configurable mail headers and logging, new main.cf
support for if-then-else expressions:
${name?{text1}:{text2}}
and for logical expressions:
${{text1}=={text2}?{text3}:{text4}}
${{text1}!={text2}?{text3}:{text4}}
Whitespace before and after {text} is ignored. This can help to
make complex expressions more readable. See the postconf(5) manpage
for further details.
The syntax of pipemap and randmap has improved. Postfix now uses
pipemap:{map1, ..., mapN} and randmap:{result1, ..., resultN}.
The old syntax was just too ugly.
It is expected that usability can be improved elsewhere in Postfix
in a similar manner. For example,
- Milter clients and policy clients with non-default settings:
smtpd_milters = {inet:host:port, timeout=xxx, default_action=yyy}, ...
- Parameter overrides in master.cf with commas and spaces:
-o { parameter = value ... }
Major changes with snapshot 20140801
====================================

View File

@ -8,8 +8,41 @@ Wish list:
Things to do after the stable release:
The pickup daemon logs warnings only when the cleanup daemon
dit not provide a "reason" attribute. Is this logic right?
Make the "relayed after delay" notification conditional on
the presence of the DSN_NOTIFY_DELAY flag.
up-convert myhostname to UTF-8 in MIME boundary strings?
Update postconf to recursively parse legacy-style mapnames
in random:, pipe:, and other multimaps.
Introduce constants to replace all the ad-hoc ", \t\r\n"
etc. for tokenization. That will have to go into an "util"
file because match_strings(3), dict_pipe(3) and dict_random(3)
depend on these definitions.
Make sure that proxy: can handle random:, pipe:, and other
multimaps.
Add a switch to consider postscreen deep protocol tests as
"completed" when receiving "RSET" after "RCPT TO" and the
session has passed all tests up to that point. RSET becomes
like QUIT except perhaps that it does not hang up.
apipe: map, splits results into address lists and performs
lookups for the invidual addresses, converting back and
forth between external and internal forms.
union: map, concatenates results, default separator is ','.
Include <3htPpS5B6bzbcpM@spike.porcupine.org> example with
filter policies for different mail streams. Correction:
filter should be content_filter. Posted Wed, 10 Sep 2014
09:53:52 -0400 (EDT).
Clarify that receive_override_options should not be used
with smtpd_proxy_filter.

View File

@ -365,14 +365,14 @@ file. </dd>
<dt> <b>pipemap</b> (read-only) </dt>
<dd> A pipeline of lookup tables. Example:
"<b><a href="DATABASE_README.html#types">pipemap</a>:</b><i>!type<sub>1</sub>:name<sub>1</sub>! ...
!type<sub>n</sub>:name<sub>n</sub></i>". Each "<a href="DATABASE_README.html#types">pipemap</a>:" query is
"<a href="DATABASE_README.html#types">pipemap</a>:{<i>type<sub>1</sub>:name<sub>1</sub>, ...,
type<sub>n</sub>:name<sub>n</sub></i>}". Each "<a href="DATABASE_README.html#types">pipemap</a>:" query is
given to the first table. Each lookup result becomes the query for
the next table in the pipeline, and the last table produces the
final result. When any table lookup produces no result, the pipeline
produces no result. The first ASCII character after "<a href="DATABASE_README.html#types">pipemap</a>:"
will be used as the separator between the lookup tables that follow
(do not use space, ",", ":" or non-ASCII). </dd>
produces no result. The first and last characters of the "<a href="DATABASE_README.html#types">pipemap</a>:"
table name must be "{" and "}". Within these, individual maps are
separated with comma or whitespace. </dd>
<dt> <b>pgsql</b> (read-only) </dt>
@ -388,11 +388,11 @@ databases. The lookup table name syntax is "<a href="proxymap.8.html">proxy</a>:
<dt> <b>randmap</b> (read-only) </dt>
<dd> An in-memory table that performs random selection. Example:
"<b><a href="DATABASE_README.html#types">randmap</a>:</b><i>!result<sub>1</sub>! ... !result<sub>n</sub></i>".
"<a href="DATABASE_README.html#types">randmap</a>:{<i>result<sub>1</sub>. ..., result<sub>n</sub></i>}".
Each table query returns a random choice from the specified results.
The first ASCII character after "<a href="DATABASE_README.html#types">randmap</a>:" will be used as the
separator between the results that follow (do not use space, ",",
":" or non-ASCII). </dd>
The first and last characters of the "<a href="DATABASE_README.html#types">randmap</a>:" table name must be
"{" and "}". Within these, individual maps are separated with comma
or whitespace. </dd>
<dt> <b>regexp</b> (read-only) </dt>

View File

@ -55,16 +55,16 @@ POSTCONF(1) POSTCONF(1)
<b>postconf -a</b>|<b>-A</b>|<b>-l</b>|<b>-m</b> [<b>-v</b>] [<b>-c</b> <i>config</i><b>_</b><i>dir</i>]
<b>DESCRIPTION</b>
By default, the <a href="postconf.1.html"><b>postconf</b>(1)</a> command displays the values of <a href="postconf.5.html"><b>main.cf</b></a> con-
By default, the <a href="postconf.1.html"><b>postconf</b>(1)</a> command displays the values of <a href="postconf.5.html"><b>main.cf</b></a> con
figuration parameters, and warns about possible mis-typed parameter
names (Postfix 2.9 and later). It can also change <a href="postconf.5.html"><b>main.cf</b></a> configura-
names (Postfix 2.9 and later). It can also change <a href="postconf.5.html"><b>main.cf</b></a> configura
tion parameter values, or display other configuration information about
the Postfix mail system.
Options:
<b>-a</b> List the available SASL server plug-in types. The SASL plug-in
type is selected with the <b><a href="postconf.5.html#smtpd_sasl_type">smtpd_sasl_type</a></b> configuration parame-
type is selected with the <b><a href="postconf.5.html#smtpd_sasl_type">smtpd_sasl_type</a></b> configuration parame
ter by specifying one of the names listed below.
<b>cyrus</b> This server plug-in is available when Postfix is built
@ -78,7 +78,7 @@ POSTCONF(1) POSTCONF(1)
This feature is available with Postfix 2.3 and later.
<b>-A</b> List the available SASL client plug-in types. The SASL plug-in
type is selected with the <b><a href="postconf.5.html#smtp_sasl_type">smtp_sasl_type</a></b> or <b><a href="postconf.5.html#lmtp_sasl_type">lmtp_sasl_type</a></b> con-
type is selected with the <b><a href="postconf.5.html#smtp_sasl_type">smtp_sasl_type</a></b> or <b><a href="postconf.5.html#lmtp_sasl_type">lmtp_sasl_type</a></b> con
figuration parameters by specifying one of the names listed
below.
@ -88,8 +88,8 @@ POSTCONF(1) POSTCONF(1)
This feature is available with Postfix 2.3 and later.
<b>-b</b> [<i>template</i><b>_</b><i>file</i>]
Display the message text that appears at the beginning of deliv-
ery status notification (DSN) messages, replacing $<b>name</b> expres-
Display the message text that appears at the beginning of deliv
ery status notification (DSN) messages, replacing $<b>name</b> expres
sions with actual values as described in <a href="bounce.5.html"><b>bounce</b>(5)</a>.
To override the built-in templates, specify a template file name
@ -125,29 +125,29 @@ POSTCONF(1) POSTCONF(1)
This feature is available with Postfix 2.9 and later.
<b>-d</b> Print <a href="postconf.5.html"><b>main.cf</b></a> default parameter settings instead of actual set-
<b>-d</b> Print <a href="postconf.5.html"><b>main.cf</b></a> default parameter settings instead of actual set
tings. Specify <b>-df</b> to fold long lines for human readability
(Postfix 2.9 and later).
<b>-e</b> Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and update parameter set-
<b>-e</b> Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and update parameter set
tings with the "<i>name=value</i>" pairs on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command
line.
With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and replace one
or more service entries with new values as specified with "<i>ser-</i>
or more service entries with new values as specified with "<i>ser</i>
<i>vice/type=value</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
With <b>-F</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and replace one
or more service fields with new values as specied with "<i>ser-</i>
<i>vice/type/field=value</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line. Cur-
rently, the "command" field contains the command name and com-
or more service fields with new values as specied with "<i>ser</i>
<i>vice/type/field=value</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line. Cur
rently, the "command" field contains the command name and com
mand arguments. this may change in the near future, so that the
"command" field contains only the command name, and a new "argu-
"command" field contains only the command name, and a new "argu
ments" pseudofield contains the command arguments.
With <b>-P</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and add or
update one or more service parameter settings (-o parame-
ter=value settings) with new values as specied with "<i>ser-</i>
update one or more service parameter settings (-o parame
ter=value settings) with new values as specied with "<i>ser</i>
<i>vice/type/parameter=value</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
In all cases the file is copied to a temporary file then renamed
@ -166,8 +166,8 @@ POSTCONF(1) POSTCONF(1)
and all fields), formatted as one "<i>service/type/field=value</i>" per
line. Specify <b>-Ff</b> to fold long lines.
Specify one or more "<i>service/type/field</i>" instances on the <a href="postconf.1.html"><b>post-</b></a>
<a href="postconf.1.html"><b>conf</b>(1)</a> command line to limit the output to fields of interest.
Specify one or more "<i>service/type/field</i>" instances on the <b>post</b>
<b>conf</b>(1) command line to limit the output to fields of interest.
Trailing parameter name or service type fields that are omitted
will be handled as "*" wildcard fields.
@ -176,7 +176,7 @@ POSTCONF(1) POSTCONF(1)
<b>-h</b> Show parameter or attribute values without the "<i>name</i> = " label
that normally precedes the value.
<b>-l</b> List the names of all supported mailbox locking methods. Post-
<b>-l</b> List the names of all supported mailbox locking methods. Post
fix supports the following methods:
<b>flock</b> A kernel-based advisory locking method for local files
@ -188,21 +188,21 @@ POSTCONF(1) POSTCONF(1)
<b>dotlock</b>
An application-level locking method. An application locks
a file named <i>filename</i> by creating a file named <i>file-</i>
a file named <i>filename</i> by creating a file named <i>file</i>
<i>name</i><b>.lock</b>. The application is expected to remove its own
lock file, as well as stale lock files that were left
behind after abnormal program termination.
<b>-m</b> List the names of all supported lookup table types. In Postfix
configuration files, lookup tables are specified as <i>type</i><b>:</b><i>name</i>,
where <i>type</i> is one of the types listed below. The table <i>name</i> syn-
tax depends on the lookup table type as described in the <a href="DATABASE_README.html">DATA</a>-
<a href="DATABASE_README.html">BASE_README</a> document.
where <i>type</i> is one of the types listed below. The table <i>name</i> syn
tax depends on the lookup table type as described in the DATA
<a href="BASE_README.html">BASE_README</a> document.
<b>btree</b> A sorted, balanced tree structure. Available on systems
with support for Berkeley DB databases.
<b>cdb</b> A read-optimized structure with no support for incremen-
<b>cdb</b> A read-optimized structure with no support for incremen
tal updates. Available on systems with support for CDB
databases.
@ -210,19 +210,19 @@ POSTCONF(1) POSTCONF(1)
Domain Routing (CIDR) patterns. This is described in
<a href="cidr_table.5.html"><b>cidr_table</b>(5)</a>.
<b>dbm</b> An indexed file type based on hashing. Available on sys-
<b>dbm</b> An indexed file type based on hashing. Available on sys
tems with support for DBM databases.
<b>environ</b>
The UNIX process environment array. The lookup key is the
variable name. Originally implemented for testing, some-
variable name. Originally implemented for testing, some
one may find this useful someday.
<b>fail</b> A table that reliably fails all requests. The lookup ta-
ble name is used for logging. This table exists to sim-
<b>fail</b> A table that reliably fails all requests. The lookup ta
ble name is used for logging. This table exists to sim
plify Postfix error tests.
<b>hash</b> An indexed file type based on hashing. Available on sys-
<b>hash</b> An indexed file type based on hashing. Available on sys
tems with support for Berkeley DB databases.
<b>internal</b>
@ -230,23 +230,23 @@ POSTCONF(1) POSTCONF(1)
when a process terminates.
<b>lmdb</b> OpenLDAP LMDB database (a memory-mapped, persistent
file). Available on systems with support for LMDB data-
file). Available on systems with support for LMDB data
bases. This is described in <a href="lmdb_table.5.html"><b>lmdb_table</b>(5)</a>.
<b>ldap</b> (read-only)
LDAP database client. This is described in <a href="ldap_table.5.html"><b>ldap_table</b>(5)</a>.
<b>memcache</b>
Memcache database client. This is described in <a href="memcache_table.5.html"><b>mem-</b></a>
<a href="memcache_table.5.html"><b>cache_table</b>(5)</a>.
Memcache database client. This is described in <b>mem</b>
<b>cache_table</b>(5).
<b>mysql</b> (read-only)
MySQL database client. Available on systems with support
for MySQL databases. This is described in <a href="mysql_table.5.html"><b>mysql_ta-</b></a>
<a href="mysql_table.5.html"><b>ble</b>(5)</a>.
for MySQL databases. This is described in <b>mysql_ta</b>
<b>ble</b>(5).
<b>pcre</b> (read-only)
A lookup table based on Perl Compatible Regular Expres-
A lookup table based on Perl Compatible Regular Expres
sions. The file format is described in <a href="pcre_table.5.html"><b>pcre_table</b>(5)</a>.
<b>pgsql</b> (read-only)
@ -254,95 +254,94 @@ POSTCONF(1) POSTCONF(1)
<a href="pgsql_table.5.html"><b>pgsql_table</b>(5)</a>.
<b>pipemap</b> (read-only)
A pipeline of lookup tables. Example:
"<b><a href="DATABASE_README.html#types">pipemap</a>:</b><i>!type</i><b>_</b><i>1:name</i><b>_</b><i>1! ... !type</i><b>_</b><i>n:name</i><b>_</b><i>n</i>". Each
"<a href="DATABASE_README.html#types">pipemap</a>:" query is given to the first table. Each
A lookup table that constructs a pipeline of tables.
Example: "<b><a href="DATABASE_README.html#types">pipemap</a>:{</b><i>type</i><b>_</b><i>1:name</i><b>_</b><i>1, ..., type</i><b>_</b><i>n:name</i><b>_</b><i>n</i><b>}</b>".
Each "<a href="DATABASE_README.html#types">pipemap</a>:" query is given to the first table. Each
lookup result becomes the query for the next table in the
pipeline, and the last table produces the final result.
When any table lookup produces no result, the pipeline
produces no result. The first ASCII character after
"<a href="DATABASE_README.html#types">pipemap</a>:" will be used as the separator between the
lookup tables that follow (do not use space, ",", ":" or
non-ASCII).
produces no result. The first and last characters of the
"<a href="DATABASE_README.html#types">pipemap</a>:" table name must be "<b>{</b>" and "<b>}</b>". Within these,
individual maps are separated with comma or whitespace.
<b>proxy</b> Postfix <a href="proxymap.8.html"><b>proxymap</b>(8)</a> client for shared access to Postfix
<b>proxy</b> Postfix <a href="proxymap.8.html"><b>proxymap</b>(8)</a> client for shared access to Postfix
databases. The table name syntax is <i>type</i><b>:</b><i>name</i>.
<b>randmap</b> (read-only)
An in-memory table that performs random selection. Exam-
ple: "<b><a href="DATABASE_README.html#types">randmap</a>:</b><i>!result</i><b>_</b><i>1! ... !result</i><b>_</b><i>n</i>". Each table query
returns a random choice from the specified results. The
first ASCII character after "<a href="DATABASE_README.html#types">randmap</a>:" will be used as
the separator between the results that follow (do not use
space, ",", ":" or non-ASCII).
An in-memory table that performs random selection. Exam
ple: "<b><a href="DATABASE_README.html#types">randmap</a>:{</b><i>result</i><b>_</b><i>1, ..., result</i><b>_</b><i>n</i><b>}</b>". Each table
query returns a random choice from the specified results.
The first and last characters of the "<a href="DATABASE_README.html#types">randmap</a>:" table
name must be "<b>{</b>" and "<b>}</b>". Within these, individual maps
are separated with comma or whitespace.
<b>regexp</b> (read-only)
A lookup table based on regular expressions. The file
A lookup table based on regular expressions. The file
format is described in <a href="regexp_table.5.html"><b>regexp_table</b>(5)</a>.
<b>sdbm</b> An indexed file type based on hashing. Available on sys-
<b>sdbm</b> An indexed file type based on hashing. Available on sys
tems with support for SDBM databases.
<b>socketmap</b> (read-only)
Sendmail-style socketmap client. The table name is
<b>inet</b>:<i>host</i>:<i>port</i>:<i>name</i> for a TCP/IP server, or <b>unix</b>:<i>path-</i>
<i>name</i>:<i>name</i> for a UNIX-domain server. This is described in
Sendmail-style socketmap client. The table name is
<b>inet</b>:<i>host</i>:<i>port</i>:<i>name</i> for a TCP/IP server, or <b>unix</b>:<i>path</i>
<i>name</i>:<i>name</i> for a UNIX-domain server. This is described in
<a href="socketmap_table.5.html"><b>socketmap_table</b>(5)</a>.
<b>sqlite</b> (read-only)
SQLite database. This is described in <a href="sqlite_table.5.html"><b>sqlite_table</b>(5)</a>.
<b>static</b> (read-only)
A table that always returns its name as lookup result.
For example, <b><a href="DATABASE_README.html#types">static</a>:foobar</b> always returns the string <b>foo-</b>
A table that always returns its name as lookup result.
For example, <b><a href="DATABASE_README.html#types">static</a>:foobar</b> always returns the string <b>foo</b>
<b>bar</b> as lookup result.
<b>tcp</b> (read-only)
TCP/IP client. The protocol is described in <a href="tcp_table.5.html"><b>tcp_table</b>(5)</a>.
<b>texthash</b> (read-only)
Produces similar results as <a href="DATABASE_README.html#types">hash</a>: files, except that you
don't need to run the <a href="postmap.1.html"><b>postmap</b>(1)</a> command before you can
use the file, and that it does not detect changes after
Produces similar results as <a href="DATABASE_README.html#types">hash</a>: files, except that you
don't need to run the <a href="postmap.1.html"><b>postmap</b>(1)</a> command before you can
use the file, and that it does not detect changes after
the file is read.
<b>unix</b> (read-only)
A limited view of the UNIX authentication database. The
A limited view of the UNIX authentication database. The
following tables are implemented:
<b>unix:passwd.byname</b>
The table is the UNIX password database. The key
is a login name. The result is a password file
The table is the UNIX password database. The key
is a login name. The result is a password file
entry in <b>passwd</b>(5) format.
<b>unix:group.byname</b>
The table is the UNIX group database. The key is a
group name. The result is a group file entry in
group name. The result is a group file entry in
<b>group</b>(5) format.
Other table types may exist depending on how Postfix was built.
Other table types may exist depending on how Postfix was built.
<b>-M</b> Show <a href="master.5.html"><b>master.cf</b></a> file contents instead of <a href="postconf.5.html"><b>main.cf</b></a> file contents.
<b>-M</b> Show <a href="master.5.html"><b>master.cf</b></a> file contents instead of <a href="postconf.5.html"><b>main.cf</b></a> file contents.
Specify <b>-Mf</b> to fold long lines for human readability.
Specify zero or more arguments, each with a <i>service-name</i> or <i>ser-</i>
<i>vice-name/service-type</i> pair, where <i>service-name</i> is the first
field of a <a href="master.5.html">master.cf</a> entry and <i>service-type</i> is one of (<b>inet</b>,
Specify zero or more arguments, each with a <i>service-name</i> or <i>ser</i>
<i>vice-name/service-type</i> pair, where <i>service-name</i> is the first
field of a <a href="master.5.html">master.cf</a> entry and <i>service-type</i> is one of (<b>inet</b>,
<b>unix</b>, <b>fifo</b>, or <b>pass</b>).
If <i>service-name</i> or <i>service-name/service-type</i> is specified, only
the matching <a href="master.5.html">master.cf</a> entries will be output. For example,
"<b>postconf -Mf smtp</b>" will output all services named "smtp", and
"<b>postconf -Mf smtp/inet</b>" will output only the smtp service that
listens on the network. Trailing service type fields that are
If <i>service-name</i> or <i>service-name/service-type</i> is specified, only
the matching <a href="master.5.html">master.cf</a> entries will be output. For example,
"<b>postconf -Mf smtp</b>" will output all services named "smtp", and
"<b>postconf -Mf smtp/inet</b>" will output only the smtp service that
listens on the network. Trailing service type fields that are
omitted will be handled as "*" wildcard fields.
This feature is available with Postfix 2.9 and later. The syntax
was changed from "<i>name.type</i>" to "<i>name/type</i>", and "*" wildcard
was changed from "<i>name.type</i>" to "<i>name/type</i>", and "*" wildcard
support was added with Postfix 2.11.
<b>-n</b> Show only configuration parameters that have explicit <i>name=value</i>
settings in <a href="postconf.5.html"><b>main.cf</b></a>. Specify <b>-nf</b> to fold long lines for human
settings in <a href="postconf.5.html"><b>main.cf</b></a>. Specify <b>-nf</b> to fold long lines for human
readability (Postfix 2.9 and later).
<b>-o</b> <i>name=value</i>
@ -354,81 +353,81 @@ POSTCONF(1) POSTCONF(1)
This feature is available with Postfix 2.11 and later.
<b>-P</b> Show <a href="master.5.html"><b>master.cf</b></a> service parameter settings (by default all ser-
vices and all parameters). formatted as one "<i>ser-</i>
<i>vice/type/parameter=value</i>" per line. Specify <b>-Pf</b> to fold long
<b>-P</b> Show <a href="master.5.html"><b>master.cf</b></a> service parameter settings (by default all ser
vices and all parameters). formatted as one "<i>ser</i>
<i>vice/type/parameter=value</i>" per line. Specify <b>-Pf</b> to fold long
lines.
Specify one or more "<i>service/type/parameter</i>" instances on the
<a href="postconf.1.html"><b>postconf</b>(1)</a> command line to limit the output to parameters of
interest. Trailing parameter name or service type fields that
Specify one or more "<i>service/type/parameter</i>" instances on the
<a href="postconf.1.html"><b>postconf</b>(1)</a> command line to limit the output to parameters of
interest. Trailing parameter name or service type fields that
are omitted will be handled as "*" wildcard fields.
This feature is available with Postfix 2.11 and later.
<b>-t</b> [<i>template</i><b>_</b><i>file</i>]
Display the templates for text that appears at the beginning of
delivery status notification (DSN) messages, without expanding
Display the templates for text that appears at the beginning of
delivery status notification (DSN) messages, without expanding
$<b>name</b> expressions.
To override the built-in templates, specify a template file name
at the end of the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line, or specify a file
at the end of the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line, or specify a file
name in <a href="postconf.5.html"><b>main.cf</b></a> with the <b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a></b> parameter.
To force selection of the built-in templates, specify an empty
template file name on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line (in shell
To force selection of the built-in templates, specify an empty
template file name on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line (in shell
language: "").
This feature is available with Postfix 2.3 and later.
<b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
<b>-v</b> Enable verbose logging for debugging purposes. Multiple <b>-v</b>
options make the software increasingly verbose.
<b>-x</b> Expand <i>$name</i> in <a href="postconf.5.html"><b>main.cf</b></a> or <a href="master.5.html"><b>master.cf</b></a> parameter values. The
<b>-x</b> Expand <i>$name</i> in <a href="postconf.5.html"><b>main.cf</b></a> or <a href="master.5.html"><b>master.cf</b></a> parameter values. The
expansion is recursive.
This feature is available with Postfix 2.10 and later.
<b>-X</b> Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and remove the parameters
named on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line. Specify a list of param-
<b>-X</b> Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and remove the parameters
named on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line. Specify a list of param
eter names, not "<i>name=value</i>" pairs.
With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and remove one
or more service entries as specified with "<i>service/type</i>" on the
With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and remove one
or more service entries as specified with "<i>service/type</i>" on the
<a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
With <b>-P</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and remove one
With <b>-P</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and remove one
or more service parameter settings (-o parameter=value settings)
as specied with "<i>service/type/parameter</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> com-
as specied with "<i>service/type/parameter</i>" on the <a href="postconf.1.html"><b>postconf</b>(1)</a> com
mand line.
In all cases the file is copied to a temporary file then renamed
into place. Specify quotes to protect special characters on the
<a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
There is no <a href="postconf.1.html"><b>postconf</b>(1)</a> command to perform the reverse opera-
There is no <a href="postconf.1.html"><b>postconf</b>(1)</a> command to perform the reverse opera
tion.
This feature is available with Postfix 2.10 and later. Support
This feature is available with Postfix 2.10 and later. Support
for -M and -P was added with Postfix 2.11.
<b>-#</b> Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and comment out the parame-
ters named on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line, so that those param-
eters revert to their default values. Specify a list of parame-
<b>-#</b> Edit the <a href="postconf.5.html"><b>main.cf</b></a> configuration file, and comment out the parame
ters named on the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line, so that those param
eters revert to their default values. Specify a list of parame
ter names, not "<i>name=value</i>" pairs.
With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and comment out
one or more service entries as specified with "<i>service/type</i>" on
With <b>-M</b>, edit the <a href="master.5.html"><b>master.cf</b></a> configuration file, and comment out
one or more service entries as specified with "<i>service/type</i>" on
the <a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
In all cases the file is copied to a temporary file then renamed
into place. Specify quotes to protect special characters on the
<a href="postconf.1.html"><b>postconf</b>(1)</a> command line.
There is no <a href="postconf.1.html"><b>postconf</b>(1)</a> command to perform the reverse opera-
There is no <a href="postconf.1.html"><b>postconf</b>(1)</a> command to perform the reverse opera
tion.
This feature is available with Postfix 2.6 and later. Support
This feature is available with Postfix 2.6 and later. Support
for -M was added with Postfix 2.11.
<b>DIAGNOSTICS</b>
@ -439,18 +438,18 @@ POSTCONF(1) POSTCONF(1)
Directory with Postfix configuration files.
<b>CONFIGURATION PARAMETERS</b>
The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro-
The following <a href="postconf.5.html"><b>main.cf</b></a> parameters are especially relevant to this pro
gram.
The text below provides only a parameter summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for
The text below provides only a parameter summary. See <a href="postconf.5.html"><b>postconf</b>(5)</a> for
more details including examples.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con
figuration files.
<b><a href="postconf.5.html#bounce_template_file">bounce_template_file</a> (empty)</b>
Pathname of a configuration file with bounce message templates.
Pathname of a configuration file with bounce message templates.
<b>FILES</b>
/etc/postfix/<a href="postconf.5.html">main.cf</a>, Postfix configuration parameters

View File

@ -42,19 +42,40 @@ that starts with whitespace continues a logical line. </p>
<ul>
<li> <p> The expressions "$name", "${name}" or "$(name)" are
recursively replaced by the value of the named parameter. </p>
<li> <p> The expressions "$name" and "${name}" are recursively
replaced with the value of the named parameter, except where noted.
An undefined parameter value is replaced with the empty value. </p>
<li> <p> The expression "${name?value}" expands to "value" when
"$name" is non-empty. This form is supported with Postfix version
2.2 and later. </p>
<li> <p> The expressions "${name?value}" and "${name?{value}}" are
replaced with "value" when "$name" is non-empty. These forms are
supported with Postfix versions &ge; 2.2 and &ge; 2.12, respectively.
</p>
<li> <p> The expression "${name:value}" expands to "value" when
"$name" is empty. This form is supported with Postfix version 2.2
and later. </p>
<li> <p> The expressions "${name:value}" and "${name?{value}}" are
replaced with "value" when "$name" is empty. These forms are supported
with Postfix versions &ge; 2.2 and &ge; 2.12, respectively. </p>
<li> <p> The expression "${name?{value1}:{value2}}" is replaced
with "value1" when "$name" is non-empty, and with "value2" when
"$name" is empty. The "{}" is required for "value1", optional for
"value2". This form is supported with Postfix versions &ge; 2.12.
</p>
<li> <p> Instead of a parameter name, the first item inside "${...}"
may be a logical expression of the form: "{value3} == {value4}"
(equality) or "{value3} != {value4}" (inequality). This form is
supported with Postfix versions &ge; 2.12. </p>
<li> <p> Each "value" is subject to recursive named parameter and
logical expression evaluation, except where noted. </p>
<li> <p> Whitespace before or after each "{value}" is ignored. </p>
<li> <p> Specify "$$" to produce a single "$" character. </p>
<li> <p> The legacy form "$(...)" is equivalent to the preferred
form "${...}". </p>
</ul>
<li> <p> When the same parameter is defined multiple times, only

View File

@ -14,11 +14,11 @@ PROXYMAP(8) PROXYMAP(8)
<b>DESCRIPTION</b>
The <a href="proxymap.8.html"><b>proxymap</b>(8)</a> server provides read-only or read-write table lookup
service to Postfix processes. These services are implemented with dis-
service to Postfix processes. These services are implemented with dis
tinct service names: <b>proxymap</b> and <b>proxywrite</b>, respectively. The purpose
of these services is:
<b>o</b> To overcome chroot restrictions. For example, a chrooted SMTP
· To overcome chroot restrictions. For example, a chrooted SMTP
server needs access to the system passwd file in order to reject
mail for non-existent local addresses, but it is not practical
to maintain a copy of the passwd file in the chroot jail. The
@ -27,7 +27,7 @@ PROXYMAP(8) PROXYMAP(8)
<a href="postconf.5.html#local_recipient_maps">local_recipient_maps</a> =
<a href="proxymap.8.html">proxy</a>:unix:passwd.byname $<a href="postconf.5.html#alias_maps">alias_maps</a>
<b>o</b> To consolidate the number of open lookup tables by sharing one
· To consolidate the number of open lookup tables by sharing one
open table among multiple processes. For example, making mysql
connections from every Postfix daemon process results in "too
many connections" errors. The solution:
@ -38,7 +38,7 @@ PROXYMAP(8) PROXYMAP(8)
The total number of connections is limited by the number of
proxymap server processes.
<b>o</b> To provide single-updater functionality for lookup tables that
· To provide single-updater functionality for lookup tables that
do not reliably support multiple writers (i.e. all file-based
tables).
@ -47,7 +47,7 @@ PROXYMAP(8) PROXYMAP(8)
<b>open</b> <i>maptype:mapname flags</i>
Open the table with type <i>maptype</i> and name <i>mapname</i>, as controlled
by <i>flags</i>. The reply includes the <i>maptype</i> dependent flags (to
distinguish a fixed string table from a regular expression ta-
distinguish a fixed string table from a regular expression ta
ble).
<b>lookup</b> <i>maptype:mapname flags key</i>
@ -101,7 +101,7 @@ PROXYMAP(8) PROXYMAP(8)
The <a href="proxymap.8.html"><b>proxymap</b>(8)</a> server opens only tables that are approved via the
<b><a href="postconf.5.html#proxy_read_maps">proxy_read_maps</a></b> or <b><a href="postconf.5.html#proxy_write_maps">proxy_write_maps</a></b> configuration parameters, does not
talk to users, and can run at fixed low privilege, chrooted or not.
However, running the proxymap server chrooted severely limits usabil-
However, running the proxymap server chrooted severely limits usabil
ity, because it can open only chrooted tables.
The <a href="proxymap.8.html"><b>proxymap</b>(8)</a> server is not a trusted daemon process, and must not be
@ -113,7 +113,7 @@ PROXYMAP(8) PROXYMAP(8)
the table directly. This allows the same <a href="postconf.5.html">main.cf</a> setting to be used by
sensitive and non-sensitive processes.
Postfix-writable data files should be stored under a dedicated direc-
Postfix-writable data files should be stored under a dedicated direc
tory that is writable only by the Postfix mail system, such as the
Postfix-owned <b><a href="postconf.5.html#data_directory">data_directory</a></b>.
@ -131,7 +131,7 @@ PROXYMAP(8) PROXYMAP(8)
The <a href="proxymap.8.html"><b>proxymap</b>(8)</a> read-write service does not explicitly close lookup
tables (even if it did, this could not be relied on, because the
process may be terminated between table updates). The read-write ser-
process may be terminated between table updates). The read-write ser
vice should therefore not be used with tables that leave persistent
storage in an inconsistent state between updates (for example, CDB).
Tables that support "sync on update" should be safe (for example,
@ -146,7 +146,7 @@ PROXYMAP(8) PROXYMAP(8)
more details including examples.
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con-
The default location of the Postfix <a href="postconf.5.html">main.cf</a> and <a href="master.5.html">master.cf</a> con
figuration files.
<b><a href="postconf.5.html#data_directory">data_directory</a> (see 'postconf -d' output)</b>

View File

@ -16,13 +16,13 @@ QMQP-SINK(1) QMQP-SINK(1)
<b>DESCRIPTION</b>
<b>qmqp-sink</b> listens on the named host (or address) and port. It receives
messages from the network and throws them away. The purpose is to mea-
messages from the network and throws them away. The purpose is to mea
sure QMQP client performance, not protocol compliance. Connections can
be accepted on IPv4 or IPv6 endpoints, or on UNIX-domain sockets. IPv4
and IPv6 are the default. This program is the complement of the <a href="qmqp-source.1.html"><b>qmqp-</b></a>
<a href="qmqp-source.1.html"><b>source</b>(1)</a> program.
Note: this is an unsupported test program. No attempt is made to main-
Note: this is an unsupported test program. No attempt is made to main
tain compatibility between successive versions.
Arguments:
@ -36,7 +36,7 @@ QMQP-SINK(1) QMQP-SINK(1)
<b>-c</b> Display a running counter that is updated whenever a delivery is
completed.
<b>-v</b> Increase verbosity. Specify <b>-v -v</b> to see some of the QMQP con-
<b>-v</b> Increase verbosity. Specify <b>-v -v</b> to see some of the QMQP con
versation.
<b>-x</b> <i>time</i>

View File

@ -19,7 +19,7 @@ SMTP-SINK(1) SMTP-SINK(1)
SMTP messages from the network and throws them away. The purpose is to
measure client performance, not protocol compliance.
<b>smtp-sink</b> may also be configured to capture each mail delivery transac-
<b>smtp-sink</b> may also be configured to capture each mail delivery transac
tion to file. Since disk latencies are large compared to network
delays, this mode of operation can reduce the maximal performance by
several orders of magnitude.
@ -28,7 +28,7 @@ SMTP-SINK(1) SMTP-SINK(1)
domain sockets. IPv4 and IPv6 are the default. This program is the
complement of the <a href="smtp-source.1.html"><b>smtp-source</b>(1)</a> program.
Note: this is an unsupported test program. No attempt is made to main-
Note: this is an unsupported test program. No attempt is made to main
tain compatibility between successive versions.
Arguments:
@ -44,7 +44,7 @@ SMTP-SINK(1) SMTP-SINK(1)
<b>-a</b> Do not announce SASL authentication support.
<b>-A</b> <i>delay</i>
Wait <i>delay</i> seconds after responding to DATA, then abort prema-
Wait <i>delay</i> seconds after responding to DATA, then abort prema
turely with a 550 reply status. Do not read further input from
the client; this is an attempt to block the client before it
sends ".". Specify a zero delay value to abort immediately.
@ -57,7 +57,7 @@ SMTP-SINK(1) SMTP-SINK(1)
Use <i>hard-bounce-reply</i> for hard reject responses. The default
reply is "500 5.3.0 Error: command failed".
<b>-c</b> Display running counters that are updated whenever an SMTP ses-
<b>-c</b> Display running counters that are updated whenever an SMTP ses
sion ends, a QUIT command is executed, or when "." is received.
<b>-C</b> Disable XCLIENT support.
@ -67,7 +67,7 @@ SMTP-SINK(1) SMTP-SINK(1)
is created by expanding the <i>dump-template</i> via strftime(3) and
appending a pseudo-random hexadecimal number (example:
"%Y%m%d%H/%M." expands into "2006081203/05.809a62e3"). If the
template contains "/" characters, missing directories are cre-
template contains "/" characters, missing directories are cre
ated automatically. The message dump format is described below.
Note: this option keeps one capture file open for every mail
@ -106,7 +106,7 @@ SMTP-SINK(1) SMTP-SINK(1)
<b>-m</b> <i>count</i> (default: 256)
An upper bound on the maximal number of simultaneous connections
that <b>smtp-sink</b> will handle. This prevents the process from run-
that <b>smtp-sink</b> will handle. This prevents the process from run
ning out of file descriptors. Excess connections will stay
queued in the TCP/IP stack.
@ -122,7 +122,7 @@ SMTP-SINK(1) SMTP-SINK(1)
CISCO PIX system. Implies <b>-e</b>.
<b>-q</b> <i>command,command,...</i>
Disconnect (without replying) after receiving one of the speci-
Disconnect (without replying) after receiving one of the speci
fied commands.
Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT,
@ -131,7 +131,7 @@ SMTP-SINK(1) SMTP-SINK(1)
from the shell. Command names are case-insensitive.
<b>-Q</b> <i>command,command,...</i>
Send a 421 reply and disconnect after receiving one of the spec-
Send a 421 reply and disconnect after receiving one of the spec
ified commands.
Examples of commands are CONNECT, HELO, EHLO, LHLO, MAIL, RCPT,
@ -165,7 +165,7 @@ SMTP-SINK(1) SMTP-SINK(1)
An optional string that is prepended to each message that is
written to a dump file (see the dump file format description
below). The following C escape sequences are supported: \a
(bell), \b (backslace), \f (formfeed), \n (newline), \r (car-
(bell), \b (backslace), \f (formfeed), \n (newline), \r (car
riage return), \t (horizontal tab), \v (vertical tab), \<i>ddd</i> (up
to three octal digits) and \\ (the backslash character).
@ -178,7 +178,7 @@ SMTP-SINK(1) SMTP-SINK(1)
window scaling implementations, specify a value &gt; 0 and &lt; 65536.
<b>-u</b> <i>username</i>
Switch to the specified user privileges after opening the net-
Switch to the specified user privileges after opening the net
work socket and optionally changing the process root directory.
This option is required when the process runs with super-user
privileges. See also the <b>-R</b> option.
@ -212,13 +212,13 @@ SMTP-SINK(1) SMTP-SINK(1)
Each dumped message contains a sequence of text lines, terminated with
the newline character. The sequence of information is as follows:
<b>o</b> The optional string specified with the <b>-S</b> option.
· The optional string specified with the <b>-S</b> option.
<b>o</b> The <b>smtp-sink</b> generated headers as documented below.
· The <b>smtp-sink</b> generated headers as documented below.
<b>o</b> The message header and body as received from the SMTP client.
· The message header and body as received from the SMTP client.
<b>o</b> An empty line.
· An empty line.
The format of the <b>smtp-sink</b> generated headers is as follows:
@ -233,11 +233,11 @@ SMTP-SINK(1) SMTP-SINK(1)
<b>X-Helo-Args:</b> <i>text</i>
The arguments of the last HELO or EHLO command before this mail
delivery transaction. This record is present only if the client
sent a recognizable HELO or EHLO command before the DATA com-
sent a recognizable HELO or EHLO command before the DATA com
mand.
<b>X-Mail-Args:</b> <i>text</i>
The arguments of the MAIL command that started this mail deliv-
The arguments of the MAIL command that started this mail deliv
ery transaction. This record is present exactly once.
<b>X-Rcpt-Args:</b> <i>text</i>
@ -246,8 +246,8 @@ SMTP-SINK(1) SMTP-SINK(1)
are in the order as sent by the client.
<b>Received:</b> <i>text</i>
A message header for compatibility with mail processing soft-
ware. This three-line header marks the end of the headers pro-
A message header for compatibility with mail processing soft
ware. This three-line header marks the end of the headers pro
vided by <b>smtp-sink</b>, and is formatted as follows:
<b>from</b> <i>helo</i> <b>([</b><i>addr</i><b>])</b>
@ -257,7 +257,7 @@ SMTP-SINK(1) SMTP-SINK(1)
<b>by</b> <i>host</i> <b>(smtp-sink) with</b> <i>proto</i> <b>id</b> <i>random</i><b>;</b>
The hostname specified with the <b>-h</b> option, the client
protocol (see <b>X-Client-Proto</b> above), and the pseudo-ran-
protocol (see <b>X-Client-Proto</b> above), and the pseudo-ran
dom portion of the per-message capture file name.
<i>time-stamp</i>

View File

@ -274,25 +274,26 @@ The file format is described in \fBpcre_table\fR(5).
PostgreSQL database client. This is described in
\fBpgsql_table\fR(5).
.IP "\fBpipemap\fR (read-only)"
A pipeline of lookup tables. Example:
"\fBpipemap:\fI!type_1:name_1! ... !type_n:name_n\fR".
A lookup table that constructs a pipeline of tables. Example:
"\fBpipemap:{\fItype_1:name_1, ..., type_n:name_n\fB}\fR".
Each "pipemap:" query is given to the first table. Each
lookup result becomes the query for the next table in the
pipeline, and the last table produces the final result.
When any table lookup produces no result, the pipeline
produces no result. The first ASCII character after "pipemap:"
will be used as the separator between the lookup tables
that follow (do not use space, ",", ":" or non-ASCII).
produces no result. The first and last characters of the
"pipemap:" table name must be "\fB{\fR" and "\fB}\fR".
Within these, individual maps are separated with comma or
whitespace.
.IP "\fBproxy\fR"
Postfix \fBproxymap\fR(8) client for shared access to Postfix
databases. The table name syntax is \fItype\fB:\fIname\fR.
.IP "\fBrandmap\fR (read-only)"
An in-memory table that performs random selection. Example:
"\fBrandmap:\fI!result_1! ... !result_n\fR". Each table query
"\fBrandmap:{\fIresult_1, ..., result_n\fB}\fR". Each table query
returns a random choice from the specified results. The first
ASCII character after "randmap:" will be used as the separator
between the results that follow (do not use space, ",", ":"
or non-ASCII).
and last characters of the "randmap:" table name must be
"\fB{\fR" and "\fB}\fR". Within these, individual maps are
separated with comma or whitespace.
.IP "\fBregexp\fR (read-only)"
A lookup table based on regular expressions. The file format
is described in \fBregexp_table\fR(5).

View File

@ -32,18 +32,36 @@ with whitespace continues a logical line.
A parameter value may refer to other parameters.
.RS
.IP \(bu
The expressions "$name", "${name}" or "$(name)" are
recursively replaced by the value of the named parameter.
The expressions "$name" and "${name}" are recursively replaced with
the value of the named parameter. An undefined parameter value is
replaced with the empty value.
.IP \(bu
The expression "${name?value}" expands to "value" when
"$name" is non-empty. This form is supported with Postfix
version 2.2 and later.
The expressions "${name?value}" and "${name?{value}}" are replaced
with "value" when "$name" is non-empty. These forms are supported
with Postfix versions >= 2.2 and >= 2.12, respectively.
.IP \(bu
The expression "${name:value}" expands to "value" when
"$name" is empty. This form is supported with Postfix
version 2.2 and later.
The expressions "${name:value}" and "${name:{value}}" are replaced
with "value" when "$name" is empty. These forms are supported with
Postfix versions >= 2.2 and >= 2.12, respectively.
.IP \(bu
The expression "${name?{value1}:{value2}}" is replaced with "value1"
when "$name" is non-empty, and with "value2" when "$name" is empty.
The "{}" is required for "value1", optional for "value2". This form
is supported with Postfix versions >= 2.12.
.IP \(bu
Instead of a parameter name, the first item inside "${...}" may be
a logical expression of the form: "{value3} == {value4}" (equality)
or "{value3} != {value4}" (inequality). This form is supported
with Postfix versions >= 2.12.
.IP \(bu
Each "value" is subject to recursive named parameter and logical
expression evaluation, except where noted.
.IP \(bu
Whitespace before or after each "{value}" is ignored.
.IP \(bu
Specify "$$" to produce a single "$" character.
.IP \(bu
The legacy form "$(...)" is equivalent to the preferred form "${...}".
.RE
.IP \(bu
When the same parameter is defined multiple times, only the last

View File

@ -365,14 +365,14 @@ file. </dd>
<dt> <b>pipemap</b> (read-only) </dt>
<dd> A pipeline of lookup tables. Example:
"<b>pipemap:</b><i>!type<sub>1</sub>:name<sub>1</sub>! ...
!type<sub>n</sub>:name<sub>n</sub></i>". Each "pipemap:" query is
"pipemap:{<i>type<sub>1</sub>:name<sub>1</sub>, ...,
type<sub>n</sub>:name<sub>n</sub></i>}". Each "pipemap:" query is
given to the first table. Each lookup result becomes the query for
the next table in the pipeline, and the last table produces the
final result. When any table lookup produces no result, the pipeline
produces no result. The first ASCII character after "pipemap:"
will be used as the separator between the lookup tables that follow
(do not use space, ",", ":" or non-ASCII). </dd>
produces no result. The first and last characters of the "pipemap:"
table name must be "{" and "}". Within these, individual maps are
separated with comma or whitespace. </dd>
<dt> <b>pgsql</b> (read-only) </dt>
@ -388,11 +388,11 @@ databases. The lookup table name syntax is "proxy:type:table".
<dt> <b>randmap</b> (read-only) </dt>
<dd> An in-memory table that performs random selection. Example:
"<b>randmap:</b><i>!result<sub>1</sub>! ... !result<sub>n</sub></i>".
"randmap:{<i>result<sub>1</sub>. ..., result<sub>n</sub></i>}".
Each table query returns a random choice from the specified results.
The first ASCII character after "randmap:" will be used as the
separator between the results that follow (do not use space, ",",
":" or non-ASCII). </dd>
The first and last characters of the "randmap:" table name must be
"{" and "}". Within these, individual maps are separated with comma
or whitespace. </dd>
<dt> <b>regexp</b> (read-only) </dt>

View File

@ -42,19 +42,40 @@ that starts with whitespace continues a logical line. </p>
<ul>
<li> <p> The expressions "$name", "${name}" or "$(name)" are
recursively replaced by the value of the named parameter. </p>
<li> <p> The expressions "$name" and "${name}" are recursively
replaced with the value of the named parameter, except where noted.
An undefined parameter value is replaced with the empty value. </p>
<li> <p> The expression "${name?value}" expands to "value" when
"$name" is non-empty. This form is supported with Postfix version
2.2 and later. </p>
<li> <p> The expressions "${name?value}" and "${name?{value}}" are
replaced with "value" when "$name" is non-empty. These forms are
supported with Postfix versions &ge; 2.2 and &ge; 2.12, respectively.
</p>
<li> <p> The expression "${name:value}" expands to "value" when
"$name" is empty. This form is supported with Postfix version 2.2
and later. </p>
<li> <p> The expressions "${name:value}" and "${name?{value}}" are
replaced with "value" when "$name" is empty. These forms are supported
with Postfix versions &ge; 2.2 and &ge; 2.12, respectively. </p>
<li> <p> The expression "${name?{value1}:{value2}}" is replaced
with "value1" when "$name" is non-empty, and with "value2" when
"$name" is empty. The "{}" is required for "value1", optional for
"value2". This form is supported with Postfix versions &ge; 2.12.
</p>
<li> <p> Instead of a parameter name, the first item inside "${...}"
may be a logical expression of the form: "{value3} == {value4}"
(equality) or "{value3} != {value4}" (inequality). This form is
supported with Postfix versions &ge; 2.12. </p>
<li> <p> Each "value" is subject to recursive named parameter and
logical expression evaluation, except where noted. </p>
<li> <p> Whitespace before or after each "{value}" is ignored. </p>
<li> <p> Specify "$$" to produce a single "$" character. </p>
<li> <p> The legacy form "$(...)" is equivalent to the preferred
form "${...}". </p>
</ul>
<li> <p> When the same parameter is defined multiple times, only

View File

@ -32,18 +32,36 @@ with whitespace continues a logical line.
A parameter value may refer to other parameters.
.RS
.IP \(bu
The expressions "$name", "${name}" or "$(name)" are
recursively replaced by the value of the named parameter.
The expressions "$name" and "${name}" are recursively replaced with
the value of the named parameter. An undefined parameter value is
replaced with the empty value.
.IP \(bu
The expression "${name?value}" expands to "value" when
"$name" is non-empty. This form is supported with Postfix
version 2.2 and later.
The expressions "${name?value}" and "${name?{value}}" are replaced
with "value" when "$name" is non-empty. These forms are supported
with Postfix versions >= 2.2 and >= 2.12, respectively.
.IP \(bu
The expression "${name:value}" expands to "value" when
"$name" is empty. This form is supported with Postfix
version 2.2 and later.
The expressions "${name:value}" and "${name:{value}}" are replaced
with "value" when "$name" is empty. These forms are supported with
Postfix versions >= 2.2 and >= 2.12, respectively.
.IP \(bu
The expression "${name?{value1}:{value2}}" is replaced with "value1"
when "$name" is non-empty, and with "value2" when "$name" is empty.
The "{}" is required for "value1", optional for "value2". This form
is supported with Postfix versions >= 2.12.
.IP \(bu
Instead of a parameter name, the first item inside "${...}" may be
a logical expression of the form: "{value3} == {value4}" (equality)
or "{value3} != {value4}" (inequality). This form is supported
with Postfix versions >= 2.12.
.IP \(bu
Each "value" is subject to recursive named parameter and logical
expression evaluation, except where noted.
.IP \(bu
Whitespace before or after each "{value}" is ignored.
.IP \(bu
Specify "$$" to produce a single "$" character.
.IP \(bu
The legacy form "$(...)" is equivalent to the preferred form "${...}".
.RE
.IP \(bu
When the same parameter is defined multiple times, only the last

View File

@ -50,7 +50,7 @@ typedef struct BOUNCE_TEMPLATE {
#define bounce_template_encoding(t) ((t)->mime_encoding)
#define bounce_template_charset(t) ((t)->mime_charset)
typedef int (*BOUNCE_XP_PRN_FN) (VSTREAM *, const char *, ...);
typedef int PRINTFLIKE(2, 3) (*BOUNCE_XP_PRN_FN) (VSTREAM *, const char *,...);
typedef int (*BOUNCE_XP_PUT_FN) (VSTREAM *, const char *);
extern BOUNCE_TEMPLATE *bounce_template_create(const BOUNCE_TEMPLATE *);

View File

@ -580,8 +580,11 @@ static void cleanup_header_callback(void *context, int header_class,
if (hdr_opts->type == HDR_RESENT_MESSAGE_ID)
msg_info("%s: resent-message-id=%s", state->queue_id, hdrval);
if (hdr_opts->type == HDR_RECEIVED)
if (++state->hop_count >= var_hopcount_limit)
if (++state->hop_count >= var_hopcount_limit) {
msg_warn("%s: message rejected: hopcount exceeded",
state->queue_id);
state->errs |= CLEANUP_STAT_HOPS;
}
if (CLEANUP_OUT_OK(state)) {
if (hdr_opts->flags & HDR_OPT_RR)
state->resent = "Resent-";

View File

@ -227,7 +227,7 @@ int main(int argc, char **argv)
vstream_fflush(VSTREAM_OUT);
continue;
}
target = mystrtok(&bufp, " \t");
target = mystrtokq(&bufp, " \t");
junk = mystrtok(&bufp, " \t");
if (strcmp(cmd, "file") == 0 && target && !junk) {
data_redirect_file(result, target);

View File

@ -133,7 +133,7 @@ static int dict_memcache_set(DICT_MC *dict_mc, const char *value, int ttl)
{
VSTREAM *fp;
int count;
int data_len = strlen(value);
size_t data_len = strlen(value);
/*
* Return a permanent error if we can't store this data. This results in
@ -153,7 +153,8 @@ static int dict_memcache_set(DICT_MC *dict_mc, const char *value, int ttl)
if ((fp = auto_clnt_access(dict_mc->clnt)) == 0) {
break;
} else if (memcache_printf(fp, "set %s %d %d %ld",
STR(dict_mc->key_buf), dict_mc->mc_flags, ttl, data_len) < 0
STR(dict_mc->key_buf), dict_mc->mc_flags,
ttl, (long) data_len) < 0
|| memcache_fwrite(fp, value, strlen(value)) < 0
|| memcache_get(fp, dict_mc->clnt_buf,
dict_mc->max_line) < 0) {

View File

@ -20,7 +20,7 @@
* Patches change both the patchlevel and the release date. Snapshots have no
* patchlevel; they change the release date only.
*/
#define MAIL_RELEASE_DATE "20140907"
#define MAIL_RELEASE_DATE "20140921"
#define MAIL_VERSION_NUMBER "2.12"
#ifdef SNAPSHOT

View File

@ -115,6 +115,7 @@ MAPS *maps_create(const char *title, const char *map_names, int dict_flags)
char *temp;
char *bufp;
static char sep[] = " \t,\r\n";
static char parens[] = "{}";
MAPS *maps;
char *map_type_name;
VSTRING *map_type_name_flags;
@ -138,7 +139,7 @@ MAPS *maps_create(const char *title, const char *map_names, int dict_flags)
#define OPEN_FLAGS O_RDONLY
while ((map_type_name = mystrtok(&bufp, sep)) != 0) {
while ((map_type_name = mystrtokq(&bufp, sep, parens)) != 0) {
vstring_sprintf(map_type_name_flags, "%s(%o,%s)",
map_type_name, OPEN_FLAGS,
dict_flags_str(dict_flags));

View File

@ -16,7 +16,7 @@
*/
extern int memcache_get(VSTREAM *, VSTRING *, ssize_t);
extern int memcache_vprintf(VSTREAM *, const char *, va_list);
extern int memcache_printf(VSTREAM *, const char *fmt,...);
extern int PRINTFLIKE(2, 3) memcache_printf(VSTREAM *, const char *fmt,...);
extern int memcache_fread(VSTREAM *, VSTRING *, ssize_t);
extern int memcache_fwrite(VSTREAM *, const char *, ssize_t);

View File

@ -120,7 +120,7 @@ SERVER_ACL *server_acl_parse(const char *extern_acl, const char *origin)
* chroot jail, while access lists are evaluated after entering the
* chroot jail.
*/
while ((acl = mystrtok(&bp, SERVER_ACL_SEPARATORS)) != 0) {
while ((acl = mystrtokq(&bp, SERVER_ACL_SEPARATORS, "{}")) != 0) {
if (strchr(acl, ':') != 0) {
if (strchr(origin, ':') != 0) {
msg_warn("table %s: lookup result \"%s\" is not allowed"

View File

@ -49,7 +49,7 @@ tests: test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 \
test22 test23 test24 test25 test26 test27 test28 test29 test30 test4b \
test31 test32 test33 test34 test35 test36 test37 test39 test40 test41 \
test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 \
test52 test53 test54 test55 test56
test52 test53 test54 test55 test56 test57 test58
root_tests:
@ -72,6 +72,7 @@ test1: $(PROG) test1.ref
touch main.cf master.cf
echo smtpd_restriction_classes = foo bar >> main.cf
echo foo = yes >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test1.tmp 2>&1
diff test1.ref test1.tmp
rm -f main.cf master.cf test1.tmp
@ -83,6 +84,7 @@ test2: $(PROG) test2.ref
touch main.cf master.cf
echo restriction_classes = foo bar >> main.cf
echo foo = yes >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test2.tmp 2>&1
diff test2.ref test2.tmp
rm -f main.cf master.cf test2.tmp
@ -95,6 +97,7 @@ test3: $(PROG) test3.ref
echo foo = yes >> main.cf
echo 'bar = $$foo' >> main.cf
echo 'always_bcc = $$bar' >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test3.tmp 2>&1
diff test3.ref test3.tmp
rm -f main.cf master.cf test3.tmp
@ -108,6 +111,7 @@ test4: $(PROG) test4.ref
echo 'bar = $$foo' >> main.cf
echo smtpd unix - n n - 0 smtpd >> master.cf
echo ' -o always_bcc=$$bar' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test4.tmp 2>&1
diff test4.ref test4.tmp
rm -f main.cf master.cf test4.tmp
@ -123,6 +127,7 @@ test4b: $(PROG) test4b.ref
echo smtpd1 unix - n n - 0 smtpd >> master.cf
echo ' -o foo=xxx -o bar=yyy -o baz=zzz' >> master.cf
echo '#smtpd2 unix - n n - 0 smtpd' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test4b.tmp 2>&1
diff test4b.ref test4b.tmp
rm -f main.cf master.cf test4b.tmp
@ -135,6 +140,7 @@ test5: $(PROG) test5.ref
touch main.cf master.cf
echo smtpd unix - n n - 0 smtpd >> master.cf
echo ' -o bar=yes -o always_bcc=$$bar -o' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test5.tmp 2>&1
diff test5.ref test5.tmp
rm -f main.cf master.cf test5.tmp
@ -145,6 +151,7 @@ test6: $(PROG) test6.ref
rm -f main.cf master.cf
touch main.cf master.cf
echo whatevershebrings unix - n n - 0 pipe >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -c . 2>&1 | grep whatevershebrings >test6.tmp
diff test6.ref test6.tmp
rm -f main.cf master.cf test6.tmp
@ -155,6 +162,7 @@ test7: $(PROG) test7.ref
rm -f main.cf master.cf
touch main.cf master.cf
echo whatevershebrings unix - n n - 0 spawn >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -c . 2>&1 | grep whatevershebrings >test7.tmp
diff test7.ref test7.tmp
rm -f main.cf master.cf test7.tmp
@ -164,6 +172,7 @@ test8: $(PROG) test8.ref
touch main.cf master.cf
echo whatevershebrings inet - n n - 0 spawn >> master.cf
echo whatevershebrings_time_limit=1 >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -c . 2>&1 | grep whatevershebrings >test8.tmp
diff test8.ref test8.tmp
rm -f main.cf master.cf test8.tmp
@ -173,6 +182,7 @@ test9: $(PROG) test9.ref
touch main.cf master.cf
echo foo inet - n n - 0 spawn >> master.cf
echo bar unix - n n - 0 spawn >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -c . -M '*'/inet >test9.tmp 2>&1
diff test9.ref test9.tmp
rm -f main.cf master.cf test9.tmp
@ -182,6 +192,7 @@ test10: $(PROG) test10.ref
touch main.cf master.cf
echo foo inet - n n - 0 spawn >> master.cf
echo bar unix - n n - 0 spawn >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -c . -M bar/inet foo/unix >test10.tmp 2>&1
diff test10.ref test10.tmp
rm -f main.cf master.cf test10.tmp
@ -191,6 +202,7 @@ test11: $(PROG) test11.ref
touch main.cf master.cf
echo foo inet - n n - 0 spawn >> master.cf
echo bar unix - n n - 0 spawn >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -c . -M >test11.tmp 2>&1
diff test11.ref test11.tmp
rm -f main.cf master.cf test11.tmp
@ -205,6 +217,7 @@ test12: $(PROG) test12.ref
echo ' -o always_bcc=$$bar -o' >> master.cf
echo foo inet - n n - 0 spawn >> master.cf
echo ' -o always_bcc=$$bar -o' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -c . -M >test12.tmp 2>&1
diff test12.ref test12.tmp
rm -f main.cf master.cf test12.tmp
@ -218,6 +231,7 @@ test13: $(PROG) test13.ref
echo baz=xx >> main.cf
echo foo inet - n n - 0 spawn >> master.cf
echo ' -o smtpd_restriction_classes=bar' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test13.tmp 2>&1
diff test13.ref test13.tmp
rm -f main.cf master.cf test13.tmp
@ -230,6 +244,7 @@ test14: $(PROG) test14.ref
echo smtpd_restriction_classes=bar >> main.cf
echo foo inet - n n - 0 spawn >> master.cf
echo ' -o bar=yes -o baz=xx' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test14.tmp 2>&1
diff test14.ref test14.tmp
rm -f main.cf master.cf test14.tmp
@ -243,6 +258,7 @@ test15: $(PROG) test15.ref
echo baz=yy >> main.cf
echo foo inet - n n - 0 spawn >> master.cf
echo ' -o bar=yes -o always_bcc=$$bar$$baz' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test15.tmp 2>&1
diff test15.ref test15.tmp
rm -f main.cf master.cf test15.tmp
@ -251,14 +267,14 @@ test15: $(PROG) test15.ref
test16: $(PROG) test16.ref
rm -f main.cf master.cf
touch main.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test16.tmp 2>&1
diff test16.ref test16.tmp
rm -f main.cf master.cf test16.tmp
test17: $(PROG) test17.ref
rm -f main.cf master.cf
touch main.cf
touch -t 197101010000 main.cf
-./$(PROG) -Mc . >test17.tmp 2>&1; exit 0
diff test17.ref test17.tmp
rm -f main.cf master.cf test17.tmp
@ -270,6 +286,7 @@ test18: $(PROG) test18.ref
touch main.cf master.cf
echo virtual_maps=xxx >> main.cf
echo smtpd_client_connection_limit_exceptions=yyy >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test18.tmp 2>&1
diff test18.ref test18.tmp
rm -f main.cf master.cf test18.tmp
@ -281,6 +298,7 @@ test19: $(PROG) test19.ref
touch main.cf master.cf
echo forward_path='$$'aaaa >> main.cf
echo default_rbl_reply='$$'bbbb >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test19.tmp 2>&1
diff test19.ref test19.tmp
rm -f main.cf master.cf test19.tmp
@ -292,6 +310,7 @@ test20: $(PROG) test20.ref
touch main.cf master.cf
echo foo inet - n n - 0 spawn >> master.cf
echo ' -o always_bcc=$$bar$$baz' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mfc . >test20.tmp 2>&1
diff test20.ref test20.tmp
rm -f main.cf master.cf test20.tmp
@ -303,6 +322,7 @@ test21: $(PROG) test21.ref
touch main.cf master.cf
echo forward_path = xxxxxxxxxxxxx xxxxxxxxxxxxxx xxxxxxxxxxxx \
xxxxxxxxxxxxx xxxxxxxxxxxxxx >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -nfc . >test21.tmp 2>&1
diff test21.ref test21.tmp
rm -f main.cf master.cf test21.tmp
@ -313,6 +333,7 @@ test22: $(PROG) test22.ref
rm -f main.cf master.cf
touch main.cf master.cf
echo whatevershebrings unix - n n - 0 smtp >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -c . 2>&1 | grep whatevershebrings >test22.tmp
diff test22.ref test22.tmp
rm -f main.cf master.cf test22.tmp
@ -326,6 +347,7 @@ test23: $(PROG) test23.ref
echo name = value >> main.cf
echo whatevershebrings unix - n n - 0 smtp >> master.cf
echo ' -o always_bcc=$$name' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -c . -nC builtin >test23.tmp 2>&1
diff test23.ref test23.tmp
rm -f main.cf master.cf test23.tmp
@ -337,6 +359,7 @@ test24: $(PROG) test24.ref
echo name = value >> main.cf
echo whatevershebrings unix - n n - 0 smtp >> master.cf
echo ' -o always_bcc=$$name' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -c . -nC user >test24.tmp 2>&1
diff test24.ref test24.tmp
rm -f main.cf master.cf test24.tmp
@ -348,6 +371,7 @@ test25: $(PROG) test25.ref
echo name = value >> main.cf
echo whatevershebrings unix - n n - 0 smtp >> master.cf
echo ' -o always_bcc=$$name' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -c . -C service 2>&1 | grep whatevershebrings >test25.tmp
diff test25.ref test25.tmp
rm -f main.cf master.cf test25.tmp
@ -361,6 +385,7 @@ test26: $(PROG) test26.ref
echo name = value >> main.cf
echo whatevershebrings unix - n n - 0 smtp >> master.cf
echo ' -o always_bcc=$$name' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . -C all >test26.tmp 2>&1
diff test26.ref test26.tmp
rm -f main.cf master.cf test26.tmp
@ -372,6 +397,7 @@ test27: $(PROG) test27.ref
echo name = value >> main.cf
echo whatevershebrings unix - n n - 0 smtp >> master.cf
echo ' -o always_bcc=$$name' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -c . -C all 2>&1 | grep whatevershebrings >test27.tmp
diff test27.ref test27.tmp
rm -f main.cf master.cf test27.tmp
@ -392,6 +418,7 @@ test28: $(PROG) test28.ref
echo ' -o body_checks=$$db:zz' >> master.cf
echo 'zz_domain = whatever' >> main.cf
echo 'aa_domain = whatever' >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test28.tmp 2>&1
diff test28.ref test28.tmp
rm -f main.cf master.cf test28.tmp
@ -416,6 +443,7 @@ test29: $(PROG) test29.ref
echo 'memcachexx = proxy:memcache:memcachefoo' >> main.cf
echo 'memcachefoo_domain = bar' >> main.cf
echo 'memcachefoo_domainx = bar' >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test29.tmp 2>&1
diff test29.ref test29.tmp
rm -f main.cf master.cf test29.tmp
@ -432,6 +460,7 @@ test30: $(PROG) test30.ref
echo ' -o bodyx_checks=$$p2' >> master.cf
echo ' -oheader_checks=$$p3' >> master.cf
echo ' -oheaderx_checks=$$p4' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -nc . >test30.tmp 2>&1
diff test30.ref test30.tmp
rm -f main.cf master.cf test30.tmp
@ -443,6 +472,7 @@ test31: $(PROG) test31.ref
touch main.cf master.cf
echo 'smtpd_helo_restrictions=whatever' >> main.cf
echo 'smtpd_sender_restrictions=$$smtpd_helo_restrictions' >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -nxc . >test31.tmp 2>&1
diff test31.ref test31.tmp
rm -f main.cf master.cf test31.tmp
@ -453,6 +483,7 @@ test32: $(PROG) test32.ref
rm -f main.cf master.cf
touch main.cf master.cf
echo 'relay_domains=whatever' >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -xc . fast_flush_domains >test32.tmp 2>&1
diff test32.ref test32.tmp
rm -f main.cf master.cf test32.tmp
@ -464,6 +495,7 @@ test33: $(PROG) test33.ref
touch main.cf master.cf
echo 'mydestination=whatever' >> main.cf
echo 'always_bcc=$$relay_domains' >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -xc . always_bcc >test33.tmp 2>&1
diff test33.ref test33.tmp
rm -f main.cf master.cf test33.tmp
@ -474,6 +506,7 @@ test34: $(PROG) test34.ref
echo 'mydestination=whatever' >> main.cf
echo 'process_name=xxx' >> main.cf
echo 'process_id=yyy' >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -xc . mydestination process_name >test34.tmp 2>&1
diff test34.ref test34.tmp
rm -f main.cf master.cf test34.tmp
@ -485,6 +518,7 @@ test35: $(PROG) test35.ref
echo ' -o body_checks=whatever' >> master.cf
echo ' -o process_name=aaa' >> master.cf
echo ' -o process_id=bbb' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -xc . process_name >test35.tmp 2>&1
diff test35.ref test35.tmp
rm -f main.cf master.cf test35.tmp
@ -494,6 +528,7 @@ test36: $(PROG) test36.ref
touch main.cf master.cf
echo 'mydestination=$$virtual_mapx' >> main.cf
echo 'virtual_alias_maps=$$virtual_maps' >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -nxc . >test36.tmp 2>&1
diff test36.ref test36.tmp
rm -f main.cf master.cf test36.tmp
@ -507,6 +542,7 @@ test37: $(PROG) test37.ref
echo ' -o mydestination=$$xxx' >> master.cf
echo ' -o always_bcc=$$aaa' >> master.cf
echo ' -o aaa=ccc' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mfxc . >test37.tmp 2>&1
diff test37.ref test37.tmp
rm -f main.cf master.cf test37.tmp
@ -517,6 +553,7 @@ test39: $(PROG) test39.ref
echo foo unix - n n - 0 other >> master.cf
echo bar inet - n n - 0 other >> master.cf
echo baz unix - n n - 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mfc . '*'/unix >test39.tmp 2>&1
diff test39.ref test39.tmp
rm -f main.cf master.cf test39.tmp
@ -528,6 +565,7 @@ test40: $(PROG) test40.ref
echo ' -voaaa=bbb' >> master.cf
echo ' -vo ccc=$$aaa' >> master.cf
echo ' -v -oddd=$$ccc' >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mfxc . '*'/unix >test40.tmp 2>&1
diff test40.ref test40.tmp
rm -f main.cf master.cf test40.tmp
@ -538,6 +576,7 @@ test41: $(PROG) test41.ref
echo foo unix - n n - 0 other >> master.cf
echo bar unix - n n - 0 other >> master.cf
echo baz unix - n n - 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Pc . bar/unix/xxx=yyy bar/unix/aaa=bbb >test41.tmp 2>&1
./$(PROG) -Mfc. >>test41.tmp 2>&1
./$(PROG) -Pc . bar/unix/xxx=YYY bar/unix/aaa=BBB >>test41.tmp 2>&1
@ -552,6 +591,7 @@ test42: $(PROG) test42.ref
echo foo unix - n n - 0 other >> master.cf
echo bar unix - n n - 0 other >> master.cf
echo baz unix - n n - 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Pc . bar/unix/xxx=yyy bar/unix/aaa=bbb >test42.tmp 2>&1
./$(PROG) -Mfc. >>test42.tmp 2>&1
./$(PROG) -Pc . >>test42.tmp 2>&1
@ -566,6 +606,7 @@ test43: $(PROG) test43.ref
echo foo unix - n n - 0 other >> master.cf
echo bar unix - n n - 0 other >> master.cf
echo baz unix - n n - 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Fc . bar/unix/chroot=y bar/unix/command='aa -stuffobb=cc dd' >test43.tmp 2>&1
./$(PROG) -Mfc. >>test43.tmp 2>&1
diff test43.ref test43.tmp
@ -577,6 +618,7 @@ test44: $(PROG) test44.ref
echo foo unix - n n - 0 other >> master.cf
echo bar unix - n n - 0 other >> master.cf
echo baz unix - n n - 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mc . bar/unix='xx inet - n n - 0 aa -stuffobb=cc dd' >test44.tmp 2>&1
./$(PROG) -Mfc. >>test44.tmp 2>&1
diff test44.ref test44.tmp
@ -588,6 +630,7 @@ test45: $(PROG) test45.ref
echo foo unix - n n - 0 other >> master.cf
echo bar xxxx - n n - 0 other >> master.cf
echo baz unix - n n - 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mfc. >test45.tmp 2>&1 || true
diff test45.ref test45.tmp
rm -f main.cf master.cf test45.tmp
@ -598,6 +641,7 @@ test46: $(PROG) test46.ref
echo foo unix - n n - 0 other >> master.cf
echo bar inet X n n - 0 other >> master.cf
echo baz unix - n n - 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mfc. >test46.tmp 2>&1 || true
diff test46.ref test46.tmp
rm -f main.cf master.cf test46.tmp
@ -608,6 +652,7 @@ test47: $(PROG) test47.ref
echo foo unix - n n - 0 other >> master.cf
echo bar inet - X n - 0 other >> master.cf
echo baz unix - n n - 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mfc. >test47.tmp 2>&1 || true
diff test47.ref test47.tmp
rm -f main.cf master.cf test47.tmp
@ -618,6 +663,7 @@ test48: $(PROG) test48.ref
echo foo unix - n n - 0 other >> master.cf
echo bar inet - n X - 0 other >> master.cf
echo baz unix - n n - 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mfc. >test48.tmp 2>&1 || true
diff test48.ref test48.tmp
rm -f main.cf master.cf test48.tmp
@ -628,6 +674,7 @@ test49: $(PROG) test49.ref
echo foo unix - n n - 0 other >> master.cf
echo bar inet - n n X 0 other >> master.cf
echo baz unix - n n - 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mfc. >test49.tmp 2>&1 || true
diff test49.ref test49.tmp
rm -f main.cf master.cf test49.tmp
@ -638,6 +685,7 @@ test50: $(PROG) test50.ref
echo foo unix - n n - 0 other >> master.cf
echo bar inet - n n - X other >> master.cf
echo baz unix - n n - 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mfc. >test50.tmp 2>&1 || true
diff test50.ref test50.tmp
rm -f main.cf master.cf test50.tmp
@ -648,6 +696,7 @@ test51: $(PROG) test51.ref
echo foo unix - n n -? 0 other >> master.cf
echo bar inet - n n X? 0 other >> master.cf
echo baz unix - n n 0? 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -Mfc. >test51.tmp 2>&1 || true
diff test51.ref test51.tmp
rm -f main.cf master.cf test51.tmp
@ -658,6 +707,7 @@ test52: $(PROG) test52.ref
echo foo unix - n n - 0 other >> master.cf
echo bar inet - n n 0 0 other >> master.cf
echo baz unix - n n 0 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -MXc. bar/inet foo/unix xxx/yyy
./$(PROG) -Mfc. >test52.tmp 2>&1 || true
diff test52.ref test52.tmp
@ -669,6 +719,7 @@ test53: $(PROG) test53.ref
echo foo unix - n n - 0 other >> master.cf
echo bar inet - n n 0 0 other >> master.cf
echo baz unix - n n 0 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -M#c. bar/inet xxx/yyy
diff test53.ref master.cf
rm -f main.cf master.cf test53.tmp
@ -679,6 +730,7 @@ test54: $(PROG) test54.ref
echo foo unix - n n - 0 other >> master.cf
echo bar inet - n n 0 0 other >> master.cf
echo baz unix - n n 0 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -M#c. bar/inet foo/unix
diff test54.ref master.cf
rm -f main.cf master.cf test54.tmp
@ -689,6 +741,7 @@ test55: $(PROG) test55.ref
echo foo unix - n n - 0 other >> master.cf
echo bar inet - n n 0 0 other >> master.cf
echo baz unix - n n 0 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -M#c. bar/inet baz/unix
diff test55.ref master.cf
rm -f main.cf master.cf test55.tmp
@ -701,10 +754,41 @@ test56: $(PROG) test56.ref
echo " -o first" >> master.cf
echo " -o second" >> master.cf
echo baz unix - n n 0 0 other >> master.cf
touch -t 197101010000 main.cf
./$(PROG) -M#c. bar/inet xxx/yyy
diff test56.ref master.cf
rm -f main.cf master.cf test56.tmp
# Many more tests in util/mac_expand.in.
test57: $(PROG) test57.ref
rm -f main.cf master.cf
touch main.cf master.cf
echo 'x = $${{1} == {2}?{error}:x-value}' >> main.cf
echo 'y = y-value' >> main.cf
echo 'bar = $${x?{$$y}:$$z}' >> main.cf
echo 'baz = $${x?{$$z}:$$y}' >> main.cf
echo 'foo = $$bar$$baz' >> main.cf
echo 't1 = Postfix 2.11 $${{$${x?bug:x}} == {bug}?in}compatible' >> main.cf
echo 't2 = $$t1' >> main.cf
touch -t 197101010000 main.cf
./$(PROG) -nxc. >test57.tmp 2>&1
diff test57.ref test57.tmp
rm -f main.cf master.cf test57.tmp
test58: $(PROG) test58.ref
rm -f main.cf master.cf
touch main.cf master.cf
echo 'mydestination = foo bar pipemap:{ldap:xxx, memcache:yy} randmap:{xx' >> main.cf
echo 'xxx_domain = foo' >> main.cf
echo 'xxx_bogus = foo' >> main.cf
echo 'yy_backup = bbb' >> main.cf
echo 'yy_bogus = bbb' >> main.cf
touch -t 197101010000 main.cf
$(SHLIB_ENV) ./postconf -nc. >test58.tmp 2>&1 || true
diff test58.ref test58.tmp
rm -f main.cf master.cf test58.tmp
printfck: $(OBJS) $(PROG)
rm -rf printfck
mkdir printfck
@ -812,6 +896,8 @@ postconf_dbms.o: ../../include/htable.h
postconf_dbms.o: ../../include/mac_expand.h
postconf_dbms.o: ../../include/mac_parse.h
postconf_dbms.o: ../../include/mail_conf.h
postconf_dbms.o: ../../include/mail_params.h
postconf_dbms.o: ../../include/msg.h
postconf_dbms.o: ../../include/myflock.h
postconf_dbms.o: ../../include/name_code.h
postconf_dbms.o: ../../include/split_at.h

View File

@ -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).

View File

@ -288,7 +288,7 @@ extern char *pcf_expand_parameter_value(VSTRING *, int, const char *,
/*
* postconf_print.c.
*/
extern void pcf_print_line(VSTREAM *, int, const char *,...);
extern void PRINTFLIKE(3, 4) pcf_print_line(VSTREAM *, int, const char *,...);
/*
* postconf_unused.c.

View File

@ -223,12 +223,11 @@ static const char *pcf_check_mydomainname(void)
return (domain);
/*
* Use the hostname when it is not a FQDN ("foo"), or when the hostname
* actually is a domain name ("foo.com").
* Use a default domain when the hostname is not a FQDN ("foo").
*/
if (var_myhostname == 0)
pcf_get_myhostname();
if ((dot = strchr(var_myhostname, '.')) == 0 || strchr(dot + 1, '.') == 0)
if ((dot = strchr(var_myhostname, '.')) == 0)
return (domain = DEF_MYDOMAIN);
return (domain = mystrdup(dot + 1));
}

View File

@ -54,10 +54,12 @@
#include <split_at.h>
#include <mac_expand.h>
#include <dict.h>
#include <msg.h>
/* Global library. */
#include <mail_conf.h>
#include <mail_params.h>
#include <dict_proxy.h>
#include <dict_ldap.h>
#include <dict_mysql.h>
@ -142,35 +144,31 @@ static const PCF_DBMS_INFO pcf_dbms_info[] = {
0,
};
/* pcf_register_dbms_parameters - look for database_type:prefix_name */
/*
* Pseudo-databases that wrap around other databases.
*/
static const char *pcf_multi_dbms_names[] = {
"pipemap", "addr_pipemap", "unionmap", 0,
};
void pcf_register_dbms_parameters(const char *param_value,
/* pcf_register_dbms_helper - parse one possible database type:name */
static void pcf_register_dbms_helper(char *str_value,
const char *(flag_parameter) (const char *, int, PCF_MASTER_ENT *),
PCF_MASTER_ENT *local_scope)
{
const PCF_DBMS_INFO *dp;
char *bufp;
size_t len;
char *db_type;
char *prefix;
static VSTRING *buffer = 0;
static VSTRING *candidate = 0;
const char **cpp;
/*
* XXX This does not examine both sides of conditional macro expansion,
* and may expand the "wrong" conditional macros. This is the best we can
* do for legacy database configuration support.
* 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

View File

@ -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

View File

@ -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

View File

@ -651,6 +651,7 @@ DICT *dict_proxy_open(const char *map, int open_flags, int dict_flags)
static void post_jail_init(char *service_name, char **unused_argv)
{
const char *sep = ", \t\r\n";
const char *parens = "{}";
char *saved_filter;
char *bp;
char *type_name;
@ -679,7 +680,7 @@ static void post_jail_init(char *service_name, char **unused_argv)
saved_filter = bp = mystrdup(proxy_writer ? var_proxy_write_maps :
var_proxy_read_maps);
proxy_auth_maps = htable_create(13);
while ((type_name = mystrtok(&bp, sep)) != 0) {
while ((type_name = mystrtokq(&bp, sep, parens)) != 0) {
if (strncmp(type_name, PROXY_COLON, PROXY_COLON_LEN))
continue;
do {

View File

@ -497,7 +497,7 @@ static ARGV *smtpd_check_parse(int flags, const char *checks)
#define SMTPD_CHECK_PARSE_MAPS (1<<1)
#define SMTPD_CHECK_PARSE_ALL (~0)
while ((name = mystrtok(&bp, RESTRICTION_SEPARATORS)) != 0) {
while ((name = mystrtokq(&bp, RESTRICTION_SEPARATORS, "{}")) != 0) {
argv_add(argv, name, (char *) 0);
if ((flags & SMTPD_CHECK_PARSE_POLICY)
&& last && strcasecmp(last, CHECK_POLICY_SERVICE) == 0)
@ -1044,10 +1044,11 @@ static int reject_unknown_client(SMTPD_STATE *state)
if (msg_verbose)
msg_info("%s: %s %s", myname, state->name, state->addr);
/* RFC 7372: Email Authentication Status Codes. */
if (state->name_status != SMTPD_PEER_CODE_OK)
return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
state->name_status >= SMTPD_PEER_CODE_PERM ?
var_unk_client_code : 450, "4.7.1",
var_unk_client_code : 450, "4.7.25",
"Client host rejected: cannot find your hostname, [%s]",
state->addr));
return (SMTPD_CHECK_DUNNO);
@ -2437,7 +2438,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
*/
#define ADDROF(x) ((char *) &(x))
restrictions = argv_split(value, RESTRICTION_SEPARATORS);
restrictions = argv_splitq(value, RESTRICTION_SEPARATORS, "{}");
memcpy(ADDROF(savebuf), ADDROF(smtpd_check_buf), sizeof(savebuf));
status = setjmp(smtpd_check_buf);
if (status != 0) {
@ -3940,7 +3941,7 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
SMTPD_NAME_CLIENT, def_acl);
} else if (is_map_command(state, name, CHECK_REVERSE_CLIENT_ACL, &cpp)) {
status = check_namadr_access(state, *cpp, state->reverse_name, state->addr,
FULL, &found, state->namaddr,
FULL, &found, state->reverse_name,
SMTPD_NAME_REV_CLIENT, def_acl);
forbid_whitelist(state, name, status, state->reverse_name);
} else if (strcasecmp(name, REJECT_MAPS_RBL) == 0) {
@ -4030,21 +4031,21 @@ static int generic_checks(SMTPD_STATE *state, ARGV *restrictions,
} else if (is_map_command(state, name, CHECK_REVERSE_CLIENT_NS_ACL, &cpp)) {
if (strcasecmp(state->reverse_name, "unknown") != 0) {
status = check_server_access(state, *cpp, state->reverse_name,
T_NS, state->namaddr,
T_NS, state->reverse_name,
SMTPD_NAME_REV_CLIENT, def_acl);
forbid_whitelist(state, name, status, state->reverse_name);
}
} else if (is_map_command(state, name, CHECK_REVERSE_CLIENT_MX_ACL, &cpp)) {
if (strcasecmp(state->reverse_name, "unknown") != 0) {
status = check_server_access(state, *cpp, state->reverse_name,
T_MX, state->namaddr,
T_MX, state->reverse_name,
SMTPD_NAME_REV_CLIENT, def_acl);
forbid_whitelist(state, name, status, state->reverse_name);
}
} else if (is_map_command(state, name, CHECK_REVERSE_CLIENT_A_ACL, &cpp)) {
if (strcasecmp(state->reverse_name, "unknown") != 0) {
status = check_server_access(state, *cpp, state->reverse_name,
T_A, state->namaddr,
T_A, state->reverse_name,
SMTPD_NAME_REV_CLIENT, def_acl);
forbid_whitelist(state, name, status, state->reverse_name);
}

View File

@ -200,22 +200,23 @@ static void disconnect(SINK_STATE *state)
static void connect_event(int unused_event, char *context)
{
int sock = CAST_CHAR_PTR_TO_INT(context);
struct sockaddr sa;
SOCKADDR_SIZE len = sizeof(sa);
struct sockaddr_storage ss;
SOCKADDR_SIZE len = sizeof(ss);
struct sockaddr *sa = (struct sockaddr *) &ss;
SINK_STATE *state;
int fd;
if ((fd = accept(sock, &sa, &len)) >= 0) {
if ((fd = accept(sock, sa, &len)) >= 0) {
if (msg_verbose)
msg_info("connect (%s)",
#ifdef AF_LOCAL
sa.sa_family == AF_LOCAL ? "AF_LOCAL" :
sa->sa_family == AF_LOCAL ? "AF_LOCAL" :
#else
sa.sa_family == AF_UNIX ? "AF_UNIX" :
sa->sa_family == AF_UNIX ? "AF_UNIX" :
#endif
sa.sa_family == AF_INET ? "AF_INET" :
sa->sa_family == AF_INET ? "AF_INET" :
#ifdef AF_INET6
sa.sa_family == AF_INET6 ? "AF_INET6" :
sa->sa_family == AF_INET6 ? "AF_INET6" :
#endif
"unknown protocol family");
non_blocking(fd, NON_BLOCKING);

View File

@ -1298,31 +1298,32 @@ static void disconnect(SINK_STATE *state)
static void connect_event(int unused_event, char *unused_context)
{
struct sockaddr sa;
SOCKADDR_SIZE len = sizeof(sa);
struct sockaddr_storage ss;
SOCKADDR_SIZE len = sizeof(ss);
struct sockaddr *sa = (struct sockaddr *) &ss;
SINK_STATE *state;
int fd;
if ((fd = sane_accept(sock, &sa, &len)) >= 0) {
if ((fd = sane_accept(sock, sa, &len)) >= 0) {
/* Safety: limit the number of open sockets and capture files. */
if (++client_count == max_client_count)
event_disable_readwrite(sock);
state = (SINK_STATE *) mymalloc(sizeof(*state));
if (strchr((char *) proto_info->sa_family_list, sa.sa_family))
SOCKADDR_TO_HOSTADDR(&sa, len, &state->client_addr,
(MAI_SERVPORT_STR *) 0, sa.sa_family);
if (strchr((char *) proto_info->sa_family_list, sa->sa_family))
SOCKADDR_TO_HOSTADDR(sa, len, &state->client_addr,
(MAI_SERVPORT_STR *) 0, sa->sa_family);
else
strncpy(state->client_addr.buf, "local", sizeof("local"));
if (msg_verbose)
msg_info("connect (%s %s)",
#ifdef AF_LOCAL
sa.sa_family == AF_LOCAL ? "AF_LOCAL" :
sa->sa_family == AF_LOCAL ? "AF_LOCAL" :
#else
sa.sa_family == AF_UNIX ? "AF_UNIX" :
sa->sa_family == AF_UNIX ? "AF_UNIX" :
#endif
sa.sa_family == AF_INET ? "AF_INET" :
sa->sa_family == AF_INET ? "AF_INET" :
#ifdef AF_INET6
sa.sa_family == AF_INET6 ? "AF_INET6" :
sa->sa_family == AF_INET6 ? "AF_INET6" :
#endif
"unknown protocol family",
state->client_addr.buf);
@ -1340,7 +1341,7 @@ static void connect_event(int unused_event, char *unused_context)
state->delayed_args = 0;
/* Initialize file capture attributes. */
#ifdef AF_INET6
if (sa.sa_family == AF_INET6)
if (sa->sa_family == AF_INET6)
state->addr_prefix = "ipv6:";
else
#endif

View File

@ -38,7 +38,7 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \
dict_fail.c msg_rate_delay.c dict_surrogate.c warn_stat.c \
dict_sockmap.c line_number.c recv_pass_attr.c pass_accept.c \
poll_fd.c timecmp.c slmdb.c dict_pipe.c dict_random.c \
valid_utf8_hostname.c midna.c
valid_utf8_hostname.c midna.c argv_splitq.c balpar.c
OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \
attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \
@ -78,7 +78,7 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \
dict_fail.o msg_rate_delay.o dict_surrogate.o warn_stat.o \
dict_sockmap.o line_number.o recv_pass_attr.o pass_accept.o \
poll_fd.o timecmp.o $(NON_PLUGIN_MAP_OBJ) dict_pipe.o dict_random.o \
valid_utf8_hostname.o midna.o
valid_utf8_hostname.o midna.o argv_splitq.o balpar.o
# MAP_OBJ is for maps that may be dynamically loaded with dynamicmaps.cf.
# When hard-linking these, makedefs sets NON_PLUGIN_MAP_OBJ=$(MAP_OBJ),
# otherwise it sets the PLUGIN_* macros.
@ -763,6 +763,14 @@ argv_split.o: stringops.h
argv_split.o: sys_defs.h
argv_split.o: vbuf.h
argv_split.o: vstring.h
argv_splitq.o: argv.h
argv_splitq.o: argv_splitq.c
argv_splitq.o: msg.h
argv_splitq.o: mymalloc.h
argv_splitq.o: stringops.h
argv_splitq.o: sys_defs.h
argv_splitq.o: vbuf.h
argv_splitq.o: vstring.h
attr_clnt.o: attr.h
attr_clnt.o: attr_clnt.c
attr_clnt.o: attr_clnt.h
@ -847,6 +855,11 @@ auto_clnt.o: split_at.h
auto_clnt.o: sys_defs.h
auto_clnt.o: vbuf.h
auto_clnt.o: vstream.h
balpar.o: balpar.c
balpar.o: stringops.h
balpar.o: sys_defs.h
balpar.o: vbuf.h
balpar.o: vstring.h
base32_code.o: base32_code.c
base32_code.o: base32_code.h
base32_code.o: msg.h
@ -1156,6 +1169,7 @@ dict_random.o: msg.h
dict_random.o: myflock.h
dict_random.o: mymalloc.h
dict_random.o: myrand.h
dict_random.o: stringops.h
dict_random.o: sys_defs.h
dict_random.o: vbuf.h
dict_random.o: vstream.h

View File

@ -35,6 +35,10 @@ extern ARGV *argv_split(const char *, const char *);
extern ARGV *argv_split_count(const char *, const char *, ssize_t);
extern ARGV *argv_split_append(ARGV *, const char *, const char *);
extern ARGV *argv_splitq(const char *, const char *, const char *);
extern ARGV *argv_splitq_count(const char *, const char *, const char *, ssize_t);
extern ARGV *argv_splitq_append(ARGV *, const char *, const char *, const char *);
#define ARGV_FAKE_BEGIN(fake_argv, arg) { \
ARGV fake_argv; \
char *__fake_argv_args__[2]; \

View File

@ -0,0 +1,118 @@
/*++
/* NAME
/* argv_splitq 3
/* SUMMARY
/* string array utilities
/* SYNOPSIS
/* #include <argv.h>
/*
/* ARGV *argv_splitq(string, delim, parens)
/* const char *string;
/* const char *delim;
/* const char *parens;
/*
/* ARGV *argv_splitq_count(string, delim, parens, count)
/* const char *string;
/* const char *delim;
/* const char *parens;
/* ssize_t count;
/*
/* ARGV *argv_splitq_append(argv, string, delim, parens)
/* ARGV *argv;
/* const char *string;
/* const char *delim;
/* const char *parens;
/* DESCRIPTION
/* argv_splitq() breaks up \fIstring\fR into tokens according
/* to the delimiters specified in \fIdelim\fR, while avoiding
/* splitting text between matching parentheses. The result is
/* a null-terminated string array.
/*
/* argv_splitq_count() is like argv_splitq() but stops splitting
/* input after at most \fIcount\fR -1 times and leaves the
/* remainder, if any, in the last array element. It is an error
/* to specify a count < 1.
/*
/* argv_splitq_append() performs the same operation as argv_splitq(),
/* but appends the result to an existing string array.
/* SEE ALSO
/* mystrtokq(), safe string splitter.
/* DIAGNOSTICS
/* Fatal errors: memory allocation problem.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System libraries. */
#include <sys_defs.h>
#include <string.h>
/* Application-specific. */
#include "mymalloc.h"
#include "stringops.h"
#include "argv.h"
#include "msg.h"
/* argv_splitq - split string into token array */
ARGV *argv_splitq(const char *string, const char *delim, const char *parens)
{
ARGV *argvp = argv_alloc(1);
char *saved_string = mystrdup(string);
char *bp = saved_string;
char *arg;
while ((arg = mystrtokq(&bp, delim, parens)) != 0)
argv_add(argvp, arg, (char *) 0);
argv_terminate(argvp);
myfree(saved_string);
return (argvp);
}
/* argv_splitq_count - split string into token array */
ARGV *argv_splitq_count(const char *string, const char *delim,
const char *parens, ssize_t count)
{
ARGV *argvp = argv_alloc(1);
char *saved_string = mystrdup(string);
char *bp = saved_string;
char *arg;
if (count < 1)
msg_panic("argv_splitq_count: bad count: %ld", (long) count);
while (count-- > 1 && (arg = mystrtokq(&bp, delim, parens)) != 0)
argv_add(argvp, arg, (char *) 0);
if (*bp)
bp += strspn(bp, delim);
if (*bp)
argv_add(argvp, bp, (char *) 0);
argv_terminate(argvp);
myfree(saved_string);
return (argvp);
}
/* argv_splitq_append - split string into token array, append to array */
ARGV *argv_splitq_append(ARGV *argvp, const char *string, const char *delim,
const char *parens)
{
char *saved_string = mystrdup(string);
char *bp = saved_string;
char *arg;
while ((arg = mystrtokq(&bp, delim, parens)) != 0)
argv_add(argvp, arg, (char *) 0);
argv_terminate(argvp);
myfree(saved_string);
return (argvp);
}

56
postfix/src/util/balpar.c Normal file
View File

@ -0,0 +1,56 @@
/*++
/* NAME
/* balpar 3
/* SUMMARY
/* determine length of string in parentheses
/* SYNOPSIS
/* #include <stringops.h>
/*
/* size_t balpar(string, parens)
/* const char *string;
/* const char *parens;
/* DESCRIPTION
/* balpar() determines the length of a string enclosed in
/* the specified parentheses, zero in case of error.
/* SEE ALSO
/* A balpar() routine appears in Brian W. Kernighan, P.J. Plauger:
/* "Software Tools", Addison-Wesley 1976. This function is different.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include <sys_defs.h>
/* Utility library. */
#include <stringops.h>
/* balpar - return length of {text} */
size_t balpar(const char *string, const char *parens)
{
const char *cp;
int level;
int ch;
if (*string != parens[0])
return (0);
for (level = 1, cp = string + 1; (ch = *cp) != 0; cp++) {
if (ch == parens[1]) {
if (--level == 0)
return (cp - string + 1);
} else if (ch == parens[0]) {
level++;
}
}
return (0);
}

View File

@ -244,7 +244,7 @@ void dict_test(int, char **);
* functionality.
*/
extern int dict_allow_surrogate;
extern DICT *dict_surrogate(const char *, const char *, int, int, const char *,...);
extern DICT *PRINTFLIKE(5, 6) dict_surrogate(const char *, const char *, int, int, const char *,...);
/*
* This name is reserved for matchlist error handling.

View File

@ -343,13 +343,7 @@ static const DICT_OPEN_INFO dict_open_info[] = {
DICT_TYPE_SOCKMAP, dict_sockmap_open,
DICT_TYPE_FAIL, dict_fail_open,
DICT_TYPE_PIPE, dict_pipe_open,
#ifdef DICT_TYPE_PIPE_LEGACY
DICT_TYPE_PIPE_LEGACY, dict_pipe_open,
#endif
DICT_TYPE_RANDOM, dict_random_open,
#ifdef DICT_TYPE_RANDOM_LEGACY
DICT_TYPE_RANDOM_LEGACY, dict_random_open,
#endif
#ifndef USE_DYNAMIC_MAPS
#ifdef HAS_PCRE
DICT_TYPE_PCRE, dict_pcre_open,
@ -530,6 +524,9 @@ DICT_MAPNAMES_EXTEND_FN dict_mapnames_extend(DICT_MAPNAMES_EXTEND_FN new_cb)
#ifdef TEST
DEFINE_DICT_LMDB_MAP_SIZE;
DEFINE_DICT_DB_CACHE_SIZE;
/*
* Proof-of-concept test program.
*/

View File

@ -12,7 +12,7 @@
/* int dict_flags;
/* DESCRIPTION
/* dict_pipe_open() opens a pipeline of one or more tables.
/* Example: "\fBpipemap:\fI!type_1:name_1! ... !type_n:name_n\fR".
/* Example: "\fBpipemap:{\fItype_1:name_1, ... ,type_n:name_n\fR}".
/*
/* Each "pipemap:" query is given to the first table. Each
/* lookup result becomes the query for the next table in the
@ -20,9 +20,9 @@
/* When any table lookup produces no result, the pipeline
/* produces no result.
/*
/* The first ASCII character after "pipemap:" will be used as
/* the separator between the lookup tables that follow (do not
/* use space, ",", ":" or non-ASCII).
/* The first and last characters of the "pipemap:" table name
/* must be '{' and '}'. Within these, individual maps are
/* separated with comma or whitespace.
/*
/* The open_flags and dict_flags arguments are passed on to
/* the underlying dictionaries.
@ -114,7 +114,7 @@ DICT *dict_pipe_open(const char *name, int open_flags, int dict_flags)
DICT *dict;
int match_flags = 0;
struct DICT_OWNER aggr_owner;
char delim[2];
size_t len;
/*
* Clarity first. Let the optimizer worry about redundant code.
@ -135,32 +135,24 @@ DICT *dict_pipe_open(const char *name, int open_flags, int dict_flags)
open_flags, dict_flags,
"%s:%s map requires O_RDONLY access mode",
DICT_TYPE_PIPE, name));
if (name[0] == ':')
DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name,
open_flags, dict_flags,
"invalid list delimiter \"%c\" in \"%s:%s\"",
name[0], DICT_TYPE_PIPE, name));
/*
* Split the table name on the user-specified delimiter.
* Split the table name into its constituent parts.
*/
delim[0] = name[0]; /* XXX ASCII delimiter */
delim[1] = 0;
saved_name = mystrdup(name + 1); /* XXX ASCII delimiter */
if (*saved_name == 0)
if ((len = balpar(name, "{}")) == 0 || name[len] != 0
|| *(saved_name = mystrndup(name + 1, len - 2)) == 0)
DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name,
open_flags, dict_flags,
"bad syntax: \"%s:%s\"; "
"need \"%s:%stype:name%s...\"",
"need \"%s:{type:name...}\"",
DICT_TYPE_PIPE, name,
DICT_TYPE_PIPE, delim, delim));
DICT_TYPE_PIPE));
/*
* The least-trusted table in the pipeline determines the over-all trust
* level. The first table determines the pattern-matching flags.
*/
DICT_OWNER_AGGREGATE_INIT(aggr_owner);
argv = argv_split(saved_name, delim);
argv = argv_splitq(saved_name, ", \t\r\n", "{}");
for (cpp = argv->argv; (dict_type_name = *cpp) != 0; cpp++) {
if (msg_verbose)
msg_info("%s: %s", myname, dict_type_name);
@ -168,9 +160,9 @@ DICT *dict_pipe_open(const char *name, int open_flags, int dict_flags)
DICT_PIPE_RETURN(dict_surrogate(DICT_TYPE_PIPE, name,
open_flags, dict_flags,
"bad syntax: \"%s:%s\"; "
"need \"%s:%stype:name%s...\"",
"need \"%s:{type:name...}\"",
DICT_TYPE_PIPE, name,
DICT_TYPE_PIPE, delim, delim));
DICT_TYPE_PIPE));
if ((dict = dict_handle(dict_type_name)) == 0)
dict = dict_open(dict_type_name, open_flags, dict_flags);
dict_register(dict_type_name, dict);

View File

@ -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

View File

@ -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 <msg.h>
#include <mymalloc.h>
#include <myrand.h>
#include <stringops.h>
#include <dict_random.h>
/* Application-specific. */
@ -80,7 +81,7 @@ DICT *dict_random_open(const char *name, int open_flags, int dict_flags)
{
DICT_RANDOM *dict_random;
char *saved_name = 0;
char delim[2];
size_t len;
/*
* Clarity first. Let the optimizer worry about redundant code.
@ -101,17 +102,16 @@ DICT *dict_random_open(const char *name, int open_flags, int dict_flags)
DICT_TYPE_RANDOM, name));
/*
* Split the name on the user-specified delimiter.
* Split the name name into its constituent parts.
*/
delim[0] = name[0]; /* XXX ASCII delimiter */
delim[1] = 0;
saved_name = mystrdup(name + 1); /* XXX ASCII delimiter */
if (*saved_name == 0)
if ((len = balpar(name, "{}")) == 0 || name[len] != 0
|| *(saved_name = mystrndup(name + 1, len - 2)) == 0)
DICT_RANDOM_RETURN(dict_surrogate(DICT_TYPE_RANDOM, name,
open_flags, dict_flags,
"bad syntax: \"%s:%s\"; need \"%s:%svalue%s...\"",
"bad syntax: \"%s:%s\"; "
"need \"%s:{type:name...}\"",
DICT_TYPE_RANDOM, name,
DICT_TYPE_RANDOM, delim, delim));
DICT_TYPE_RANDOM));
/*
* Bundle up the result.
@ -121,9 +121,9 @@ DICT *dict_random_open(const char *name, int open_flags, int dict_flags)
dict_random->dict.lookup = dict_random_lookup;
dict_random->dict.close = dict_random_close;
dict_random->dict.flags = dict_flags | DICT_FLAG_PATTERN;
dict_random->replies = argv_split(saved_name, delim);
dict_random->replies = argv_splitq(saved_name, ", \t\r\n", "{}");
dict_random->dict.owner.status = DICT_OWNER_TRUSTED;
dict_random->dict.owner.uid = 0;
DICT_RANDOM_RETURN(DICT_DEBUG(&dict_random->dict));
DICT_RANDOM_RETURN(DICT_DEBUG (&dict_random->dict));
}

View File

@ -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

View File

@ -24,9 +24,6 @@
#include <dict_lmdb.h>
#include <dict_db.h>
DEFINE_DICT_LMDB_MAP_SIZE;
DEFINE_DICT_DB_CACHE_SIZE;
static NORETURN usage(char *myname)
{
msg_fatal("usage: %s type:file read|write|create [flags...]", myname);

View File

@ -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 <vstring.h>
#include <mymalloc.h>
#include <stringops.h>
#include <name_code.h>
#include <mac_parse.h>
#include <mac_expand.h>
@ -120,95 +145,366 @@ typedef struct {
char *context; /* caller context */
int status; /* findings */
int level; /* nesting level */
} MAC_EXP;
} MAC_EXP_CONTEXT;
/*
* Support for logical expressions.
*
* As of Postfix 2.2, ${attr-name?result} or ${attr-name:result} return the
* result respectively when the parameter value is non-empty, or when the
* parameter value is undefined or empty; support for the ternary ?:
* operator was anticipated, but not implemented for 10 years.
*
* To make ${logical-expr?result} and ${logical-expr:result} work as expected
* without breaking the way that ? and : work, logical expressions evaluate
* to a non-empty or empty value. It does not matter what non-empty value we
* use for TRUE. However we must not use the undefined (null pointer) value
* for FALSE - that would raise the MAC_PARSE_UNDEF flag.
*
* The value of a logical expression can be exposed with ${logical-expr}, i.e.
* a logical expression that is not followed by ? or : conditional
* expansion.
*/
#define MAC_EXP_BVAL_TRUE "true"
#define MAC_EXP_BVAL_FALSE ""
/*
* Relational operator. For now, we test only for (in)equality.
*/
#define MAC_EXP_OP_STR_EQ "=="
#define MAC_EXP_OP_STR_NE "!="
#define MAC_EXP_OP_STR_ANY "\"" MAC_EXP_OP_STR_EQ \
"\" or \"" MAC_EXP_OP_STR_NE "\""
#define MAC_EXP_OP_TOK_NONE 0
#define MAC_EXP_OP_TOK_EQ 1
#define MAC_EXP_OP_TOK_NE 2
static const NAME_CODE mac_exp_op_table[] =
{
MAC_EXP_OP_STR_EQ, MAC_EXP_OP_TOK_EQ,
MAC_EXP_OP_STR_NE, MAC_EXP_OP_TOK_NE,
0, MAC_EXP_OP_TOK_NONE,
};
/*
* The whitespace separator set.
*/
#define MAC_EXP_WHITESPACE " \t\r\n"
/* mac_exp_parse_error - report parse error, set error flag, return status */
static int PRINTFLIKE(2, 3) mac_exp_parse_error(MAC_EXP_CONTEXT *mc,
const char *fmt,...)
{
va_list ap;
va_start(ap, fmt);
vmsg_warn(fmt, ap);
va_end(ap);
return (mc->status |= MAC_PARSE_ERROR);
};
/* MAC_EXP_ERR_RETURN - report parse error, set error flag, return status */
#define MAC_EXP_ERR_RETURN(mc, fmt, ...) do { \
return (mac_exp_parse_error(mc, fmt, __VA_ARGS__)); \
} while (0)
/*
* Postfix 2.12 introduces support for {text} operands. Only with these do
* we support the ternary ?: operator and logical operators.
*
* We cannot support operators in random text, because that would break Postfix
* 2.11 compatibility. For example, with the expression "${name?value}", the
* value is random text that may contain ':', '?', '{' and '}' characters.
* In particular, with Postfix 2.2 .. 2.11, "${name??foo:{b}ar}" evaluates
* to "??foo:{b}ar" or empty. There are explicit tests in this directory and
* the postconf directory to ensure that Postfix 2.11 compatibility is
* maintained.
*
* Ideally, future Postfix configurations enclose random text operands inside
* {} braces. These allow whitespace around operands, which improves
* readability.
*/
/* MAC_EXP_FIND_LEFT_CURLY - skip over whitespace to '{', advance read ptr */
#define MAC_EXP_FIND_LEFT_CURLY(len, cp) \
((cp[len = strspn(cp, MAC_EXP_WHITESPACE)] == '{') ? \
(cp += len) : 0)
/* mac_exp_extract_curly_payload - balance {}, skip whitespace, return payload */
static char *mac_exp_extract_curly_payload(MAC_EXP_CONTEXT *mc, char **bp)
{
char *payload;
char *cp;
int level;
int ch;
/*
* Extract the payload and balance the {}. The caller is expected to skip
* leading whitespace before the {. See MAC_EXP_FIND_LEFT_CURLY().
*/
for (level = 1, cp = *bp, payload = ++cp; /* see below */ ; cp++) {
if ((ch = *cp) == 0) {
mac_exp_parse_error(mc, "unbalanced {} in attribute expression: "
"\"%s\"",
*bp);
return (0);
} else if (ch == '{') {
level++;
} else if (ch == '}') {
if (--level <= 0)
break;
}
}
*cp++ = 0;
/*
* Skip trailing whitespace after }.
*/
*bp = cp + strspn(cp, MAC_EXP_WHITESPACE);
return (payload);
}
/* mac_exp_parse_logical - parse logical expression, advance read ptr */
static int mac_exp_parse_logical(MAC_EXP_CONTEXT *mc, const char **lookup,
char **bp)
{
const char myname[] = "mac_exp_parse_logical";
char *cp = *bp;
VSTRING *left_op_buf;
VSTRING *rite_op_buf;
const char *left_op_strval;
const char *rite_op_strval;
char *op_strval;
size_t op_len;
int op_tokval;
int op_result;
size_t tmp_len;
/*
* Left operand. The caller is expected to skip leading whitespace before
* the {. See MAC_EXP_FIND_LEFT_CURLY().
*/
if ((left_op_strval = mac_exp_extract_curly_payload(mc, &cp)) == 0)
return (mc->status);
/*
* Operator. Todo: regexp operator.
*/
op_len = strspn(cp, "<>!=?+-*/~&|%"); /* for better diagnostics. */
op_strval = mystrndup(cp, op_len);
op_tokval = name_code(mac_exp_op_table, NAME_CODE_FLAG_NONE, op_strval);
myfree(op_strval);
if (op_tokval == MAC_EXP_OP_TOK_NONE)
MAC_EXP_ERR_RETURN(mc, "%s expected at: \"...%s}>>>%.20s\"",
MAC_EXP_OP_STR_ANY, left_op_strval, cp);
cp += op_len;
/*
* Right operand. Todo: syntax may depend on operator.
*/
if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp) == 0)
MAC_EXP_ERR_RETURN(mc, "\"{expression}\" expected at: "
"\"...{%s} %.*s>>>%.20s\"",
left_op_strval, (int) op_len, op_strval, cp);
if ((rite_op_strval = mac_exp_extract_curly_payload(mc, &cp)) == 0)
return (mc->status);
/*
* Evaluate the logical expression. Todo: regexp support.
*/
mc->status |=
mac_expand(left_op_buf = vstring_alloc(100), left_op_strval,
mc->flags, mc->filter, mc->lookup, mc->context);
mc->status |=
mac_expand(rite_op_buf = vstring_alloc(100), rite_op_strval,
mc->flags, mc->filter, mc->lookup, mc->context);
op_result =
strcmp(vstring_str(left_op_buf), vstring_str(rite_op_buf));
vstring_free(left_op_buf);
vstring_free(rite_op_buf);
if (mc->status & MAC_PARSE_ERROR)
return (mc->status);
/*
* Here, we fake up a non-empty or empty parameter value lookup result,
* for compatibility with the historical code that looks named parameter
* values.
*/
switch (op_tokval) {
case MAC_EXP_OP_TOK_EQ:
*lookup = op_result == 0 ?
MAC_EXP_BVAL_TRUE : MAC_EXP_BVAL_FALSE;
break;
case MAC_EXP_OP_TOK_NE:
*lookup = op_result != 0 ?
MAC_EXP_BVAL_TRUE : MAC_EXP_BVAL_FALSE;
break;
default:
msg_panic("%s: unknown macro operator code %d",
myname, op_tokval);
}
*bp = cp;
return (0);
}
/* mac_expand_callback - callback for mac_parse */
static int mac_expand_callback(int type, VSTRING *buf, char *ptr)
{
MAC_EXP *mc = (MAC_EXP *) ptr;
const char myname[] = "mac_expand_callback";
MAC_EXP_CONTEXT *mc = (MAC_EXP_CONTEXT *) ptr;
int lookup_mode;
const char *text;
const char *lookup;
char *cp;
int ch;
ssize_t len;
ssize_t res_len;
ssize_t tmp_len;
const char *res_iftrue;
const char *res_iffalse;
/*
* Sanity check.
*/
if (mc->level++ > 100) {
msg_warn("unreasonable macro call nesting: \"%s\"", vstring_str(buf));
mc->status |= MAC_PARSE_ERROR;
}
if (mc->level++ > 100)
mac_exp_parse_error(mc, "unreasonable macro call nesting: \"%s\"",
vstring_str(buf));
if (mc->status & MAC_PARSE_ERROR)
return (mc->status);
/*
* $Name etc. reference.
*
* In order to support expansion of lookup results, we must save the lookup
* result. We use the input buffer since it will not be needed anymore.
* Named parameter or logical expression. In case of a syntax error,
* return without doing damage, and issue a warning instead.
*/
if (type == MAC_PARSE_EXPR) {
cp = vstring_str(buf);
/*
* Look for the ? or : delimiter. In case of a syntax error, return
* without doing damage, and issue a warning instead.
* Logical expression. If recursion is disabled, perform only one
* level of $name expansion.
*/
for (cp = vstring_str(buf); /* void */ ; cp++) {
if ((ch = *cp) == 0) {
lookup_mode = MAC_EXP_MODE_USE;
break;
}
if (ch == '?' || ch == ':') {
*cp++ = 0;
lookup_mode = MAC_EXP_MODE_TEST;
break;
}
if (!ISALNUM(ch) && ch != '_') {
msg_warn("macro name syntax error: \"%s\"", vstring_str(buf));
mc->status |= MAC_PARSE_ERROR;
if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) {
if (mac_exp_parse_logical(mc, &lookup, &cp) != 0)
return (mc->status);
/*
* Look for the ? or : operator.
*/
if ((ch = *cp) != 0) {
if (ch != '?' && ch != ':')
MAC_EXP_ERR_RETURN(mc, "\"?\" or \":\" expected at: "
"\"...}>>>%.20s\"", cp);
cp++;
}
}
/*
* Look up the named parameter.
* Named parameter.
*/
text = mc->lookup(vstring_str(buf), lookup_mode, mc->context);
else {
/*
* Look for the ? or : operator. In case of a syntax error,
* return without doing damage, and issue a warning instead.
*/
for ( /* void */ ; /* void */ ; cp++) {
if ((ch = *cp) == 0) {
lookup_mode = MAC_EXP_MODE_USE;
break;
}
if (ch == '?' || ch == ':') {
*cp++ = 0;
lookup_mode = MAC_EXP_MODE_TEST;
break;
}
if (!ISALNUM(ch) && ch != '_') {
MAC_EXP_ERR_RETURN(mc, "attribute name syntax error at: "
"\"...%.*s>>>%.20s\"",
(int) (cp - vstring_str(buf)),
vstring_str(buf), cp);
}
}
/*
* Look up the named parameter. Todo: allow the lookup function
* to specify if the result is safe for $name expanson.
*/
lookup = mc->lookup(vstring_str(buf), lookup_mode, mc->context);
}
/*
* Perform the requested substitution.
* Return the requested result. After parsing the result operand
* following ?, we fall through to parse the result operand following
* :. This is necessary with the ternary ?: operator: first, with
* MAC_EXP_FLAG_SCAN to parse both result operands with mac_parse(),
* and second, to find garbage after any result operand. Without
* MAC_EXP_FLAG_SCAN the content of only one of the ?: result
* operands will be parsed with mac_parse(); syntax errors in the
* other operand will be missed.
*/
switch (ch) {
case '?':
if ((text != 0 && *text != 0) || (mc->flags & MAC_EXP_FLAG_SCAN))
mac_parse(cp, mac_expand_callback, (char *) mc);
break;
if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) {
if ((res_iftrue = mac_exp_extract_curly_payload(mc, &cp)) == 0)
return (mc->status);
} else {
res_iftrue = cp;
cp = ""; /* no left-over text */
}
if ((lookup != 0 && *lookup != 0) || (mc->flags & MAC_EXP_FLAG_SCAN))
mc->status |= mac_parse(res_iftrue, mac_expand_callback,
(char *) mc);
if (*cp == 0) /* end of input, OK */
break;
if (*cp != ':') /* garbage */
MAC_EXP_ERR_RETURN(mc, "\":\" expected at: "
"\"...%s}>>>%.20s\"", res_iftrue, cp);
cp += 1;
/* FALLTHROUGH: do not remove, see comment above. */
case ':':
if (text == 0 || *text == 0 || (mc->flags & MAC_EXP_FLAG_SCAN))
mac_parse(cp, mac_expand_callback, (char *) mc);
if (MAC_EXP_FIND_LEFT_CURLY(tmp_len, cp)) {
if ((res_iffalse = mac_exp_extract_curly_payload(mc, &cp)) == 0)
return (mc->status);
} else {
res_iffalse = cp;
cp = ""; /* no left-over text */
}
if (lookup == 0 || *lookup == 0 || (mc->flags & MAC_EXP_FLAG_SCAN))
mc->status |= mac_parse(res_iffalse, mac_expand_callback,
(char *) mc);
if (*cp != 0) /* garbage */
MAC_EXP_ERR_RETURN(mc, "unexpected input at: "
"\"...%s}>>>%.20s\"", res_iffalse, cp);
break;
default:
if (text == 0) {
case 0:
if (lookup == 0) {
mc->status |= MAC_PARSE_UNDEF;
} else if (*text == 0 || (mc->flags & MAC_EXP_FLAG_SCAN)) {
} else if (*lookup == 0 || (mc->flags & MAC_EXP_FLAG_SCAN)) {
/* void */ ;
} else if (mc->flags & MAC_EXP_FLAG_RECURSE) {
vstring_strcpy(buf, text);
mac_parse(vstring_str(buf), mac_expand_callback, (char *) mc);
vstring_strcpy(buf, lookup);
mc->status |= mac_parse(vstring_str(buf), mac_expand_callback,
(char *) mc);
} else {
len = VSTRING_LEN(mc->result);
vstring_strcat(mc->result, text);
res_len = VSTRING_LEN(mc->result);
vstring_strcat(mc->result, lookup);
if (mc->flags & MAC_EXP_FLAG_PRINTABLE) {
printable(vstring_str(mc->result) + len, '_');
printable(vstring_str(mc->result) + res_len, '_');
} else if (mc->filter) {
cp = vstring_str(mc->result) + len;
cp = vstring_str(mc->result) + res_len;
while (*(cp += strspn(cp, mc->filter)))
*cp++ = '_';
}
}
break;
default:
msg_panic("%s: unknown operator code %d", myname, ch);
}
}
@ -229,7 +525,7 @@ int mac_expand(VSTRING *result, const char *pattern, int flags,
const char *filter,
MAC_EXP_LOOKUP_FN lookup, char *context)
{
MAC_EXP mc;
MAC_EXP_CONTEXT mc;
int status;
/*

View File

@ -1,18 +1,60 @@
name1 = name1-value
name2 =
${name1?name 1 defined, |$name1|$name2|}
${name1:name 1 undefined, |$name1|$name2|}
${name2?name 2 defined, |$name1|$name2|}
${name2:name 2 undefined, |$name1|$name2|}
|$name1|$name2|
$(name1
$(name )
${${name1} != {}?name 1 defined, |$name1|$name2|}
${ ${name1} != {}?name 1 defined, |$name1|$name2|}
${ ${name1} ?name 1 defined, |$name1|$name2|}
${{$name1} ? {name 1 defined, |$name1|$name2|} : {name 1 undefined, |$name1|$name2|} }
${x{$name1} != {}?{name 1 defined, |$name1|$name2|}}
${{$name1}x?{name 1 defined, |$name1|$name2|}}
${{$name1} != {}x{name 1 defined, |$name1|$name2|}}
${{$name1} != {}?x{name 1 defined, |$name1|$name2|}}
${{$name2} != {}?x{name 2 defined, |$name1|$name2|}:{name 2 undefined, |$name1|$name2|}}
${{$name1} != {}?{name 1 defined, |$name1|$name2|}x}
${{$name1} != {}?{name 1 defined, |$name1|$name2|}x:{name 1 undefined, |$name1|$name2|}}
${{$name1} != {}?{name 1 defined, |$name1|$name2|}:x{name 1 undefined, |$name1|$name2|}}
${{$name2} != {}?{name 2 defined, |$name1|$name2|}:x{name 2 undefined, |$name1|$name2|}}
${{text}}
${{text}?{non-empty}:{empty}}
${{text} = {}}
${{${ name1}} == {}}
${name1?{${ name1}}:{${name2}}}
${name2?{${ name1}}:{${name2}}}
${name2?{${name1}}:{${ name2}}}
${name2:{${name1}}:{${name2}}}
${name2?{${name1}}?{${name2}}}
${{${name1?bug:test}} != {bug:test}?{Error: NOT}:{Good:}} Postfix 2.11 compatible
${{${name1??bug}} != {?bug}?{Error: NOT}:{Good:}} Postfix 2.11 compatible
${{${name2::bug}} != {:bug}?{Error: NOT}:{Good:}} Postfix 2.11 compatible
${{xx}==(yy)?{oops}:{phew}}
name2 = name2-value
name1 = name1-value
${name1?name 1 defined, |$name1|$name2|}
${name1:name 1 undefined, |$name1|$name2|}
${name2?name 2 defined, |$name1|$name2|}
${name2:name 2 undefined, |$name1|$name2|}
|$name1|$name2|
${{$name1} != {}?{name 1 defined, |$name1|$name2|}}
${{$name1} != {}:{name 1 undefined, |$name1|$name2|}}
${{$name1} == {}?{name 1 undefined, |$name1|$name2|}}
${{$name1} == {}:{name 1 defined, |$name1|$name2|}}
${name1?{name 1 defined, |$name1|$name2|}:{name 1 undefined, |$name1|$name2|}}
${{$name1} != {}?{name 1 defined, |$name1|$name2|}:{name 1 undefined, |$name1|$name2|}}
${{$name1} != {} ? {name 1 defined, |$name1|$name2|} : {name 1 undefined, |$name1|$name2|}}
${{$name1} != {}?{name 1 defined, |$name1|$name2|}:name 1 undefined, |$name1|$name2|}
${{$name1} != {} ? {name 1 defined, |$name1|$name2|} : name 1 undefined, |$name1|$name2|}
${{$name1} != {}}
${{$name1} == {}}
${{$name2} != {}?{name 2 defined, |$name1|$name2|}}
${{$name2} != {}:{name 2 undefined, |$name1|$name2|}}
${{$name2} == {}?{name 2 undefined, |$name1|$name2|}}
${{$name2} == {}:{name 2 defined, |$name1|$name2|}}
${name2?{name 2 defined, |$name1|$name2|}:{name 2 undefined, |$name1|$name2|}}
${{$name2} != {}?{name 2 defined, |$name1|$name2|}:{name 2 undefined, |$name1|$name2|}}
${{$name2} != {} ? {name 2 defined, |$name1|$name2|} : {name 2 undefined, |$name1|$name2|}}
${{$name2} != {}?{name 2 defined, |$name1|$name2|}:name 2 undefined, |$name1|$name2|}
${{$name2} != {} ? {name 2 defined, |$name1|$name2|} : name 2 undefined, |$name1|$name2|}
${{$name2} != {}}
${{$name2} == {}}

View File

@ -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

View File

@ -114,7 +114,7 @@ static ARGV *match_list_parse(ARGV *list, char *string, int init_match)
* /filename contents are expanded in-line. To support !/filename we
* prepend the negation operator to each item from the file.
*/
while ((start = mystrtok(&bp, delim)) != 0) {
while ((start = mystrtokq(&bp, delim, "{}")) != 0) {
if (*start == '#') {
msg_warn("%s: comment at end of line is not supported: %s %s",
myname, start, bp);

View File

@ -43,7 +43,7 @@ extern void msg_error_clear(void);
extern MSG_CLEANUP_FN msg_cleanup(MSG_CLEANUP_FN);
extern void PRINTFLIKE(4, 5) msg_rate_delay(time_t *, int,
void (*log_fn) (const char *,...),
void PRINTFLIKE(1, 2) (*log_fn) (const char *,...),
const char *,...);
/* LICENSE

View File

@ -9,11 +9,21 @@
/* char *mystrtok(bufp, delimiters)
/* char **bufp;
/* const char *delimiters;
/*
/* char *mystrtokq(bufp, delimiters, parens)
/* char **bufp;
/* const char *delimiters;
/* const char *parens;
/* DESCRIPTION
/* mystrtok() splits a buffer on the specified \fIdelimiters\fR.
/* Tokens are delimited by runs of delimiters, so this routine
/* cannot return zero-length tokens.
/*
/* mystrtokq() is like mystrtok() but will not split text
/* between balanced parentheses. \fIparens\fR specifies the
/* opening and closing parenthesis (one of each). The set of
/* \fIparens\fR must be distinct from the set of \fIdelimiters\fR.
/*
/* The \fIbufp\fR argument specifies the start of the search; it
/* is updated with each call. The input is destroyed.
/*
@ -65,6 +75,41 @@ char *mystrtok(char **src, const char *sep)
return (start);
}
/* mystrtokq - safe tokenizer with quoting support */
char *mystrtokq(char **src, const char *sep, const char *parens)
{
char *start = *src;
static char *cp;
int ch;
int level;
/*
* Skip over leading delimiters.
*/
start += strspn(start, sep);
if (*start == 0) {
*src = start;
return (0);
}
/*
* Parse out the next token.
*/
for (level = 0, cp = start; (ch = *(unsigned char *) cp) != 0; cp++) {
if (ch == parens[0]) {
level++;
} else if (level > 0 && ch == parens[1]) {
level--;
} else if (level == 0 && strchr(sep, ch) != 0) {
*cp++ = 0;
break;
}
}
*src = cp;
return (start);
}
#ifdef TEST
/*
@ -80,10 +125,15 @@ int main(void)
char *start;
char *str;
while (vstring_fgets(vp, VSTREAM_IN)) {
while (vstring_fgets(vp, VSTREAM_IN) && VSTRING_LEN(vp) > 0) {
start = vstring_str(vp);
while ((str = mystrtok(&start, " \t\r\n")) != 0)
vstream_printf(">%s<\n", str);
if (strchr(start, '{') == 0) {
while ((str = mystrtok(&start, " \t\r\n")) != 0)
vstream_printf(">%s<\n", str);
} else {
while ((str = mystrtokq(&start, " \t\r\n", "{}")) != 0)
vstream_printf(">%s<\n", str);
}
vstream_fflush(VSTREAM_OUT);
}
vstring_free(vp);

View File

@ -28,6 +28,7 @@ extern char *skipblanks(const char *);
extern char *trimblanks(char *, int);
extern char *concatenate(const char *,...);
extern char *mystrtok(char **, const char *);
extern char *mystrtokq(char **, const char *, const char *);
extern char *translit(char *, const char *, const char *);
#ifndef HAVE_BASENAME
#define basename postfix_basename
@ -43,6 +44,7 @@ extern int allspace(const char *);
extern int allascii(const char *);
extern const char *split_nameval(char *, char **, char **);
extern int valid_utf8_string(const char *, ssize_t);
extern size_t balpar(const char *, const char *);
/* LICENSE
/* .ad

View File

@ -151,7 +151,7 @@ VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap)
* strings, since we are ging to let sprintf() do the hard work.
* In regular expression notation, we recognize:
*
* %-?0?([0-9]+|\*)?\.?([0-9]+|\*)?l?[a-zA-Z]
* %-?0?([0-9]+|\*)?\.(?[0-9]+|\*)?l?[a-zA-Z]
*
* which includes some combinations that do not make sense. Garbage
* in, garbage out.
@ -178,21 +178,24 @@ VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap)
msg_warn("%s: bad width %d in %.50s", myname, width, format);
width = 0;
}
if (*cp == '.') /* width/precision separator */
if (*cp == '.') { /* width/precision separator */
VSTRING_ADDCH(fmt, *cp++);
if (*cp == '*') { /* dynamic precision */
prec = va_arg(ap, int);
VSTRING_ADDNUM(fmt, prec);
cp++;
} else { /* hard-coded precision */
for (prec = 0; ch = *cp, ISDIGIT(ch); cp++) {
prec = prec * 10 + ch - '0';
VSTRING_ADDCH(fmt, ch);
if (*cp == '*') { /* dynamic precision */
prec = va_arg(ap, int);
VSTRING_ADDNUM(fmt, prec);
cp++;
} else { /* hard-coded precision */
for (prec = 0; ch = *cp, ISDIGIT(ch); cp++) {
prec = prec * 10 + ch - '0';
VSTRING_ADDCH(fmt, ch);
}
}
}
if (prec < 0) {
msg_warn("%s: bad precision %d in %.50s", myname, prec, format);
prec = 0;
if (prec < 0) {
msg_warn("%s: bad precision %d in %.50s", myname, prec, format);
prec = -1;
}
} else {
prec = -1;
}
if ((long_flag = (*cp == 'l')) != 0)/* long whatever */
VSTRING_ADDCH(fmt, *cp++);
@ -210,7 +213,7 @@ VBUF *vbuf_print(VBUF *bp, const char *format, va_list ap)
switch (*cp) {
case 's': /* string-valued argument */
s = va_arg(ap, char *);
if (prec > 0 || (width > 0 && width > strlen(s))) {
if (prec >= 0 || (width > 0 && width > strlen(s))) {
if (VBUF_SPACE(bp, (width > prec ? width : prec) + INT_SPACE))
return (bp);
sprintf((char *) bp->ptr, vstring_str(fmt), s);