From: Wietse Z Venema Date: Tue, 27 Jan 2026 05:00:00 +0000 (-0500) Subject: postfix-3.12-20260127 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=refs%2Fheads%2Fmaster;p=thirdparty%2Fpostfix.git postfix-3.12-20260127 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 9a8801222..f0e214b2b 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -30338,3 +30338,43 @@ Apologies for any names omitted. Cleanup: unbroke Valgrind tests. Files: postmulti/Makefile.in, proxymap/Makefile.in. + + Cleanup: missing "4.7.1" enhanced status code in Milter + "shutdown" response. File: milter/milter8.c. + +20260119 + + Cleanup: change the domain name length limit from 1024 to + MAXDNAME (defined in ) which is 1025. File: + src/dns/dns.h. + +20260126 + + Cleanup: postconf logged errors when a 'raw parameter' $name + (such as $smtpd_expansion_filter) appeared in the value of + a non-raw parameter. The solution was to selectively disable + mac_expand() recursion for 'raw' parameters. Files: + util/mac_expand.[hc], postconf/postconf_lookup.c + +20260127 + + Usability: when building on a Linux system that has no + Berkeley DB header file installed, "make makefiles" will + now suggest building with -DNO_DB instead. File: makedefs. + + Usability: when building with -DNO_DB, makedefs will now + remind the user to override the default_database_type and + default_cache_db_type with names other than 'hash' or + 'btree'. File: makedefs. + + Added notes about MariaDB support, suggested by Otto + Kekäläinen. Files: proto/MYSQL_README.html proto/mysql_table. + + Feature: "postconf -PP service/type/parameter" will list + parameters that are not set in master.cf. This is useful + because the value of such a parameter may depend on some + other parameter setting in master.cf. The initial use case + is to support tools that scan Postfix configurations for + deprecated lookup tables. A typical example is "postconf + -PPx '*/*/proxy_read_maps". Files: postconf/postconf.[hc], + postconf/postconf_master.c, postconf/Makefile.in. diff --git a/postfix/README_FILES/MYSQL_README b/postfix/README_FILES/MYSQL_README index 8b8b3b190..c2a62797b 100644 --- a/postfix/README_FILES/MYSQL_README +++ b/postfix/README_FILES/MYSQL_README @@ -4,11 +4,15 @@ PPoossttffiixx MMyySSQQLL HHoowwttoo IInnttrroodduuccttiioonn -The Postfix mysql map type allows you to hook up Postfix to a MySQL database. -This implementation allows for multiple mysql databases: you can use one for a -virtual(5) table, one for an access(5) table, and one for an aliases(5) table -if you want. You can specify multiple servers for the same database, so that -Postfix can switch to a good database server if one goes bad. +The Postfix mysql map type allows you to hook up Postfix to MySQL or MariaDB +databases. This implementation allows for multiple databases: you can use one +for a virtual(5) table, one for an access(5) table, and one for an aliases(5) +table if you want. You can specify multiple servers for the same database, so +that Postfix can switch to a good database server if one goes bad. + +Even though the component name is 'mysql' in Postfix, MariaDB/MySQL client and +servers can be used interchangeably as the functionality Postfix relies on is +available in both. However, the code bases may still diverge in the future. Busy mail servers using mysql maps will generate lots of concurrent mysql clients, so the mysql server(s) should be run with this fact in mind. You can @@ -30,6 +34,11 @@ obtained from: https://www.mysql.com/downloads/ +Many Linux distributions compile Postfix MySQL support using the equivalent +client library from MariaDB: + + https://mariadb.org/connector-c/ + In order to build Postfix with mysql map support, you will need to add - DHAS_MYSQL and -I for the directory containing the mysql headers, and the mysqlclient library (and libm) to AUXLIBS_MYSQL, for example: diff --git a/postfix/auxiliary/fix-grep/fix-grep.sh b/postfix/auxiliary/fix-grep/fix-grep.sh index 4faf3443e..271986158 100755 --- a/postfix/auxiliary/fix-grep/fix-grep.sh +++ b/postfix/auxiliary/fix-grep/fix-grep.sh @@ -1,7 +1,7 @@ #!/bin/sh # Fix grep -[EF] for systems that require the historical forms egrep -# and fgrep. Run this script in the top-level Postfix directory as +# and fgrep. Run this script from the top-level Postfix directory as # sh auxiliary/fix-grep/fix-grep.sh # Use only historical grep syntax. diff --git a/postfix/html/MYSQL_README.html b/postfix/html/MYSQL_README.html index f8ced9330..ff76d68fa 100644 --- a/postfix/html/MYSQL_README.html +++ b/postfix/html/MYSQL_README.html @@ -5,7 +5,7 @@ -Postfix MySQL Howto +Postfix MySQL/MariaDB Howto @@ -20,13 +20,18 @@

Introduction

-

The Postfix mysql map type allows you to hook up Postfix to a -MySQL database. This implementation allows for multiple mysql +

The Postfix mysql map type allows you to hook up Postfix to +MySQL or MariaDB databases. This implementation allows for multiple databases: you can use one for a virtual(5) table, one for an access(5) table, and one for an aliases(5) table if you want. You can specify multiple servers for the same database, so that Postfix can switch to a good database server if one goes bad.

+

Even though the component name is 'mysql' in Postfix, MariaDB/MySQL +client and servers can be used interchangeably as the functionality +Postfix relies on is available in both. However, the code bases may +still diverge in the future.

+

Busy mail servers using mysql maps will generate lots of concurrent mysql clients, so the mysql server(s) should be run with this fact in mind. You can reduce the number of concurrent mysql @@ -50,6 +55,13 @@ which can be obtained from:

https://www.mysql.com/downloads/

+

Many Linux distributions compile Postfix MySQL support using +the equivalent client library from MariaDB:

+ +
+

https://mariadb.org/connector-c/

+
+

In order to build Postfix with mysql map support, you will need to add -DHAS_MYSQL and -I for the directory containing the mysql headers, and the mysqlclient library (and libm) to AUXLIBS_MYSQL, for example:

