From: Wietse Venema Date: Sat, 8 May 1999 05:00:00 +0000 (-0500) Subject: snapshot-19990508 X-Git-Tag: v20010228~108 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=5d89d7ae949c24646506d5cd75304437e37ecdd4;p=thirdparty%2Fpostfix.git snapshot-19990508 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/DEBUG_README b/postfix/DEBUG_README index 1ed6e6bfc..be66f3ad2 100644 --- a/postfix/DEBUG_README +++ b/postfix/DEBUG_README @@ -19,9 +19,9 @@ from or to the loopback interface: 3 - Making daemon programs more verbose ======================================= -Append one or more -v options to commands in /etc/postfix/master.cf -and type "postfix reload". This will cause a lot of activity to be -logged to the syslog daemon. +Append one or more -v options to selected daemon definitions in +/etc/postfix/master.cf and type "postfix reload". This will cause +a lot of activity to be logged to the syslog daemon. 4 - Tracing a Postfix daemon process ==================================== @@ -34,9 +34,12 @@ call tracer. For example: # truss -p process-id # ktrace -p process-id -This can give valuable information about what a process is attempting -to do. This is as much information as you can get without running -a debugger program, as described in the next section. +See your system documentation for details. + +Tracing a running process can give valuable information about what +a process is attempting to do. This is as much information as you +can get without running an interactive debugger program, as described +in the next section. 5 - Running daemon programs under an interactive debugger ========================================================= @@ -53,6 +56,8 @@ that it invokes the debugger of your choice, for example: PATH=/usr/bin:/usr/X11R6/bin xxgdb $daemon_directory/$process_name $process_id & sleep 5 +If you use xxgdb, be sure that gdb is in the command search path. + Export XAUTHORITY so that X access control works, for example: % setenv XAUTHORITY ~/.Xauthority diff --git a/postfix/HISTORY b/postfix/HISTORY index 8c534d4ab..b2f54c9f6 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -2706,13 +2706,45 @@ Apologies for any names omitted. ${name:text} expands to text when name is undefined, otherwise the result is empty. File: util/mac_expand.c. - Feature: conditional macro expansion in forward_path and - mailbox_command of $user, $home, $shell, $recipient, - $extension, $domain, and $recipient_delimiter. Files: - local/command.c, local/dotforward.c, local/local_expand.c. + Feature: conditional macro expansion of the forward_path and + mailbox_command configuration parameters of $user, $home, + $shell, $recipient, $extension, $domain, $mailbox and + $recipient_delimiter. Files: local/command.c, + local/dotforward.c, local/local_expand.c. 19990506 Cleanup: eliminated misleading warnings about unknown HELO etc. SMTPD restrictions when the HELO etc. information is not available. File: smtpd/smtpd_check.c. + +19990507 + + Feature: all smtpd reject messages now contain the MAIL + FROM and RCPT TO addresses, if available. + +19990508 + + Feature: conditional macro expansion of the luser_relay + configuration parameter. It is no longer possible to specify + /file/name or "|command" destinations. File: local/unknown.c. + + Cleanup: changed the mac_parse interface so that the + application callback routine can return status information. + Updated the dict_regexp and dict_pcre modules accordingly. + + Cleanup: changed the mac_expand interface so that the caller + provides an attribute lookup routine, instead of having to + provide a copy of all attributes upfront. Files: + util/mac_expand.c, local/local_expand.c. + + Feature: control over how address extensions are propagated + to other addresses. By default, propagation of unmatched + address extensions is now restricted to canonical and + virtual mappings. Specify "propagate_unmatched_extension + = canonical, virtual, alias, forward, include" to restore + previous behavior. + + Feature: command_expansion_filter and forward_expansion_filter + configuration parameters that control what characters may + appear in $name expansions of mailbox_command and forward_path. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 451172248..3c0413232 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -1,13 +1,22 @@ Incompatible changes with snapshot-19990504: =========================================== +- The Postfix local delivery agent no longer automatically propagates +the address extension to aliases/include/forward addresses. Specify +"propagate_unmatched_extensions = canonical, virtual, alias, forward, +include" to restore the old behavior. + +- The luser_relay syntax has changed. You can specify one address, +and it is subjected to $user, etc. expansions. See conf/main.cf. + +- The mailbox_command parameter is now subjected to $name expansion +(see below). This means that you can no longer use shell variables +in mailbox_command, or that you have to use $$ instead of $. + - File system reorganization: daemon executables are in the libexec subdirectory, command executables in the bin subdirectory. The -INSTALL instructions now install daemons and commands into separate -directories. - -- The local delivery agent no longer appends address extensions to -addresses found in aliases, :include: files or .forward files. +INSTALL instructions now recommend installing daemons and commands +into separate directories. Major changes with snapshot-19990504: ===================================== @@ -18,15 +27,18 @@ of Solaris workarounds. - Specify "forward_path = /var/forward/$user" to avoid looking up .forward files in user home directories. The default value is -$home/.forward$recipient_delimiter$extension,$home/.forward. Initial -forward_path code by Philip A. Prindeville, Mirapoint, Inc., USA. - -- Conditional macro expansion in forward_path and mailbox_command. -$name expands to itself. ${name?value} expands to value when $name -is defined. ${name:value} expands to value when $name is not -defined. With ${name?value} and ${name:value}, the value is subject -to $name expansion. Available macros are: $user, $shell, $home, -$recipient, $extension, $domain, and $recipient_delimiter. +$home/.forward$recipient_delimiter$extension, $home/.forward. +Initial code by Philip A. Prindeville, Mirapoint, Inc., USA. + +- Conditional $name expansion in forward_path, mailbox_command, +and luser_relay. Available names are: $user (bare user name) $shell +(user login shell), $home (user home directory), $recipient +(everything to the left of @), $extension (optional address +extension), $domain (everything to the right of @), and +$recipient_delimiter. A simple $name expands as usual. ${name?value} +expands to value when $name is defined. ${name:value} expands to +value when $name is not defined. With ${name?value} and ${name:value}, +the value is subject to another iteration of $name expansion. - POSIX regular expression support, enabled by default on 4.4BSD, LINUX, HP-UX, and Solaris 2.5 and later. See conf/sample-regexp.cf. @@ -45,7 +57,8 @@ will be delegated to an external command. - Regular expression support for all lookup tables, including access control (full mail addresses only), address rewriting (canonical/virtual, full mail addresses only) and transport tables (full domain names -only). However, regular expressions are not allowed for aliases. +only). However, regular expressions are not allowed for aliases, +because that would open up security exposures. - Automatic detection of changes to DB or DBM lookup tables. This eliminates the need to run "postfix reload" after each change to @@ -53,7 +66,7 @@ the SMTP access table, or to the canonical, virtual, transport or aliases tables. - New error mailer. Specify ".domain.name error:domain is undeliverable" -in the transport table to bounce entire domains. +in the transport table to bounce mail for entire domains. - No more Postfix lockups on Solaris (knock on wood). The code no longer uses Solaris UNIX-domain sockets, because they are still diff --git a/postfix/bounce/.indent.pro b/postfix/bounce/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/bounce/.indent.pro +++ b/postfix/bounce/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/cleanup/.indent.pro b/postfix/cleanup/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/cleanup/.indent.pro +++ b/postfix/cleanup/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/cleanup/Makefile.in b/postfix/cleanup/Makefile.in index 63cc3edc6..2ee78f30e 100644 --- a/postfix/cleanup/Makefile.in +++ b/postfix/cleanup/Makefile.in @@ -78,6 +78,7 @@ cleanup.o: ../include/bounce.h cleanup.o: ../include/mail_params.h cleanup.o: ../include/mail_stream.h cleanup.o: ../include/mail_addr.h +cleanup.o: ../include/ext_prop.h cleanup.o: ../include/mail_server.h cleanup.o: cleanup.h cleanup.o: ../include/argv.h @@ -98,6 +99,7 @@ cleanup_envelope.o: ../include/cleanup_user.h cleanup_envelope.o: ../include/tok822.h cleanup_envelope.o: ../include/resolve_clnt.h cleanup_envelope.o: ../include/mail_params.h +cleanup_envelope.o: ../include/ext_prop.h cleanup_envelope.o: cleanup.h cleanup_envelope.o: ../include/argv.h cleanup_envelope.o: ../include/maps.h @@ -197,6 +199,7 @@ cleanup_message.o: ../include/mail_params.h cleanup_message.o: ../include/mail_date.h cleanup_message.o: ../include/mail_addr.h cleanup_message.o: ../include/is_header.h +cleanup_message.o: ../include/ext_prop.h cleanup_message.o: cleanup.h cleanup_message.o: ../include/maps.h cleanup_message.o: ../include/dict.h @@ -225,6 +228,7 @@ cleanup_out_recipient.o: ../include/argv.h cleanup_out_recipient.o: ../include/been_here.h cleanup_out_recipient.o: ../include/mail_params.h cleanup_out_recipient.o: ../include/rec_type.h +cleanup_out_recipient.o: ../include/ext_prop.h cleanup_out_recipient.o: cleanup.h cleanup_out_recipient.o: ../include/vstring.h cleanup_out_recipient.o: ../include/vbuf.h diff --git a/postfix/cleanup/cleanup.c b/postfix/cleanup/cleanup.c index 2127a96d9..2a66a5f1a 100644 --- a/postfix/cleanup/cleanup.c +++ b/postfix/cleanup/cleanup.c @@ -142,6 +142,7 @@ #include #include #include +#include /* Single-threaded server skeleton. */ @@ -172,6 +173,7 @@ char *var_header_checks; /* any header checks */ int var_dup_filter_limit; /* recipient dup filter */ char *var_empty_addr; /* destination of bounced bounces */ int var_delay_warn_time; /* delay that triggers warning */ +char *var_prop_extension; /* propagate unmatched extension */ /* * Mappings. @@ -183,6 +185,11 @@ MAPS *cleanup_header_checks; MAPS *cleanup_virtual_maps; ARGV *cleanup_masq_domains; + /* + * Address extension propagation restrictions. + */ +int cleanup_ext_prop_mask; + /* cleanup_service - process one request to inject a message into the queue */ static void cleanup_service(VSTREAM *src, char *unused_service, char **argv) @@ -421,6 +428,11 @@ static void post_jail_init(char *unused_name, char **unused_argv) */ if (var_message_limit > 0) set_file_limit((off_t) var_message_limit); + + /* + * Control how unmatched extensions are propagated. + */ + cleanup_ext_prop_mask = ext_prop_mask(var_prop_extension); } /* main - the main program */ @@ -443,6 +455,7 @@ int main(int argc, char **argv) VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0, VAR_MASQ_EXCEPTIONS, DEF_MASQ_EXCEPTIONS, &var_masq_exceptions, 0, 0, VAR_HEADER_CHECKS, DEF_HEADER_CHECKS, &var_header_checks, 0, 0, + VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0, 0, }; diff --git a/postfix/cleanup/cleanup.h b/postfix/cleanup/cleanup.h index 7523262c6..69fc01429 100644 --- a/postfix/cleanup/cleanup.h +++ b/postfix/cleanup/cleanup.h @@ -66,18 +66,15 @@ extern MAPS *cleanup_virtual_maps; extern ARGV *cleanup_masq_domains; /* - * Saved queue file name, so the file can be removed in case of a fatal - * run-time error. + * Restrictions on extension propagation. */ -extern char *cleanup_path; +extern int cleanup_ext_prop_mask; /* - * Tunable parameters. + * Saved queue file name, so the file can be removed in case of a fatal + * run-time error. */ -extern int var_bounce_limit; /* max bounce message size */ -extern int var_message_limit; /* max message size */ -extern int var_hopcount_limit; /* max mailer hop count */ -extern int var_header_limit; /* max header length */ +extern char *cleanup_path; /* * cleanup_state.c @@ -128,14 +125,14 @@ extern void cleanup_rewrite_tree(TOK822 *); /* * cleanup_map11.c */ -extern void cleanup_map11_external(VSTRING *, MAPS *); -extern void cleanup_map11_internal(VSTRING *, MAPS *); -extern void cleanup_map11_tree(TOK822 *, MAPS *); +extern void cleanup_map11_external(VSTRING *, MAPS *, int); +extern void cleanup_map11_internal(VSTRING *, MAPS *, int); +extern void cleanup_map11_tree(TOK822 *, MAPS *, int); /* * cleanup_map1n.c */ -ARGV *cleanup_map1n_internal(char *, MAPS *); +ARGV *cleanup_map1n_internal(char *, MAPS *, int); /* * cleanup_masquerade.c diff --git a/postfix/cleanup/cleanup_envelope.c b/postfix/cleanup/cleanup_envelope.c index fbef42f97..e7c29bf1c 100644 --- a/postfix/cleanup/cleanup_envelope.c +++ b/postfix/cleanup/cleanup_envelope.c @@ -44,6 +44,7 @@ #include #include #include +#include /* Application-specific. */ @@ -104,9 +105,11 @@ void cleanup_envelope(void) } else if (type == REC_TYPE_FROM) { cleanup_rewrite_internal(clean_addr, STR(cleanup_inbuf)); if (cleanup_send_canon_maps) - cleanup_map11_internal(clean_addr, cleanup_send_canon_maps); + cleanup_map11_internal(clean_addr, cleanup_send_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); if (cleanup_comm_canon_maps) - cleanup_map11_internal(clean_addr, cleanup_comm_canon_maps); + cleanup_map11_internal(clean_addr, cleanup_comm_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); if (cleanup_masq_domains) cleanup_masquerade_internal(clean_addr, cleanup_masq_domains); CLEANUP_OUT_BUF(type, clean_addr); @@ -121,9 +124,11 @@ void cleanup_envelope(void) cleanup_rewrite_internal(clean_addr, *STR(cleanup_inbuf) ? STR(cleanup_inbuf) : var_empty_addr); if (cleanup_rcpt_canon_maps) - cleanup_map11_internal(clean_addr, cleanup_rcpt_canon_maps); + cleanup_map11_internal(clean_addr, cleanup_rcpt_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); if (cleanup_comm_canon_maps) - cleanup_map11_internal(clean_addr, cleanup_comm_canon_maps); + cleanup_map11_internal(clean_addr, cleanup_comm_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); cleanup_out_recipient(STR(clean_addr)); if (cleanup_recip == 0) cleanup_recip = mystrdup(STR(clean_addr)); diff --git a/postfix/cleanup/cleanup_map11.c b/postfix/cleanup/cleanup_map11.c index cac495f9a..0680191a8 100644 --- a/postfix/cleanup/cleanup_map11.c +++ b/postfix/cleanup/cleanup_map11.c @@ -6,17 +6,20 @@ /* SYNOPSIS /* #include /* -/* void cleanup_map11_external(addr, maps) +/* void cleanup_map11_external(addr, maps, propagate) /* VSTRING *addr; /* MAPS *maps; +/* int propagate; /* -/* void cleanup_map11_internal(addr, maps) +/* void cleanup_map11_internal(addr, maps, propagate) /* VSTRING *addr; /* MAPS *maps; +/* int propagate; /* -/* void cleanup_map11_tree(tree, maps) +/* void cleanup_map11_tree(tree, maps, propagate) /* TOK822 *tree; /* MAPS *maps; +/* int propagate; /* DESCRIPTION /* This module performs one-to-one map lookups. /* @@ -24,6 +27,8 @@ /* subjected to another iteration of rewriting and mapping. /* Recursion continues until an address maps onto itself, /* or until an unreasonable recursion level is reached. +/* An unmatched address extension is propagated when +/* \fIpropagate\fR is non-zero. /* /* cleanup_map11_external() looks up the external (quoted) string /* form of an address in the maps specified via the \fImaps\fR argument. @@ -82,7 +87,7 @@ /* cleanup_map11_external - one-to-one table lookups */ -void cleanup_map11_external(VSTRING *addr, MAPS *maps) +void cleanup_map11_external(VSTRING *addr, MAPS *maps, int propagate) { int count; int expand_to_self; @@ -96,7 +101,7 @@ void cleanup_map11_external(VSTRING *addr, MAPS *maps) * the place. */ for (count = 0; count < MAX_RECURSION; count++) { - if ((new_addr = mail_addr_map(maps, STR(addr))) != 0) { + if ((new_addr = mail_addr_map(maps, STR(addr), propagate)) != 0) { if (new_addr->argc > 1) msg_warn("multi-valued %s entry for %s", maps->title, STR(addr)); @@ -122,7 +127,7 @@ void cleanup_map11_external(VSTRING *addr, MAPS *maps) /* cleanup_map11_tree - rewrite address node */ -void cleanup_map11_tree(TOK822 *tree, MAPS *maps) +void cleanup_map11_tree(TOK822 *tree, MAPS *maps, int propagate) { VSTRING *temp = vstring_alloc(100); @@ -133,7 +138,7 @@ void cleanup_map11_tree(TOK822 *tree, MAPS *maps) * the place. */ tok822_externalize(temp, tree->head, TOK822_STR_DEFL); - cleanup_map11_external(temp, maps); + cleanup_map11_external(temp, maps, propagate); tok822_free_tree(tree->head); tree->head = tok822_scan(STR(temp), &tree->tail); vstring_free(temp); @@ -141,7 +146,7 @@ void cleanup_map11_tree(TOK822 *tree, MAPS *maps) /* cleanup_map11_internal - rewrite address internal form */ -void cleanup_map11_internal(VSTRING *addr, MAPS *maps) +void cleanup_map11_internal(VSTRING *addr, MAPS *maps, int propagate) { VSTRING *temp = vstring_alloc(100); @@ -152,7 +157,7 @@ void cleanup_map11_internal(VSTRING *addr, MAPS *maps) * the place. */ quote_822_local(temp, STR(addr)); - cleanup_map11_external(temp, maps); + cleanup_map11_external(temp, maps, propagate); unquote_822_local(addr, STR(temp)); vstring_free(temp); } diff --git a/postfix/cleanup/cleanup_map1n.c b/postfix/cleanup/cleanup_map1n.c index 2addef510..2bab163df 100644 --- a/postfix/cleanup/cleanup_map1n.c +++ b/postfix/cleanup/cleanup_map1n.c @@ -61,7 +61,7 @@ /* cleanup_map1n_internal - one-to-many table lookups */ -ARGV *cleanup_map1n_internal(char *addr, MAPS *maps) +ARGV *cleanup_map1n_internal(char *addr, MAPS *maps, int propagate) { ARGV *argv; ARGV *lookup; @@ -102,7 +102,7 @@ ARGV *cleanup_map1n_internal(char *addr, MAPS *maps) cleanup_queue_id, maps->title, addr); break; } - if ((lookup = mail_addr_map(maps, argv->argv[arg])) != 0) { + if ((lookup = mail_addr_map(maps, argv->argv[arg], propagate)) != 0) { saved_lhs = mystrdup(argv->argv[arg]); for (i = 0; i < lookup->argc; i++) { unquote_822_local(cleanup_temp1, lookup->argv[i]); diff --git a/postfix/cleanup/cleanup_message.c b/postfix/cleanup/cleanup_message.c index 67a679197..592a8696d 100644 --- a/postfix/cleanup/cleanup_message.c +++ b/postfix/cleanup/cleanup_message.c @@ -59,6 +59,7 @@ #include #include #include +#include /* Application-specific. */ @@ -153,9 +154,11 @@ static void cleanup_rewrite_sender(HEADER_OPTS *hdr_opts) for (tpp = addr_list; *tpp; tpp++) { cleanup_rewrite_tree(*tpp); if (cleanup_send_canon_maps) - cleanup_map11_tree(*tpp, cleanup_send_canon_maps); + cleanup_map11_tree(*tpp, cleanup_send_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); if (cleanup_comm_canon_maps) - cleanup_map11_tree(*tpp, cleanup_comm_canon_maps); + cleanup_map11_tree(*tpp, cleanup_comm_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); if (cleanup_masq_domains) cleanup_masquerade_tree(*tpp, cleanup_masq_domains); if (hdr_opts->type == HDR_FROM && cleanup_from == 0) @@ -194,9 +197,11 @@ static void cleanup_rewrite_recip(HEADER_OPTS *hdr_opts) for (tpp = addr_list; *tpp; tpp++) { cleanup_rewrite_tree(*tpp); if (cleanup_rcpt_canon_maps) - cleanup_map11_tree(*tpp, cleanup_rcpt_canon_maps); + cleanup_map11_tree(*tpp, cleanup_rcpt_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); if (cleanup_comm_canon_maps) - cleanup_map11_tree(*tpp, cleanup_comm_canon_maps); + cleanup_map11_tree(*tpp, cleanup_comm_canon_maps, + cleanup_ext_prop_mask & EXT_PROP_CANONICAL); tok822_internalize(cleanup_temp1, tpp[0]->head, TOK822_STR_DEFL); if (cleanup_recip == 0 && (hdr_opts->flags & HDR_OPT_EXTRACT) != 0) argv_add((hdr_opts->flags & HDR_OPT_RR) ? diff --git a/postfix/cleanup/cleanup_out_recipient.c b/postfix/cleanup/cleanup_out_recipient.c index e1899a20b..596e17258 100644 --- a/postfix/cleanup/cleanup_out_recipient.c +++ b/postfix/cleanup/cleanup_out_recipient.c @@ -47,6 +47,7 @@ #include #include #include +#include /* Application-specific. */ @@ -63,7 +64,8 @@ void cleanup_out_recipient(char *recip) if (been_here_fixed(cleanup_dups, recip) == 0) cleanup_out_string(REC_TYPE_RCPT, recip); } else { - argv = cleanup_map1n_internal(recip, cleanup_virtual_maps); + argv = cleanup_map1n_internal(recip, cleanup_virtual_maps, + cleanup_ext_prop_mask & EXT_PROP_VIRTUAL); for (cpp = argv->argv; *cpp; cpp++) if (been_here_fixed(cleanup_dups, *cpp) == 0) cleanup_out_string(REC_TYPE_RCPT, *cpp); diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index e79c06851..e6657abff 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -250,11 +250,10 @@ program_directory = /some/where/postfix/bin # #fallback_transport = -# The luser_relay parameter specifies an optional destination (@domain, -# address) for unknown recipients. By default, mail for unknown -# local recipients is bounced. +# The luser_relay parameter specifies an optional destination address +# for unknown recipients. By default, mail for unknown local recipients +# is bounced. # -# Specify @domain in order to keep the original recipient name. # The following expansions are done on luser_relay: $user (recipient # username), $shell (recipient shell), $home (recipient home directory), # $recipient (full recipient address), $extension (recipient address @@ -262,7 +261,8 @@ program_directory = /some/where/postfix/bin # localpart), $recipient_delimiter. Specify ${name?value} or # ${name:value} to expand value only when $name does (does not) exist. # -# luser_relay = @other.host +# luser_relay = $user@other.host +# luser_relay = $mailbox@other.host # luser_relay = admin+$mailbox # JUNK MAIL CONTROLS diff --git a/postfix/conf/main.cf.default b/postfix/conf/main.cf.default index eccb1988a..102876586 100644 --- a/postfix/conf/main.cf.default +++ b/postfix/conf/main.cf.default @@ -14,6 +14,7 @@ bounce_notice_recipient = postmaster bounce_size_limit = 50000 canonical_maps = command_directory = $program_directory +command_expansion_filter = 1234567890!@%-_=+:,./abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ command_time_limit = 1000 daemon_directory = $program_directory daemon_timeout = 18000 @@ -40,6 +41,7 @@ fallback_relay = fallback_transport = fork_attempts = 5 fork_delay = 1 +forward_expansion_filter = 1234567890!@%-_=+:,./abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ forward_path = $home/.forward${recipient_delimiter}${extension},$home/.forward hash_queue_depth = 2 hash_queue_names = defer @@ -61,7 +63,7 @@ luser_relay = mail_name = Postfix mail_owner = postfix mail_spool_directory = /var/mail -mail_version = Snapshot-19990507 +mail_version = Snapshot-19990508 mailbox_command = mailbox_transport = maps_rbl_domains = rbl.maps.vix.com @@ -81,6 +83,7 @@ notify_classes = resource,software owner_request_special = yes process_id_directory = pid program_directory = /usr/libexec/postfix +propagate_unmatched_extension = canonical, virtual qmgr_message_active_limit = 1000 qmgr_message_recipient_limit = 10000 queue_directory = /var/spool/postfix diff --git a/postfix/conf/sample-local.cf b/postfix/conf/sample-local.cf index b083dffa5..f6dce292b 100644 --- a/postfix/conf/sample-local.cf +++ b/postfix/conf/sample-local.cf @@ -71,11 +71,10 @@ default_privs = nobody # home_mailbox = Maildir/ home_mailbox = -# The luser_relay parameter specifies an optional destination -# (@domain, address, "|command", /file/name) for unknown recipients. -# By default, mail for unknown local recipients is bounced. +# The luser_relay parameter specifies an optional destination address +# for unknown recipients. By default, mail for unknown local recipients +# is bounced. # -# Specify @domain in order to keep the original recipient name. # The following expansions are done on luser_relay: $user (recipient # username), $shell (recipient shell), $home (recipient home directory), # $recipient (full recipient address), $extension (recipient address @@ -83,7 +82,8 @@ home_mailbox = # localpart), $recipient_delimiter. Specify ${name?value} or # ${name:value} to expand value only when $name does (does not) exist. # -# luser_relay = @other.host +# luser_relay = $user@other.host +# luser_relay = $mailbox@other.host # luser_relay = admin+$mailbox # The mail_spool_directory parameter specifies the directory where diff --git a/postfix/dns/.indent.pro b/postfix/dns/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/dns/.indent.pro +++ b/postfix/dns/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/error/.indent.pro b/postfix/error/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/error/.indent.pro +++ b/postfix/error/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/error/Makefile.in b/postfix/error/Makefile.in index 96f90c0fa..62f013b27 100644 --- a/postfix/error/Makefile.in +++ b/postfix/error/Makefile.in @@ -62,4 +62,6 @@ error.o: ../include/deliver_request.h error.o: ../include/vstring.h error.o: ../include/recipient_list.h error.o: ../include/mail_queue.h +error.o: ../include/bounce.h +error.o: ../include/deliver_completed.h error.o: ../include/mail_server.h diff --git a/postfix/fsstone/.indent.pro b/postfix/fsstone/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/fsstone/.indent.pro +++ b/postfix/fsstone/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/global/.indent.pro b/postfix/global/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/global/.indent.pro +++ b/postfix/global/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/global/Makefile.in b/postfix/global/Makefile.in index 9f78c7c3e..cc1c3b7cc 100644 --- a/postfix/global/Makefile.in +++ b/postfix/global/Makefile.in @@ -16,7 +16,7 @@ SRCS = been_here.c bounce.c canon_addr.c cleanup_strerror.c clnt_stream.c \ recipient_list.c record.c remove.c resolve_clnt.c resolve_local.c \ rewrite_clnt.c sent.c smtp_stream.c split_addr.c string_list.c \ sys_exits.c timed_ipc.c tok822_find.c tok822_node.c tok822_parse.c \ - tok822_resolve.c tok822_rewrite.c tok822_tree.c + tok822_resolve.c tok822_rewrite.c tok822_tree.c ext_prop.c OBJS = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \ debug_peer.o debug_process.o defer.o deliver_completed.o \ deliver_flock.o deliver_pass.o deliver_request.o domain_list.o \ @@ -34,7 +34,7 @@ OBJS = been_here.o bounce.o canon_addr.o cleanup_strerror.o clnt_stream.o \ recipient_list.o record.o remove.o resolve_clnt.o resolve_local.o \ rewrite_clnt.o sent.o smtp_stream.o split_addr.o string_list.o \ sys_exits.o timed_ipc.o tok822_find.o tok822_node.o tok822_parse.o \ - tok822_resolve.o tok822_rewrite.o tok822_tree.o + tok822_resolve.o tok822_rewrite.o tok822_tree.o ext_prop.o HDRS = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \ config.h debug_peer.h debug_process.h defer.h deliver_completed.h \ deliver_flock.h deliver_pass.h deliver_request.h domain_list.h \ @@ -48,7 +48,7 @@ HDRS = been_here.h bounce.h canon_addr.h cleanup_user.h clnt_stream.h \ quote_822_local.h rec_streamlf.h rec_type.h recipient_list.h \ record.h resolve_clnt.h resolve_local.h rewrite_clnt.h sent.h \ smtp_stream.h split_addr.h string_list.h sys_exits.h timed_ipc.h \ - tok822.h + tok822.h ext_prop.h TESTSRC = rec2stream.c stream2rec.c recdump.c WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \ -Wparentheses -Wstrict-prototypes -Wswitch -Wuninitialized \ @@ -330,6 +330,10 @@ dot_lockfile.o: ../include/stringops.h dot_lockfile.o: ../include/mymalloc.h dot_lockfile.o: mail_params.h dot_lockfile.o: dot_lockfile.h +ext_prop.o: ext_prop.c +ext_prop.o: ../include/sys_defs.h +ext_prop.o: ../include/name_mask.h +ext_prop.o: ext_prop.h file_id.o: file_id.c file_id.o: ../include/sys_defs.h file_id.o: ../include/msg.h diff --git a/postfix/global/ext_prop.c b/postfix/global/ext_prop.c new file mode 100644 index 000000000..845c3a028 --- /dev/null +++ b/postfix/global/ext_prop.c @@ -0,0 +1,72 @@ +/*++ +/* NAME +/* exp_prop 3 +/* SUMMARY +/* address extension propagation control +/* SYNOPSIS +/* #include +/* +/* int ext_prop_mask(pattern) +/* const char *pattern; +/* DESCRIPTION +/* This module controld address extension propagation. +/* +/* ext_prop_mask() takes a comma-separated list of names and +/* computes the corresponding mask. The following names are +/* recognized in \fBpattern\fR, with the corresponding bit mask +/* given in parentheses: +/* .IP "canonical (EXP_PROP_CANONICAL)" +/* Propagate unmatched address extensions tothe right-hand side +/* canonical map entries. +/* .IP "virtual (EXP_PROP_VIRTUAL) +/* Propagate unmatched address extensions tothe right-hand side +/* canonical map entries. +/* .IP "alias (EXP_PROP_ALIAS) +/* Propagate unmatched address extensions tothe right-hand side +/* canonical map entries. +/* .IP "forward (EXP_PROP_FORWARD)" +/* Propagate unmatched address extensions tothe right-hand side +/* canonical map entries. +/* .IP "include (EXP_PROP_INCLUDE)" +/* Propagate unmatched address extensions tothe right-hand side +/* canonical map entries. +/* DIAGNOSTICS +/* Panic: inappropriate use. +/* LICENSE +/* .ad +/* .fi +/* The Secure Mailer license must be distributed with this software. +/* AUTHOR(S) +/* Wietse Venema +/* IBM T.J. Watson Research +/* P.O. Box 704 +/* Yorktown Heights, NY 10598, USA +/*--*/ + +/* System library. */ + +#include + +/* Utility library. */ + +#include + +/* Global library. */ + +#include + +/* ext_prop_mask - compute extension propagation mask */ + +int ext_prop_mask(const char *pattern) +{ + static NAME_MASK table[] = { + "canonical", EXT_PROP_CANONICAL, + "virtual", EXT_PROP_VIRTUAL, + "alias", EXT_PROP_ALIAS, + "forward", EXT_PROP_FORWARD, + "include", EXT_PROP_INCLUDE, + 0, + }; + + return (name_mask(table, pattern)); +} diff --git a/postfix/global/ext_prop.h b/postfix/global/ext_prop.h new file mode 100644 index 000000000..99e09112b --- /dev/null +++ b/postfix/global/ext_prop.h @@ -0,0 +1,36 @@ +#ifndef _EXT_PROP_INCLUDED_ +#define _EXT_PROP_INCLUDED_ + +/*++ +/* NAME +/* ext_prop 3h +/* SUMMARY +/* address extension propagation control +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * External interface. + */ +#define EXT_PROP_CANONICAL (1<<0) +#define EXT_PROP_VIRTUAL (1<<1) +#define EXT_PROP_ALIAS (1<<2) +#define EXT_PROP_FORWARD (1<<3) +#define EXT_PROP_INCLUDE (1<<4) + +extern int ext_prop_mask(const char *); + +/* 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 +/*--*/ + +#endif diff --git a/postfix/global/mail_addr_map.c b/postfix/global/mail_addr_map.c index 8d9436804..508251f8b 100644 --- a/postfix/global/mail_addr_map.c +++ b/postfix/global/mail_addr_map.c @@ -6,15 +6,17 @@ /* SYNOPSIS /* #include /* -/* ARGV *mail_addr_map(path, address) +/* ARGV *mail_addr_map(path, address, propagate) /* MAPS *path; /* const char *address; +/* int propagate; /* DESCRIPTION /* mail_addr_map() returns the translation for the named address, /* or a null pointer if none is found. The result is in canonical /* external (quoted) form. The search is case insensitive. /* -/* Address extensions that aren't explicitly matched in the lookup +/* When the \fBpropagate\fR argument is non-zero, +/* address extensions that aren't explicitly matched in the lookup /* table are propagated to the result addresses. The caller is /* expected to pass the result to argv_free(). /* @@ -69,7 +71,7 @@ /* mail_addr_map - map a canonical address */ -ARGV *mail_addr_map(MAPS *path, const char *address) +ARGV *mail_addr_map(MAPS *path, const char *address, int propagate) { VSTRING *buffer = 0; char *myname = "mail_addr_map"; @@ -102,7 +104,7 @@ ARGV *mail_addr_map(MAPS *path, const char *address) * Canonicalize and externalize the result, and propagate the * unmatched extension to each address found. */ - argv = mail_addr_crunch(string, extension); + argv = mail_addr_crunch(string, propagate ? extension : 0); if (buffer) vstring_free(buffer); if (msg_verbose) diff --git a/postfix/global/mail_addr_map.h b/postfix/global/mail_addr_map.h index bfc07c07c..f887c611a 100644 --- a/postfix/global/mail_addr_map.h +++ b/postfix/global/mail_addr_map.h @@ -24,7 +24,7 @@ /* * External interface. */ -extern ARGV *mail_addr_map(MAPS *, const char *); +extern ARGV *mail_addr_map(MAPS *, const char *, int); /* LICENSE /* .ad diff --git a/postfix/global/mail_params.h b/postfix/global/mail_params.h index 9fef329bf..a6650583d 100644 --- a/postfix/global/mail_params.h +++ b/postfix/global/mail_params.h @@ -328,10 +328,26 @@ extern char *var_fallback_transport; #define DEF_FORWARD_PATH "$home/.forward${recipient_delimiter}${extension},$home/.forward" extern char *var_forward_path; +#define VAR_PROP_EXTENSION "propagate_unmatched_extension" +#define DEF_PROP_EXTENSION "canonical, virtual" +extern char *var_prop_extension; + #define VAR_RCPT_DELIM "recipient_delimiter" #define DEF_RCPT_DELIM "" extern char *var_rcpt_delim; +#define VAR_CMD_EXP_FILTER "command_expansion_filter" +#define DEF_CMD_EXP_FILTER "1234567890!@%-_=+:,./\ +abcdefghijklmnopqrstuvwxyz\ +ABCDEFGHIJKLMNOPQRSTUVWXYZ" +extern char *var_cmd_exp_filter;; + +#define VAR_FWD_EXP_FILTER "forward_expansion_filter" +#define DEF_FWD_EXP_FILTER "1234567890!@%-_=+:,./\ +abcdefghijklmnopqrstuvwxyz\ +ABCDEFGHIJKLMNOPQRSTUVWXYZ" +extern char *var_fwd_exp_filter;; + #define VAR_RCPT_FDELIM "recipient_feature_delimiter" #define DEF_RCPT_FDELIM "" extern char *var_rcpt_fdelim; diff --git a/postfix/global/mail_version.h b/postfix/global/mail_version.h index bab9fdc66..540f9a01f 100644 --- a/postfix/global/mail_version.h +++ b/postfix/global/mail_version.h @@ -15,7 +15,7 @@ * Version of this program. */ #define VAR_MAIL_VERSION "mail_version" -#define DEF_MAIL_VERSION "Snapshot-19990507" +#define DEF_MAIL_VERSION "Snapshot-19990508" extern char *var_mail_version; /* LICENSE diff --git a/postfix/html/local.8.html b/postfix/html/local.8.html index f16780226..099d80630 100644 --- a/postfix/html/local.8.html +++ b/postfix/html/local.8.html @@ -44,7 +44,11 @@ LOCAL(8) LOCAL(8) address extension), $domain (recipient domain), mailbox (entire recipient address localpart) and $recipient_delim- iter. The forms ${name?value} and ${name:value} expand - conditionally to value when $name is (is not) defined. + conditionally to value when $name is (is not) defined. In + the result of name expansion, characters that have special + meaning to the shell are replaced by underscores. The list + of legal characters is specified with the forward_expan- + sion_filter configuration parameter. An alias or ~/.forward file may list any combination of external commands, destination file names, :include: @@ -55,10 +59,6 @@ LOCAL(8) LOCAL(8) When an address is found in its own alias expansion, delivery is made to the user instead. When a user is listed in the user's own ~/.forward file, delivery is made - to the user's mailbox instead. An empty ~/.forward file - means do not forward mail. - - In order to prevent the mail system from using up @@ -71,7 +71,11 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) - unreasonable amounts of memory, input records read from + to the user's mailbox instead. An empty ~/.forward file + means do not forward mail. + + In order to prevent the mail system from using up unrea- + sonable amounts of memory, input records read from :include: or from ~/.forward files are broken up into chunks of length line_length_limit. @@ -106,25 +110,21 @@ LOCAL(8) LOCAL(8) specified with the mailbox_command configuration parame- ter. The command executes with the privileges of the recipient user (exception: in case of delivery as root, - the command executes with the privileges of default_user). - The command is subject to interpolation of $user (recipi- - ent username), $home (recipient home directory), $shell - (recipient shell), $recipient (complete recipient - address), $extension (recipient address extension), - $domain (recipient domain), mailbox (entire recipient - address localpart) and $recipient_delimiter. The forms + the command executes with the privileges of + default_privs). The command is subject to interpolation + of $user (recipient username), $home (recipient home + directory), $shell (recipient shell), $recipient (complete + recipient address), $extension (recipient address exten- + sion), $domain (recipient domain), mailbox (entire recipi- + ent address localpart) and $recipient_delimiter. The forms ${name?value} and ${name:value} expand conditionally to - value when $name is (is not) defined. In the result of + value when $name is (is not) defined. In the result of name expansion, characters that have special meaning to - the shell are censored and replaced by underscores. + the shell are replaced by underscores. The list of legal + characters is specified with the command_expansion_filter + configuration parameter. Mailbox delivery can be delegated to alternative message - transports specified in the master.cf file. The mail- - box_transport configuration parameter specifies a message - transport that is to be used for all local recipients, - regardless of whether they are found in the UNIX passwd - database. The fallback_transport parameter specifies a - message transport for recipients that are not found in the @@ -137,6 +137,12 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) + transports specified in the master.cf file. The mail- + box_transport configuration parameter specifies a message + transport that is to be used for all local recipients, + regardless of whether they are found in the UNIX passwd + database. The fallback_transport parameter specifies a + message transport for recipients that are not found in the UNIX passwd database. In the case of UNIX-style mailbox delivery, the local dae- @@ -186,12 +192,6 @@ LOCAL(8) LOCAL(8) Return-Path: header with the sender envelope address, and appends an empty line. -EXTERNAL FILE DELIVERY - The allow_mail_to_files configuration parameter restricts - delivery to external files. The default setting (alias, - forward) forbids file destinations in :include: files. - Specify a pathname ending in / for qmail-compatible - 3 @@ -203,6 +203,11 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) +EXTERNAL FILE DELIVERY + The allow_mail_to_files configuration parameter restricts + delivery to external files. The default setting (alias, + forward) forbids file destinations in :include: files. + Specify a pathname ending in / for qmail-compatible maildir delivery. The local daemon prepends a "From sender time_stamp" enve- @@ -252,11 +257,6 @@ LOCAL(8) LOCAL(8) can move them to the corrupt queue afterwards. Depending on the setting of the notify_classes parameter, - the postmaster is notified of bounces and of other trou- - ble. - -BUGS - For security reasons, the message delivery status of @@ -269,6 +269,11 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) + the postmaster is notified of bounces and of other trou- + ble. + +BUGS + For security reasons, the message delivery status of external commands or of external files is never check- pointed to file. As a result, the program may occasionally deliver more than once to a command or external file. Bet- @@ -317,11 +322,6 @@ LOCAL(8) LOCAL(8) directory. Specify a path ending in / for maildir- style delivery. - luser_relay - Destination (@domain or address) for non-existent - users. The address is subjected to $name expan- - sion. - @@ -335,6 +335,11 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) + luser_relay + Destination (@domain or address) for non-existent + users. The address is subjected to $name expan- + sion. + mail_spool_directory Directory with UNIX-style mailboxes. The default pathname is system dependent. @@ -342,22 +347,23 @@ LOCAL(8) LOCAL(8) mailbox_command External command to use for mailbox delivery. The command executes with the recipient privileges - (exception: root). + (exception: root). The string is subject to $name + expansions. mailbox_transport - Message transport to use for mailbox delivery to + Message transport to use for mailbox delivery to all local recipients, whether or not they are found - in the UNIX passwd database. This parameter over- - rides all other configuration parameters that con- + in the UNIX passwd database. This parameter over- + rides all other configuration parameters that con- trol mailbox delivery, including luser_relay. Locking controls deliver_lock_attempts - Limit the number of attempts to acquire an exclu- + Limit the number of attempts to acquire an exclu- sive lock on a mailbox or external file. deliver_lock_delay - Time in seconds between successive attempts to + Time in seconds between successive attempts to acquire an exclusive lock. stale_lock_time @@ -365,30 +371,24 @@ LOCAL(8) LOCAL(8) Resource controls command_time_limit - Limit the amount of time for delivery to external + Limit the amount of time for delivery to external command. duplicate_filter_limit - Limit the size of the duplicate filter for results + Limit the size of the duplicate filter for results from alias etc. expansion. line_length_limit - Limit the amount of memory used for processing a + Limit the amount of memory used for processing a partial input line. local_destination_concurrency_limit Limit the number of parallel deliveries to the same - user. The default limit is taken from the + user. The default limit is taken from the default_destination_concurrency_limit parameter. local_destination_recipient_limit - Limit the number of recipients per message deliv- - ery. The default limit is taken from the - default_destination_recipient_limit parameter. - -Security controls - allow_mail_to_commands - Restrict the usage of mail delivery to external + Limit the number of recipients per message @@ -401,21 +401,37 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) + delivery. The default limit is taken from the + default_destination_recipient_limit parameter. + +Security controls + allow_mail_to_commands + Restrict the usage of mail delivery to external command. allow_mail_to_files - Restrict the usage of mail delivery to external + Restrict the usage of mail delivery to external file. + command_expansion_filter + What characters are allowed to appear in $name + expansions of mailbox_command. Illegal characters + are replaced by underscores. + default_privs - Default rights for delivery to external file or + Default rights for delivery to external file or command. + forward_expansion_filter + What characters are allowed to appear in $name + expansions of forward_path. Illegal characters are + replaced by underscores. + HISTORY - The Delivered-To: header appears in the qmail system by + The Delivered-To: header appears in the qmail system by Daniel Bernstein. - The maildir structure appears in the qmail system by + The maildir structure appears in the qmail system by Daniel Bernstein. SEE ALSO @@ -426,7 +442,7 @@ LOCAL(8) LOCAL(8) qmgr(8) queue manager LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) @@ -437,22 +453,6 @@ LOCAL(8) LOCAL(8) - - - - - - - - - - - - - - - - diff --git a/postfix/html/rewrite.html b/postfix/html/rewrite.html index 4673d0409..fae9e4a48 100644 --- a/postfix/html/rewrite.html +++ b/postfix/html/rewrite.html @@ -425,37 +425,41 @@ see the local delivery agent.

