-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
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
====================================
# 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
=========================================================
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
${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.
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:
=====================================
- 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.
- 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
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
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
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
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
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
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
#include <mail_params.h>
#include <mail_stream.h>
#include <mail_addr.h>
+#include <ext_prop.h>
/* Single-threaded server skeleton. */
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.
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)
*/
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 */
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,
};
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
/*
* 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
#include <cleanup_user.h>
#include <tok822.h>
#include <mail_params.h>
+#include <ext_prop.h>
/* Application-specific. */
} 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);
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));
/* SYNOPSIS
/* #include <cleanup.h>
/*
-/* 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.
/*
/* 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.
/* 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;
* 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));
/* 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);
* 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);
/* 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);
* 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);
}
/* 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;
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]);
#include <mail_date.h>
#include <mail_addr.h>
#include <is_header.h>
+#include <ext_prop.h>
/* Application-specific. */
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)
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) ?
#include <been_here.h>
#include <mail_params.h>
#include <rec_type.h>
+#include <ext_prop.h>
/* Application-specific. */
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);
#
#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
# 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
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
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
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
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
# 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
# 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
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
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
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
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 \
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 \
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 \
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
--- /dev/null
+/*++
+/* NAME
+/* exp_prop 3
+/* SUMMARY
+/* address extension propagation control
+/* SYNOPSIS
+/* #include <exp_prop.h>
+/*
+/* 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 <sys_defs.h>
+
+/* Utility library. */
+
+#include <name_mask.h>
+
+/* Global library. */
+
+#include <ext_prop.h>
+
+/* 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));
+}
--- /dev/null
+#ifndef _EXT_PROP_INCLUDED_
+#define _EXT_PROP_INCLUDED_
+
+/*++
+/* NAME
+/* ext_prop 3h
+/* SUMMARY
+/* address extension propagation control
+/* SYNOPSIS
+/* #include <ext_prop.h>
+/* 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
/* SYNOPSIS
/* #include <mail_addr_map.h>
/*
-/* 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().
/*
/* 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";
* 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)
/*
* External interface.
*/
-extern ARGV *mail_addr_map(MAPS *, const char *);
+extern ARGV *mail_addr_map(MAPS *, const char *, int);
/* LICENSE
/* .ad
#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;
* 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
address extension), <b>$domain</b> (recipient domain), <b>mailbox</b>
(entire recipient address localpart) and <b>$recipient</b><i>_</i><b>delim-</b>
<b>iter.</b> The forms <i>${name?value}</i> and <i>${name:value}</i> expand
- conditionally to <i>value</i> when <i>$name</i> is (is not) defined.
+ conditionally to <i>value</i> when <i>$name</i> is (is not) defined. In
+ the result of <i>name</i> expansion, characters that have special
+ meaning to the shell are replaced by underscores. The list
+ of legal characters is specified with the <b>forward</b><i>_</i><b>expan-</b>
+ <b>sion</b><i>_</i><b>filter</b> configuration parameter.
An alias or ~/.<b>forward</b> file may list any combination of
external commands, destination file names, <b>:include:</b>
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 ~/.<b>forward</b> file, delivery is made
- to the user's mailbox instead. An empty ~/.<b>forward</b> file
- means do not forward mail.
-
- In order to prevent the mail system from using up
LOCAL(8) LOCAL(8)
- unreasonable amounts of memory, input records read from
+ to the user's mailbox instead. An empty ~/.<b>forward</b> 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
<b>:include:</b> or from ~/.<b>forward</b> files are broken up into
chunks of length <b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>.
specified with the <b>mailbox</b><i>_</i><b>command</b> 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 <b>default</b><i>_</i><b>user</b>).
- The command is subject to interpolation of <b>$user</b> (recipi-
- ent username), <b>$home</b> (recipient home directory), <b>$shell</b>
- (recipient shell), <b>$recipient</b> (complete recipient
- address), <b>$extension</b> (recipient address extension),
- <b>$domain</b> (recipient domain), <b>mailbox</b> (entire recipient
- address localpart) and <b>$recipient</b><i>_</i><b>delimiter.</b> The forms
+ the command executes with the privileges of
+ <b>default</b><i>_</i><b>privs</b>). The command is subject to interpolation
+ of <b>$user</b> (recipient username), <b>$home</b> (recipient home
+ directory), <b>$shell</b> (recipient shell), <b>$recipient</b> (complete
+ recipient address), <b>$extension</b> (recipient address exten-
+ sion), <b>$domain</b> (recipient domain), <b>mailbox</b> (entire recipi-
+ ent address localpart) and <b>$recipient</b><i>_</i><b>delimiter.</b> The forms
<i>${name?value}</i> and <i>${name:value}</i> expand conditionally to
- <i>value</i> when <i>$name</i> is (is not) defined. In the result of
+ <i>value</i> when <i>$name</i> is (is not) defined. In the result of
<i>name</i> 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 <b>command</b><i>_</i><b>expansion</b><i>_</i><b>filter</b>
+ configuration parameter.
Mailbox delivery can be delegated to alternative message
- transports specified in the <b>master.cf</b> file. The <b>mail-</b>
- <b>box</b><i>_</i><b>transport</b> 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 <b>fallback</b><i>_</i><b>transport</b> parameter specifies a
- message transport for recipients that are not found in the
LOCAL(8) LOCAL(8)
+ transports specified in the <b>master.cf</b> file. The <b>mail-</b>
+ <b>box</b><i>_</i><b>transport</b> 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 <b>fallback</b><i>_</i><b>transport</b> 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 <b>local</b> dae-
<b>Return-Path:</b> header with the sender envelope address, and
appends an empty line.
-<b>EXTERNAL</b> <b>FILE</b> <b>DELIVERY</b>
- The <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>files</b> configuration parameter restricts
- delivery to external files. The default setting (<b>alias,</b>
- <b>forward</b>) forbids file destinations in <b>:include:</b> files.
- Specify a pathname ending in <b>/</b> for <b>qmail</b>-compatible
-
3
LOCAL(8) LOCAL(8)
+<b>EXTERNAL</b> <b>FILE</b> <b>DELIVERY</b>
+ The <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>files</b> configuration parameter restricts
+ delivery to external files. The default setting (<b>alias,</b>
+ <b>forward</b>) forbids file destinations in <b>:include:</b> files.
+ Specify a pathname ending in <b>/</b> for <b>qmail</b>-compatible
<b>maildir</b> delivery.
The <b>local</b> daemon prepends a "<b>From</b> <i>sender</i> <i>time_stamp</i>" enve-
can move them to the <b>corrupt</b> queue afterwards.
Depending on the setting of the <b>notify</b><i>_</i><b>classes</b> parameter,
- the postmaster is notified of bounces and of other trou-
- ble.
-
-<b>BUGS</b>
- For security reasons, the message delivery status of
LOCAL(8) LOCAL(8)
+ the postmaster is notified of bounces and of other trou-
+ ble.
+
+<b>BUGS</b>
+ 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-
directory. Specify a path ending in <b>/</b> for maildir-
style delivery.
- <b>luser</b><i>_</i><b>relay</b>
- Destination (<i>@domain</i> or <i>address</i>) for non-existent
- users. The <i>address</i> is subjected to <i>$name</i> expan-
- sion.
-
LOCAL(8) LOCAL(8)
+ <b>luser</b><i>_</i><b>relay</b>
+ Destination (<i>@domain</i> or <i>address</i>) for non-existent
+ users. The <i>address</i> is subjected to <i>$name</i> expan-
+ sion.
+
<b>mail</b><i>_</i><b>spool</b><i>_</i><b>directory</b>
Directory with UNIX-style mailboxes. The default
pathname is system dependent.
<b>mailbox</b><i>_</i><b>command</b>
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.
<b>mailbox</b><i>_</i><b>transport</b>
- 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 <b>luser</b><i>_</i><b>relay</b>.
<b>Locking</b> <b>controls</b>
<b>deliver</b><i>_</i><b>lock</b><i>_</i><b>attempts</b>
- 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.
<b>deliver</b><i>_</i><b>lock</b><i>_</i><b>delay</b>
- Time in seconds between successive attempts to
+ Time in seconds between successive attempts to
acquire an exclusive lock.
<b>stale</b><i>_</i><b>lock</b><i>_</i><b>time</b>
<b>Resource</b> <b>controls</b>
<b>command</b><i>_</i><b>time</b><i>_</i><b>limit</b>
- Limit the amount of time for delivery to external
+ Limit the amount of time for delivery to external
command.
<b>duplicate</b><i>_</i><b>filter</b><i>_</i><b>limit</b>
- Limit the size of the duplicate filter for results
+ Limit the size of the duplicate filter for results
from alias etc. expansion.
<b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>
- Limit the amount of memory used for processing a
+ Limit the amount of memory used for processing a
partial input line.
<b>local</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
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
<b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter.
<b>local</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
- Limit the number of recipients per message deliv-
- ery. The default limit is taken from the
- <b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter.
-
-<b>Security</b> <b>controls</b>
- <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>commands</b>
- Restrict the usage of mail delivery to external
+ Limit the number of recipients per message
LOCAL(8) LOCAL(8)
+ delivery. The default limit is taken from the
+ <b>default</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b> parameter.
+
+<b>Security</b> <b>controls</b>
+ <b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>commands</b>
+ Restrict the usage of mail delivery to external
command.
<b>allow</b><i>_</i><b>mail</b><i>_</i><b>to</b><i>_</i><b>files</b>
- Restrict the usage of mail delivery to external
+ Restrict the usage of mail delivery to external
file.
+ <b>command</b><i>_</i><b>expansion</b><i>_</i><b>filter</b>
+ What characters are allowed to appear in $name
+ expansions of mailbox_command. Illegal characters
+ are replaced by underscores.
+
<b>default</b><i>_</i><b>privs</b>
- Default rights for delivery to external file or
+ Default rights for delivery to external file or
command.
+ <b>forward</b><i>_</i><b>expansion</b><i>_</i><b>filter</b>
+ What characters are allowed to appear in $name
+ expansions of forward_path. Illegal characters are
+ replaced by underscores.
+
<b>HISTORY</b>
- The <b>Delivered-To:</b> header appears in the <b>qmail</b> system by
+ The <b>Delivered-To:</b> header appears in the <b>qmail</b> system by
Daniel Bernstein.
- The <i>maildir</i> structure appears in the <b>qmail</b> system by
+ The <i>maildir</i> structure appears in the <b>qmail</b> system by
Daniel Bernstein.
<b>SEE</b> <b>ALSO</b>
<a href="qmgr.8.html">qmgr(8)</a> queue manager
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<p>
-<b>luser_relay</b> 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.
+<b>luser_relay</b> can specify one address. It is subjected to
+<i>$name</i> expansions. The most useful examples are:
<p>
-In addition, some <b>luser_relay</b> destinations can receive
-special treatment:
+<dl>
-<p>
+<dt><b>$user@other.host</b>
-<dl>
+<dd>The bare username, without address extension, is prepended to
+<i>@other.host</i>. For example, mail for <i>username+foo</i> is
+sent to <i>username@other.host</i>.
+
+<p>
-<dt><b>luser_relay = </b><i>@some.where.else</i>
+<dt><b>$mailbox@other.host</b>
-<dd>The entire original recipient localpart is prepended. For
-example, mail for <i>unknown+foo</i> is sent to
-<i>unknown+foo@some.where.else</i>.
+<dd>The entire original recipient localpart, including address
+extension, is prepended to <i>@other.host</i>. For example, mail
+for <i>username+foo</i> is sent to <i>username+foo@other.host</i>.
<p>
-<dt><b>luser_relay = </b><i>someone@some.where.else</i>
-<dt><b>luser_relay = </b><i>someone</i>
+<dt><b>sysadmin+$user</b>
+
+<dd>The bare username, without address extension, is appended to
+<i>sysadmin</i>. For example, mail for <i>username+foo</i> is sent
+to <i>sysadmin+username</i>.
+
+<dt><b>sysadmin+$mailbox</b>
+
+<dd>The entire original recipient localpart, including address
+extension, is appended to <i>sysadmin</i>. For example, mail for
+<i>username+foo</i> is sent to <i>sysadmin+username+foo</i>.
-<dd>If no <b>recipient_delimiter</b> has been specified, mail is
-sent to the <b>luser_relay</b> address. If the <b>recipient_delimiter</b>
-has been specified, the entire original recipient localpart is
-appended as an address extension to the <b>luser_relay</b> address.
-For example, with <b>recipient_delimiter = +</b>, mail for
-<i>unknown+foo</i> is sent to <i>someone+unknown+foo@some.where.else</i>
-and <i>someone+unknown+foo</i>, respectively.
</dl>
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
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
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
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
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
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
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
/* .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,
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.
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,
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.
#include <been_here.h>
#include <mail_params.h>
#include <mail_conf.h>
+#include <ext_prop.h>
/* Application-specific. */
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.
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)
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));
#include <defer.h>
#include <been_here.h>
#include <mail_params.h>
+#include <ext_prop.h>
/* Application-specific. */
* 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)
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))
/* \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
/* 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),
/* (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.
/* 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.
/* 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
#include <mail_conf.h>
#include <been_here.h>
#include <mail_params.h>
+#include <ext_prop.h>
/* Single server skeleton. */
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 */
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 */
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[] = {
extern int local_file_deliver_mask;
extern int local_cmd_deliver_mask;
+ /*
+ * Restrictions on extension propagation.
+ */
+extern int local_ext_prop_mask;
+
/*
* delivered.c
*/
/*
* 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<<MAC_PARSE_USER)
/* LICENSE
/* .ad
/* SYNOPSIS
/* #include "local.h"
/*
-/* HTABLE *local_expand(state, usr_attr)
+/* int local_expand(result, pattern, state, usr_attr, filter)
+/* VSTRING *result;
+/* const char *pattern;
/* LOCAL_STATE state;
/* USER_ATTR usr_attr;
+/* const char *filter;
/* DESCRIPTION
-/* local_expand() instantiates an attribute table for $name
-/* expansion.
-/*
+/* local_expand() performs conditional and unconditional $name
+/* expansion based on message delivery attributes.
+/* The result is the bitwise OR or zero or more of the following:
+/* .IP LOCAL_EXP_EXTENSION_MATCHED
+/* The result of expansion contains the $extension attribute.
+/* .IP MAC_PARSE_XXX
+/* See mac_parse(3).
+/* .PP
/* Attributes:
/* .IP domain
/* The recipient address domain.
/* The recipient user name.
/* .PP
/* Arguments:
+/* .IP result
+/* Storage for the result of expansion. The buffer is truncated
+/* upon entry.
+/* .IP pattern
+/* The string with unconditional and conditional macro expansions.
/* .IP state
/* Message delivery attributes (sender, recipient etc.).
/* Attributes describing alias, include or forward expansion.
/* A table with delivered-to: addresses taken from the message.
/* .IP usr_attr
/* Attributes describing user rights and environment.
+/* .IP filter
+/* A null pointer, or a string of allowed characters in $name
+/* expansions. Illegal characters are replaced by underscores.
/* DIAGNOSTICS
/* Fatal errors: out of memory.
/* SEE ALSO
/* Utility library. */
-#include <htable.h>
+#include <vstring.h>
+#include <mac_expand.h>
/* Global library */
#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);
}
#include <bounce.h>
#include <mail_params.h>
#include <split_addr.h>
+#include <ext_prop.h>
/* Application-specific. */
/*
* 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
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;
* Clean up.
*/
myfree(state.msg_attr.local);
+ myfree(state.msg_attr.user);
return (rcpt_stat);
}
/* 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
#include <msg.h>
#include <stringops.h>
#include <mymalloc.h>
+#include <vstring.h>
/* Global library. */
{
char *myname = "deliver_unknown";
int status;
- char *dest;
- char *saved_extension;
+ VSTRING *expand_luser;
/*
* Make verbose logging easier to understand.
"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.
\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
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),
(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.
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
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
/* 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;
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 */
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
-THTABLE_INFO
-TINET_ADDR_LIST
-TINT_TABLE
+-TLOCAL_EXP
-TLOCAL_STATE
-TMAC_EXP
-TMAC_HEAD
rm -f valid_hostname.tmp
mac_expand_test: mac_expand mac_expand.in mac_expand.ref
- ./mac_expand <mac_expand.in >mac_expand.tmp
+ ./mac_expand <mac_expand.in >mac_expand.tmp 2>&1
diff mac_expand.ref mac_expand.tmp
rm -f mac_expand.tmp
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
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
/* 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";
} else {
vstring_strcat(ctxt->buf, STR(buf));
}
+ return(0);
}
/* dict_eval - expand embedded dictionary references */
* 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;
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);
}
/*
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));
* 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;
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,
} else
/* Straight text - duplicate with no substitution */
vstring_strcat(ctxt->buf, vstring_str(buf));
+ return (0);
}
/*
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));
/* SYNOPSIS
/* #include <mac_expand.h>
/*
-/* 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)"
/* $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
/* 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
#include <msg.h>
#include <vstring.h>
#include <mymalloc.h>
-#include <htable.h>
#include <mac_parse.h>
#include <mac_expand.h>
/*
- * 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.
* 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.
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++;
- }
}
/*
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);
}
* This code certainly deserves a stand-alone test program.
*/
#include <stringops.h>
+#include <htable.h>
#include <vstream.h>
#include <vstring_vstream.h>
+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);
* 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);
* 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);
}
* Utility library.
*/
#include <vstring.h>
+#include <mac_parse.h>
/*
- * 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
-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|
/* SYNOPSIS
/* #include <mac_parse.h>
/*
-/* 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
/* .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
* 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 */
static char open_paren[] = "({";
static char close_paren[] = ")}";
int level;
+ int status = 0;
#define SKIP(start, var, cond) \
for (var = start; *var && (cond); var++);
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) */
for (ep = vp; level > 0; ep++) {
if (*ep == 0) {
msg_warn("truncated macro reference: \"%s\"", value);
+ status |= MAC_PARSE_ERROR;
break;
}
if (*ep == *pp)
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
#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