diff --git a/postfix/html/mysql_table.5.html b/postfix/html/mysql_table.5.html index 8c8e5588e..5fecce71e 100644 --- a/postfix/html/mysql_table.5.html +++ b/postfix/html/mysql_table.5.html @@ -8,7 +8,7 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) NAME - mysql_table - Postfix MySQL client configuration + mysql_table - Postfix MySQL/MariaDB client configuration SYNOPSIS postmap -q "string" mysql:/etc/postfix/filename @@ -27,49 +27,54 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) The file /etc/postfix/mysql-aliases.cf has the same format as the Post- fix main.cf file, and can specify the parameters described below. + Even though the component name is 'mysql' in Postfix, MariaDB/MySQL + client and servers can be used interchangeably as the functionality + Postfix relies on is available in both. However, their code bases may + still diverge in the future. + LIST MEMBERSHIP - When using SQL to store lists such as $mynetworks, $mydestination, - $relay_domains, $local_recipient_maps, etc., it is important to under- + When using SQL to store lists such as $mynetworks, $mydestination, + $relay_domains, $local_recipient_maps, etc., it is important to under- stand that the table must store each list member as a separate key. The - table lookup verifies the *existence* of the key. See "Postfix lists + table lookup verifies the *existence* of the key. See "Postfix lists versus tables" in the DATABASE_README document for a discussion. - Do NOT create tables that return the full list of domains in $mydesti- + Do NOT create tables that return the full list of domains in $mydesti- nation or $relay_domains etc., or IP addresses in $mynetworks. DO create tables with each matching item as a key and with an arbitrary - value. With SQL databases it is not uncommon to return the key itself + value. With SQL databases it is not uncommon to return the key itself or a constant value. MYSQL PARAMETERS - hosts The hosts that Postfix will try to connect to and query from. + hosts The hosts that Postfix will try to connect to and query from. Specify unix: for UNIX domain sockets, inet: for TCP connections (default). Examples: hosts = inet:host1.some.domain inet:host2.some.domain:port hosts = host1.some.domain host2.some.domain:port hosts = unix:/file/name - The hosts are tried in random order, with all connections over + The hosts are tried in random order, with all connections over UNIX domain sockets being tried before those over TCP. The con- - nections are automatically closed after being idle for about 1 + nections are automatically closed after being idle for about 1 minute, and are re-opened as necessary. Postfix versions 2.0 and earlier do not randomize the host order. NOTE: if you specify localhost as a hostname (even if you prefix - it with inet:), MySQL will connect to the default UNIX domain + it with inet:), MySQL will connect to the default UNIX domain socket. In order to instruct MySQL to connect to localhost over TCP you have to specify hosts = 127.0.0.1 - NOTE: if the hosts setting specifies only one server, this - client assumes that the target is a load balancer and will - reconnect immediately after a single failure. With Postfix ver- + NOTE: if the hosts setting specifies only one server, this + client assumes that the target is a load balancer and will + reconnect immediately after a single failure. With Postfix ver- sions 3.9 and earlier, specify the same server twice. user password - The user name and password to log into the mysql server. Exam- + The user name and password to log into the mysql server. Exam- ple: user = someone password = some_password @@ -78,15 +83,15 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) dbname = customer_database charset (default: utf8mb4) - The default MySQL client character set; this also implies the + The default MySQL client character set; this also implies the collation order. - This parameter is available with Postfix 3.9 and later. With - earlier Postfix versions, the default was chosen by the MySQL + This parameter is available with Postfix 3.9 and later. With + earlier Postfix versions, the default was chosen by the MySQL implementation (utf8mb4 as of MySQL 8.0, latin1 historically). idle_interval (default: 60) - The number of seconds after which an idle database connection + The number of seconds after which an idle database connection will be closed. This feature is available in Postfix 3.9 and later. @@ -95,63 +100,63 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) The number of seconds that a database connection will be skipped after an error. - NOTE: if the hosts setting specifies only one server, this - client assumes that the target is a load balancer and will - reconnect immediately after a single failure. With Postfix ver- + NOTE: if the hosts setting specifies only one server, this + client assumes that the target is a load balancer and will + reconnect immediately after a single failure. With Postfix ver- sions 3.9 and earlier, specify the same server twice. This feature is available in Postfix 3.9 and later. - query The SQL query template used to search the database, where %s is - a substitute for the address Postfix is trying to resolve, e.g. + query The SQL query template used to search the database, where %s is + a substitute for the address Postfix is trying to resolve, e.g. query = SELECT replacement FROM aliases WHERE mailbox = '%s' - By default, every query must return a result set (instead of - storing its results in a table); with "require_result_set = no" - (Postfix 3.2 and later), the absence of a result set is treated + By default, every query must return a result set (instead of + storing its results in a table); with "require_result_set = no" + (Postfix 3.2 and later), the absence of a result set is treated as "not found". This parameter supports the following '%' expansions: %% This is replaced by a literal '%' character. - %s This is replaced by the input key. SQL quoting is used - to make sure that the input key does not add unexpected + %s This is replaced by the input key. SQL quoting is used + to make sure that the input key does not add unexpected metacharacters. %u When the input key is an address of the form user@domain, - %u is replaced by the SQL quoted local part of the - address. Otherwise, %u is replaced by the entire search - string. If the localpart is empty, the query is sup- + %u is replaced by the SQL quoted local part of the + address. Otherwise, %u is replaced by the entire search + string. If the localpart is empty, the query is sup- pressed and returns no results. %d When the input key is an address of the form user@domain, - %d is replaced by the SQL quoted domain part of the - address. Otherwise, the query is suppressed and returns + %d is replaced by the SQL quoted domain part of the + address. Otherwise, the query is suppressed and returns no results. %[SUD] The upper-case equivalents of the above expansions behave - in the query parameter identically to their lower-case - counter-parts. With the result_format parameter (see - below), they expand the input key rather than the result + in the query parameter identically to their lower-case + counter-parts. With the result_format parameter (see + below), they expand the input key rather than the result value. - %[1-9] The patterns %1, %2, ... %9 are replaced by the corre- - sponding most significant component of the input key's - domain. If the input key is user@mail.example.com, then + %[1-9] The patterns %1, %2, ... %9 are replaced by the corre- + sponding most significant component of the input key's + domain. If the input key is user@mail.example.com, then %1 is com, %2 is example and %3 is mail. If the input key - is unqualified or does not have enough domain components - to satisfy all the specified patterns, the query is sup- + is unqualified or does not have enough domain components + to satisfy all the specified patterns, the query is sup- pressed and returns no results. - The domain parameter described below limits the input keys to - addresses in matching domains. When the domain parameter is + The domain parameter described below limits the input keys to + addresses in matching domains. When the domain parameter is non-empty, SQL queries for unqualified addresses or addresses in non-matching domains are suppressed and return no results. - This parameter is available with Postfix 2.2. In prior releases - the SQL query was built from the separate parameters: - select_field, table, where_field and additional_conditions. The + This parameter is available with Postfix 2.2. In prior releases + the SQL query was built from the separate parameters: + select_field, table, where_field and additional_conditions. The mapping from the old parameters to the equivalent query is: SELECT [select_field] @@ -159,50 +164,50 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) WHERE [where_field] = '%s' [additional_conditions] - The '%s' in the WHERE clause expands to the escaped search - string. With Postfix 2.2 these legacy parameters are used if + The '%s' in the WHERE clause expands to the escaped search + string. With Postfix 2.2 these legacy parameters are used if the query parameter is not specified. NOTE: DO NOT put quotes around the query parameter. result_format (default: %s) Format template applied to result attributes. Most commonly used - to append (or prepend) text to the result. This parameter sup- + to append (or prepend) text to the result. This parameter sup- ports the following '%' expansions: %% This is replaced by a literal '%' character. - %s This is replaced by the value of the result attribute. + %s This is replaced by the value of the result attribute. When result is empty it is skipped. %u When the result attribute value is an address of the form - user@domain, %u is replaced by the local part of the - address. When the result has an empty localpart it is + user@domain, %u is replaced by the local part of the + address. When the result has an empty localpart it is skipped. - %d When a result attribute value is an address of the form - user@domain, %d is replaced by the domain part of the - attribute value. When the result is unqualified it is + %d When a result attribute value is an address of the form + user@domain, %d is replaced by the domain part of the + attribute value. When the result is unqualified it is skipped. %[SUD1-9] - The upper-case and decimal digit expansions interpolate - the parts of the input key rather than the result. Their - behavior is identical to that described with query, and - in fact because the input key is known in advance, - queries whose key does not contain all the information - specified in the result template are suppressed and + The upper-case and decimal digit expansions interpolate + the parts of the input key rather than the result. Their + behavior is identical to that described with query, and + in fact because the input key is known in advance, + queries whose key does not contain all the information + specified in the result template are suppressed and return no results. For example, using "result_format = smtp:[%s]" allows one to use a mailHost attribute as the basis of a transport(5) table. After - applying the result format, multiple values are concatenated as + applying the result format, multiple values are concatenated as comma separated strings. The expansion_limit and parameter - explained below allows one to restrict the number of values in + explained below allows one to restrict the number of values in the result, which is especially useful for maps that must return at most one value. - The default value %s specifies that each result value should be + The default value %s specifies that each result value should be used as is. This parameter is available with Postfix 2.2 and later. @@ -210,15 +215,15 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) NOTE: DO NOT put quotes around the result format! domain (default: no domain list) - This is a list of domain names, paths to files, or "type:table" + This is a list of domain names, paths to files, or "type:table" databases. When specified, only fully qualified search keys with - a *non-empty* localpart and a matching domain are eligible for + a *non-empty* localpart and a matching domain are eligible for lookup: 'user' lookups, bare domain lookups and "@domain" - lookups are not performed. This can significantly reduce the + lookups are not performed. This can significantly reduce the query load on the MySQL server. domain = postfix.org, hash:/etc/postfix/searchdomains - It is best not to use SQL to store the domains eligible for SQL + It is best not to use SQL to store the domains eligible for SQL lookups. This parameter is available with Postfix 2.2 and later. @@ -227,43 +232,43 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) the input keys are always unqualified. expansion_limit (default: 0) - A limit on the total number of result elements returned (as a + A limit on the total number of result elements returned (as a comma separated list) by a lookup against the map. A setting of - zero disables the limit. Lookups fail with a temporary error if - the limit is exceeded. Setting the limit to 1 ensures that + zero disables the limit. Lookups fail with a temporary error if + the limit is exceeded. Setting the limit to 1 ensures that lookups do not return multiple values. option_file - Read options from the given file instead of the default my.cnf - location. This reads options from the [client] option group, + Read options from the given file instead of the default my.cnf + location. This reads options from the [client] option group, optionally followed by options from the group given with option_group. This parameter is available with Postfix 2.11 and later. option_group (default: Postfix >=3.2: client, <= 3.1: empty) - Read options from the given group of the mysql options file, + Read options from the given group of the mysql options file, after reading options from the [client] group. - Postfix 3.2 and later read [client] option group settings by - default. To disable this specify no option_file and specify + Postfix 3.2 and later read [client] option group settings by + default. To disable this specify no option_file and specify "option_group =" (i.e. an empty value). - Postfix 3.1 and earlier don't read [client] option group set- - tings unless a non-empty option_file or option_group value are + Postfix 3.1 and earlier don't read [client] option group set- + tings unless a non-empty option_file or option_group value are specified. To enable this, specify, for example, "option_group = client". This parameter is available with Postfix 2.11 and later. require_result_set (default: yes) - If "yes", require that every query returns a result set. If + If "yes", require that every query returns a result set. If "no", treat the absence of a result set as "not found". This parameter is available with Postfix 3.2 and later. TLS-RELATED SETTINGS - See https://dev.mysql.com/doc/c-api/en/mysql-options.html or + See https://dev.mysql.com/doc/c-api/en/mysql-options.html or https://mariadb.com/kb/en/mysql_optionsv/ for details of the underlying MYSQL_OPT_SSL_* features. @@ -273,19 +278,19 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) This parameter is available with Postfix 2.11 and later. tls_key_file - File containing the private key corresponding to tls_cert_file. + File containing the private key corresponding to tls_cert_file. This parameter is available with Postfix 2.11 and later. tls_CAfile - File containing X509 certificates for all of the Certification - Authorities the client will recognize. Takes precedence over + File containing X509 certificates for all of the Certification + Authorities the client will recognize. Takes precedence over tls_CApath. This parameter is available with Postfix 2.11 and later. tls_CApath - Directory containing X509 Certification Authority certificates + Directory containing X509 Certification Authority certificates in separate individual files. This parameter is available with Postfix 2.11 and later. @@ -296,13 +301,13 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) This parameter is available with Postfix 2.11 and later. tls_verify_cert (default: no) - Verify that the server's name matches the common name in the + Verify that the server's name matches the common name in the certificate. This parameter is available with Postfix 2.11 and later. USING MYSQL STORED PROCEDURES - Postfix 3.2 and later support calling a stored procedure instead of + Postfix 3.2 and later support calling a stored procedure instead of using a SELECT statement in the query, e.g. query = CALL lookup('%s') @@ -310,17 +315,17 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) The previously described '%' expansions can be used in the parameter(s) to the stored procedure. - By default, every stored procedure call must return a result set, i.e. - every code path must execute a SELECT statement that returns a result + By default, every stored procedure call must return a result set, i.e. + every code path must execute a SELECT statement that returns a result set (instead of storing its results in a table). With - "require_result_set = no", the absence of a result set is treated as + "require_result_set = no", the absence of a result set is treated as "not found". - A stored procedure must not return multiple result sets. That is, - there must be no code path that executes multiple SELECT statements + A stored procedure must not return multiple result sets. That is, + there must be no code path that executes multiple SELECT statements that return a result (instead of storing their results in a table). - The following is an example of a stored procedure returning a single + The following is an example of a stored procedure returning a single result set: CREATE [DEFINER=`user`@`host`] PROCEDURE @@ -332,26 +337,26 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) END OBSOLETE MAIN.CF PARAMETERS - For compatibility with other Postfix lookup tables, MySQL parameters - can also be defined in main.cf. In order to do that, specify as MySQL - source a name that doesn't begin with a slash or a dot. The MySQL - parameters will then be accessible as the name you've given the source - in its definition, an underscore, and the name of the parameter. For - example, if the map is specified as "mysql:mysqlname", the parameter + For compatibility with other Postfix lookup tables, MySQL parameters + can also be defined in main.cf. In order to do that, specify as MySQL + source a name that doesn't begin with a slash or a dot. The MySQL + parameters will then be accessible as the name you've given the source + in its definition, an underscore, and the name of the parameter. For + example, if the map is specified as "mysql:mysqlname", the parameter "hosts" would be defined in main.cf as "mysqlname_hosts". - Note: with this form, the passwords for the MySQL sources are written - in main.cf, which is normally world-readable. Support for this form + Note: with this form, the passwords for the MySQL sources are written + in main.cf, which is normally world-readable. Support for this form will be removed in a future Postfix version. OBSOLETE QUERY INTERFACE - This section describes an interface that is deprecated as of Postfix - 2.2. It is replaced by the more general query interface described - above. If the query parameter is defined, the legacy parameters - described here ignored. Please migrate to the new interface as the + This section describes an interface that is deprecated as of Postfix + 2.2. It is replaced by the more general query interface described + above. If the query parameter is defined, the legacy parameters + described here ignored. Please migrate to the new interface as the legacy interface may be removed in a future release. - The following parameters can be used to fill in a SELECT template + The following parameters can be used to fill in a SELECT template statement of the form: SELECT [select_field] @@ -360,7 +365,7 @@ MYSQL_TABLE(5) MYSQL_TABLE(5) [additional_conditions] The specifier %s is replaced by the search string, and is escaped so if - it contains single quotes or other odd characters, it will not cause a + it contains single quotes or other odd characters, it will not cause a parse error, or worse, a security problem. select_field diff --git a/postfix/html/postconf.1.html b/postfix/html/postconf.1.html index 9fe7e2704..c8320cdbf 100644 --- a/postfix/html/postconf.1.html +++ b/postfix/html/postconf.1.html @@ -448,6 +448,10 @@ POSTCONF(1) POSTCONF(1) vices and all parameters), formatted as "service/type/parame- ter=value", one per line. Specify -Pf to fold long lines. + Specify multiple -P options to also list service parameters that + are not set in master.cf. This is useful with -x to evaluate the + impact of master.cf overrides on other parameter settings. + Specify one or more "service/type/parameter" instances on the postconf(1) command line to limit the output to parameters of interest. Trailing parameter name or service type fields that diff --git a/postfix/makedefs b/postfix/makedefs index 5a8becff0..605fc9663 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -577,6 +577,7 @@ case "$SYSTEM.$RELEASE" in # by including and by linking with -ldb echo "No include file found." 1>&2 echo "Install the appropriate db*-devel package first." 1>&2 + echo "Alternatively, build with CCARGS=\"-NO_DB ...\"" 1>&2 exit 1 fi SYSLIBS="-ldb" @@ -657,6 +658,7 @@ EOF # by including and by linking with -ldb echo "No include file found." 1>&2 echo "Install the appropriate db*-devel package first." 1>&2 + echo "Alternatively, build with CCARGS=\"-NO_DB ...\"" 1>&2 exit 1 fi SYSLIBS="-ldb" @@ -695,6 +697,7 @@ EOF # by including and by linking with -ldb echo "No include file found." 1>&2 echo "Install the appropriate db*-devel package first." 1>&2 + echo "Alternatively, build with CCARGS=\"-NO_DB ...\"" 1>&2 exit 1 fi SYSLIBS="-ldb" @@ -1323,6 +1326,80 @@ do done; exit 1) || error "invalid option '$opt' in POSTFIX_INSTALL_OPTS" done +# If -DNO_DB is specified then 'hash' and 'btree' will be unavailable. + +check_no_db_overrides() +{ + trap 'rm -f makedefs.test makedefs.test.[co]' 0 1 2 3 15 + cat src/util/sys_defs.h >makedefs.test.c + cat >>makedefs.test.c <<'EOF' +#include +#include +#include +int main(void) +{ + struct def_info { + const char *mac_name; + const char *parm_name; + const char *def_type; + const char *problem; + }; + struct def_info def_infos[] = { + {"DEF_DB_TYPE", "default_database_type", DEF_DB_TYPE}, + {"DEF_CACHE_DB_TYPE", "default_cache_db_type", DEF_CACHE_DB_TYPE}, + }; + const char *bdb_types[] = {"hash", "btree"}; + struct def_info *pdef_info; + const char *const * pbdb_type; + const char *three_bs = "\\\\\\"; + int status = 0; + + for (pdef_info = def_infos; pdef_info < def_infos + 2; pdef_info++) { + for (pbdb_type = bdb_types; pbdb_type < bdb_types + 2; pbdb_type++) { + if (strcmp(pdef_info->def_type, *pbdb_type) == 0) { + pdef_info->problem = *pbdb_type; + status = 1; + } + } + } + if (status != 0) { + fprintf(stderr, "\nBuilding with -DNO_DB, but at least one default " + "setting still\nrequires 'hash' or 'btree':\n"); + for (pdef_info = def_infos; pdef_info < def_infos + 2; pdef_info++) { + if (pdef_info->problem) { + fprintf(stderr, " %s=%s\n", pdef_info->parm_name, + pdef_info->problem); + } + } + fprintf(stderr, "\nTo fix, specify types other than 'hash' or 'btree', " + "for example:\n make makefiles ... \\\n "); + for (pdef_info = def_infos; pdef_info < def_infos + 2; pdef_info++) { + if (pdef_info->problem) { + fprintf(stderr, " %s=lmdb", pdef_info->parm_name); + } + } + fprintf(stderr, " ...\n\nOr, using older syntax:\n"); + fprintf(stderr, " make makefiles ... \\\n CCARGS=\"$CCARGS -DNO_DB"); + for (pdef_info = def_infos; pdef_info < def_infos + 2; pdef_info++) { + if (pdef_info->problem) { + fprintf(stderr, " -D%s=%s\"lmdb%s\"", + pdef_info->mac_name, three_bs, three_bs); + } + } + fprintf(stderr, " ...\" ...\n\n"); + } + exit(status); +} +EOF + eval ${CC-gcc} ${CCARGS} -D$SYSTYPE -o makedefs.test makedefs.test.c || exit 1 + ./makedefs.test || exit 1 + rm -f makedefs.test makedefs.test.[co] +} + +case "$CCARGS" in + *-DNO_DB*) check_no_db_overrides || exit 1;; +esac + # Finally... sed 's/ */ /g' < -Postfix MySQL Howto +Postfix MySQL/MariaDB Howto @@ -20,13 +20,18 @@