-luser_relay can specify any number of destinations that are -valid in an alias file. In fact, the same restrictions for command -and file destinations apply as for true aliases. +luser_relay can specify one address. It is subjected to +$name expansions. The most useful examples are:

-In addition, some luser_relay destinations can receive -special treatment: +

-

+

$user@other.host -
+
The bare username, without address extension, is prepended to +@other.host. For example, mail for username+foo is +sent to username@other.host. + +

-

luser_relay = @some.where.else +
$mailbox@other.host -
The entire original recipient localpart is prepended. For -example, mail for unknown+foo is sent to -unknown+foo@some.where.else. +
The entire original recipient localpart, including address +extension, is prepended to @other.host. For example, mail +for username+foo is sent to username+foo@other.host.

-

luser_relay = someone@some.where.else -
luser_relay = someone +
sysadmin+$user + +
The bare username, without address extension, is appended to +sysadmin. For example, mail for username+foo is sent +to sysadmin+username. + +
sysadmin+$mailbox + +
The entire original recipient localpart, including address +extension, is appended to sysadmin. For example, mail for +username+foo is sent to sysadmin+username+foo. -
If no recipient_delimiter has been specified, mail is -sent to the luser_relay address. If the recipient_delimiter -has been specified, the entire original recipient localpart is -appended as an address extension to the luser_relay address. -For example, with recipient_delimiter = +, mail for -unknown+foo is sent to someone+unknown+foo@some.where.else -and someone+unknown+foo, respectively.
diff --git a/postfix/local/.indent.pro b/postfix/local/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/local/.indent.pro +++ b/postfix/local/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/local/Makefile.in b/postfix/local/Makefile.in index 943b2b70f..38e36a2bb 100644 --- a/postfix/local/Makefile.in +++ b/postfix/local/Makefile.in @@ -98,6 +98,7 @@ command.o: ../include/vbuf.h command.o: ../include/vstream.h command.o: ../include/argv.h command.o: ../include/mac_expand.h +command.o: ../include/mac_parse.h command.o: ../include/defer.h command.o: ../include/bounce.h command.o: ../include/sent.h @@ -156,11 +157,13 @@ dotforward.o: ../include/iostuff.h dotforward.o: ../include/stringops.h dotforward.o: ../include/mymalloc.h dotforward.o: ../include/mac_expand.h +dotforward.o: ../include/mac_parse.h dotforward.o: ../include/mypwd.h dotforward.o: ../include/bounce.h dotforward.o: ../include/been_here.h dotforward.o: ../include/mail_params.h dotforward.o: ../include/mail_conf.h +dotforward.o: ../include/ext_prop.h dotforward.o: local.h dotforward.o: ../include/tok822.h dotforward.o: ../include/resolve_clnt.h @@ -244,6 +247,7 @@ include.o: ../include/bounce.h include.o: ../include/defer.h include.o: ../include/been_here.h include.o: ../include/mail_params.h +include.o: ../include/ext_prop.h include.o: local.h include.o: ../include/vstring.h include.o: ../include/tok822.h @@ -286,17 +290,21 @@ local.o: ../include/mail_params.h local.o: ../include/mail_addr.h local.o: ../include/mail_conf.h local.o: ../include/been_here.h +local.o: ../include/ext_prop.h local.o: ../include/mail_server.h local.o: local.h local.o: ../include/tok822.h local.o: ../include/resolve_clnt.h local_expand.o: local_expand.c local_expand.o: ../include/sys_defs.h -local_expand.o: ../include/htable.h +local_expand.o: ../include/vstring.h +local_expand.o: ../include/vbuf.h +local_expand.o: ../include/mac_expand.h +local_expand.o: ../include/mac_parse.h +local_expand.o: ../include/mail_params.h local_expand.o: local.h +local_expand.o: ../include/htable.h local_expand.o: ../include/vstream.h -local_expand.o: ../include/vbuf.h -local_expand.o: ../include/vstring.h local_expand.o: ../include/been_here.h local_expand.o: ../include/tok822.h local_expand.o: ../include/resolve_clnt.h @@ -365,6 +373,7 @@ recipient.o: ../include/vbuf.h recipient.o: ../include/bounce.h recipient.o: ../include/mail_params.h recipient.o: ../include/split_addr.h +recipient.o: ../include/ext_prop.h recipient.o: local.h recipient.o: ../include/vstring.h recipient.o: ../include/been_here.h @@ -411,16 +420,16 @@ unknown.o: ../include/sys_defs.h unknown.o: ../include/msg.h unknown.o: ../include/stringops.h unknown.o: ../include/mymalloc.h +unknown.o: ../include/vstring.h +unknown.o: ../include/vbuf.h unknown.o: ../include/been_here.h unknown.o: ../include/mail_params.h unknown.o: ../include/mail_proto.h unknown.o: ../include/vstream.h -unknown.o: ../include/vbuf.h unknown.o: ../include/iostuff.h unknown.o: ../include/bounce.h unknown.o: local.h unknown.o: ../include/htable.h -unknown.o: ../include/vstring.h unknown.o: ../include/tok822.h unknown.o: ../include/resolve_clnt.h unknown.o: ../include/deliver_request.h diff --git a/postfix/local/command.c b/postfix/local/command.c index 481223fc5..4d61c7284 100644 --- a/postfix/local/command.c +++ b/postfix/local/command.c @@ -24,9 +24,9 @@ /* .IP usr_attr /* Attributes describing user rights and environment. /* .IP command -/* The shell command to be executed, after $name expansion of recipient -/* attributes. If possible, the command is executed without actually -/* invoking a shell. +/* The shell command to be executed. If possible, the command is +/* executed without actually invoking a shell. if the command is +/* the mailbox_command, it is subjected to $name expansion. /* DIAGNOSTICS /* deliver_command() returns non-zero when delivery should be /* tried again, @@ -83,11 +83,8 @@ int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, char *command) int deliver_status; ARGV *env; int copy_flags; - static char *ok_chars = "1234567890!@%-_=+:,./\ -abcdefghijklmnopqrstuvwxyz\ -ABCDEFGHIJKLMNOPQRSTUVWXYZ"; - VSTRING *expanded_cmd; - HTABLE *expand_attr; + VSTRING *expanded_cmd = 0; + char *xcommand ; /* * Make verbose logging easier to understand. @@ -152,21 +149,18 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ"; argv_add(env, "SHELL", usr_attr.shell, ARGV_END); argv_terminate(env); - expanded_cmd = vstring_alloc(10); if (command == var_mailbox_command) { - expand_attr = local_expand(state, usr_attr); - mac_expand(expanded_cmd, command, MAC_EXP_FLAG_NONE, - MAC_EXP_ARG_FILTER, ok_chars, - MAC_EXP_ARG_TABLE, expand_attr, - 0); - htable_free(expand_attr, (void (*) (char *)) 0); - } else - vstring_strcpy(expanded_cmd, command); - + expanded_cmd = vstring_alloc(100); + local_expand(expanded_cmd, command, &state, + &usr_attr, var_cmd_exp_filter); + xcommand = vstring_str(expanded_cmd); + } else { + xcommand = command; + } cmd_status = pipe_command(state.msg_attr.fp, why, PIPE_CMD_UID, usr_attr.uid, PIPE_CMD_GID, usr_attr.gid, - PIPE_CMD_COMMAND, vstring_str(expanded_cmd), + PIPE_CMD_COMMAND, xcommand, PIPE_CMD_COPY_FLAGS, copy_flags, PIPE_CMD_SENDER, state.msg_attr.sender, PIPE_CMD_DELIVERED, state.msg_attr.delivered, @@ -176,7 +170,8 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ"; PIPE_CMD_END); argv_free(env); - vstring_free(expanded_cmd); + if (expanded_cmd) + vstring_free(expanded_cmd); /* * Depending on the result, bounce or defer the message. diff --git a/postfix/local/dotforward.c b/postfix/local/dotforward.c index aef9e0eed..b2250d9cc 100644 --- a/postfix/local/dotforward.c +++ b/postfix/local/dotforward.c @@ -76,6 +76,7 @@ #include #include #include +#include /* Application-specific. */ @@ -102,9 +103,7 @@ int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) char *lhs; char *next; const char *forward_path; - HTABLE *expand_attr; - HTABLE *record_attr; - HTABLE_INFO *extension_record; + int expand_status; /* * Make verbose logging easier to understand. @@ -184,13 +183,11 @@ int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) state.msg_attr.owner = state.msg_attr.recipient; /* - * Assume that usernames do not have file system meta characters. Open - * the .forward file as the user. Ignore files that aren't regular files, - * files that are owned by the wrong user, or files that have world write - * permission enabled. + * Search the forward_path for an existing forward file. * - * If a forward file name includes the address extension, don't propagate - * the extension to the recipient addresses. + * If unmatched extensions should never be propagated, or if a forward file + * name includes the address extension, don't propagate the extension to + * the recipient addresses. */ #define STR(x) vstring_str(x) @@ -200,33 +197,32 @@ int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) next = saved_forward_path; lookup_status = -1; - expand_attr = local_expand(state, usr_attr); - record_attr = htable_create(0); - extension_record = htable_enter(record_attr, "extension", (char *) 0); - while ((lhs = mystrtok(&next, ", \t\r\n")) != 0) { - VSTRING_RESET(path); - extension_record->value = 0; - if (mac_expand(path, lhs, MAC_EXP_FLAG_NONE, - MAC_EXP_ARG_TABLE, expand_attr, - MAC_EXP_ARG_RECORD, record_attr, - 0) == 0) { + expand_status = local_expand(path, lhs, &state, + &usr_attr, var_fwd_exp_filter); + if ((expand_status & (MAC_PARSE_ERROR | MAC_PARSE_UNDEF)) == 0) { lookup_status = lstat_as(STR(path), &st, usr_attr.uid, usr_attr.gid); if (msg_verbose) - msg_info("%s: path %s status %d", myname, - STR(path), lookup_status); + msg_info("%s: path %s expand_status %d look_status %d", myname, + STR(path), expand_status, lookup_status); if (lookup_status >= 0) { - if (extension_record->value != 0) + if ((expand_status & LOCAL_EXP_EXTENSION_MATCHED) != 0 + || (local_ext_prop_mask & EXT_PROP_FORWARD) == 0) state.msg_attr.unmatched = 0; break; } } } - htable_free(expand_attr, (void (*) (char *)) 0); - htable_free(record_attr, (void (*) (char *)) 0); - + /* + * Process the forward file. + * + * Assume that usernames do not have file system meta characters. Open the + * .forward file as the user. Ignore files that aren't regular files, + * files that are owned by the wrong user, or files that have world write + * permission enabled. + */ if (lookup_status >= 0) { if (S_ISREG(st.st_mode) == 0) { msg_warn("file %s is not a regular file", STR(path)); diff --git a/postfix/local/include.c b/postfix/local/include.c index 38a8d72ad..e5ac21798 100644 --- a/postfix/local/include.c +++ b/postfix/local/include.c @@ -67,6 +67,7 @@ #include #include #include +#include /* Application-specific. */ @@ -169,6 +170,8 @@ int deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path) * The command and file delivery routines are responsible for setting the * proper delivery rights. These are the rights of the default user, in * case the :include: is in a root-owned alias. + * + * Don't propagate unmatched extensions unless permitted to do so. */ #define FOPEN_AS(p,u,g) ((fd = open_as(p,O_RDONLY,0,u,g)) >= 0 ? \ vstream_fdopen(fd,O_RDONLY) : 0) @@ -177,6 +180,8 @@ int deliver_include(LOCAL_STATE state, USER_ATTR usr_attr, char *path) status = bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), "cannot open include file %s: %m", path); } else { + if ((local_ext_prop_mask & EXT_PROP_INCLUDE) == 0) + state.msg_attr.unmatched = 0; close_on_exec(vstream_fileno(fp), CLOSE_ON_EXEC); status = deliver_token_stream(state, usr_attr, fp, (int *) 0); if (vstream_fclose(fp)) diff --git a/postfix/local/local.c b/postfix/local/local.c index 7a29b49b0..a910855be 100644 --- a/postfix/local/local.c +++ b/postfix/local/local.c @@ -40,6 +40,10 @@ /* \fB$recipient_delimiter.\fR The forms \fI${name?value}\fR and /* \fI${name:value}\fR expand conditionally to \fIvalue\fR when /* \fI$name\fR is (is not) defined. +/* In the result of \fIname\fR expansion, characters that have special +/* meaning to the shell are replaced by underscores. The list of legal +/* characters is specified with the \fBforward_expansion_filter\fR +/* configuration parameter. /* /* An alias or ~/.\fBforward\fR file may list any combination of external /* commands, destination file names, \fB:include:\fR directives, or @@ -90,7 +94,7 @@ /* with the \fBmailbox_command\fR configuration parameter. The command /* executes with the privileges of the recipient user (exception: in /* case of delivery as root, the command executes with the privileges -/* of \fBdefault_user\fR). +/* of \fBdefault_privs\fR). /* The command is subject to interpolation of \fB$user\fR (recipient /* username), \fB$home\fR (recipient home directory), \fB$shell\fR /* (recipient shell), \fB$recipient\fR (complete recipient address), @@ -98,9 +102,11 @@ /* (recipient domain), \fBmailbox\fR (entire recipient address /* localpart) and \fB$recipient_delimiter.\fR The forms /* \fI${name?value}\fR and \fI${name:value}\fR expand conditionally to -/* \fIvalue\fR when \fI$name\fR is (is not) defined. In the result -/* of \fIname\fR expansion, characters that have special meaning to -/* the shell are censored and replaced by underscores. +/* \fIvalue\fR when \fI$name\fR is (is not) defined. +/* In the result of \fIname\fR expansion, characters that have special +/* meaning to the shell are replaced by underscores. The list of legal +/* characters is specified with the \fBcommand_expansion_filter\fR +/* configuration parameter. /* /* Mailbox delivery can be delegated to alternative message transports /* specified in the \fBmaster.cf\fR file. @@ -267,7 +273,8 @@ /* dependent. /* .IP \fBmailbox_command\fR /* External command to use for mailbox delivery. The command executes -/* with the recipient privileges (exception: root). +/* with the recipient privileges (exception: root). The string is subject +/* to $name expansions. /* .IP \fBmailbox_transport\fR /* Message transport to use for mailbox delivery to all local /* recipients, whether or not they are found in the UNIX passwd database. @@ -310,8 +317,14 @@ /* Restrict the usage of mail delivery to external command. /* .IP \fBallow_mail_to_files\fR /* Restrict the usage of mail delivery to external file. +/* .IP \fBcommand_expansion_filter\fR +/* What characters are allowed to appear in $name expansions of +/* mailbox_command. Illegal characters are replaced by underscores. /* .IP \fBdefault_privs\fR /* Default rights for delivery to external file or command. +/* .IP \fBforward_expansion_filter\fR +/* What characters are allowed to appear in $name expansions of +/* forward_path. Illegal characters are replaced by underscores. /* HISTORY /* .ad /* .fi @@ -371,6 +384,7 @@ #include #include #include +#include /* Single server skeleton. */ @@ -398,9 +412,13 @@ char *var_mail_spool_dir; char *var_mailbox_transport; char *var_fallback_transport; char *var_forward_path; +char *var_cmd_exp_filter; +char *var_fwd_exp_filter; +char *var_prop_extension; int local_cmd_deliver_mask; int local_file_deliver_mask; +int local_ext_prop_mask; /* local_deliver - deliver message with extreme prejudice */ @@ -521,6 +539,7 @@ static void local_mask_init(void) local_file_deliver_mask = name_mask(file_mask, var_allow_files); local_cmd_deliver_mask = name_mask(command_mask, var_allow_commands); + local_ext_prop_mask = ext_prop_mask(var_prop_extension); } /* pre_accept - see if tables have changed */ @@ -565,6 +584,9 @@ int main(int argc, char **argv) VAR_MAIL_SPOOL_DIR, DEF_MAIL_SPOOL_DIR, &var_mail_spool_dir, 0, 0, VAR_MAILBOX_TRANSP, DEF_MAILBOX_TRANSP, &var_mailbox_transport, 0, 0, VAR_FALLBACK_TRANSP, DEF_FALLBACK_TRANSP, &var_fallback_transport, 0, 0, + VAR_CMD_EXP_FILTER, DEF_CMD_EXP_FILTER, &var_cmd_exp_filter, 1, 0, + VAR_FWD_EXP_FILTER, DEF_FWD_EXP_FILTER, &var_fwd_exp_filter, 1, 0, + VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0, 0, }; static CONFIG_BOOL_TABLE bool_table[] = { diff --git a/postfix/local/local.h b/postfix/local/local.h index 0ec125ecc..63e7b1fcc 100644 --- a/postfix/local/local.h +++ b/postfix/local/local.h @@ -164,6 +164,11 @@ extern int deliver_unknown(LOCAL_STATE, USER_ATTR); extern int local_file_deliver_mask; extern int local_cmd_deliver_mask; + /* + * Restrictions on extension propagation. + */ +extern int local_ext_prop_mask; + /* * delivered.c */ @@ -187,7 +192,9 @@ extern int feature_control(const char *); /* * local_expand.c */ -HTABLE *local_expand(LOCAL_STATE, USER_ATTR); +int local_expand(VSTRING *, const char *, LOCAL_STATE *, USER_ATTR *, const char *); + +#define LOCAL_EXP_EXTENSION_MATCHED (1< +#include +#include /* Global library */ @@ -71,25 +88,55 @@ #include "local.h" -/* local_expand - set up macro expansion attributes */ +typedef struct { + LOCAL_STATE *state; + USER_ATTR *usr_attr; + int status; +} LOCAL_EXP; + +/* local_expand_lookup - mac_expand() lookup routine */ + +static const char *local_expand_lookup(const char *name, int mode, char *ptr) +{ + LOCAL_EXP *local = (LOCAL_EXP *) ptr; + +#define STREQ(x,y) (*(x) == *(y) && strcmp((x), (y)) == 0) + + if (STREQ(name, "user")) { + return (local->usr_attr->logname); + } else if (STREQ(name, "home")) { + return (local->usr_attr->home); + } else if (STREQ(name, "shell")) { + return (local->usr_attr->shell); + } else if (STREQ(name, "domain")) { + return (local->state->msg_attr.domain); + } else if (STREQ(name, "mailbox")) { + return (local->state->msg_attr.local); + } else if (STREQ(name, "recipient")) { + return (local->state->msg_attr.recipient); + } else if (STREQ(name, "extension")) { + if (mode == MAC_EXP_MODE_USE) + local->status |= LOCAL_EXP_EXTENSION_MATCHED; + return (local->state->msg_attr.extension); + } else if (STREQ(name, "recipient_delimiter")) { + return (*var_rcpt_delim ? var_rcpt_delim : 0); + } else { + return (0); + } +} -HTABLE *local_expand(LOCAL_STATE state, USER_ATTR usr_attr) +/* local_expand - expand message delivery attributes */ + +int local_expand(VSTRING *result, const char *pattern, + LOCAL_STATE *state, USER_ATTR *usr_attr, const char *filter) { - HTABLE *expand_attr; - - /* - * Impedance matching between the local delivery agent data structures - * and the mac_expand() interface. The CPU cycles wasted will be - * negligible. - */ - expand_attr = htable_create(0); - htable_enter(expand_attr, "user", usr_attr.logname); - htable_enter(expand_attr, "home", usr_attr.home); - htable_enter(expand_attr, "shell", usr_attr.shell); - htable_enter(expand_attr, "domain", state.msg_attr.domain); - htable_enter(expand_attr, "mailbox", state.msg_attr.local); - htable_enter(expand_attr, "recipient", state.msg_attr.recipient); - htable_enter(expand_attr, "extension", state.msg_attr.extension); - htable_enter(expand_attr, "recipient_delimiter", var_rcpt_delim); - return (expand_attr); + LOCAL_EXP local; + int expand_status; + + local.state = state; + local.usr_attr = usr_attr; + local.status = 0; + expand_status = mac_expand(result, pattern, MAC_EXP_FLAG_NONE, + filter, local_expand_lookup, (char *) &local); + return (local.status | expand_status); } diff --git a/postfix/local/recipient.c b/postfix/local/recipient.c index 9680f2f8c..252071f84 100644 --- a/postfix/local/recipient.c +++ b/postfix/local/recipient.c @@ -81,6 +81,7 @@ #include #include #include +#include /* Application-specific. */ @@ -113,15 +114,19 @@ static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr) /* * Otherwise, alias expansion has highest precedence. First look up the - * full localpart, then the bare user. + * full localpart, then the bare user. Obey the address extension + * propagation policy. */ state.msg_attr.unmatched = 0; if (deliver_alias(state, usr_attr, state.msg_attr.local, &status)) return (status); - state.msg_attr.unmatched = state.msg_attr.extension; - if (state.msg_attr.extension != 0) + if (state.msg_attr.extension != 0) { + if (local_ext_prop_mask & EXT_PROP_ALIAS) + state.msg_attr.unmatched = state.msg_attr.extension; if (deliver_alias(state, usr_attr, state.msg_attr.user, &status)) return (status); + state.msg_attr.unmatched = state.msg_attr.extension; + } /* * Special case for mail locally forwarded or aliased to a different @@ -202,7 +207,7 @@ int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr) if (*var_rcpt_delim) { state.msg_attr.extension = split_addr(state.msg_attr.local, *var_rcpt_delim); - if (strchr(state.msg_attr.extension, '/')) { + if (state.msg_attr.extension && strchr(state.msg_attr.extension, '/')) { msg_warn("%s: address with illegal extension: %s", state.msg_attr.queue_id, state.msg_attr.local); state.msg_attr.extension = 0; @@ -222,6 +227,7 @@ int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr) * Clean up. */ myfree(state.msg_attr.local); + myfree(state.msg_attr.user); return (rcpt_stat); } diff --git a/postfix/local/unknown.c b/postfix/local/unknown.c index 0689289a1..c24ef79f1 100644 --- a/postfix/local/unknown.c +++ b/postfix/local/unknown.c @@ -16,27 +16,15 @@ /* fallback_transport parameter, delivery is delegated to the /* named transport. /* .IP \(bu -/* If an alternative address is specified via the luser_relay +/* If an alternative address is specified via the luser_relay /* configuration parameter, mail is forwarded to that address. /* .IP \(bu /* Otherwise the recipient is bounced. /* .PP -/* If the luser_relay parameter specifies a @domain, the entire -/* original recipient localpart is prepended. For example: with -/* "luser_relay = @some.where", unknown+foo becomes -/* unknown+foo@some.where. -/* -/* Otherwise, the luser_relay parameter can specify any number of -/* destinations that are valid in an alias file or in a .forward file. -/* For example, a destination could be an address, a "|command" or -/* a /file/name. The luser_relay feature is treated as an alias, and -/* the usual restrictions for command and file destinations apply. -/* -/* If the luser_relay destination is a mail address, and the -/* recipient delimiter has been defined, the entire original recipient -/* localpart is appended as an address extension. For example: with -/* "luser_relay = someone@some.where", unknown+foo becomes -/* someone+unknown+foo@some.where. +/* The luser_relay parameter is subjected to $name expansion of +/* the standard message attributes: $user, $home, $shell, $domain, +/* $recipient, $mailbox, $extension, $recipient_delimiter, not +/* all of which actually make sense. /* /* Arguments: /* .IP state @@ -68,6 +56,7 @@ #include #include #include +#include /* Global library. */ @@ -86,8 +75,7 @@ int deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr) { char *myname = "deliver_unknown"; int status; - char *dest; - char *saved_extension; + VSTRING *expand_luser; /* * Make verbose logging easier to understand. @@ -120,60 +108,16 @@ int deliver_unknown(LOCAL_STATE state, USER_ATTR usr_attr) "unknown user: \"%s\"", state.msg_attr.local)); /* - * EXTERNAL LOOP CONTROL - * - * Set the delivered message attribute to the recipient, so that this - * message will list the correct forwarding address. - */ - state.msg_attr.delivered = state.msg_attr.recipient; - - /* - * DELIVERY POLICY - * - * The luser relay is just another alias. Update the expansion type - * attribute, so we can decide if deliveries to |command and /file/name - * are allowed at all. - */ - state.msg_attr.exp_type = EXPAND_TYPE_ALIAS; - - /* - * DELIVERY RIGHTS - * - * What rights to use for |command and /file/name deliveries? The luser - * relay is a root-owned alias, so we use default rights. - */ - RESET_USER_ATTR(usr_attr, state.level); - - /* - * If the luser destination is specified as @domain, prepend the - * localpart. The local resolver will append the optional address - * extension, so we don't do that here. - */ - if (*var_luser_relay == '@') { /* @domain */ - dest = concatenate(state.msg_attr.local, var_luser_relay, (char *) 0); - status = deliver_token_string(state, usr_attr, dest, (int *) 0); - myfree(dest); - } - - /* - * Otherwise, optionally arrange for the local resolver to append the - * entire localpart, including the optional address extension, to the - * destination localpart. + * Subject the luser_relay address to $name expansion, disable + * propagation of unmatched address extension, and re-inject the address + * into the delivery machinery. Donot give special treatment to "|stuff" + * or /stuff. */ - else { /* other */ - if ((saved_extension = state.msg_attr.extension) != 0) - state.msg_attr.extension = concatenate(state.msg_attr.local, - var_rcpt_delim, - state.msg_attr.extension, - (char *) 0); - else if (*var_rcpt_delim) - state.msg_attr.extension = state.msg_attr.local; - status = deliver_token_string(state, usr_attr, var_luser_relay, - (int *) 0); - if (saved_extension != 0) - myfree(state.msg_attr.extension); - state.msg_attr.extension = saved_extension; - } + state.msg_attr.unmatched = 0; + expand_luser = vstring_alloc(100); + local_expand(expand_luser, var_luser_relay, &state, &usr_attr, (char *) 0); + status = deliver_resolve_addr(state, usr_attr, vstring_str(expand_luser)); + vstring_free(expand_luser); /* * Done. diff --git a/postfix/man/man8/local.8 b/postfix/man/man8/local.8 index bfa8abb16..7c9c5dea0 100644 --- a/postfix/man/man8/local.8 +++ b/postfix/man/man8/local.8 @@ -48,6 +48,10 @@ extension), \fB$domain\fR (recipient domain), \fBmailbox\fR \fB$recipient_delimiter.\fR The forms \fI${name?value}\fR and \fI${name:value}\fR expand conditionally to \fIvalue\fR when \fI$name\fR is (is not) defined. +In the result of \fIname\fR expansion, characters that have special +meaning to the shell are replaced by underscores. The list of legal +characters is specified with the \fBcommand_expansion_filter\fR +configuration parameter. An alias or ~/.\fBforward\fR file may list any combination of external commands, destination file names, \fB:include:\fR directives, or @@ -102,7 +106,7 @@ Mailbox delivery can be delegated to an external command specified with the \fBmailbox_command\fR configuration parameter. The command executes with the privileges of the recipient user (exception: in case of delivery as root, the command executes with the privileges -of \fBdefault_user\fR). +of \fBdefault_privs\fR). The command is subject to interpolation of \fB$user\fR (recipient username), \fB$home\fR (recipient home directory), \fB$shell\fR (recipient shell), \fB$recipient\fR (complete recipient address), @@ -110,9 +114,11 @@ username), \fB$home\fR (recipient home directory), \fB$shell\fR (recipient domain), \fBmailbox\fR (entire recipient address localpart) and \fB$recipient_delimiter.\fR The forms \fI${name?value}\fR and \fI${name:value}\fR expand conditionally to -\fIvalue\fR when \fI$name\fR is (is not) defined. In the result -of \fIname\fR expansion, characters that have special meaning to -the shell are censored and replaced by underscores. +\fIvalue\fR when \fI$name\fR is (is not) defined. +In the result of \fIname\fR expansion, characters that have special +meaning to the shell are replaced by underscores. The list of legal +characters is specified with the \fBforward_expansion_filter\fR +configuration parameter. Mailbox delivery can be delegated to alternative message transports specified in the \fBmaster.cf\fR file. @@ -338,8 +344,14 @@ The default limit is taken from the Restrict the usage of mail delivery to external command. .IP \fBallow_mail_to_files\fR Restrict the usage of mail delivery to external file. +.IP \fBcommand_expansion_filter\fR +What characters are allowed to appear in $name expansions +of mailbox_command. .IP \fBdefault_privs\fR Default rights for delivery to external file or command. +.IP \fBforward_expansion_filter\fR +What characters are allowed to appear in $name expansions +of forward_path. .SH HISTORY .na .nf diff --git a/postfix/master/.indent.pro b/postfix/master/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/master/.indent.pro +++ b/postfix/master/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/pickup/.indent.pro b/postfix/pickup/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/pickup/.indent.pro +++ b/postfix/pickup/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/pipe/.indent.pro b/postfix/pipe/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/pipe/.indent.pro +++ b/postfix/pipe/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/pipe/pipe.c b/postfix/pipe/pipe.c index c61005f39..4aa7bbdbd 100644 --- a/postfix/pipe/pipe.c +++ b/postfix/pipe/pipe.c @@ -251,7 +251,7 @@ typedef struct { /* parse_callback - callback for mac_parse() */ -static void parse_callback(int type, VSTRING *buf, char *context) +static int parse_callback(int type, VSTRING *buf, char *context) { int *expand_flag = (int *) context; @@ -268,6 +268,7 @@ static void parse_callback(int type, VSTRING *buf, char *context) else if (strcmp(vstring_str(buf), PIPE_DICT_MAILBOX) == 0) *expand_flag |= PIPE_FLAG_MAILBOX; } + return (0); } /* expand_argv - expand macros in the argument vector */ diff --git a/postfix/postalias/.indent.pro b/postfix/postalias/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/postalias/.indent.pro +++ b/postfix/postalias/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/postcat/.indent.pro b/postfix/postcat/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/postcat/.indent.pro +++ b/postfix/postcat/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/postconf/.indent.pro b/postfix/postconf/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/postconf/.indent.pro +++ b/postfix/postconf/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/postdrop/.indent.pro b/postfix/postdrop/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/postdrop/.indent.pro +++ b/postfix/postdrop/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/postfix/.indent.pro b/postfix/postfix/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/postfix/.indent.pro +++ b/postfix/postfix/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/postkick/.indent.pro b/postfix/postkick/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/postkick/.indent.pro +++ b/postfix/postkick/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/postlock/.indent.pro b/postfix/postlock/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/postlock/.indent.pro +++ b/postfix/postlock/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/postlog/.indent.pro b/postfix/postlog/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/postlog/.indent.pro +++ b/postfix/postlog/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/postmap/.indent.pro b/postfix/postmap/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/postmap/.indent.pro +++ b/postfix/postmap/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/postsuper/.indent.pro b/postfix/postsuper/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/postsuper/.indent.pro +++ b/postfix/postsuper/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/qmgr/.indent.pro b/postfix/qmgr/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/qmgr/.indent.pro +++ b/postfix/qmgr/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/sendmail/.indent.pro b/postfix/sendmail/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/sendmail/.indent.pro +++ b/postfix/sendmail/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/showq/.indent.pro b/postfix/showq/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/showq/.indent.pro +++ b/postfix/showq/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/smtp/.indent.pro b/postfix/smtp/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/smtp/.indent.pro +++ b/postfix/smtp/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/smtpd/.indent.pro b/postfix/smtpd/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/smtpd/.indent.pro +++ b/postfix/smtpd/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/smtpstone/.indent.pro b/postfix/smtpstone/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/smtpstone/.indent.pro +++ b/postfix/smtpstone/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/trivial-rewrite/.indent.pro b/postfix/trivial-rewrite/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/trivial-rewrite/.indent.pro +++ b/postfix/trivial-rewrite/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/util/.indent.pro b/postfix/util/.indent.pro index ce21b568d..4972290d9 100644 --- a/postfix/util/.indent.pro +++ b/postfix/util/.indent.pro @@ -42,6 +42,7 @@ -THTABLE_INFO -TINET_ADDR_LIST -TINT_TABLE +-TLOCAL_EXP -TLOCAL_STATE -TMAC_EXP -TMAC_HEAD diff --git a/postfix/util/Makefile.in b/postfix/util/Makefile.in index 95603e95a..8ad363f7b 100644 --- a/postfix/util/Makefile.in +++ b/postfix/util/Makefile.in @@ -256,7 +256,7 @@ valid_hostname_test: valid_hostname valid_hostname.in valid_hostname.ref rm -f valid_hostname.tmp mac_expand_test: mac_expand mac_expand.in mac_expand.ref - ./mac_expand mac_expand.tmp + ./mac_expand mac_expand.tmp 2>&1 diff mac_expand.ref mac_expand.tmp rm -f mac_expand.tmp @@ -476,6 +476,7 @@ get_hostname.o: mymalloc.h get_hostname.o: msg.h get_hostname.o: valid_hostname.h get_hostname.o: get_hostname.h +hattr.o: hattr.c htable.o: htable.c htable.o: sys_defs.h htable.o: mymalloc.h @@ -544,7 +545,6 @@ mac_expand.o: msg.h mac_expand.o: vstring.h mac_expand.o: vbuf.h mac_expand.o: mymalloc.h -mac_expand.o: htable.h mac_expand.o: mac_parse.h mac_expand.o: mac_expand.h mac_parse.o: mac_parse.c diff --git a/postfix/util/dict.c b/postfix/util/dict.c index 899c6c50b..4a836ba42 100644 --- a/postfix/util/dict.c +++ b/postfix/util/dict.c @@ -359,7 +359,7 @@ struct dict_eval_context { /* dict_eval_action - macro parser call-back routine */ -static void dict_eval_action(int type, VSTRING *buf, char *ptr) +static int dict_eval_action(int type, VSTRING *buf, char *ptr) { struct dict_eval_context *ctxt = (struct dict_eval_context *) ptr; char *myname = "dict_eval_action"; @@ -389,6 +389,7 @@ static void dict_eval_action(int type, VSTRING *buf, char *ptr) } else { vstring_strcat(ctxt->buf, STR(buf)); } + return(0); } /* dict_eval - expand embedded dictionary references */ diff --git a/postfix/util/dict_pcre.c b/postfix/util/dict_pcre.c index 67e53d17e..2d0be6b7a 100644 --- a/postfix/util/dict_pcre.c +++ b/postfix/util/dict_pcre.c @@ -107,7 +107,7 @@ struct dict_pcre_context { * Macro expansion callback - replace $0-${99} with strings cut from * matched string. */ -static void dict_pcre_action(int type, VSTRING *buf, char *ptr) +static int dict_pcre_action(int type, VSTRING *buf, char *ptr) { struct dict_pcre_context *ctxt = (struct dict_pcre_context *) ptr; const char *pp; @@ -120,17 +120,20 @@ static void dict_pcre_action(int type, VSTRING *buf, char *ptr) n, &pp); if (ret < 0) { if (ret == PCRE_ERROR_NOSUBSTRING) - msg_warn("regexp %s, line %d: replace index out of range", + msg_fatal("regexp %s, line %d: replace index out of range", ctxt->dict_name, ctxt->lineno); else - msg_warn("regexp %s, line %d: pcre_get_substring error: %d", + msg_fatal("regexp %s, line %d: pcre_get_substring error: %d", ctxt->dict_name, ctxt->lineno, ret); - return; } + if (*pp == 0) + return (MAC_PARSE_UNDEF); vstring_strcat(ctxt->buf, pp); } else /* Straight text - duplicate with no substitution */ vstring_strcat(ctxt->buf, vstring_str(buf)); + + return (0); } /* @@ -198,7 +201,9 @@ static const char *dict_pcre_lookup(DICT *dict, const char *name) ctxt.dict_name = dict_pcre->map; ctxt.lineno = pcre_list->lineno; - mac_parse(pcre_list->replace, dict_pcre_action, (char *) &ctxt); + if (mac_parse(pcre_list->replace, dict_pcre_action, (char *) &ctxt) & MAC_PARSE_ERROR) + msg_fatal("regexp map %s, line %d: bad replacement syntax", + dict_pcre->map, pcre_list->lineno); VSTRING_TERMINATE(buf); return (vstring_str(buf)); diff --git a/postfix/util/dict_regexp.c b/postfix/util/dict_regexp.c index 9aeee007b..d42deef0f 100644 --- a/postfix/util/dict_regexp.c +++ b/postfix/util/dict_regexp.c @@ -100,7 +100,7 @@ static void dict_regexp_update(DICT *dict, const char *unused_name, * Macro expansion callback - replace $0-${99} with strings cut from * matched string. */ -static void dict_regexp_action(int type, VSTRING *buf, char *ptr) +static int dict_regexp_action(int type, VSTRING *buf, char *ptr) { struct dict_regexp_context *ctxt = (struct dict_regexp_context *) ptr; DICT_REGEXP_RULE *rule = ctxt->rule; @@ -109,14 +109,12 @@ static void dict_regexp_action(int type, VSTRING *buf, char *ptr) if (type == MAC_PARSE_VARNAME) { n = atoi(vstring_str(buf)); - if (n >= dict->nmatch) { - msg_warn("regexp %s, line %d: replace index out of range", - dict->map, rule->lineno); - return; - } + if (n >= dict->nmatch) + msg_fatal("regexp %s, line %d: replacement index out of range", + dict->map, rule->lineno); if (dict->pmatch[n].rm_so < 0 || dict->pmatch[n].rm_so == dict->pmatch[n].rm_eo) { - return; /* empty string or not + return (MAC_PARSE_UNDEF); /* empty string or not * matched */ } vstring_strncat(ctxt->buf, ctxt->subject + dict->pmatch[n].rm_so, @@ -124,6 +122,7 @@ static void dict_regexp_action(int type, VSTRING *buf, char *ptr) } else /* Straight text - duplicate with no substitution */ vstring_strcat(ctxt->buf, vstring_str(buf)); + return (0); } /* @@ -175,7 +174,9 @@ static const char *dict_regexp_lookup(DICT *dict, const char *name) ctxt.rule = rule; ctxt.dict = dict_regexp; - mac_parse(rule->replace, dict_regexp_action, (char *) &ctxt); + if (mac_parse(rule->replace, dict_regexp_action, (char *) &ctxt) & MAC_PARSE_ERROR) + msg_fatal("regexp map %s, line %d: bad replacement syntax.", + dict_regexp->map, rule->lineno); VSTRING_TERMINATE(buf); return (vstring_str(buf)); diff --git a/postfix/util/mac_expand.c b/postfix/util/mac_expand.c index 23f0756ab..23c239b05 100644 --- a/postfix/util/mac_expand.c +++ b/postfix/util/mac_expand.c @@ -6,18 +6,21 @@ /* SYNOPSIS /* #include /* -/* int mac_expand(result, pattern, flags, key, ...) +/* int mac_expand(result, pattern, flags, filter, lookup, context) /* VSTRING *result; /* const char *pattern; /* int flags; -/* int key; +/* const char *filter; +/* 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. /* The algorithm can search multiple user-specified symbol tables. -/* In the text below, an attribute is "defined" when its value is a -/* string of non-zero length. In all other cases the attribute is -/* considered "undefined". +/* +/* In the text below, an attribute is considered "undefined" when its +/* value is a null pointer. In all other cases the attribute is +/* considered "defined". /* /* The following expansions are implemented: /* .IP "$name, ${name}, $(name)" @@ -30,12 +33,9 @@ /* $name expansion. Otherwise, the expansion is empty. /* .IP "${name:text}, $(name:text)" /* Conditional expansion. If the named attribute is undefined, the -/* the expansion is the given text, after another iteration of $name -/* expansion. Otherwise, the expansion is empty. +/* the expansion is the given text, subjected to another iteration +/* of $name expansion. Otherwise, the expansion is empty. /* .PP -/* mac_expand() replaces $name etc. instances in \fBpattern\fR -/* and stores the result into \fBresult\fR. -/* /* Arguments: /* .IP result /* Storage for the result of expansion. The result is truncated @@ -49,39 +49,28 @@ /* Expand $name recursively. /* .RE /* The constant MAC_EXP_FLAG_NONE specifies a manifest null value. -/* .IP key -/* The attribute information is specified as a null-terminated list. -/* Attributes may appear multiple times; the right-most definition -/* of an attribute determines the result of attribute lookup. -/* .sp -/* The following keys are understood (types of arguments indicated -/* in parentheses): -/* .RS -/* .IP "MAC_EXP_ARG_ATTR (char *, char *)" -/* The next two arguments specify an attribute name and its attribute -/* string value. Specify a null pointer or empty string for an -/* attribute value that is unset. Attribute keys and string values -/* are copied. -/* .IP "MAC_EXP_ARG_TABLE (HTABLE *)" -/* The next argument is a hash table with attribute names and values. -/* Specify a null pointer or empty string for an attribute value that -/* is unset. Hash tables are not copied. -/* .IP "MAC_EXP_ARG_RECORD (HTABLE *)" -/* Record in the specified table how many times an attribute was -/* referenced. -/* .RE -/* .IP MAC_EXP_ARG_END -/* A manifest constant that indicates the end of the argument list. +/* .IP filter +/* A null pointer, or a null-terminated array of characters that +/* are allowed to appear in an expansion. Illegal characters are +/* replaced by underscores. +/* .IP lookup +/* The attribute lookup routine. Arguments are: the attribute name, +/* MAC_EXP_MODE_TEST to test the existence of the named attribute +/* 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 means that the requested attribute was not defined. +/* .IP context +/* Caller context that is passed on to the attribute lookup routine. /* DIAGNOSTICS /* Fatal errors: out of memory. Warnings: syntax errors, unreasonable /* macro nesting. /* /* The result value is the binary OR of zero or more of the following: -/* .IP MAC_EXP_FLAG_ERROR -/* A syntax error was foud in the \fBpattern\fR, or some macro had +/* .IP MAC_PARSE_ERROR +/* A syntax error was found in \fBpattern\fR, or some macro had /* an unreasonable nesting depth. -/* .IP MAC_EXP_FLAG_UNDEF -/* The pattern contains a reference to an undefined attribute. +/* .IP MAC_PARSE_UNDEF +/* A macro was expanded but not defined. /* SEE ALSO /* mac_parse(3) locate macro references in string. /* LICENSE @@ -106,59 +95,43 @@ #include #include #include -#include #include #include /* - * Little helper structure. Name-value pairs given as explicit arguments are - * stored into private hash tables. Hash tables provided by the caller are - * simply referenced. For now we do only hash tables. The structure can be - * generalized when needed. + * Little helper structure. */ -struct table_info { - int status; /* who owns this table */ - HTABLE *table; /* table reference */ -}; - -#define MAC_EXP_STAT_UNUSED 0 /* this slot is unused */ -#define MAC_EXP_STAT_PRIVATE 1 /* we own this table */ -#define MAC_EXP_STAT_EXTERN 2 /* caller owns this table */ - -#define MAC_EXP_MIN_LEN 2 /* min number of table slots */ - -struct MAC_EXP { +typedef struct { VSTRING *result; /* result buffer */ - const char *filter; /* safe character list */ - int clobber; /* safe replacement */ - int flags; /* findings, features */ + int flags; /* features */ + const char *filter; /* character filter */ + MAC_EXP_LOOKUP_FN lookup; /* lookup routine */ + char *context; /* caller context */ + int status; /* findings */ int level; /* nesting level */ - HTABLE *record; /* record of substitutions */ - int len; /* table list length */ - int last; /* last element used */ - struct table_info table_info[MAC_EXP_MIN_LEN]; -}; +} MAC_EXP; /* mac_expand_callback - callback for mac_parse */ -static void mac_expand_callback(int type, VSTRING *buf, char *ptr) +static int mac_expand_callback(int type, VSTRING *buf, char *ptr) { char *myname = "mac_expand_callback"; MAC_EXP *mc = (MAC_EXP *) ptr; - HTABLE_INFO *ht; - char *text; + int lookup_mode; + const char *text; char *cp; int ch; - int n; + int len; /* * Sanity check. */ if (mc->level++ > 100) { msg_warn("unreasonable macro call nesting: \"%s\"", vstring_str(buf)); - mc->flags |= MAC_EXP_FLAG_ERROR; - return; + mc->status |= MAC_PARSE_ERROR; } + if (mc->status & MAC_PARSE_ERROR) + return (mc->status); /* * $Name reference. @@ -170,28 +143,27 @@ static void mac_expand_callback(int type, VSTRING *buf, char *ptr) * without doing damage. We do not have enough context to produce an * understandable error message, so don't try. */ - for (cp = vstring_str(buf); (ch = *cp) != 0; cp++) { + 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->flags |= MAC_EXP_FLAG_ERROR; - return; + mc->status |= MAC_PARSE_ERROR; + return (mc->status); } } /* * Look up the named parameter. */ - for (text = 0, n = mc->last; n >= 0; n--) { - ht = htable_locate(mc->table_info[n].table, vstring_str(buf)); - if (ht != 0) { - text = ht->value; - break; - } - } + text = mc->lookup(vstring_str(buf), lookup_mode, mc->context); /* * Perform the requested substitution. @@ -207,30 +179,22 @@ static void mac_expand_callback(int type, VSTRING *buf, char *ptr) break; default: if (text == 0) { - mc->flags |= MAC_EXP_FLAG_UNDEF; - break; - } - if (mc->filter) { - vstring_strcpy(buf, text); - text = vstring_str(buf); - for (cp = text; (cp += strspn(cp, mc->filter))[0]; /* void */ ) - *cp++ = mc->clobber; - } - if (mc->flags & MAC_EXP_FLAG_RECURSE) + mc->status |= MAC_PARSE_UNDEF; + } else if (*text == 0) { + /* void */ ; + } else if (mc->flags & MAC_EXP_FLAG_RECURSE) { mac_parse(text, mac_expand_callback, (char *) mc); - else + } else { + len = VSTRING_LEN(mc->result); vstring_strcat(mc->result, text); + if (mc->filter) { + cp = vstring_str(mc->result) + len; + while (*(cp += strspn(cp, mc->filter))) + *cp++ = '_'; + } + } break; } - - /* - * Record keeping... - */ - if (mc->record) { - if ((ht = htable_locate(mc->record, vstring_str(buf))) == 0) - ht = htable_enter(mc->record, vstring_str(buf), (char *) 0); - ht->value++; - } } /* @@ -249,101 +213,32 @@ static void mac_expand_callback(int type, VSTRING *buf, char *ptr) text ? text : "(undef)"); mc->level--; -} - -/* mac_expand_addtable - add table to expansion context */ -static MAC_EXP *mac_expand_addtable(MAC_EXP *mc, HTABLE *table, int status) -{ - mc->last += 1; - if (mc->last >= mc->len) { - mc->len *= 2; - mc = (MAC_EXP *) myrealloc((char *) mc, sizeof(*mc) + sizeof(mc->table_info[0]) * (mc->len - MAC_EXP_MIN_LEN)); - } - mc->table_info[mc->last].table = table; - mc->table_info[mc->last].status = status; - return (mc); + return (mc->status); } /* mac_expand - expand $name instances */ -int mac_expand(VSTRING *result, const char *pattern, int flags, int key,...) +int mac_expand(VSTRING *result, const char *pattern, int flags, + const char *filter, + MAC_EXP_LOOKUP_FN lookup, char *context) { - MAC_EXP *mc; - va_list ap; - char *name; - char *value; - HTABLE *table; - HTABLE_INFO *ht; + MAC_EXP mc; int status; /* - * Initialize. - */ - mc = (MAC_EXP *) mymalloc(sizeof(*mc)); - mc->result = result; - mc->flags = (flags & MAC_EXP_FLAG_INMASK); - mc->filter = 0; - mc->clobber = '_'; - mc->record = 0; - mc->level = 0; - mc->len = MAC_EXP_MIN_LEN; - mc->last = -1; - - /* - * Stash away the attributes. - */ - for (va_start(ap, key); key != 0; key = va_arg(ap, int)) { - switch (key) { - case MAC_EXP_ARG_ATTR: - name = va_arg(ap, char *); - value = va_arg(ap, char *); - if (mc->last < 0 - || mc->table_info[mc->last].status != MAC_EXP_STAT_PRIVATE) { - table = htable_create(0); - mc = mac_expand_addtable(mc, table, MAC_EXP_STAT_PRIVATE); - } else - table = mc->table_info[mc->last].table; - if ((ht = htable_locate(table, name)) == 0) - ht = htable_enter(table, name, (char *) 0); - if (ht->value != 0) - myfree(ht->value); - ht->value = (value ? mystrdup(value) : 0); - break; - case MAC_EXP_ARG_TABLE: - table = va_arg(ap, HTABLE *); - mc = mac_expand_addtable(mc, table, MAC_EXP_STAT_EXTERN); - break; - case MAC_EXP_ARG_FILTER: - mc->filter = va_arg(ap, char *); - break; - case MAC_EXP_ARG_CLOBBER: - mc->clobber = va_arg(ap, int); - break; - case MAC_EXP_ARG_RECORD: - mc->record = va_arg(ap, HTABLE *); - break; - } - } - va_end(ap); - - /* - * Do the substitutions. + * Bundle up the request and do the substitutions. */ + mc.result = result; + mc.flags = flags; + mc.filter = filter; + mc.lookup = lookup; + mc.context = context; + mc.status = 0; + mc.level = 0; VSTRING_RESET(result); - mac_parse(pattern, mac_expand_callback, (char *) mc); + status = mac_parse(pattern, mac_expand_callback, (char *) &mc); VSTRING_TERMINATE(result); - status = (mc->flags & MAC_EXP_FLAG_OUTMASK); - - /* - * Clean up. - */ - while (mc->last >= 0) { - if (mc->table_info[mc->last].status == MAC_EXP_STAT_PRIVATE) - htable_free(mc->table_info[mc->last].table, myfree); - mc->last--; - } - myfree((char *) mc); return (status); } @@ -354,9 +249,17 @@ int mac_expand(VSTRING *result, const char *pattern, int flags, int key,...) * This code certainly deserves a stand-alone test program. */ #include +#include #include #include +static const char *lookup(const char *name, int unused_mode, char *context) +{ + HTABLE *table = (HTABLE *) context; + + return (htable_find(table, name)); +} + int main(int unused_argc, char **unused_argv) { VSTRING *buf = vstring_alloc(100); @@ -375,6 +278,8 @@ int main(int unused_argc, char **unused_argv) * Read a block of definitions, terminated with an empty line. */ while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { + vstream_printf("<< %s\n", vstring_str(buf)); + vstream_fflush(VSTREAM_OUT); if (VSTRING_LEN(buf) == 0) break; cp = vstring_str(buf); @@ -387,13 +292,14 @@ int main(int unused_argc, char **unused_argv) * Read a block of patterns, terminated with an empty line or EOF. */ while (vstring_get_nonl(buf, VSTREAM_IN) != VSTREAM_EOF) { + vstream_printf("<< %s\n", vstring_str(buf)); + vstream_fflush(VSTREAM_OUT); if (VSTRING_LEN(buf) == 0) break; cp = vstring_str(buf); VSTRING_RESET(result); stat = mac_expand(result, vstring_str(buf), MAC_EXP_FLAG_NONE, - MAC_EXP_ARG_TABLE, table, - MAC_EXP_ARG_END); + (char *) 0, lookup, (char *) table); vstream_printf("stat=%d result=%s\n", stat, vstring_str(result)); vstream_fflush(VSTREAM_OUT); } diff --git a/postfix/util/mac_expand.h b/postfix/util/mac_expand.h index cf61b686e..5ae6b5b99 100644 --- a/postfix/util/mac_expand.h +++ b/postfix/util/mac_expand.h @@ -15,32 +15,23 @@ * Utility library. */ #include +#include /* - * External interface. + * Features. */ -typedef struct MAC_EXP MAC_EXP; - #define MAC_EXP_FLAG_NONE (0) -#define MAC_EXP_FLAG_UNDEF (1<<0) -#define MAC_EXP_FLAG_RECURSE (1<<1) -#define MAC_EXP_FLAG_ERROR (1<<2) - -#define MAC_EXP_FLAG_INMASK MAC_EXP_FLAG_RECURSE -#define MAC_EXP_FLAG_OUTMASK (MAC_EXP_FLAG_UNDEF | MAC_EXP_FLAG_ERROR) +#define MAC_EXP_FLAG_RECURSE (1<<0) -#define MAC_EXP_ARG_END 0 -#define MAC_EXP_ARG_ATTR 1 -#define MAC_EXP_ARG_TABLE 2 -#define MAC_EXP_ARG_FILTER 3 -#define MAC_EXP_ARG_CLOBBER 4 -#define MAC_EXP_ARG_RECORD 5 + /* + * Real lookup or just a test? + */ +#define MAC_EXP_MODE_TEST (0) +#define MAC_EXP_MODE_USE (1) -extern MAC_EXP *mac_expand_update(MAC_EXP *, int,...); -extern int mac_expand_use(MAC_EXP *, VSTRING *, const char *, int); -extern void mac_expand_free(MAC_EXP *); +typedef const char *(*MAC_EXP_LOOKUP_FN)(const char *, int, char *); -extern int mac_expand(VSTRING *, const char *, int, int,...); +extern int mac_expand(VSTRING *, const char *, int, const char *, MAC_EXP_LOOKUP_FN, char *); /* LICENSE /* .ad diff --git a/postfix/util/mac_expand.ref b/postfix/util/mac_expand.ref index 0fb6cea98..08e1d87ea 100644 --- a/postfix/util/mac_expand.ref +++ b/postfix/util/mac_expand.ref @@ -1,13 +1,33 @@ -stat=1 result=name 1 defined, |name1-value|| +<< name1 = name1-value +<< name2 = +<< +<< ${name1?name 1 defined, |$name1|$name2|} +stat=2 result=name 1 defined, |name1-value|| +<< ${name1:name 1 undefined, |$name1|$name2|} stat=0 result= +<< ${name2?name 2 defined, |$name1|$name2|} stat=0 result= -stat=1 result=name 2 undefined, |name1-value|| -stat=1 result=|name1-value|| -stat=0 result=name1-value -stat=4 result= +<< ${name2:name 2 undefined, |$name1|$name2|} +stat=2 result=name 2 undefined, |name1-value|| +<< |$name1|$name2| +stat=2 result=|name1-value|| +<< $(name1 +unknown: warning: truncated macro reference: "$(name1" +stat=1 result=name1-value +<< $(name ) +unknown: warning: macro name syntax error: "name " +stat=1 result= +<< +<< name2 = name2-value +<< +<< ${name1?name 1 defined, |$name1|$name2|} stat=0 result= -stat=1 result=name 1 undefined, ||name2-value| -stat=1 result=name 2 defined, ||name2-value| +<< ${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|} stat=0 result= -stat=1 result=||name2-value| +<< |$name1|$name2| +stat=2 result=||name2-value| diff --git a/postfix/util/mac_parse.c b/postfix/util/mac_parse.c index 30bee99cb..de655c579 100644 --- a/postfix/util/mac_parse.c +++ b/postfix/util/mac_parse.c @@ -6,14 +6,15 @@ /* SYNOPSIS /* #include /* -/* void mac_parse(string, action, context) +/* int mac_parse(string, action, context) /* const char *string; -/* void (*action)(int type, VSTRING *buf, char *context); +/* int (*action)(int type, VSTRING *buf, char *context); /* DESCRIPTION -/* This module recognizes macro references in null-terminated -/* strings. Macro references have the form $name, $(name) or -/* ${name}. A macro name consists of alphanumerics and/or -/* underscore. Other text is treated as literal text. +/* This module recognizes macro expressions in null-terminated +/* strings. Macro expressions have the form $name, $(text) or +/* ${text}. A macro name consists of alphanumerics and/or +/* underscore. Text other than macro expressions is treated +/* as literal text. /* /* mac_parse() breaks up its string argument into macro references /* and other text, and invokes the \fIaction\fR routine for each item @@ -24,11 +25,25 @@ /* .IP MAC_PARSE_LITERAL /* The text in \fIbuf\fR is literal text. /* .IP MAC_PARSE_VARNAME -/* The text in \fIbuf\fR is a macro name. +/* The text in \fIbuf\fR is a macro expression. +/* .PP +/* The action routine result value is the bit-wise OR of zero or more +/* of the following: +/* .IP MAC_PARSE_ERROR +/* A parsing error was detected. +/* .IP MAC_PARSE_UNDEF +/* A macro was expanded but not defined. /* SEE ALSO /* dict(3) dictionary interface. /* DIAGNOSTICS /* Fatal errors: out of memory. malformed macro name. +/* +/* The result value is the bit-wise OR of zero or more of the +/* following: +/* .IP MAC_PARSE_ERROR +/* A parsing error was detected. +/* .IP MAC_PARSE_UNDEF +/* A macro was expanded but not defined. /* LICENSE /* .ad /* .fi @@ -54,16 +69,16 @@ * Helper macro for consistency. Null-terminate the temporary buffer, * execute the action, and reset the temporary buffer for re-use. */ -#define MAC_PARSE_ACTION(type, buf, context) \ +#define MAC_PARSE_ACTION(status, type, buf, context) \ { \ VSTRING_TERMINATE(buf); \ - action(type, buf, context); \ + status |= action(type, buf, context); \ VSTRING_RESET(buf); \ } /* mac_parse - split string into literal text and macro references */ -void mac_parse(const char *value, MAC_PARSE_FN action, char *context) +int mac_parse(const char *value, MAC_PARSE_FN action, char *context) { char *myname = "mac_parse"; VSTRING *buf = vstring_alloc(1); /* result buffer */ @@ -73,6 +88,7 @@ void mac_parse(const char *value, MAC_PARSE_FN action, char *context) static char open_paren[] = "({"; static char close_paren[] = ")}"; int level; + int status = 0; #define SKIP(start, var, cond) \ for (var = start; *var && (cond); var++); @@ -89,7 +105,7 @@ void mac_parse(const char *value, MAC_PARSE_FN action, char *context) vp += 2; } else { /* found bare $ */ if (VSTRING_LEN(buf) > 0) - MAC_PARSE_ACTION(MAC_PARSE_LITERAL, buf, context); + MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context); vp += 1; pp = open_paren; if (*vp == *pp || *vp == *++pp) { /* ${x} or $(x) */ @@ -98,6 +114,7 @@ void mac_parse(const char *value, MAC_PARSE_FN action, char *context) for (ep = vp; level > 0; ep++) { if (*ep == 0) { msg_warn("truncated macro reference: \"%s\"", value); + status |= MAC_PARSE_ERROR; break; } if (*ep == *pp) @@ -112,18 +129,23 @@ void mac_parse(const char *value, MAC_PARSE_FN action, char *context) vstring_strncat(buf, vp, ep - vp); vp = ep; } - if (VSTRING_LEN(buf) == 0) + if (VSTRING_LEN(buf) == 0) { + status |= MAC_PARSE_ERROR; msg_warn("empty macro name: \"%s\"", value); - MAC_PARSE_ACTION(MAC_PARSE_VARNAME, buf, context); + break; + } + MAC_PARSE_ACTION(status, MAC_PARSE_VARNAME, buf, context); } } - if (VSTRING_LEN(buf) > 0) - MAC_PARSE_ACTION(MAC_PARSE_LITERAL, buf, context); + if (VSTRING_LEN(buf) > 0 && (status & MAC_PARSE_ERROR) == 0) + MAC_PARSE_ACTION(status, MAC_PARSE_LITERAL, buf, context); /* * Cleanup. */ vstring_free(buf); + + return (status); } #ifdef TEST diff --git a/postfix/util/mac_parse.h b/postfix/util/mac_parse.h index 856fb9830..1acbb60d5 100644 --- a/postfix/util/mac_parse.h +++ b/postfix/util/mac_parse.h @@ -22,9 +22,13 @@ #define MAC_PARSE_LITERAL 1 #define MAC_PARSE_VARNAME 2 -typedef void (*MAC_PARSE_FN)(int, VSTRING *, char *); +#define MAC_PARSE_ERROR (1<<0) +#define MAC_PARSE_UNDEF (1<<1) +#define MAC_PARSE_USER 2 /* start user definitions */ -extern void mac_parse(const char *, MAC_PARSE_FN, char *); +typedef int (*MAC_PARSE_FN)(int, VSTRING *, char *); + +extern int mac_parse(const char *, MAC_PARSE_FN, char *); /* LICENSE /* .ad