From: Wietse Venema Date: Fri, 7 May 1999 05:00:00 +0000 (-0500) Subject: snapshot-19990507 X-Git-Tag: v20010228~109 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1c552f9bc7e99e6ca27c4ecb7a15d1e00791c5f7;p=thirdparty%2Fpostfix.git snapshot-19990507 --- diff --git a/postfix/conf/main.cf b/postfix/conf/main.cf index 35e3aedf7..e79c06851 100644 --- a/postfix/conf/main.cf +++ b/postfix/conf/main.cf @@ -228,9 +228,9 @@ program_directory = /some/where/postfix/bin # The following expansions are done on mailbox_command: $user (recipient # username), $shell (recipient shell), $home (recipient home directory), # $recipient (full recipient address), $extension (recipient address -# extension), $domain (recipient domain), $recipient_delimiter. Specify -# ${name?value} or ${name:value} to expand value only when $name does -# (does not) exist. +# extension), $domain (recipient domain), $mailbox (entire recipient +# localpart), $recipient_delimiter. Specify ${name?value} or +# ${name:value} to expand value only when $name does (does not) exist. # # Avoid shell meta characters because they will force Postfix to run # an expensive shell process. Procmail alone is expensive enough. @@ -250,16 +250,20 @@ program_directory = /some/where/postfix/bin # #fallback_transport = -# 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 (@domain, +# address) for unknown recipients. By default, mail for unknown +# local recipients is bounced. # # Specify @domain in order to keep the original recipient name. -# If an address is specified, and if a recipient delimiter is -# specified, the original recipient name is appended to the addres -# localpart. +# 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 +# extension), $domain (recipient domain), $mailbox (entire recipient +# localpart), $recipient_delimiter. Specify ${name?value} or +# ${name:value} to expand value only when $name does (does not) exist. # -# luser_relay = +# luser_relay = @other.host +# luser_relay = admin+$mailbox # JUNK MAIL CONTROLS # diff --git a/postfix/conf/main.cf.default b/postfix/conf/main.cf.default index 83ded8275..eccb1988a 100644 --- a/postfix/conf/main.cf.default +++ b/postfix/conf/main.cf.default @@ -61,7 +61,7 @@ luser_relay = mail_name = Postfix mail_owner = postfix mail_spool_directory = /var/mail -mail_version = Snapshot-19990505 +mail_version = Snapshot-19990507 mailbox_command = mailbox_transport = maps_rbl_domains = rbl.maps.vix.com diff --git a/postfix/conf/sample-local.cf b/postfix/conf/sample-local.cf index 379db6436..b083dffa5 100644 --- a/postfix/conf/sample-local.cf +++ b/postfix/conf/sample-local.cf @@ -30,9 +30,9 @@ # The following expansions are done on forward_path: $user (recipient # username), $shell (recipient shell), $home (recipient home directory), # $recipient (full recipient address), $extension (recipient address -# extension), $domain (recipient domain), $recipient_delimiter. Specify -# ${name?value} or ${name:value} to expand value only when $name does -# (does not) exist. +# extension), $domain (recipient domain), $mailbox (entire recipient +# localpart), $recipient_delimiter. Specify ${name?value} or +# ${name:value} to expand value only when $name does (does not) exist. # #forward_path = /var/forward/$user forward_path = $home/.forward$recipient_delimiter$extension,$home/.forward @@ -76,11 +76,15 @@ home_mailbox = # By default, mail for unknown local recipients is bounced. # # Specify @domain in order to keep the original recipient name. -# If an address is specified, and if a recipient delimiter is -# specified, the original recipient name is appended to the addres -# localpart. +# 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 +# extension), $domain (recipient domain), $mailbox (entire recipient +# localpart), $recipient_delimiter. Specify ${name?value} or +# ${name:value} to expand value only when $name does (does not) exist. # -# luser_relay = +# luser_relay = @other.host +# luser_relay = admin+$mailbox # The mail_spool_directory parameter specifies the directory where # UNIX-style mailboxes are kept. The default setting depends on the @@ -97,9 +101,9 @@ home_mailbox = # The following expansions are done on mailbox_command: $user (recipient # username), $shell (recipient shell), $home (recipient home directory), # $recipient (full recipient address), $extension (recipient address -# extension), $domain (recipient domain), $recipient_delimiter. Specify -# ${name?value} or ${name:value} to expand value only when $name does -# (does not) exist. +# extension), $domain (recipient domain), $mailbox (entire recipient +# localpart), $recipient_delimiter. Specify ${name?value} or +# ${name:value} to expand value only when $name does (does not) exist. # # Avoid shell meta characters because they will force Postfix to run # an expensive shell process. Procmail alone is expensive enough. diff --git a/postfix/global/mail_version.h b/postfix/global/mail_version.h index 97dc67f3a..bab9fdc66 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-19990505" +#define DEF_MAIL_VERSION "Snapshot-19990507" extern char *var_mail_version; /* LICENSE diff --git a/postfix/html/local.8.html b/postfix/html/local.8.html index 336b5359e..f16780226 100644 --- a/postfix/html/local.8.html +++ b/postfix/html/local.8.html @@ -41,10 +41,10 @@ LOCAL(8) LOCAL(8) interpolation of $user (recipient username), $home (recip- ient home directory), $shell (recipient shell), $recipient (complete recipient address), $extension (recipient - address extension), $domain (recipient domain) and $recip- - ient_delimiter. The forms ${name?value} and ${name:value} - expand conditionally to value when $name is (is not) - defined. + 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. An alias or ~/.forward file may list any combination of external commands, destination file names, :include: @@ -111,20 +111,20 @@ LOCAL(8) LOCAL(8) ent username), $home (recipient home directory), $shell (recipient shell), $recipient (complete recipient address), $extension (recipient address extension), - $domain (recipient domain) and $recipient_delimiter. The - forms ${name?value} and ${name:value} expand conditionally - to value when $name is (is not) defined. In the result of - name expansion, characters that have special meaning to + $domain (recipient domain), mailbox (entire recipient + 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 + name expansion, characters that have special meaning to the shell are censored and replaced by underscores. - 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 + 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 - UNIX passwd database. @@ -137,37 +137,39 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) + UNIX passwd database. + In the case of UNIX-style mailbox delivery, the local dae- mon prepends a "From sender time_stamp" envelope header to - each message, prepends a Delivered-To: header with the + each message, prepends a Delivered-To: header with the envelope recipient address, prepends a Return-Path: header - with the envelope sender address, prepends a > character - to lines beginning with "From ", and appends an empty - line. The mailbox is locked for exclusive access while - delivery is in progress. In case of problems, an attempt + with the envelope sender address, prepends a > character + to lines beginning with "From ", and appends an empty + line. The mailbox is locked for exclusive access while + delivery is in progress. In case of problems, an attempt is made to truncate the mailbox to its original length. In the case of maildir delivery, the local daemon prepends a Delivered-To: header with the envelope recipient address - and prepends a Return-Path: header with the envelope + and prepends a Return-Path: header with the envelope sender address. EXTERNAL COMMAND DELIVERY - The allow_mail_to_commands configuration parameter - restricts delivery to external commands. The default set- - ting (alias, forward) forbids command destinations in + The allow_mail_to_commands configuration parameter + restricts delivery to external commands. The default set- + ting (alias, forward) forbids command destinations in :include: files. - The command is executed directly where possible. Assis- - tance by the shell (/bin/sh on UNIX systems) is used only - when the command contains shell magic characters, or when + The command is executed directly where possible. Assis- + tance by the shell (/bin/sh on UNIX systems) is used only + when the command contains shell magic characters, or when the command invokes a shell built-in command. - A limited amount of command output (standard output and - standard error) is captured for inclusion with non-deliv- - ery status reports. A command is forcibly terminated if - it does not complete within command_time_limit seconds. - Command exit status codes are expected to follow the con- + A limited amount of command output (standard output and + standard error) is captured for inclusion with non-deliv- + ery status reports. A command is forcibly terminated if + it does not complete within command_time_limit seconds. + Command exit status codes are expected to follow the con- ventions defined in <sysexits.h>. When mail is delivered on behalf of a user, the HOME, LOG- @@ -179,18 +181,16 @@ LOCAL(8) LOCAL(8) The current working directory is the mail queue directory. The local daemon prepends a "From sender time_stamp" enve- - lope header to each message, prepends a Delivered-To: - header with the recipient envelope address, prepends a - Return-Path: header with the sender envelope address, and + lope header to each message, prepends a Delivered-To: + header with the recipient envelope address, prepends a + 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. + 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. - @@ -203,40 +203,42 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) + maildir delivery. + The local daemon prepends a "From sender time_stamp" enve- - lope header to each message, prepends a Delivered-To: - header with the recipient envelope address, prepends a > - character to lines beginning with "From ", and appends an - empty line. The envelope sender address is available in - the Return-Path: header. When the destination is a regu- + lope header to each message, prepends a Delivered-To: + header with the recipient envelope address, prepends a > + character to lines beginning with "From ", and appends an + empty line. The envelope sender address is available in + the Return-Path: header. When the destination is a regu- lar file, it is locked for exclusive access while delivery is in progress. In case of problems, an attempt is made to truncate a regular file to its original length. In the case of maildir delivery, the local daemon prepends - a Delivered-To: header with the envelope recipient - address. The envelope sender address is available in the + a Delivered-To: header with the envelope recipient + address. The envelope sender address is available in the Return-Path: header. ADDRESS EXTENSION - The optional recipient_delimiter configuration parameter - specifies how to separate address extensions from local + The optional recipient_delimiter configuration parameter + specifies how to separate address extensions from local recipient names. - For example, with "recipient_delimiter = +", mail for - name+foo is delivered to the alias name+foo or to the - alias name, to the destinations listed in ~name/.for- + For example, with "recipient_delimiter = +", mail for + name+foo is delivered to the alias name+foo or to the + alias name, to the destinations listed in ~name/.for- ward+foo or in ~name/.forward, to the mailbox owned by the user name, or it is sent back as undeliverable. - In all cases the local daemon prepends a `Delivered-To: + In all cases the local daemon prepends a `Delivered-To: name+foo' header line. DELIVERY RIGHTS - Deliveries to external files and external commands are + Deliveries to external files and external commands are made with the rights of the receiving user on whose behalf - the delivery is made. In the absence of a user context, - the local daemon uses the owner rights of the :include: + the delivery is made. In the absence of a user context, + the local daemon uses the owner rights of the :include: file or alias database. When those files are owned by the superuser, delivery is made with the rights specified with the default_privs configuration parameter. @@ -245,18 +247,16 @@ LOCAL(8) LOCAL(8) RFC 822 (ARPA Internet Text Messages) DIAGNOSTICS - Problems and transactions are logged to syslogd(8). Cor- - rupted message files are marked so that the queue manager + Problems and transactions are logged to syslogd(8). Cor- + rupted message files are marked so that the queue manager 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- + 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 - external commands or of external files is never check- - pointed to file. As a result, the program may occasionally + For security reasons, the message delivery status of @@ -269,17 +269,19 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) + 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- ter safe than sorry. - Mutually-recursive aliases or ~/.forward files are not - detected early. The resulting mail forwarding loop is + Mutually-recursive aliases or ~/.forward files are not + detected early. The resulting mail forwarding loop is broken by the use of the Delivered-To: message header. CONFIGURATION PARAMETERS - The following main.cf parameters are especially relevant - to this program. See the Postfix main.cf file for syntax - details and for default values. Use the postfix reload + The following main.cf parameters are especially relevant + to this program. See the Postfix main.cf file for syntax + details and for default values. Use the postfix reload command after a configuration change. Miscellaneous @@ -287,10 +289,8 @@ LOCAL(8) LOCAL(8) List of alias databases. forward_path - Search list for .forward files. The following - macros are recognized: $home (home directory), - $user (login name), $extension (address extension), - $recipient_delimiter (address extension delimiter). + Search list for .forward files. The names are sub- + ject to $name expansion. local_command_shell Shell to use for external command execution (for @@ -319,8 +319,8 @@ LOCAL(8) LOCAL(8) luser_relay Destination (@domain or address) for non-existent - users. The address can be any destination that is - valid in an alias file. + users. The address is subjected to $name expan- + sion. diff --git a/postfix/local/Makefile.in b/postfix/local/Makefile.in index 0eb8d6d3e..943b2b70f 100644 --- a/postfix/local/Makefile.in +++ b/postfix/local/Makefile.in @@ -97,6 +97,7 @@ command.o: ../include/vstring.h command.o: ../include/vbuf.h command.o: ../include/vstream.h command.o: ../include/argv.h +command.o: ../include/mac_expand.h command.o: ../include/defer.h command.o: ../include/bounce.h command.o: ../include/sent.h @@ -154,7 +155,7 @@ dotforward.o: ../include/lstat_as.h dotforward.o: ../include/iostuff.h dotforward.o: ../include/stringops.h dotforward.o: ../include/mymalloc.h -dotforward.o: ../include/mac_parse.h +dotforward.o: ../include/mac_expand.h dotforward.o: ../include/mypwd.h dotforward.o: ../include/bounce.h dotforward.o: ../include/been_here.h @@ -291,13 +292,11 @@ 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/vstring.h -local_expand.o: ../include/vbuf.h -local_expand.o: ../include/mac_expand.h -local_expand.o: ../include/mail_params.h -local_expand.o: local.h local_expand.o: ../include/htable.h +local_expand.o: local.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 diff --git a/postfix/local/alias.c b/postfix/local/alias.c index 982567bab..cd975ebcd 100644 --- a/postfix/local/alias.c +++ b/postfix/local/alias.c @@ -6,9 +6,10 @@ /* SYNOPSIS /* #include "local.h" /* -/* int deliver_alias(state, usr_attr, statusp) +/* int deliver_alias(state, usr_attr, name, statusp) /* LOCAL_STATE state; /* USER_ATTR usr_attr; +/* char *name; /* int *statusp; /* DESCRIPTION /* deliver_alias() looks up the expansion of the recipient in @@ -37,6 +38,8 @@ /* A table with delivered-to: addresses taken from the message. /* .IP usr_attr /* User attributes (rights, environment). +/* .IP name +/* The alias to be looked up. /* .IP statusp /* Delivery status. See below. /* DIAGNOSTICS @@ -119,7 +122,8 @@ static uid_t dict_owner(char *table) /* deliver_alias - expand alias file entry */ -int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) +int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, + char *name, int *statusp) { char *myname = "deliver_alias"; const char *alias_result; @@ -161,15 +165,15 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) * a possible alias loop. */ if (state.msg_attr.exp_from != 0 - && strcasecmp(state.msg_attr.exp_from, state.msg_attr.local) == 0) + && strcasecmp(state.msg_attr.exp_from, name) == 0) return (NO); if (state.level > 100) { - msg_warn("possible alias database loop for %s", state.msg_attr.local); + msg_warn("possible alias database loop for %s", name); *statusp = bounce_append(BOUNCE_FLAG_KEEP, BOUNCE_ATTR(state.msg_attr), - "possible alias database loop for %s", state.msg_attr.local); + "possible alias database loop for %s", name); return (YES); } - state.msg_attr.exp_from = state.msg_attr.local; + state.msg_attr.exp_from = name; /* * There are a bunch of roles that we're trying to keep track of. @@ -196,10 +200,9 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) msg_warn("invalid alias map type: %s", *cpp); continue; } - if ((alias_result = dict_get(dict, state.msg_attr.local)) != 0) { + if ((alias_result = dict_get(dict, name)) != 0) { if (msg_verbose) - msg_info("%s: %s: %s = %s", myname, *cpp, - state.msg_attr.local, alias_result); + msg_info("%s: %s: %s = %s", myname, *cpp, name, alias_result); /* * DELIVERY POLICY @@ -243,7 +246,7 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) #define STR(x) vstring_str(x) #define OWNER_ASSIGN(own) \ (own = (var_ownreq_special == 0 ? 0 : \ - concatenate("owner-", state.msg_attr.local, (char *) 0))) + concatenate("owner-", name, (char *) 0))) expansion = mystrdup(alias_result); if (OWNER_ASSIGN(owner) != 0 && maps_find(maps, owner, @@ -292,8 +295,7 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) return (YES); } else { if (msg_verbose) - msg_info("%s: %s: %s not found", myname, *cpp, - state.msg_attr.local); + msg_info("%s: %s: %s not found", myname, *cpp, name); } } @@ -303,9 +305,9 @@ int deliver_alias(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) */ #define STREQ(x,y) (strcasecmp(x,y) == 0) - if (STREQ(state.msg_attr.local, MAIL_ADDR_MAIL_DAEMON) - || STREQ(state.msg_attr.local, MAIL_ADDR_POSTMASTER)) { - msg_warn("required alias not found: %s", state.msg_attr.local); + if (STREQ(name, MAIL_ADDR_MAIL_DAEMON) + || STREQ(name, MAIL_ADDR_POSTMASTER)) { + msg_warn("required alias not found: %s", name); *statusp = sent(SENT_ATTR(state.msg_attr), "discarded"); return (YES); } diff --git a/postfix/local/command.c b/postfix/local/command.c index c6a4b2425..481223fc5 100644 --- a/postfix/local/command.c +++ b/postfix/local/command.c @@ -57,6 +57,7 @@ #include #include #include +#include /* Global library. */ @@ -86,6 +87,7 @@ int deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, char *command) abcdefghijklmnopqrstuvwxyz\ ABCDEFGHIJKLMNOPQRSTUVWXYZ"; VSTRING *expanded_cmd; + HTABLE *expand_attr; /* * Make verbose logging easier to understand. @@ -151,9 +153,14 @@ ABCDEFGHIJKLMNOPQRSTUVWXYZ"; argv_terminate(env); expanded_cmd = vstring_alloc(10); - if (command == var_mailbox_command) - local_expand(expanded_cmd, command, state, usr_attr, ok_chars); - else + 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); cmd_status = pipe_command(state.msg_attr.fp, why, diff --git a/postfix/local/deliver_attr.c b/postfix/local/deliver_attr.c index 7b62509df..11dbf09a5 100644 --- a/postfix/local/deliver_attr.c +++ b/postfix/local/deliver_attr.c @@ -52,8 +52,11 @@ void deliver_attr_init(DELIVER_ATTR *attrp) attrp->offset = 0; attrp->sender = 0; attrp->recipient = 0; + attrp->domain = 0; attrp->local = 0; + attrp->user = 0; attrp->extension = 0; + attrp->unmatched = 0; attrp->owner = 0; attrp->delivered = 0; attrp->relay = 0; @@ -74,8 +77,11 @@ void deliver_attr_dump(DELIVER_ATTR *attrp) msg_info("offset: %ld", attrp->offset); msg_info("sender: %s", attrp->sender ? attrp->sender : "null"); msg_info("recipient: %s", attrp->recipient ? attrp->recipient : "null"); + msg_info("domain: %s", attrp->domain ? attrp->domain : "null"); msg_info("local: %s", attrp->local ? attrp->local : "null"); + msg_info("user: %s", attrp->user ? attrp->user : "null"); msg_info("extension: %s", attrp->extension ? attrp->extension : "null"); + msg_info("unmatched: %s", attrp->unmatched ? attrp->unmatched : "null"); msg_info("owner: %s", attrp->owner ? attrp->owner : "null"); msg_info("delivered: %s", attrp->delivered ? attrp->delivered : "null"); msg_info("relay: %s", attrp->relay ? attrp->relay : "null"); diff --git a/postfix/local/dotforward.c b/postfix/local/dotforward.c index 0c73a981a..aef9e0eed 100644 --- a/postfix/local/dotforward.c +++ b/postfix/local/dotforward.c @@ -67,7 +67,7 @@ #include #include #include -#include +#include /* Global library. */ @@ -102,6 +102,9 @@ 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; /* * Make verbose logging easier to understand. @@ -112,8 +115,8 @@ int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) /* * Skip this module if per-user forwarding is disabled. XXX We need to - * extend the mail_conf_XXX() interface to request no expansion of $names in - * the given value or in the default value. + * extend the mail_conf_XXX() interface to request no expansion of $names + * in the given value or in the default value. */ if ((forward_path = mail_conf_lookup(VAR_FORWARD_PATH)) == 0) forward_path = DEF_FORWARD_PATH; @@ -134,7 +137,7 @@ int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) * Skip non-existing users. The mailbox delivery routine will catch the * error. */ - if ((mypwd = mypwnam(state.msg_attr.local)) == 0) + if ((mypwd = mypwnam(state.msg_attr.user)) == 0) return (NO); /* @@ -197,19 +200,33 @@ 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); - if (local_expand(path, lhs, state, usr_attr, (char *) 0) == 0) { + 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) { 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); - if (lookup_status >= 0) + if (lookup_status >= 0) { + if (extension_record->value != 0) + state.msg_attr.unmatched = 0; break; + } } } + htable_free(expand_attr, (void (*) (char *)) 0); + htable_free(record_attr, (void (*) (char *)) 0); + 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/local.c b/postfix/local/local.c index 3147a3928..7a29b49b0 100644 --- a/postfix/local/local.c +++ b/postfix/local/local.c @@ -35,7 +35,8 @@ /* \fB$user\fR (recipient username), \fB$home\fR (recipient home /* directory), \fB$shell\fR (recipient shell), \fB$recipient\fR /* (complete recipient address), \fB$extension\fR (recipient address -/* extension), \fB$domain\fR (recipient domain) and +/* extension), \fB$domain\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. @@ -94,7 +95,8 @@ /* username), \fB$home\fR (recipient home directory), \fB$shell\fR /* (recipient shell), \fB$recipient\fR (complete recipient address), /* \fB$extension\fR (recipient address extension), \fB$domain\fR -/* (recipient domain) and \fB$recipient_delimiter.\fR The forms +/* (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 @@ -235,10 +237,8 @@ /* .IP \fBalias_maps\fR /* List of alias databases. /* .IP \fBforward_path\fR -/* Search list for .forward files. The following macros are recognized: -/* \fB$home\fR (home directory), \fB$user\fR (login name), -/* \fB$extension\fR (address extension), \fB$recipient_delimiter\fR -/* (address extension delimiter). +/* Search list for .forward files. The names are subject to \fI$name\fR +/* expansion. /* .IP \fBlocal_command_shell\fR /* Shell to use for external command execution (for example, /* /some/where/smrsh -c). @@ -261,8 +261,7 @@ /* Specify a path ending in \fB/\fR for maildir-style delivery. /* .IP \fBluser_relay\fR /* Destination (\fI@domain\fR or \fIaddress\fR) for non-existent users. -/* The \fIaddress\fR can be any destination that is valid in an alias -/* file. +/* The \fIaddress\fR is subjected to \fI$name\fR expansion. /* .IP \fBmail_spool_directory\fR /* Directory with UNIX-style mailboxes. The default pathname is system /* dependent. @@ -577,6 +576,7 @@ int main(int argc, char **argv) static CONFIG_STR_TABLE raw_table[] = { VAR_FORWARD_PATH, DEF_FORWARD_PATH, &var_forward_path, 0, 0, VAR_MAILBOX_COMMAND, DEF_MAILBOX_COMMAND, &var_mailbox_command, 0, 0, + VAR_LUSER_RELAY, DEF_LUSER_RELAY, &var_luser_relay, 0, 0, 0, }; diff --git a/postfix/local/local.h b/postfix/local/local.h index 2f72de13c..0ec125ecc 100644 --- a/postfix/local/local.h +++ b/postfix/local/local.h @@ -68,8 +68,11 @@ typedef struct DELIVER_ATTR { long offset; /* data offset */ char *sender; /* taken from envelope */ char *recipient; /* taken from resolver */ - char *local; /* recipient localpart, base name */ + char *domain; /* recipient domain */ + char *local; /* recipient full localpart */ + char *user; /* recipient localpart, base name */ char *extension; /* recipient localpart, extension */ + char *unmatched; /* unmatched extension */ char *owner; /* null or list owner */ char *delivered; /* for loop detection */ char *relay; /* relay host */ @@ -136,7 +139,7 @@ typedef struct LOCAL_STATE { * "inner" nodes of the delivery graph. */ extern int deliver_recipient(LOCAL_STATE, USER_ATTR); -extern int deliver_alias(LOCAL_STATE, USER_ATTR, int *); +extern int deliver_alias(LOCAL_STATE, USER_ATTR, char *, int *); extern int deliver_dotforward(LOCAL_STATE, USER_ATTR, int *); extern int deliver_include(LOCAL_STATE, USER_ATTR, char *); extern int deliver_token(LOCAL_STATE, USER_ATTR, TOK822 *); @@ -184,7 +187,7 @@ extern int feature_control(const char *); /* * local_expand.c */ -int local_expand(VSTRING *, const char *, LOCAL_STATE, USER_ATTR, const char *); +HTABLE *local_expand(LOCAL_STATE, USER_ATTR); /* LICENSE /* .ad diff --git a/postfix/local/local_expand.c b/postfix/local/local_expand.c index e8563e45a..dbeeb9090 100644 --- a/postfix/local/local_expand.c +++ b/postfix/local/local_expand.c @@ -2,41 +2,36 @@ /* NAME /* local_expand 3 /* SUMMARY -/* expand $name based on delivery attributes +/* set up attribute list for $name expansion /* SYNOPSIS /* #include "local.h" /* -/* int local_expand(result, pattern, state, usr_attr, filter) -/* VSTRING *result; -/* const char *pattern; +/* HTABLE *local_expand(state, usr_attr) /* LOCAL_STATE state; /* USER_ATTR usr_attr; -/* const char *filter; /* DESCRIPTION -/* local_expand() expands $name instances on the basis of message -/* delivery attributes. +/* local_expand() instantiates an attribute table for $name +/* expansion. /* -/* Macros: -/* .IP $domain +/* Attributes: +/* .IP domain /* The recipient address domain. -/* .IP $extension +/* .IP extension /* The recipient address extension. -/* .IP $home +/* .IP home /* The recipient home directory. -/* .IP $recipient +/* .IP mailbox +/* The full recipient address localpart. +/* .IP recipient /* The full recipient address. -/* .IP $recipient_delimiter +/* .IP recipient_delimiter /* The recipient delimiter. -/* .IP $shell +/* .IP shell /* The recipient shell program. -/* .IP $user +/* .IP user /* The recipient user name. /* .PP /* Arguments: -/* .IP result -/* Storage for the result. The result is truncated upon entry. -/* .IP pattern -/* The input with zero or more $name references. /* .IP state /* Message delivery attributes (sender, recipient etc.). /* Attributes describing alias, include or forward expansion. @@ -44,9 +39,6 @@ /* 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 null-terminated list of characters that -/* are allowed to appear in the result if a $name expansion. /* DIAGNOSTICS /* Fatal errors: out of memory. /* SEE ALSO @@ -69,10 +61,9 @@ /* Utility library. */ -#include -#include +#include -/* Global library. */ +/* Global library */ #include @@ -80,30 +71,25 @@ #include "local.h" -/* local_expand - expand contents of .forward file */ +/* local_expand - set up macro expansion attributes */ -int local_expand(VSTRING *result, const char *pattern, - LOCAL_STATE state, USER_ATTR usr_attr, const char *filter) +HTABLE *local_expand(LOCAL_STATE state, USER_ATTR usr_attr) { - char *domain; + HTABLE *expand_attr; /* * Impedance matching between the local delivery agent data structures * and the mac_expand() interface. The CPU cycles wasted will be * negligible. */ - if ((domain = strrchr(state.msg_attr.recipient, '@')) != 0) - domain++; - - return (mac_expand(result, pattern, MAC_EXP_FLAG_NONE, - MAC_EXP_ARG_FILTER, filter, - MAC_EXP_ARG_ATTR, "user", usr_attr.logname, - MAC_EXP_ARG_ATTR, "home", usr_attr.home, - MAC_EXP_ARG_ATTR, "shell", usr_attr.shell, - MAC_EXP_ARG_ATTR, "domain", domain, - MAC_EXP_ARG_ATTR, "recipient", state.msg_attr.recipient, - MAC_EXP_ARG_ATTR, "extension", state.msg_attr.extension, - MAC_EXP_ARG_ATTR, "recipient_delimiter", - *var_rcpt_delim ? var_rcpt_delim : 0, - 0)); + 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); } diff --git a/postfix/local/mailbox.c b/postfix/local/mailbox.c index 9ac2489df..da51c7ddd 100644 --- a/postfix/local/mailbox.c +++ b/postfix/local/mailbox.c @@ -129,7 +129,7 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr) mailbox = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0); } else { spool_dir = var_mail_spool_dir; - mailbox = concatenate(spool_dir, "/", state.msg_attr.local, (char *) 0); + mailbox = concatenate(spool_dir, "/", state.msg_attr.user, (char *) 0); } /* @@ -241,7 +241,7 @@ int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) * * Don't come here more than once, whether or not the recipient exists. */ - if (been_here(state.dup_filter, "mailbox %s", state.msg_attr.local)) + if (been_here(state.dup_filter, "mailbox %s", state.msg_attr.user)) return (YES); /* @@ -256,7 +256,7 @@ int deliver_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) /* * Skip delivery when this recipient does not exist. */ - if ((mbox_pwd = mypwnam(state.msg_attr.local)) == 0) + if ((mbox_pwd = mypwnam(state.msg_attr.user)) == 0) return (NO); /* diff --git a/postfix/local/recipient.c b/postfix/local/recipient.c index 36452618f..9680f2f8c 100644 --- a/postfix/local/recipient.c +++ b/postfix/local/recipient.c @@ -105,35 +105,23 @@ static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr) * \user is special: it means don't do any alias or forward expansion. */ if (state.msg_attr.recipient[0] == '\\') { - state.msg_attr.recipient++, state.msg_attr.local++; - if (*var_rcpt_delim) - state.msg_attr.extension = - split_addr(state.msg_attr.local, *var_rcpt_delim); + state.msg_attr.recipient++, state.msg_attr.local++, state.msg_attr.user++; if (deliver_mailbox(state, usr_attr, &status) == 0) status = deliver_unknown(state, usr_attr); return (status); } /* - * Otherwise, alias expansion has highest precedence. + * Otherwise, alias expansion has highest precedence. First look up the + * full localpart, then the bare user. */ - if (deliver_alias(state, usr_attr, &status)) - return (status); - - /* - * Don't apply the recipient delimiter to reserved addresses. After - * stripping the recipient extension, try aliases again. - */ - if (*var_rcpt_delim) - state.msg_attr.extension = - split_addr(state.msg_attr.local, *var_rcpt_delim); - 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.recipient); - state.msg_attr.extension = 0; - } - if (state.msg_attr.extension && deliver_alias(state, usr_attr, &status)) + 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 (deliver_alias(state, usr_attr, state.msg_attr.user, &status)) + return (status); /* * Special case for mail locally forwarded or aliased to a different @@ -202,11 +190,26 @@ int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr) if (state.msg_attr.delivered == 0) state.msg_attr.delivered = state.msg_attr.recipient; state.msg_attr.local = mystrdup(state.msg_attr.recipient); - if (split_at_right(state.msg_attr.local, '@') == 0) - msg_warn("no @ in recipient address: %s", state.msg_attr.local); lowercase(state.msg_attr.local); + if ((state.msg_attr.domain = split_at_right(state.msg_attr.local, '@')) == 0) + msg_warn("no @ in recipient address: %s", state.msg_attr.local); state.msg_attr.features = feature_control(state.msg_attr.local); - state.msg_attr.extension = 0; + + /* + * Address extension management. + */ + state.msg_attr.user = mystrdup(state.msg_attr.local); + if (*var_rcpt_delim) { + state.msg_attr.extension = + split_addr(state.msg_attr.local, *var_rcpt_delim); + if (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; + } + } else + state.msg_attr.extension = 0; + state.msg_attr.unmatched = state.msg_attr.extension; /* * Run the recipient through the delivery switch. diff --git a/postfix/local/resolve.c b/postfix/local/resolve.c index 04ce7405d..2067c4766 100644 --- a/postfix/local/resolve.c +++ b/postfix/local/resolve.c @@ -114,24 +114,22 @@ int deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr tok822_resolve(addr, &reply); state.msg_attr.recipient = STR(reply.recipient); -#if 0 /* * Splice in the optional unmatched address extension. */ - if (state.msg_attr.extension) { + if (state.msg_attr.unmatched) { if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) { VSTRING_ADDCH(reply.recipient, *var_rcpt_delim); - vstring_strcat(reply.recipient, state.msg_attr.extension); + vstring_strcat(reply.recipient, state.msg_attr.unmatched); } else { - ext_len = strlen(state.msg_attr.extension); + ext_len = strlen(state.msg_attr.unmatched); VSTRING_SPACE(reply.recipient, ext_len + 2); memmove(ratsign + ext_len + 1, ratsign, strlen(ratsign) + 1); *ratsign = *var_rcpt_delim; - memcpy(ratsign + 1, state.msg_attr.extension, ext_len); + memcpy(ratsign + 1, state.msg_attr.unmatched, ext_len); VSTRING_SKIP(reply.recipient); } } -#endif /* * Delivery to a local or non-local address. For a while there was some diff --git a/postfix/makedefs b/postfix/makedefs index ef7e37e4e..33ae8d168 100644 --- a/postfix/makedefs +++ b/postfix/makedefs @@ -156,6 +156,8 @@ HP-UX.A.09.*) SYSTYPE=HPUX9 fi ;; HP-UX.B.10.*) SYSTYPE=HPUX10 + CCARGS="$CCARGS `nm /usr/lib/libc.a 2>/dev/null | + (grep usleep >/dev/null || echo '-Dusleep=doze')`" if [ -f /usr/lib/libdb.a ]; then CCARGS="$CCARGS -DHAS_DB" SYSLIBS=-ldb diff --git a/postfix/man/man8/local.8 b/postfix/man/man8/local.8 index dca195cb8..bfa8abb16 100644 --- a/postfix/man/man8/local.8 +++ b/postfix/man/man8/local.8 @@ -43,7 +43,8 @@ The \fBforward_path\fR parameter 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), \fB$extension\fR (recipient address -extension), \fB$domain\fR (recipient domain) and +extension), \fB$domain\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. @@ -106,7 +107,8 @@ 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), \fB$extension\fR (recipient address extension), \fB$domain\fR -(recipient domain) and \fB$recipient_delimiter.\fR The forms +(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 @@ -263,10 +265,8 @@ a configuration change. .IP \fBalias_maps\fR List of alias databases. .IP \fBforward_path\fR -Search list for .forward files. The following macros are recognized: -\fB$home\fR (home directory), \fB$user\fR (login name), -\fB$extension\fR (address extension), \fB$recipient_delimiter\fR -(address extension delimiter). +Search list for .forward files. The names are subject to \fI$name\fR +expansion. .IP \fBlocal_command_shell\fR Shell to use for external command execution (for example, /some/where/smrsh -c). @@ -289,8 +289,7 @@ Pathname of a mailbox relative to a user's home directory. Specify a path ending in \fB/\fR for maildir-style delivery. .IP \fBluser_relay\fR Destination (\fI@domain\fR or \fIaddress\fR) for non-existent users. -The \fIaddress\fR can be any destination that is valid in an alias -file. +The \fIaddress\fR is subjected to \fI$name\fR expansion. .IP \fBmail_spool_directory\fR Directory with UNIX-style mailboxes. The default pathname is system dependent. diff --git a/postfix/postconf/Makefile.in b/postfix/postconf/Makefile.in index b608720a2..44875dc5d 100644 --- a/postfix/postconf/Makefile.in +++ b/postfix/postconf/Makefile.in @@ -22,6 +22,7 @@ $(PROG): $(OBJS) $(LIBS) $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) ../conf/main.cf.default: $(PROG) Makefile + rm -f $@ ./$(PROG) -d |egrep -v '^(myhostname|mydomain|mynetworks) ' >$@ Makefile: Makefile.in diff --git a/postfix/smtpd/smtpd_check.c b/postfix/smtpd/smtpd_check.c index 426663d96..d25e29da2 100644 --- a/postfix/smtpd/smtpd_check.c +++ b/postfix/smtpd/smtpd_check.c @@ -368,7 +368,6 @@ static int smtpd_check_reject(SMTPD_STATE *state, int error_class, va_start(ap, format); vstring_vsprintf(error_text, format, ap); va_end(ap); - printable(STR(error_text), ' '); /* * Validate the response, that is, the response must begin with a @@ -382,13 +381,24 @@ static int smtpd_check_reject(SMTPD_STATE *state, int error_class, vstring_strcpy(error_text, "450 Service unavailable"); } + /* + * Give everyone involved a clue. + */ + if (state->sender) { + vstring_sprintf_append(error_text, " (from=<%s>", state->sender); + if (state->recipient) + vstring_sprintf_append(error_text, " to=<%s>", state->recipient); + VSTRING_ADDCH(error_text, ')'); + } + printable(STR(error_text), ' '); + /* * Log what is happening. When the sysadmin discards policy violation * postmaster notices, this may be the only trace left that service was * rejected. Print the request, client name/address, and response. */ - msg_info("reject: %s from %s[%s]: %s", state->where, state->name, - state->addr, STR(error_text)); + msg_info("%s: reject: %s from %s[%s]: %s", state->queue_id, state->where, + state->name, state->addr, STR(error_text)); return (SMTPD_CHECK_REJECT); } @@ -1356,6 +1366,7 @@ char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient) char **cpp; char *name; int status; + char *saved_recipient = state->recipient; /* * Initialize. @@ -1365,8 +1376,10 @@ char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient) return (0); /* - * Apply restrictions in the order as specified. + * Apply restrictions in the order as specified. Minor kluge so that we + * can delegate work to the generic routine. */ + state->recipient = mystrdup(recipient); for (cpp = rcpt_restrctions->argv; (name = *cpp) != 0; cpp++) { if (strchr(name, ':') != 0) { status = check_mail_access(state, name, recipient); @@ -1387,6 +1400,8 @@ char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient) if (status != 0) break; } + myfree(state->recipient); + state->recipient = saved_recipient; return (status == SMTPD_CHECK_REJECT ? STR(error_text) : 0); } diff --git a/postfix/util/hattr.c b/postfix/util/hattr.c new file mode 100644 index 000000000..70cd423cf --- /dev/null +++ b/postfix/util/hattr.c @@ -0,0 +1,89 @@ +/* mac_expand_update_va - update engine */ + +static MAC_EXP *mac_expand_update_va(MAC_EXP *mc, int key, va_list ap) +{ + HTABLE_INFO **ht_info; + HTABLE_INFO **ht; + HTABLE *table; + char *name; + char *value; + +#define HTABLE_CLOBBER(t, n, v) do { \ + HTABLE_INFO *_ht; \ + if ((_ht = htable_locate(t, n)) != 0) \ + _ht->value = v; \ + else \ + htable_enter(t, n, v); \ + } while(0); + + /* + * Optionally create expansion context. + */ + if (mc == 0) { + mc = (MAC_EXP *) mymalloc(sizeof(*mc)); + mc->table = htable_create(0); + mc->result = 0; + mc->flags = 0; + mc->filter = 0; + mc->clobber = '_'; + mc->level = 0; + } + + /* + * Stash away the attributes. + */ + for ( /* void */ ; key != 0; key = va_arg(ap, int)) { + switch (key) { + case MAC_EXP_ARG_ATTR: + name = va_arg(ap, char *); + value = va_arg(ap, char *); + HTABLE_CLOBBER(mc->table, name, value); + break; + case MAC_EXP_ARG_TABLE: + table = va_arg(ap, HTABLE *); + ht_info = htable_list(table); + for (ht = ht_info; *ht; ht++) + HTABLE_CLOBBER(mc->table, ht[0]->key, ht[0]->value); + myfree((char *) ht_info); + 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; + } + } + return (mc); +} + +/* mac_expand_update - update or create macro expansion context */ + +MAC_EXP *mac_expand_update(MAC_EXP *mc, int key,...) +{ + va_list ap; + + va_start(ap, key); + mc = mac_expand_update(mc, key, ap); + va_end(ap); + return (mc); +} + +/* .IP key +/* The attribute information is specified as a null-terminated list. +/* Attributes are defined left to right; only the last definition +/* of an attribute is remembered. +/* 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 string value for an attribute that is +/* known but unset. Attribute string values are not copied. +/* .IP "MAC_EXP_ARG_TABLE (HTABLE *)" +/* The next argument is a hash table with attribute names and values. +/* Specify a null string value for an attribute that is known but unset. +/* Attribute string values are not copied. +/* .RE +/* .IP MAC_EXP_ARG_END +/* A manifest constant that indicates the end of the argument list. diff --git a/postfix/util/htable.c b/postfix/util/htable.c index 299d0c614..29ad1236b 100644 --- a/postfix/util/htable.c +++ b/postfix/util/htable.c @@ -70,12 +70,12 @@ /* /* htable_delete() removes one entry that was stored under the given key. /* If the free_fn argument is not a null pointer, the corresponding -/* function is called with as argument the value that was stored under +/* function is called with as argument the non-zero value stored under /* the key. /* /* htable_free() destroys a hash table, including contents. If the free_fn /* argument is not a null pointer, the corresponding function is called -/* for each table entry, with as argument the value that was stored +/* for each table entry, with as argument the non-zero value stored /* with the entry. /* /* htable_walk() invokes the action function for each table entry, with @@ -261,7 +261,7 @@ void htable_delete(HTABLE *table, const char *key, void (*free_fn) (char *)) *h = ht->next; table->used--; myfree(ht->key); - if (free_fn) + if (free_fn && ht->value) (*free_fn) (ht->value); myfree((char *) ht); return; @@ -285,7 +285,7 @@ void htable_free(HTABLE *table, void (*free_fn) (char *)) for (ht = *h++; ht; ht = next) { next = ht->next; myfree(ht->key); - if (free_fn) + if (free_fn && ht->value) (*free_fn) (ht->value); myfree((char *) ht); } diff --git a/postfix/util/mac_expand.c b/postfix/util/mac_expand.c index 6ee96b07a..23f0756ab 100644 --- a/postfix/util/mac_expand.c +++ b/postfix/util/mac_expand.c @@ -6,54 +6,37 @@ /* SYNOPSIS /* #include /* -/* MAC_EXP *mac_exp_update(handle, key, ...) -/* MAC_EXP *handle; -/* int key; -/* -/* int mac_expand_use(handle, result, pattern, flags) -/* MAC_EXP *handle; -/* VSTRING *result; -/* const char *pattern; -/* int flags; -/* -/* void mac_expand_free(handle) -/* MAC_EXP *handle; -/* /* int mac_expand(result, pattern, flags, key, ...) /* VSTRING *result; /* const char *pattern; /* int flags; /* int key; /* DESCRIPTION -/* This module maintains a private attribute-value list and implements -/* the following expansions: +/* 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". +/* +/* The following expansions are implemented: /* .IP "$name, ${name}, $(name)" -/* The result is the value of the named attribute. Optionally, the -/* result is subjected to further expansions. +/* Unconditional expansion. If the named attribute is defined, the +/* expansion is the value of the named attribute, optionally subjected +/* to further $name expansions. Otherwise, the expansion is empty. /* .IP "${name?text}, $(name?text)" -/* If the named attribute is defined, the result is the given text, -/* after another iteration of $name expansion. Otherwise, the result is -/* empty. +/* Conditional expansion. If the named attribute is defined, the +/* expansion is the given text, subjected to another iteration of +/* $name expansion. Otherwise, the expansion is empty. /* .IP "${name:text}, $(name:text)" -/* If the named attribute is undefined, the result is the given text, -/* after another iteration of $name expansion. Otherwise, the result is -/* empty. +/* 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. /* .PP -/* max_expand_update() updates an existing macro expansion context -/* or instantiates a new one when given a null handle. The result -/* is a handle that can be used by other mac_expand_xxx() routines. -/* -/* mac_expand_use() uses a macro expansion context to replace $name etc. -/* instances in \fBpattern\fR and stores the result into \fBresult\fR. -/* -/* mac_expand_free() destroys a macro expansion context. -/* -/* mac_expand() is a convenience routine that combines all of the -/* above in one function call. +/* mac_expand() replaces $name etc. instances in \fBpattern\fR +/* and stores the result into \fBresult\fR. /* /* Arguments: -/* .IP mc -/* Macro expansion context created or update with mac_expand_update(). /* .IP result /* Storage for the result of expansion. The result is truncated /* upon entry. @@ -68,28 +51,24 @@ /* The constant MAC_EXP_FLAG_NONE specifies a manifest null value. /* .IP key /* The attribute information is specified as a null-terminated list. -/* Attributes are defined left to right; only the last definition -/* of an attribute is remembered. +/* 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 string value for an attribute that is -/* known but unset. Attribute string values are not copied. +/* 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 string value for an attribute that is known but unset. -/* Attribute string values are not copied. -/* .IP "MAC_EXP_ARG_FILTER (char *)" -/* The next argument specifies a null-terminated list of characters -/* that are allowed to appear in $name expansions. By default, illegal -/* characters are replaced by underscore. Only the last specified -/* filter takes effect. Specify a null pointer to disable filtering. -/* .IP "MAC_EXP_ARG_CLOBBER (int)" -/* Character value to be used when the result of expansion is not -/* allowed according to the MAC_EXP_ARG_FILTER argument. Only the -/* last specified replacement value takes effect. +/* 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. @@ -98,10 +77,11 @@ /* 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 +/* an unreasonable nesting depth. /* .IP MAC_EXP_FLAG_UNDEF -/* The pattern contains a reference to an unknown parameter or to -/* a parameter whose value is not defined. -/* A zero-length string was used as replacement. +/* The pattern contains a reference to an undefined attribute. /* SEE ALSO /* mac_parse(3) locate macro references in string. /* LICENSE @@ -118,7 +98,6 @@ /* System library. */ #include -#include #include #include @@ -132,16 +111,32 @@ #include /* - * Little helper structure. + * 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. */ +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 { - HTABLE *table; /* private symbol table */ VSTRING *result; /* result buffer */ const char *filter; /* safe character list */ int clobber; /* safe replacement */ int flags; /* findings, features */ int level; /* nesting level */ - jmp_buf jbuf; /* escape */ + 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_expand_callback - callback for mac_parse */ @@ -154,10 +149,15 @@ static void mac_expand_callback(int type, VSTRING *buf, char *ptr) char *text; char *cp; int ch; + int n; + /* + * Sanity check. + */ if (mc->level++ > 100) { msg_warn("unreasonable macro call nesting: \"%s\"", vstring_str(buf)); - longjmp(mc->jbuf, 1); + mc->flags |= MAC_EXP_FLAG_ERROR; + return; } /* @@ -177,15 +177,21 @@ static void mac_expand_callback(int type, VSTRING *buf, char *ptr) } if (!ISALNUM(ch) && ch != '_') { msg_warn("macro name syntax error: \"%s\"", vstring_str(buf)); - longjmp(mc->jbuf, 1); + mc->flags |= MAC_EXP_FLAG_ERROR; + return; } } /* * Look up the named parameter. */ - text = (ht = htable_locate(mc->table, vstring_str(buf))) == 0 ? - 0 : ht->value; + 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; + } + } /* * Perform the requested substitution. @@ -207,7 +213,7 @@ static void mac_expand_callback(int type, VSTRING *buf, char *ptr) if (mc->filter) { vstring_strcpy(buf, text); text = vstring_str(buf); - for (cp = text; (cp += strspn(cp, mc->filter))[0];) + for (cp = text; (cp += strspn(cp, mc->filter))[0]; /* void */ ) *cp++ = mc->clobber; } if (mc->flags & MAC_EXP_FLAG_RECURSE) @@ -216,6 +222,15 @@ static void mac_expand_callback(int type, VSTRING *buf, char *ptr) vstring_strcat(mc->result, text); 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++; + } } /* @@ -236,53 +251,68 @@ static void mac_expand_callback(int type, VSTRING *buf, char *ptr) mc->level--; } -/* mac_expand_update_va - update engine */ +/* mac_expand_addtable - add table to expansion context */ -static MAC_EXP *mac_expand_update_va(MAC_EXP *mc, int key, va_list ap) +static MAC_EXP *mac_expand_addtable(MAC_EXP *mc, HTABLE *table, int status) { - HTABLE_INFO **ht_info; - HTABLE_INFO **ht; - HTABLE *table; + 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); +} + +/* mac_expand - expand $name instances */ + +int mac_expand(VSTRING *result, const char *pattern, int flags, int key,...) +{ + MAC_EXP *mc; + va_list ap; char *name; char *value; - -#define HTABLE_CLOBBER(t, n, v) do { \ - HTABLE_INFO *_ht; \ - if ((_ht = htable_locate(t, n)) != 0) \ - _ht->value = v; \ - else \ - htable_enter(t, n, v); \ - } while(0); + HTABLE *table; + HTABLE_INFO *ht; + int status; /* - * Optionally create expansion context. + * Initialize. */ - if (mc == 0) { - mc = (MAC_EXP *) mymalloc(sizeof(*mc)); - mc->table = htable_create(0); - mc->result = 0; - mc->flags = 0; - mc->filter = 0; - mc->clobber = '_'; - mc->level = 0; - } + 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 ( /* void */ ; key != 0; key = va_arg(ap, int)) { + 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 *); - HTABLE_CLOBBER(mc->table, name, value); + 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 *); - ht_info = htable_list(table); - for (ht = ht_info; *ht; ht++) - HTABLE_CLOBBER(mc->table, ht[0]->key, ht[0]->value); - myfree((char *) ht_info); + mc = mac_expand_addtable(mc, table, MAC_EXP_STAT_EXTERN); break; case MAC_EXP_ARG_FILTER: mc->filter = va_arg(ap, char *); @@ -290,69 +320,31 @@ static MAC_EXP *mac_expand_update_va(MAC_EXP *mc, int key, va_list ap) case MAC_EXP_ARG_CLOBBER: mc->clobber = va_arg(ap, int); break; + case MAC_EXP_ARG_RECORD: + mc->record = va_arg(ap, HTABLE *); + break; } } - return (mc); -} - -/* mac_expand_update - update or create macro expansion context */ - -MAC_EXP *mac_expand_update(MAC_EXP *mc, int key,...) -{ - va_list ap; - - va_start(ap, key); - mc = mac_expand_update(mc, key, ap); - va_end(ap); - return (mc); -} - -/* mac_expand_use - string expansion */ - -int mac_expand_use(MAC_EXP *mc, VSTRING *result, const char *pattern, int flags) -{ - VSTRING_RESET(result); - mc->result = result; - mc->level = 0; - mc->flags = flags; - if (setjmp(mc->jbuf) == 0) - mac_parse(pattern, mac_expand_callback, (char *) mc); - VSTRING_TERMINATE(result); - return (mc->flags & MAC_EXP_FLAG_UNDEF); -} - -/* mac_expand_free - destroy macro expansion context */ - -void mac_expand_free(MAC_EXP *mc) -{ - htable_free(mc->table, (void (*) (char *)) 0); - myfree((char *) mc); -} - -/* mac_expand - expand $name instances */ - -int mac_expand(VSTRING *result, const char *pattern, int flags, int key,...) -{ - MAC_EXP *mc = 0; - va_list ap; - int status; - - /* - * Stash away the attributes. - */ - va_start(ap, key); - mc = mac_expand_update_va(mc, key, ap); va_end(ap); /* * Do the substitutions. */ - status = mac_expand_use(mc, result, pattern, flags); + VSTRING_RESET(result); + mac_parse(pattern, mac_expand_callback, (char *) mc); + VSTRING_TERMINATE(result); + status = (mc->flags & MAC_EXP_FLAG_OUTMASK); /* * Clean up. */ - mac_expand_free(mc); + 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); } @@ -365,12 +357,6 @@ int mac_expand(VSTRING *result, const char *pattern, int flags, int key,...) #include #include -static void nfree(char *ptr) -{ - if (ptr) - myfree(ptr); -} - int main(int unused_argc, char **unused_argv) { VSTRING *buf = vstring_alloc(100); @@ -411,7 +397,7 @@ int main(int unused_argc, char **unused_argv) vstream_printf("stat=%d result=%s\n", stat, vstring_str(result)); vstream_fflush(VSTREAM_OUT); } - htable_free(table, nfree); + htable_free(table, myfree); vstream_printf("\n"); } diff --git a/postfix/util/mac_expand.h b/postfix/util/mac_expand.h index 9a3cce818..cf61b686e 100644 --- a/postfix/util/mac_expand.h +++ b/postfix/util/mac_expand.h @@ -24,12 +24,17 @@ 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_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 extern MAC_EXP *mac_expand_update(MAC_EXP *, int,...); extern int mac_expand_use(MAC_EXP *, VSTRING *, const char *, int); diff --git a/postfix/util/mac_expand.ref b/postfix/util/mac_expand.ref index 7fb290928..0fb6cea98 100644 --- a/postfix/util/mac_expand.ref +++ b/postfix/util/mac_expand.ref @@ -4,7 +4,7 @@ stat=0 result= stat=1 result=name 2 undefined, |name1-value|| stat=1 result=|name1-value|| stat=0 result=name1-value -stat=0 result= +stat=4 result= stat=0 result= stat=1 result=name 1 undefined, ||name2-value|