Introduction

-

The Postfix mysql map type allows you to hook up Postfix to a -MySQL database. This implementation allows for multiple mysql +

The Postfix mysql map type allows you to hook up Postfix to +MySQL or MariaDB databases. This implementation allows for multiple databases: you can use one for a virtual(5) table, one for an access(5) table, and one for an aliases(5) table if you want. You can specify multiple servers for the same database, so that Postfix can switch to a good database server if one goes bad.

+

Even though the component name is 'mysql' in Postfix, MariaDB/MySQL +client and servers can be used interchangeably as the functionality +Postfix relies on is available in both. However, the code bases may +still diverge in the future.

+

Busy mail servers using mysql maps will generate lots of concurrent mysql clients, so the mysql server(s) should be run with this fact in mind. You can reduce the number of concurrent mysql @@ -50,6 +55,13 @@ which can be obtained from:

https://www.mysql.com/downloads/

+

Many Linux distributions compile Postfix MySQL support using +the equivalent client library from MariaDB:

+ +
+

https://mariadb.org/connector-c/

+
+

In order to build Postfix with mysql map support, you will need to add -DHAS_MYSQL and -I for the directory containing the mysql headers, and the mysqlclient library (and libm) to AUXLIBS_MYSQL, for example:

diff --git a/postfix/proto/mysql_table b/postfix/proto/mysql_table index d93d405a8..278a14689 100644 --- a/postfix/proto/mysql_table +++ b/postfix/proto/mysql_table @@ -2,7 +2,7 @@ # NAME # mysql_table 5 # SUMMARY -# Postfix MySQL client configuration +# Postfix MySQL/MariaDB client configuration # SYNOPSIS # \fBpostmap -q "\fIstring\fB" mysql:/etc/postfix/\fIfilename\fR # @@ -22,6 +22,11 @@ # The file /etc/postfix/mysql-aliases.cf has the same format as # the Postfix main.cf file, and can specify the parameters # described below. +# +# Even though the component name is 'mysql' in Postfix, +# MariaDB/MySQL client and servers can be used interchangeably as +# the functionality Postfix relies on is available in both. However, +# their code bases may still diverge in the future. # LIST MEMBERSHIP # .ad # .fi diff --git a/postfix/proto/stop.double-history b/postfix/proto/stop.double-history index 17fbca92c..b6a2c6f60 100644 --- a/postfix/proto/stop.double-history +++ b/postfix/proto/stop.double-history @@ -218,3 +218,6 @@ proto proto socketmap_table qmqpd qmqpd c tls tls_misc c default value with a lookup table File proxymap proxymap c in file name and in restriction_classes File proxymap proxymap c proxymap proxymap c + src dns dns h + postconf postconf hc postconf postconf_master c + PPx proxy_read_maps Files postconf postconf hc diff --git a/postfix/proto/stop.spell-cc b/postfix/proto/stop.spell-cc index b4af84a6f..57e7c41b6 100644 --- a/postfix/proto/stop.spell-cc +++ b/postfix/proto/stop.spell-cc @@ -1890,3 +1890,4 @@ dfhHjnopqvx fhHjoqvx joqvx ajRv +allparams diff --git a/postfix/proto/stop.spell-history b/postfix/proto/stop.spell-history index 4d200d5ba..b119d4179 100644 --- a/postfix/proto/stop.spell-history +++ b/postfix/proto/stop.spell-history @@ -121,3 +121,9 @@ jl Ankit Kulkarni Wordsmithing +Fusté +Kekäläinen +MAXDNAME +unbroke +PPx +unbroke diff --git a/postfix/proto/stop.spell-proto-html b/postfix/proto/stop.spell-proto-html index 77069db6b..cd491eb95 100644 --- a/postfix/proto/stop.spell-proto-html +++ b/postfix/proto/stop.spell-proto-html @@ -404,3 +404,4 @@ nocertmatch noencryption nomatch nostarttls +mariadb diff --git a/postfix/src/dns/dns.h b/postfix/src/dns/dns.h index aeec149e9..6d8c6b25b 100644 --- a/postfix/src/dns/dns.h +++ b/postfix/src/dns/dns.h @@ -334,7 +334,7 @@ extern int dns_get_h_errno(void); /* * How long can a DNS name or single text value be? */ -#define DNS_NAME_LEN 1024 +#define DNS_NAME_LEN MAXDNAME /* From def_action, "tempfail") == 0) { reply = "451 4.7.1 Service unavailable - try again later"; } else if (strcasecmp(milter->def_action, "shutdown") == 0) { - reply = "421 Service unavailable - try again later"; + reply = "421 4.7.1 Service unavailable - try again later"; } else if (strcasecmp(milter->def_action, "quarantine") == 0) { reply = "Hdefault_action"; } else { diff --git a/postfix/src/postconf/Makefile.in b/postfix/src/postconf/Makefile.in index 75a6f428f..8ac6499d9 100644 --- a/postfix/src/postconf/Makefile.in +++ b/postfix/src/postconf/Makefile.in @@ -28,7 +28,7 @@ LIBS = ../../lib/libxsasl.a \ ../../lib/lib$(LIB_PREFIX)dns$(LIB_SUFFIX) \ ../../lib/lib$(LIB_PREFIX)global$(LIB_SUFFIX) \ ../../lib/lib$(LIB_PREFIX)util$(LIB_SUFFIX) -HTABLE_FIX = NORANDOMIZE=1 +HTABLE_FIX = NORANDOMIZE=1 LD_PRELOAD=../../lib/nosleep.so .c.o:; $(CC) $(CFLAGS) -c $*.c @@ -56,9 +56,10 @@ tests: test1 test2 test3 test4 test5 test6 test7 test8 test9 test10 test11 \ test42 test43 test44 test45 test46 test47 test48 test49 test50 test51 \ test52 test53 test54 test55 test56 test57 test58 test59 test60 test61 \ test62 test63 test64 test65 test66 test67 test68 test69 test70 test71 \ - test72 test73 test74 test75 test76 test78 test79 json_tests + test72 test73 test74 test75 test76 test78 test79 json_tests80-90 \ + test91 test92 -json_tests: test80 test81 test82 test83 test84 test85 test86 test87 +json_tests80-90: test80 test81 test82 test83 test84 test85 test86 test87 root_tests: @@ -1254,6 +1255,45 @@ test87: $(PROG) test87.ref diff test87.ref test87.tmp rm -f main.cf master.cf test87.tmp +# postconf -PP will list parameters not set in master.cf. +test91: $(PROG) test91.ref + rm -f main.cf master.cf test91.tmp + touch main.cf master.cf test91.tmp + #echo 'foo/unix/proxymap = foo_map' >> test91.tmp + #echo 'bar/unix/proxymap = bar_map' >> test91.tmp + #echo 'baz/unix/proxymap = baz_map' >> test91.tmp + echo 'foo_proxy_write_maps=foo_map' >> main.cf + echo 'bar_proxy_write_maps=bar_map' >> main.cf + echo 'proxy_write_maps=baz_map' >> main.cf + touch -t 197601010000 main.cf + echo foo unix - n n - 0 foo >> master.cf + echo ' -o proxy_write_maps=$$foo_proxy_write_maps' >> master.cf + echo bar unix - n n - 0 bar >> master.cf + echo ' -o proxy_write_maps=$$bar_proxy_write_maps' >> master.cf + echo baz unix - n n - 0 baz >> master.cf + touch -t 197601010000 master.cf + $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -xPPc. \ + '*/*/proxy_write_maps' >test91.tmp 2>&1 + diff test91.ref test91.tmp + rm -f main.cf master.cf test91.tmp + +# postconf -PP will list the same parameters as "postconf", but it does +# that once for every master.cf service, and will show the settings that +# are in effect for that service. +test92: $(PROG) + rm -f main.cf master.cf test92.tmp + touch main.cf master.cf test92.tmp + touch -t 197601010000 main.cf + echo foo unix - n n - 0 foo >> master.cf + touch -t 197601010000 master.cf + $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -c. | \ + grep -E -v '^(process_id|process_name|service_name) =' >test92.tmp + $(HTABLE_FIX) $(SHLIB_ENV) $(VALGRIND) ./$(PROG) -PPc. | \ + sed 's;foo/unix/;;' | \ + grep -E -v '^(process_id|process_name|service_name) =' | \ + diff test92.tmp - + rm -f main.cf master.cf test92.tmp + tidy: clean depend: $(MAKES) diff --git a/postfix/src/postconf/postconf.c b/postfix/src/postconf/postconf.c index c53ca4a9a..5b1b5f027 100644 --- a/postfix/src/postconf/postconf.c +++ b/postfix/src/postconf/postconf.c @@ -465,6 +465,11 @@ /* "\fIservice/type/parameter=value\fR", one per line. Specify /* \fB-Pf\fR to fold long lines. /* +/* Specify multiple \fB-P\fR options to also list service parameters +/* that are not set in master.cf. This is useful with \fB-x\fR to +/* evaluate the impact of master.cf overrides on other parameter +/* settings. +/* /* Specify one or more "\fIservice/type/parameter\fR" instances /* on the \fBpostconf\fR(1) command line to limit the output /* to parameters of interest. Trailing parameter name or @@ -673,6 +678,7 @@ static const int pcf_incompat_options[] = { |PCF_SHOW_LOCKS | PCF_SHOW_MAPS | PCF_DUMP_DSN_TEMPL | PCF_MAIN_PARAM \ |PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM | PCF_SHOW_TLS, /* Modifiers. */ + PCF_MASTER_PP | PCF_EDIT_CONF | PCF_COMMENT_OUT | PCF_EDIT_EXCL, PCF_SHOW_DEFS | PCF_EDIT_CONF | PCF_SHOW_NONDEF | PCF_COMMENT_OUT \ |PCF_EDIT_EXCL, PCF_FOLD_LINE | PCF_EDIT_CONF | PCF_COMMENT_OUT | PCF_EDIT_EXCL, @@ -710,7 +716,10 @@ static const int pcf_compat_options[][2] = { |PCF_SHOW_JSON)}, {PCF_MASTER_PARAM, (PCF_EDIT_CONF | PCF_EDIT_EXCL | PCF_FOLD_LINE \ |PCF_HIDE_NAME | PCF_MAIN_OVER | PCF_SHOW_EVAL \ - |PCF_HIDE_VALUE | PCF_SHOW_JSON)}, + |PCF_HIDE_VALUE | PCF_SHOW_JSON | PCF_MASTER_PP)}, + {PCF_MASTER_PP, (PCF_FOLD_LINE \ + |PCF_HIDE_NAME | PCF_MAIN_OVER | PCF_SHOW_EVAL \ + |PCF_HIDE_VALUE | PCF_SHOW_JSON | PCF_MASTER_PARAM)}, {PCF_SHOW_JSON, (PCF_MAIN_PARAM | PCF_MASTER_ENTRY | PCF_MASTER_FLD \ |PCF_MASTER_PARAM | PCF_MAIN_OVER | PCF_SHOW_EVAL \ |PCF_SHOW_NONDEF | PCF_SHOW_DEFS)}, @@ -746,6 +755,7 @@ static const NAME_MASK pcf_compat_names[] = { "-x", PCF_SHOW_EVAL, "-X", PCF_EDIT_EXCL, "-#", PCF_COMMENT_OUT, + "-PP", PCF_MASTER_PP, 0, }; @@ -947,7 +957,10 @@ int main(int argc, char **argv) pcf_cmd_mode |= PCF_MAIN_PARAM; break; case 'P': - pcf_cmd_mode |= PCF_MASTER_PARAM; + if (pcf_cmd_mode & PCF_MASTER_PARAM) + pcf_cmd_mode |= PCF_MASTER_PP; + else + pcf_cmd_mode |= PCF_MASTER_PARAM; break; case 'q': pcf_cmd_mode &= ~(PCF_WARN_UNUSED_DEPRECATED); @@ -1074,8 +1087,8 @@ int main(int argc, char **argv) pcf_show_master_fields(VSTREAM_OUT, pcf_cmd_mode, argc - optind, argv + optind); else if (pcf_cmd_mode & PCF_MASTER_PARAM) - pcf_show_master_params(VSTREAM_OUT, pcf_cmd_mode, argc - optind, - argv + optind); + pcf_show_master_params(VSTREAM_OUT, pcf_cmd_mode, param_class, + argc - optind, argv + optind); else pcf_show_master_entries(VSTREAM_OUT, pcf_cmd_mode, argc - optind, argv + optind); diff --git a/postfix/src/postconf/postconf.h b/postfix/src/postconf/postconf.h index 9415f6f6c..6ee26262e 100644 --- a/postfix/src/postconf/postconf.h +++ b/postfix/src/postconf/postconf.h @@ -48,6 +48,7 @@ #define PCF_SHOW_TLS (1<<21) /* TLS support introspection */ #define PCF_WARN_UNUSED_DEPRECATED (1<<22) /* As the name says */ #define PCF_SHOW_JSON (1 << 23) /* JSON output */ +#define PCF_MASTER_PP (1<<24) /* multiple -P */ #define PCF_DEF_MODE (PCF_WARN_UNUSED_DEPRECATED) @@ -202,7 +203,7 @@ extern void pcf_print_master_entry(VSTREAM *, int, PCF_MASTER_ENT *); extern void pcf_free_master_entry(PCF_MASTER_ENT *); extern void pcf_show_master_fields(VSTREAM *, int, int, char **); extern void pcf_edit_master_field(PCF_MASTER_ENT *, int, const char *); -extern void pcf_show_master_params(VSTREAM *, int, int, char **); +extern void pcf_show_master_params(VSTREAM *, int, int, int, char **); extern void pcf_edit_master_param(PCF_MASTER_ENT *, int, const char *, const char *); #define PCF_WARN_ON_OPEN_ERROR 0 diff --git a/postfix/src/postconf/postconf_lookup.c b/postfix/src/postconf/postconf_lookup.c index 5185681e7..18e3e16ef 100644 --- a/postfix/src/postconf/postconf_lookup.c +++ b/postfix/src/postconf/postconf_lookup.c @@ -152,6 +152,17 @@ static const char *pcf_lookup_parameter_value_wrapper(const char *key, (PCF_PARAM_NODE *) 0)); } +/* pcf_dont_parse_parameter_value - disable recursion for raw parameters */ + +static bool pcf_dont_parse_parameter_value(const char *key, void *unused_context) +{ + PCF_PARAM_NODE *node; + + /* Only built-in parameters can be 'raw'. No need to inspect local_scope. */ + return ((node = PCF_PARAM_TABLE_FIND(pcf_param_table, key)) != 0 + && PCF_RAW_PARAMETER(node)); +} + /* pcf_expand_parameter_value - expand $name in parameter value */ char *pcf_expand_parameter_value(VSTRING *buf, int mode, const char *value, @@ -180,8 +191,9 @@ char *pcf_expand_parameter_value(VSTRING *buf, int mode, const char *value, eval_ctx.mode = (mode & ~PCF_SHOW_NONDEF); eval_ctx.local_scope = local_scope; - status = mac_expand(buf, value, MAC_EXP_FLAG_RECURSE, DONT_FILTER, - pcf_lookup_parameter_value_wrapper, (void *) &eval_ctx); + status = mac_expand7(buf, value, MAC_EXP_FLAG_RECURSE, DONT_FILTER, + pcf_lookup_parameter_value_wrapper, + pcf_dont_parse_parameter_value, (void *) &eval_ctx); if (status & MAC_PARSE_ERROR) msg_fatal("macro processing error"); if (msg_verbose > 1) { diff --git a/postfix/src/postconf/postconf_master.c b/postfix/src/postconf/postconf_master.c index 48061a106..0be59f2fb 100644 --- a/postfix/src/postconf/postconf_master.c +++ b/postfix/src/postconf/postconf_master.c @@ -27,9 +27,10 @@ /* int field; /* const char *new_value; /* -/* void pcf_show_master_params(fp, mode, argc, **param_filters) +/* void pcf_show_master_params(fp, mode, param_class, argc, **param_filters) /* VSTREAM *fp; /* int mode; +/* int param_class; /* int argc; /* char **param_filters; /* @@ -63,7 +64,9 @@ /* or multi-column attribute. /* /* pcf_show_master_params() writes name/type/parameter=value -/* records to the specified stream. +/* records to the specified stream. Like show_parameters(), +/* this may list either selected parameters, or all parameters that +/* match the param_class argument. /* /* pcf_edit_master_param() updates, removes or adds the named /* parameter in a master.cf entry (the remove request ignores @@ -1106,14 +1109,69 @@ static int pcf_sort_argv_cb(const void *a, const void *b) return (strcmp(*(char **) a, *(char **) b)); } +/* merge_main_master_parameters - all parameters as sen by this service */ + +static DICT *merge_main_master_parameters(int mode, PCF_MASTER_ENT *masterp, + int param_class) +{ + DICT *dict; + PCF_PARAM_INFO **main_list; + PCF_PARAM_INFO **main_ht; + const char *dict_spec = "merged_dict"; + const char *param_name; + const char *param_value; + int how; + + /* + * With -PP, use the merged main/master.cf settings instead of + * masterp->allparams (the master.cf settings for this service). + */ + dict = dict_ht_open(dict_spec, O_CREAT | O_RDWR, 0); + dict_register(dict_spec, dict); + + /* + * For each parameter in the main.cf namespace, look up its effective + * value (from master.cf or main.cf, or use the default). + */ + main_list = PCF_PARAM_TABLE_LIST(pcf_param_table); + for (main_ht = main_list; *main_ht; main_ht++) { + param_name = PCF_PARAM_INFO_NAME(*main_ht); + if (param_class && !(PCF_PARAM_INFO_NODE(*main_ht)->flags & param_class)) + continue; + if ((param_value = + pcf_lookup_parameter_value(mode, param_name, masterp, + PCF_PARAM_INFO_NODE(*main_ht))) == 0) + msg_panic("%s: parameter name not found: %s", __func__, param_name); + dict->update(dict, param_name, param_value); + } + myfree((void *) main_list); + + /* + * Add master.cf settings with service-specific custom names. Skip + * settings that were already copied in the above loop. + */ + if (masterp->all_params) { + DICT *all_params = masterp->all_params; + + for (how = DICT_SEQ_FUN_FIRST; + all_params->sequence(all_params, how, ¶m_name, + ¶m_value) == 0; + how = DICT_SEQ_FUN_NEXT) { + if (dict->lookup(dict, param_name) == 0) + dict->update(dict, param_name, param_value); + } + } + return (dict); +} + /* pcf_show_master_any_param - show any parameter in master.cf service entry */ static void pcf_show_master_any_param(VSTREAM *fp, int mode, - PCF_MASTER_ENT *masterp) + PCF_MASTER_ENT *masterp, + DICT *dict) { const char *myname = "pcf_show_master_any_param"; ARGV *argv = argv_alloc(10); - DICT *dict = masterp->all_params; const char *param_name; const char *param_value; int param_count = 0; @@ -1152,7 +1210,8 @@ static void pcf_show_master_any_param(VSTREAM *fp, int mode, /* pcf_show_master_params - show master.cf params */ -void pcf_show_master_params(VSTREAM *fp, int mode, int argc, char **argv) +void pcf_show_master_params(VSTREAM *fp, int mode, int param_class, + int argc, char **argv) { PCF_MASTER_ENT *masterp; PCF_MASTER_FLD_REQ *field_reqs; @@ -1181,14 +1240,20 @@ void pcf_show_master_params(VSTREAM *fp, int mode, int argc, char **argv) * Iterate over the master table. */ for (masterp = pcf_master_table; masterp->argv != 0; masterp++) { - if ((dict = masterp->all_params) != 0) { + if (mode & PCF_MASTER_PP) { + dict = merge_main_master_parameters(mode, masterp, + argc > 0 ? 0 : param_class); + } else { + dict = masterp->all_params; + } + if (dict != 0) { if (argc > 0) { for (req = field_reqs; req < field_reqs + argc; req++) { if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern, masterp->argv->argv[0], masterp->argv->argv[1])) { if (PCF_IS_MAGIC_PARAM_PATTERN(req->param_pattern)) { - pcf_show_master_any_param(fp, mode, masterp); + pcf_show_master_any_param(fp, mode, masterp, dict); req->match_count += 1; } else if ((param_value = dict_get(dict, req->param_pattern)) != 0) { @@ -1200,9 +1265,11 @@ void pcf_show_master_params(VSTREAM *fp, int mode, int argc, char **argv) } } } else { - pcf_show_master_any_param(fp, mode, masterp); + pcf_show_master_any_param(fp, mode, masterp, dict); } } + if (mode & PCF_MASTER_PP) + dict_close(dict); } /* diff --git a/postfix/src/postconf/postconf_user.c b/postfix/src/postconf/postconf_user.c index 46f4d6180..fdaf8442d 100644 --- a/postfix/src/postconf/postconf_user.c +++ b/postfix/src/postconf/postconf_user.c @@ -108,6 +108,7 @@ static HTABLE *pcf_rest_class_table; PCF_PARAM_CTX _ctx; \ _ctx.local_scope = (scope); \ _ctx.param_class = (class); \ + /* No MAC_EXP_FLAG_RECURSE, therefore no need for DONT_PARSE feature. */ \ (void) mac_expand(NO_SCAN_RESULT, (value), MAC_EXP_FLAG_SCAN, \ NO_SCAN_FILTER, pcf_flag_user_parameter_wrapper, (void *) &_ctx); \ } while (0) diff --git a/postfix/src/postconf/test91.ref b/postfix/src/postconf/test91.ref new file mode 100644 index 000000000..26f354d36 --- /dev/null +++ b/postfix/src/postconf/test91.ref @@ -0,0 +1,3 @@ +foo/unix/proxy_write_maps = foo_map +bar/unix/proxy_write_maps = bar_map +baz/unix/proxy_write_maps = baz_map diff --git a/postfix/src/util/mac_expand.c b/postfix/src/util/mac_expand.c index c3afed605..a00612bf5 100644 --- a/postfix/src/util/mac_expand.c +++ b/postfix/src/util/mac_expand.c @@ -6,14 +6,24 @@ /* SYNOPSIS /* #include /* -/* int mac_expand(result, pattern, flags, filter, lookup, context) +/* int mac_expand7(result, pattern, flags, filter, lookup, +/* dont_expand, context) /* VSTRING *result; /* const char *pattern; /* int flags; /* const char *filter; /* const char *lookup(const char *key, int mode, void *context) +/* const bool dont_parse(const char *key, void *context) /* void *context; /* AUXILIARY FUNCTIONS +/* int mac_expand(result, pattern, flags, filter, lookup, context) +/* VSTRING *result; +/* const char *pattern; +/* int flags; +/* const char *filter; +/* const char *lookup(const char *key, int mode, void *context) +/* void *context; +/* /* typedef MAC_EXP_OP_RES (*MAC_EXPAND_RELOP_FN) ( /* const char *left, /* int tok_val, @@ -121,8 +131,14 @@ /* or MAC_EXP_MODE_USE to use the value of the named attribute, /* and the caller context that was given to mac_expand(). A null /* result value means that the requested attribute was not defined. +/* .IP don_parse +/* An optional function that disables the MAC_EXP_FLAG_RECURSE +/* feature with lookup() results for a specific attribute. Arguments +/* are: the attribute name, and the caller context that was given +/* to mac_expand(). Specify null to disable this feature. /* .IP context -/* Caller context that is passed on to the attribute lookup routine. +/* Caller context that is passed on to the attribute lookup and +/* don_parse routines. /* .PP /* mac_expand_add_relop() registers a function that implements /* support for custom relational operators. Custom operator names @@ -220,6 +236,7 @@ typedef struct { int flags; /* features */ const char *filter; /* character filter */ MAC_EXP_LOOKUP_FN lookup; /* lookup routine */ + MAC_EXP_DONT_PARSE_FN dont_parse; /* veto routine */ void *context; /* caller context */ int status; /* findings */ int level; /* nesting level */ @@ -657,6 +674,7 @@ static int mac_expand_callback(int type, VSTRING *buf, void *ptr) ssize_t tmp_len; const char *res_iftrue; const char *res_iffalse; + int dont_parse = false; /* * Sanity check. @@ -735,6 +753,8 @@ static int mac_expand_callback(int type, VSTRING *buf, void *ptr) * Look up the named parameter. Todo: allow the lookup function * to specify if the result is safe for $name expansion. */ + if (mc->dont_parse) + dont_parse = mc->dont_parse(start, mc->context); lookup = mc->lookup(start, lookup_mode, mc->context); } @@ -789,7 +809,7 @@ static int mac_expand_callback(int type, VSTRING *buf, void *ptr) mc->status |= MAC_PARSE_UNDEF; } else if (*lookup == 0 || (mc->flags & MAC_EXP_FLAG_SCAN)) { /* void */ ; - } else if (mc->flags & MAC_EXP_FLAG_RECURSE) { + } else if ((mc->flags & MAC_EXP_FLAG_RECURSE) && !dont_parse) { vstring_strcpy(buf, lookup); mc->status |= mac_parse(vstring_str(buf), mac_expand_callback, (void *) mc); @@ -821,12 +841,29 @@ static int mac_expand_callback(int type, VSTRING *buf, void *ptr) return (mc->status); } + /* + * ABI compatibility wrapper. + */ +#undef mac_expand +int mac_expand(VSTRING *, const char *, int, const char *, + MAC_EXP_LOOKUP_FN, void *); + /* mac_expand - expand $name instances */ int mac_expand(VSTRING *result, const char *pattern, int flags, const char *filter, MAC_EXP_LOOKUP_FN lookup, void *context) { + return (mac_expand7(result, pattern, flags, filter, lookup, + (MAC_EXP_DONT_PARSE_FN) 0, context)); +} + +int mac_expand7(VSTRING *result, const char *pattern, int flags, + const char *filter, + MAC_EXP_LOOKUP_FN lookup, + MAC_EXP_DONT_PARSE_FN dont_parse, void *context) +{ + MAC_EXP_CONTEXT mc; int status; @@ -837,6 +874,7 @@ int mac_expand(VSTRING *result, const char *pattern, int flags, mc.flags = flags; mc.filter = filter; mc.lookup = lookup; + mc.dont_parse = dont_parse; mc.context = context; mc.status = 0; mc.level = 0; diff --git a/postfix/src/util/mac_expand.h b/postfix/src/util/mac_expand.h index 70954e602..17adb9d9c 100644 --- a/postfix/src/util/mac_expand.h +++ b/postfix/src/util/mac_expand.h @@ -57,13 +57,18 @@ extern MAC_EXP_OP_RES mac_exp_op_res_bool[2]; #define MAC_EXP_MODE_USE (1) typedef const char *(*MAC_EXP_LOOKUP_FN) (const char *, int, void *); +typedef bool(*MAC_EXP_DONT_PARSE_FN) (const char *, void *); typedef MAC_EXP_OP_RES(*MAC_EXPAND_RELOP_FN) (const char *, int, const char *); typedef int (*MAC_EXPAND_NAMED_FN) (VSTRING *, const char *); -extern int mac_expand(VSTRING *, const char *, int, const char *, MAC_EXP_LOOKUP_FN, void *); +extern int mac_expand7(VSTRING *, const char *, int, const char *, MAC_EXP_LOOKUP_FN, MAC_EXP_DONT_PARSE_FN, void *); extern void mac_expand_add_relop(int *, const char *, MAC_EXPAND_RELOP_FN); extern void mac_expand_add_named_fn(const char *, MAC_EXPAND_NAMED_FN); +#define mac_expand(res, pat, flags, filter, lookup, ctx) \ + mac_expand7((res), (pat), (flags), (filter), (lookup), \ + (MAC_EXP_DONT_PARSE_FN) 0, (ctx)) + /* LICENSE /* .ad /* .fi