From: Wietse Venema Date: Mon, 10 May 1999 05:00:00 +0000 (-0500) Subject: snapshot-19990510 X-Git-Tag: v20010228~106 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=82f1b7e36935c1913af2d0ac33b11eb6eff71839;p=thirdparty%2Fpostfix.git snapshot-19990510 --- diff --git a/postfix/HISTORY b/postfix/HISTORY index 005c7eacb..79eb92e26 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -2706,11 +2706,10 @@ Apologies for any names omitted. ${name:text} expands to text when name is undefined, otherwise the result is empty. File: util/mac_expand.c. - Feature: conditional macro expansion 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. + Feature: conditional macro expansion of the forward_path + configuration parameters of $user, $home, $shell, $recipient, + $extension, $domain, $mailbox and $recipient_delimiter. + Files: local/dotforward.c, local/local_expand.c. 19990506 @@ -2751,10 +2750,38 @@ Apologies for any names omitted. 19990509 - Feature: command_expansion_filter to control what characters - may appearin message attributes that are exported via - environment variables. + Feature: USER, EXTENSION and DOMAIN environment variables + are exported to shell commands (including mailbox_command). + + Feature: new command_expansion_filter parameter to control + what characters may appear in message attributes that are + exported via environment variables. Cleanup: SMTPD reject messages are more informative, and more complete sender/recipient information is logged for the local sysadmin. + +19990510 + + Bugfix: missing MIME header in postmaster bounce notices. + Found by Samuel Tardieu, Ecole Nationale Superieure des + Telecommunications, France. + + Feature: UCE restrictions are always delayed until RCPT + TO, VRFY or ETRN. To change back to the default specify + "smtpd_delay_reject = no" in /etc/postfix/main.cf. + + Bugfix: missing duplicate filter call. This caused too many + deliveries when a user is listed multiple times in an alias. + Reported by Hideyuki Suzuki, School of Engineering, University + of Tokyo. + + Feature: it is now possible to move queue files back into + the maildrop queue, so that they can benefit from changes + in canonical and virtual mappings. In order to make this + possible, some restrictions on queue file contents were + relaxed. Files: pickup/pickup.c, cleanup/cleanup_extracted.c. + + Feature: made a start with integrating Joerg Henne's + dictionary extensions to remove entries and to iterate over + entries. That code is almost four months old by now. diff --git a/postfix/RELEASE_NOTES b/postfix/RELEASE_NOTES index 00f110894..cbd9aa256 100644 --- a/postfix/RELEASE_NOTES +++ b/postfix/RELEASE_NOTES @@ -1,6 +1,10 @@ Incompatible changes with snapshot-19990509: =========================================== +- The SMTP server now always delays UCE restrictions until RCPT +TO, VRFY or ETRN command. To change back to the default specify +"smtpd_delay_reject = no" in /etc/postfix/main.cf. + - 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, diff --git a/postfix/bounce/bounce_notify_service.c b/postfix/bounce/bounce_notify_service.c index 47c9d4f3f..53ef22c7d 100644 --- a/postfix/bounce/bounce_notify_service.c +++ b/postfix/bounce/bounce_notify_service.c @@ -131,6 +131,15 @@ static int bounce_header(VSTREAM *bounce, VSTRING *buf, const char *dest, post_mail_fputs(bounce, ""); post_mail_fputs(bounce, "This is a MIME-encapsulated message."); post_mail_fputs(bounce, ""); + + /* + * More MIME header. + */ + post_mail_fprintf(bounce, "--%s", boundary); + post_mail_fprintf(bounce, "Content-Description: %s", "Notification"); + post_mail_fprintf(bounce, "Content-Type: %s", "text/plain"); + post_mail_fputs(bounce, ""); + return (vstream_ferror(bounce)); } @@ -140,14 +149,6 @@ static int bounce_boilerplate(VSTREAM *bounce, VSTRING *buf, const char *boundary, int flush) { - /* - * MIME header. - */ - post_mail_fprintf(bounce, "--%s", boundary); - post_mail_fprintf(bounce, "Content-Description: %s", "Notification"); - post_mail_fprintf(bounce, "Content-Type: %s", "text/plain"); - post_mail_fputs(bounce, ""); - /* * Print the message body with the problem report. XXX For now, we use a * fixed bounce template. We could use a site-specific parametrized diff --git a/postfix/cleanup/cleanup_extracted.c b/postfix/cleanup/cleanup_extracted.c index 783e93b59..3bd442b79 100644 --- a/postfix/cleanup/cleanup_extracted.c +++ b/postfix/cleanup/cleanup_extracted.c @@ -40,15 +40,20 @@ #include #include #include +#include +#include /* Application-specific. */ #include "cleanup.h" +#define STR(x) vstring_str(x) + /* cleanup_extracted - generate segment with header-extracted information */ void cleanup_extracted(void) { + VSTRING *clean_addr; ARGV *rcpt; char **cpp; int type; @@ -59,25 +64,38 @@ void cleanup_extracted(void) * * XXX Rely on the front-end programs to enforce record size limits. */ - if ((type = rec_get(cleanup_src, cleanup_inbuf, 0)) < 0) { - cleanup_errs |= CLEANUP_STAT_BAD; - return; - } - - /* - * Require that the client sends an empty record group. It is OUR job to - * extract information from message headers. Clients can specify options - * via special message header lines. - * - * XXX Keep reading in case of trouble, so that the sender is ready for our - * status report. - */ - if (type != REC_TYPE_END) { - msg_warn("unexpected record type %d in extracted segment", type); - cleanup_errs |= CLEANUP_STAT_BAD; - if (type >= 0) - cleanup_skip(); - return; + while (CLEANUP_OUT_OK()) { + if ((type = rec_get(cleanup_src, cleanup_inbuf, 0)) < 0) { + cleanup_errs |= CLEANUP_STAT_BAD; + return; + } + if (type == REC_TYPE_RRTO) { + /* XXX Use extracted information instead. */ ; + } else if (type == REC_TYPE_ERTO) { + /* XXX Use extracted information instead. */ ; + } else if (type == REC_TYPE_RCPT) { + clean_addr = vstring_alloc(100); + 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_ext_prop_mask & EXT_PROP_CANONICAL); + if (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)); + vstring_free(clean_addr); + } else if (type == REC_TYPE_END) { + break; + } else { + msg_warn("unexpected record type %d in extracted segment", type); + cleanup_errs |= CLEANUP_STAT_BAD; + if (type >= 0) + cleanup_skip(); + return; + } } /* diff --git a/postfix/conf/main.cf.default b/postfix/conf/main.cf.default index c0a62a28e..fed943f16 100644 --- a/postfix/conf/main.cf.default +++ b/postfix/conf/main.cf.default @@ -41,7 +41,6 @@ fallback_relay = fallback_transport = fork_attempts = 5 fork_delay = 1 -forward_expansion_filter = 1234567890!@%-_=+:,./abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ forward_path = $home/.forward${recipient_delimiter}${extension},$home/.forward hash_queue_depth = 2 hash_queue_names = defer @@ -63,7 +62,7 @@ luser_relay = mail_name = Postfix mail_owner = postfix mail_spool_directory = /var/mail -mail_version = Snapshot-19990509 +mail_version = Snapshot-19990510 mailbox_command = mailbox_transport = maps_rbl_domains = rbl.maps.vix.com @@ -113,6 +112,7 @@ smtp_skip_4xx_greeting = no smtp_skip_quit_response = yes smtpd_banner = $myhostname ESMTP $mail_name smtpd_client_restrictions = +smtpd_delay_reject = yes smtpd_error_sleep_time = 5 smtpd_etrn_restrictions = smtpd_hard_error_limit = 100 diff --git a/postfix/conf/transport b/postfix/conf/transport index 524c84d00..2a1333e0c 100644 --- a/postfix/conf/transport +++ b/postfix/conf/transport @@ -71,6 +71,14 @@ # This directs mail for \fIuser\fR@\fBfoo.org\fR to host \fBbar.org\fR # port \fB2025\fR. Instead of a numerical port a symbolic name may be # used. Specify [] around the destination in order to disable MX lookups. +# +# The error mailer can be used to bounce mail: +# +# .ti +5 +# \fB\&.foo.org error:mail for *.foo.org is not deliverable\fR +# +# This causes all mail for \fIuser\fR@\fIanything\fBfoo.org\fR +# to be bounced. # CONFIGURATION PARAMETERS # .ad # .fi diff --git a/postfix/error/error.c b/postfix/error/error.c index 4e3c09da2..3b43f519b 100644 --- a/postfix/error/error.c +++ b/postfix/error/error.c @@ -101,9 +101,7 @@ static int deliver_message(DELIVER_REQUEST *request) msg_info("deliver_message: from %s", request->sender); /* - * Sanity checks. The smtp server is unprivileged and chrooted, so we can - * afford to distribute the data censoring code, instead of having it all - * in one place. + * Sanity checks. */ if (request->nexthop[0] == 0) msg_fatal("empty nexthop hostname"); diff --git a/postfix/global/mail_params.h b/postfix/global/mail_params.h index a51fc0e17..7e9ebb493 100644 --- a/postfix/global/mail_params.h +++ b/postfix/global/mail_params.h @@ -342,12 +342,6 @@ 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; @@ -784,6 +778,10 @@ extern int var_maps_rbl_code; #define DEF_MAPS_RBL_DOMAINS "rbl.maps.vix.com" extern char *var_maps_rbl_domains; +#define VAR_SMTPD_DELAY_REJECT "smtpd_delay_reject" +#define DEF_SMTPD_DELAY_REJECT 1 +extern int var_smtpd_delay_reject; + /* * Other. */ diff --git a/postfix/global/mail_version.h b/postfix/global/mail_version.h index f52bb08cb..648c4f857 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-19990509" +#define DEF_MAIL_VERSION "Snapshot-19990510" extern char *var_mail_version; /* LICENSE diff --git a/postfix/html/local.8.html b/postfix/html/local.8.html index 99a318052..ae92f3a6c 100644 --- a/postfix/html/local.8.html +++ b/postfix/html/local.8.html @@ -44,11 +44,7 @@ LOCAL(8) LOCAL(8) address extension), $domain (recipient domain), mailbox (entire recipient address localpart) and $recipient_delim- iter. The forms ${name?value} and ${name:value} expand - conditionally to value when $name is (is not) defined. In - the result of name expansion, characters that have special - meaning to the shell are replaced by underscores. The list - of legal characters is specified with the forward_expan- - sion_filter configuration parameter. + 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: @@ -59,6 +55,10 @@ LOCAL(8) LOCAL(8) When an address is found in its own alias expansion, delivery is made to the user instead. When a user is listed in the user's own ~/.forward file, delivery is made + to the user's mailbox instead. An empty ~/.forward file + means do not forward mail. + + In order to prevent the mail system from using up @@ -71,11 +71,7 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) - to the user's mailbox instead. An empty ~/.forward file - means do not forward mail. - - In order to prevent the mail system from using up unrea- - sonable amounts of memory, input records read from + unreasonable amounts of memory, input records read from :include: or from ~/.forward files are broken up into chunks of length line_length_limit. @@ -125,6 +121,10 @@ LOCAL(8) LOCAL(8) 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 + 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 @@ -137,10 +137,6 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) - 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 is made to truncate the mailbox to its original length. @@ -191,6 +187,10 @@ LOCAL(8) LOCAL(8) dependent default path, and the TZ (time zone) environment variable is always passed on without change. + 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: @@ -203,10 +203,6 @@ LOCAL(8) LOCAL(8) 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 appends an empty line. @@ -256,7 +252,11 @@ LOCAL(8) LOCAL(8) superuser, delivery is made with the rights specified with the default_privs configuration parameter. +STANDARDS + RFC 822 (ARPA Internet Text Messages) +DIAGNOSTICS + Problems and transactions are logged to syslogd(8). @@ -269,13 +269,8 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) -STANDARDS - 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 - can move them to the corrupt queue afterwards. + Corrupted message files are marked so that the queue man- + ager 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- @@ -323,6 +318,11 @@ LOCAL(8) LOCAL(8) Mailbox delivery fallback_transport Message transport for recipients that are not found + in the UNIX passwd database. This parameter over- + rides luser_relay. + + home_mailbox + Pathname of a mailbox relative to a user's home @@ -335,11 +335,6 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) - in the UNIX passwd database. This parameter over- - rides luser_relay. - - home_mailbox - Pathname of a mailbox relative to a user's home directory. Specify a path ending in / for maildir- style delivery. @@ -390,6 +385,11 @@ LOCAL(8) LOCAL(8) Limit the amount of memory used for processing a partial input line. + local_destination_concurrency_limit + Limit the number of parallel deliveries to the same + user. The default limit is taken from the + default_destination_concurrency_limit parameter. + 6 @@ -401,11 +401,6 @@ LOCAL(8) LOCAL(8) LOCAL(8) LOCAL(8) - local_destination_concurrency_limit - Limit the number of parallel deliveries to the same - user. The default limit is taken from the - default_destination_concurrency_limit parameter. - local_destination_recipient_limit Limit the number of recipients per message deliv- ery. The default limit is taken from the @@ -429,11 +424,6 @@ LOCAL(8) LOCAL(8) Default rights for delivery to external file or command. - forward_expansion_filter - What characters are allowed to appear in $name - expansions of forward_path. Illegal characters are - replaced by underscores. - HISTORY The Delivered-To: header appears in the qmail system by Daniel Bernstein. @@ -455,18 +445,6 @@ LOCAL(8) LOCAL(8) AUTHOR(S) Wietse Venema IBM T.J. Watson Research - - - - 7 - - - - - -LOCAL(8) LOCAL(8) - - P.O. Box 704 Yorktown Heights, NY 10598, USA @@ -480,51 +458,7 @@ LOCAL(8) LOCAL(8) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 8 + 7 diff --git a/postfix/local/dotforward.c b/postfix/local/dotforward.c index b2250d9cc..a5ae42efe 100644 --- a/postfix/local/dotforward.c +++ b/postfix/local/dotforward.c @@ -198,8 +198,7 @@ int deliver_dotforward(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) lookup_status = -1; while ((lhs = mystrtok(&next, ", \t\r\n")) != 0) { - expand_status = local_expand(path, lhs, &state, - &usr_attr, var_fwd_exp_filter); + expand_status = local_expand(path, lhs, &state, &usr_attr, (char *) 0); if ((expand_status & (MAC_PARSE_ERROR | MAC_PARSE_UNDEF)) == 0) { lookup_status = lstat_as(STR(path), &st, usr_attr.uid, usr_attr.gid); diff --git a/postfix/local/local.c b/postfix/local/local.c index 90c61da71..9e529b346 100644 --- a/postfix/local/local.c +++ b/postfix/local/local.c @@ -40,10 +40,6 @@ /* \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 @@ -325,9 +321,6 @@ /* 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 @@ -416,7 +409,6 @@ 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; @@ -588,7 +580,6 @@ int main(int argc, char **argv) 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, }; diff --git a/postfix/local/recipient.c b/postfix/local/recipient.c index 252071f84..2b443006f 100644 --- a/postfix/local/recipient.c +++ b/postfix/local/recipient.c @@ -179,6 +179,12 @@ int deliver_recipient(LOCAL_STATE state, USER_ATTR usr_attr) if (msg_verbose) MSG_LOG_STATE(myname, state); + /* + * Duplicate filter. + */ + if (been_here(state.dup_filter, "recipient %s", state.msg_attr.recipient)) + return (0); + /* * With each level of recursion, detect and break external message * forwarding loops. diff --git a/postfix/man/man8/local.8 b/postfix/man/man8/local.8 index bb41b82b0..d87023edf 100644 --- a/postfix/man/man8/local.8 +++ b/postfix/man/man8/local.8 @@ -48,10 +48,6 @@ extension), \fB$domain\fR (recipient domain), \fBmailbox\fR \fB$recipient_delimiter.\fR The forms \fI${name?value}\fR and \fI${name:value}\fR expand conditionally to \fIvalue\fR when \fI$name\fR is (is not) defined. -In the result of \fIname\fR expansion, characters that have special -meaning to the shell are replaced by underscores. The list of legal -characters is specified with the \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 @@ -353,9 +349,6 @@ 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. .SH HISTORY .na .nf diff --git a/postfix/pickup/pickup.c b/postfix/pickup/pickup.c index 7359d4ce8..c7c6f5ff3 100644 --- a/postfix/pickup/pickup.c +++ b/postfix/pickup/pickup.c @@ -257,13 +257,13 @@ static int pickup_copy(VSTREAM *qfile, VSTREAM *cleanup, return (status); /* - * Send the segment with information extracted from message headers. At - * this stage of the mail processing pipeline, that segment should be - * empty, so we require that the it is. This record group ends with a - * type REC_TYPE_END record. + * Send the segment with information extracted from message headers. + * Permit a non-empty extracted segment, so that list manager software + * can to output recipients after the message, and so that sysadmins can + * re-inject messages after a change of configuration. */ rec_fputs(cleanup, REC_TYPE_XTRA, ""); - if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_NOEXTRACT)) != 0) + if ((status = copy_segment(qfile, cleanup, info, buf, REC_TYPE_EXTRACT)) != 0) return (status); /* diff --git a/postfix/smtpd/smtpd.c b/postfix/smtpd/smtpd.c index 44322368b..c5939921e 100644 --- a/postfix/smtpd/smtpd.c +++ b/postfix/smtpd/smtpd.c @@ -277,6 +277,7 @@ int var_smtpd_err_sleep; int var_non_fqdn_code; char *var_always_bcc; char *var_error_rcpt; +int var_smtpd_delay_reject; /* * Global state, for stand-alone mode queue file cleanup. When this is @@ -314,6 +315,12 @@ static int helo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) return (-1); } collapse_args(argc, argv); + if (SMTPD_STAND_ALONE(state) == 0 + && var_smtpd_delay_reject == 0 + && (err = smtpd_check_helo(state, argv[1].strval)) != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); + } state->helo_name = mystrdup(printable(argv[1].strval, '?')); state->protocol = "SMTP"; smtpd_chat_reply(state, "250 %s", var_myhostname); @@ -337,6 +344,12 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) return (-1); } collapse_args(argc, argv); + if (SMTPD_STAND_ALONE(state) == 0 + && var_smtpd_delay_reject == 0 + && (err = smtpd_check_helo(state, argv[1].strval)) != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); + } state->helo_name = mystrdup(printable(argv[1].strval, '?')); state->protocol = "ESMTP"; smtpd_chat_reply(state, "250-%s", var_myhostname); @@ -451,6 +464,12 @@ static int mail_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) } } state->time = time((time_t *) 0); + if (SMTPD_STAND_ALONE(state) == 0 + && var_smtpd_delay_reject == 0 + && (err = smtpd_check_mail(state, argv[3].strval)) != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); + } if ((err = smtpd_check_size(state, size)) != 0) { smtpd_chat_reply(state, "%s", err); return (-1); @@ -533,25 +552,10 @@ static int rcpt_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) smtpd_chat_reply(state, "452 Error: too many recipients"); return (-1); } - if (SMTPD_STAND_ALONE(state) == 0) { - if ((err = smtpd_check_client(state)) != 0) { - smtpd_chat_reply(state, "%s", err); - return (-1); - } - if (state->helo_name - && (err = smtpd_check_helo(state, state->helo_name)) != 0) { - smtpd_chat_reply(state, "%s", err); - return (-1); - } - if (state->sender - && (err = smtpd_check_mail(state, state->sender)) != 0) { - smtpd_chat_reply(state, "%s", err); - return (-1); - } - if ((err = smtpd_check_rcpt(state, argv[3].strval)) != 0) { - smtpd_chat_reply(state, "%s", err); - return (-1); - } + if (SMTPD_STAND_ALONE(state) == 0 + && (err = smtpd_check_rcpt(state, argv[3].strval)) != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); } /* @@ -795,23 +799,8 @@ static int vrfy_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) return (-1); } collapse_args(argc, argv); - if (SMTPD_STAND_ALONE(state) == 0) { - if ((err = smtpd_check_client(state)) != 0) { - smtpd_chat_reply(state, "%s", err); - return (-1); - } - if (state->helo_name - && (err = smtpd_check_helo(state, state->helo_name)) != 0) { - smtpd_chat_reply(state, "%s", err); - return (-1); - } - if (state->sender - && (err = smtpd_check_mail(state, state->sender)) != 0) { - smtpd_chat_reply(state, "%s", err); - return (-1); - } + if (SMTPD_STAND_ALONE(state) == 0) err = smtpd_check_rcpt(state, argv[1].strval); - } /* * End untokenize. @@ -862,20 +851,10 @@ static int etrn_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv) * rejected. RFC 1985 requires that 459 be sent when the server refuses * to perform the request. */ - if (SMTPD_STAND_ALONE(state) == 0) { - if ((err = smtpd_check_client(state)) != 0) { - smtpd_chat_reply(state, "%s", err); - return (-1); - } - if (state->helo_name - && (err = smtpd_check_helo(state, state->helo_name)) != 0) { - smtpd_chat_reply(state, "%s", err); - return (-1); - } - if ((err = smtpd_check_etrn(state, argv[1].strval)) != 0) { - smtpd_chat_reply(state, "%s", err); - return (-1); - } + if (SMTPD_STAND_ALONE(state) == 0 + && (err = smtpd_check_etrn(state, argv[1].strval)) != 0) { + smtpd_chat_reply(state, "%s", err); + return (-1); } /* @@ -1068,10 +1047,16 @@ static void smtpd_service(VSTREAM *stream, char *unused_service, char **argv) debug_peer_check(state.name, state.addr); /* - * Greet the client and log the connection event. + * See if we want to talk to this client at all. Then, log the connection + * event. */ - smtpd_chat_reply(&state, "220 %s", var_smtpd_banner); - msg_info("connect from %s[%s]", state.name, state.addr); + if (var_smtpd_delay_reject == 0 + && (state.access_denied = smtpd_check_client(&state)) != 0) { + smtpd_chat_reply(&state, "%s", state.access_denied); + } else { + smtpd_chat_reply(&state, "220 %s", var_smtpd_banner); + msg_info("connect from %s[%s]", state.name, state.addr); + } /* * Provide the SMTP service. @@ -1177,6 +1162,7 @@ int main(int argc, char **argv) }; static CONFIG_BOOL_TABLE bool_table[] = { VAR_HELO_REQUIRED, DEF_HELO_REQUIRED, &var_helo_required, + VAR_SMTPD_DELAY_REJECT, DEF_SMTPD_DELAY_REJECT, &var_smtpd_delay_reject, 0, }; static CONFIG_STR_TABLE str_table[] = { diff --git a/postfix/smtpd/smtpd_check.c b/postfix/smtpd/smtpd_check.c index 677fa8ee5..3bc3d11a4 100644 --- a/postfix/smtpd/smtpd_check.c +++ b/postfix/smtpd/smtpd_check.c @@ -1433,6 +1433,16 @@ char *smtpd_check_rcpt(SMTPD_STATE *state, char *recipient) char *name; int status; char *saved_recipient = state->recipient; + char *err; + + /* + * Apply delayed restrictions. + */ + if (var_smtpd_delay_reject) + if ((err = smtpd_check_client(state)) != 0 + || (err = smtpd_check_helo(state, state->helo_name)) != 0 + || (err = smtpd_check_mail(state, state->sender)) != 0) + return (err); /* * Initialize. @@ -1484,6 +1494,15 @@ char *smtpd_check_etrn(SMTPD_STATE *state, char *domain) char **cpp; char *name; int status; + char *err; + + /* + * Apply delayed restrictions. + */ + if (var_smtpd_delay_reject) + if ((err = smtpd_check_client(state)) != 0 + || (err = smtpd_check_helo(state, state->helo_name)) != 0) + return (err); /* * Initialize. @@ -1644,6 +1663,7 @@ int var_maps_rbl_code; int var_access_map_code; int var_reject_code; int var_non_fqdn_code; +int var_smtpd_delay_reject; static INT_TABLE int_table[] = { "msg_verbose", 0, &msg_verbose, @@ -1656,6 +1676,7 @@ static INT_TABLE int_table[] = { VAR_ACCESS_MAP_CODE, DEF_ACCESS_MAP_CODE, &var_access_map_code, VAR_REJECT_CODE, DEF_REJECT_CODE, &var_reject_code, VAR_NON_FQDN_CODE, DEF_NON_FQDN_CODE, &var_non_fqdn_code, + VAR_SMTPD_DELAY_REJECT, DEF_SMTPD_DELAY_REJECT, &var_smtpd_delay_reject, 0, }; diff --git a/postfix/smtpd/smtpd_check.in b/postfix/smtpd/smtpd_check.in index a2777351d..a395b94e6 100644 --- a/postfix/smtpd/smtpd_check.in +++ b/postfix/smtpd/smtpd_check.in @@ -3,6 +3,7 @@ # #! ../bin/postmap smtpd_check_access #msg_verbose 1 +smtpd_delay_reject 0 mynetworks 127.0.0.0/8,168.100.189.0/28 relay_domains porcupine.org # diff --git a/postfix/smtpd/smtpd_check.in2 b/postfix/smtpd/smtpd_check.in2 index 039a9cfea..ba539f00e 100644 --- a/postfix/smtpd/smtpd_check.in2 +++ b/postfix/smtpd/smtpd_check.in2 @@ -3,6 +3,7 @@ # #! ../bin/postmap smtpd_check_access #msg_verbose 1 +smtpd_delay_reject 0 mynetworks 127.0.0.0/8,168.100.189.0/28 relay_domains porcupine.org # diff --git a/postfix/smtpd/smtpd_check.ref b/postfix/smtpd/smtpd_check.ref index 7f26c23aa..c6c6eec83 100644 --- a/postfix/smtpd/smtpd_check.ref +++ b/postfix/smtpd/smtpd_check.ref @@ -3,6 +3,8 @@ >>> # >>> #! ../bin/postmap smtpd_check_access >>> #msg_verbose 1 +>>> smtpd_delay_reject 0 +OK >>> mynetworks 127.0.0.0/8,168.100.189.0/28 OK >>> relay_domains porcupine.org diff --git a/postfix/smtpd/smtpd_check.ref2 b/postfix/smtpd/smtpd_check.ref2 index 12b3b352a..a396d7329 100644 --- a/postfix/smtpd/smtpd_check.ref2 +++ b/postfix/smtpd/smtpd_check.ref2 @@ -3,6 +3,8 @@ >>> # >>> #! ../bin/postmap smtpd_check_access >>> #msg_verbose 1 +>>> smtpd_delay_reject 0 +OK >>> mynetworks 127.0.0.0/8,168.100.189.0/28 OK >>> relay_domains porcupine.org diff --git a/postfix/util/dict.c b/postfix/util/dict.c index 4a836ba42..292dab003 100644 --- a/postfix/util/dict.c +++ b/postfix/util/dict.c @@ -28,6 +28,16 @@ /* const char *dict_name; /* const char *member; /* +/* int dict_delete(dict_name, member) +/* const char *dict_name; +/* const char *member; +/* +/* int dict_sequence(dict_name, func, member, value) +/* const char *dict_name; +/* int func; +/* const char **member; +/* const char **value; +/* /* const char *dict_eval(dict_name, string, int recursive) /* const char *dict_name; /* const char *string; @@ -93,6 +103,17 @@ /* underlying dictionary method. Make a copy if the result is to be /* modified, or if the result is to survive multiple dict_lookup() calls. /* +/* dict_delete() removes the named member from the named dictionary. +/* The result is non-zero when the member does not exist. +/* +/* dict_sequence() steps throuh the named dictionary and returns +/* keys and values in some implementation-defined order. The func +/* argument is DICT_SEQ_FUN_FIRST to set the cursor to the first +/* entry or DICT_SEQ_FUN_NEXT so select the next entry. The result +/* is owned by the underlying dictionary method. Make a copy if the +/* result is to be modified, or if the result is to survive multiple +/* dict_sequence() calls. +/* /* dict_eval() expands macro references in the specified string. /* The result is owned by the dictionary manager. Make a copy if the /* result is to survive multiple dict_eval() calls. When the @@ -288,6 +309,51 @@ const char *dict_lookup(const char *dict_name, const char *member) return (ret); } +/* dict_delete - delete dictionary entry */ + +int dict_delete(const char *dict_name, const char *member) +{ + char *myname = "dict_delete"; + DICT_NODE *node; + DICT *dict; + int result; + + if ((node = dict_node(dict_name)) == 0) { + if (dict_unknown_allowed == 0) + msg_fatal("%s: unknown dictionary: %s", myname, dict_name); + dict = dict_ht_open(htable_create(0), myfree); + dict_register(dict_name, dict); + } else + dict = node->dict; + if (msg_verbose > 1) + msg_info("%s: delete %s", myname, member); + if ((result = dict->delete(dict, member)) != 0 && dict_unknown_allowed == 0) + msg_fatal("%s: dictionary %s: unknown member: %s", + myname, dict_name, member); + return (result); +} + +/* dict_sequence - traverse dictionary */ + +int dict_sequence(const char *dict_name, const int func, + const char **member, const char **value) +{ + char *myname = "dict_sequence"; + DICT_NODE *node; + DICT *dict; + + if ((node = dict_node(dict_name)) == 0) { + if (dict_unknown_allowed == 0) + msg_fatal("%s: unknown dictionary: %s", myname, dict_name); + dict = dict_ht_open(htable_create(0), myfree); + dict_register(dict_name, dict); + } else + dict = node->dict; + if (msg_verbose > 1) + msg_info("%s: sequence func %d", myname, func); + return (dict->sequence(dict, func, member, value)); +} + /* dict_load_file - read entries from text file */ void dict_load_file(const char *dict_name, const char *path) @@ -389,7 +455,7 @@ static int dict_eval_action(int type, VSTRING *buf, char *ptr) } else { vstring_strcat(ctxt->buf, STR(buf)); } - return(0); + return (0); } /* dict_eval - expand embedded dictionary references */ diff --git a/postfix/util/dict.h b/postfix/util/dict.h index 2dd4d5dc0..6dd6c9d71 100644 --- a/postfix/util/dict.h +++ b/postfix/util/dict.h @@ -20,6 +20,7 @@ * Utility library. */ #include +#include /* * Generic dictionary interface - in reality, a dictionary extends this @@ -29,6 +30,8 @@ typedef struct DICT { int flags; /* see below */ const char *(*lookup) (struct DICT *, const char *); void (*update) (struct DICT *, const char *, const char *); + int (*delete) (struct DICT *, const char *); + int (*sequence) (struct DICT *, int, const char **, const char **); void (*close) (struct DICT *); int fd; /* for dict_update() lock */ time_t mtime; /* mod time at open */ @@ -41,6 +44,7 @@ typedef struct DICT { #define DICT_FLAG_FIXED (1<<4) /* fixed key map */ #define DICT_FLAG_PATTERN (1<<5) /* keys are patterns */ #define DICT_FLAG_LOCK (1<<6) /* lock before access */ +#define DICT_FLAG_DUP_REPLACE (1<<7) /* if file, replace dups */ extern int dict_unknown_allowed; extern int dict_errno; @@ -48,27 +52,36 @@ extern int dict_errno; #define DICT_ERR_RETRY 1 /* soft error */ /* - * High-level interface, with logical dictionary names and with implied - * locking. + * Sequence function types. + */ +#define DICT_SEQ_FUN_FIRST 0 /* set cursor to first record */ +#define DICT_SEQ_FUN_NEXT 1 /* set cursor to next record */ + + /* + * High-level interface, with logical dictionary names. */ extern void dict_register(const char *, DICT *); extern DICT *dict_handle(const char *); extern void dict_unregister(const char *); extern void dict_update(const char *, const char *, const char *); extern const char *dict_lookup(const char *, const char *); +extern int dict_delete(const char *, const char *); +extern int dict_sequence(const char *, const int, const char **, const char **); extern void dict_load_file(const char *, const char *); extern void dict_load_fp(const char *, VSTREAM *); extern const char *dict_eval(const char *, const char *, int); /* - * Low-level interface, with physical dictionary handles and no implied - * locking. + * Low-level interface, with physical dictionary handles. */ extern DICT *dict_open(const char *, int, int); extern DICT *dict_open3(const char *, const char *, int, int); extern void dict_open_register(const char *, DICT *(*) (const char *, int, int)); + #define dict_get(dp, key) (dp)->lookup((dp), (key)) #define dict_put(dp, key, val) (dp)->update((dp), (key), (val)) +#define dict_del(dp, key) (dp)->delete((dp), (key)) +#define dict_seq(dp, f, key, val) (dp)->sequence((dp), (f), (key), (val)) #define dict_close(dp) (dp)->close(dp) typedef void (*DICT_WALK_ACTION) (const char *, DICT *, char *); extern void dict_walk(DICT_WALK_ACTION, char *); diff --git a/postfix/util/dict_db.c b/postfix/util/dict_db.c index 36e3bb969..60e86fe0b 100644 --- a/postfix/util/dict_db.c +++ b/postfix/util/dict_db.c @@ -188,7 +188,8 @@ static void dict_db_update(DICT *dict, const char *name, const char *value) /* * Do the update. */ - if ((status = db->put(db, &db_key, &db_value, R_NOOVERWRITE)) < 0) + if ((status = db->put(db, &db_key, &db_value, + (dict->flags & DICT_FLAG_DUP_REPLACE) ? 0 : R_NOOVERWRITE)) < 0) msg_fatal("error writing %s: %m", dict_db->path); if (status) { if (dict->flags & DICT_FLAG_DUP_IGNORE) @@ -206,6 +207,132 @@ static void dict_db_update(DICT *dict, const char *name, const char *value) msg_fatal("%s: unlock dictionary: %m", dict_db->path); } +/* delete one entry from the dictionary */ + +static int dict_db_delete(DICT *dict, const char *key) +{ + DICT_DB *dict_db = (DICT_DB *) dict; + DB *db = dict_db->db; + DBT db_key; + int status; + int flags = 0; + + db_key.data = (void *) key; + db_key.size = strlen(key); + + /* + * If undecided about appending a null byte to key and value, choose a + * default depending on the platform. + */ + if ((dict->flags & DICT_FLAG_TRY1NULL) + && (dict->flags & DICT_FLAG_TRY0NULL)) { +#ifdef DB_NO_TRAILING_NULL + dict->flags = DICT_FLAG_TRY0NULL; +#else + dict->flags = DICT_FLAG_TRY1NULL; +#endif + } + + /* + * Optionally append a null byte to key and value. + */ + if (dict->flags & DICT_FLAG_TRY1NULL) { + db_key.size++; + } + + /* + * Acquire an exclusive lock. + */ + if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_EXCLUSIVE) < 0) + msg_fatal("%s: lock dictionary: %m", dict_db->path); + + /* + * Do the delete operation. + */ + if ((status = db->del(db, &db_key, flags)) < 0) + msg_fatal("error deleting %s: %m", dict_db->path); + + /* + * Release the exclusive lock. + */ + if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_NONE) < 0) + msg_fatal("%s: unlock dictionary: %m", dict_db->path); + + return status; +} + +/* dict_db_sequence - traverse the dictionary */ + +static int dict_db_sequence(DICT *dict, const int function, + const char **key, const char **value) +{ + char *myname = "dict_db_sequence"; + DICT_DB *dict_db = (DICT_DB *) dict; + DB *db = dict_db->db; + DBT db_key; + DBT db_value; + int status = 0; + int db_function; + static VSTRING *key_buf; + static VSTRING *value_buf; + + /* + * determine the function + */ + switch (function) { + case DICT_SEQ_FUN_FIRST: + db_function = R_FIRST; + break; + case DICT_SEQ_FUN_NEXT: + db_function = R_NEXT; + break; + default: + msg_panic("%s: invalid function %d", myname, function); + } + + /* + * Acquire an exclusive lock. + */ + if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_EXCLUSIVE) < 0) + msg_fatal("%s: lock dictionary: %m", dict_db->path); + + if ((status = db->seq(db, &db_key, &db_value, db_function)) < 0) + msg_fatal("error seeking %s: %m", dict_db->path); + + /* + * Release the exclusive lock. + */ + if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_NONE) < 0) + msg_fatal("%s: unlock dictionary: %m", dict_db->path); + + if (status == 0) { + + /* + * See if this DB file was written with one null byte appended to key + * and value or not. + */ + if (((char *) db_key.data)[db_key.size] == 0) { + *key = db_key.data; + } else { + if (key_buf == 0) + key_buf = vstring_alloc(10); + vstring_strncpy(key_buf, db_key.data, db_key.size); + *key = vstring_str(key_buf); + } + if (((char *) db_value.data)[db_value.size] == 0) { + *value = db_value.data; + } else { + if (value_buf == 0) + value_buf = vstring_alloc(10); + vstring_strncpy(value_buf, db_value.data, db_value.size); + *value = vstring_str(value_buf); + } + } + return status; +} + + + /* dict_db_close - close data base */ static void dict_db_close(DICT *dict) @@ -235,6 +362,8 @@ static DICT *dict_db_open(const char *path, int flags, int type, dict_db = (DICT_DB *) mymalloc(sizeof(*dict_db)); dict_db->dict.lookup = dict_db_lookup; dict_db->dict.update = dict_db_update; + dict_db->dict.delete = dict_db_delete; + dict_db->dict.sequence = dict_db_sequence; dict_db->dict.close = dict_db_close; dict_db->dict.fd = db->fd(db); if (fstat(dict_db->dict.fd, &st) < 0) diff --git a/postfix/util/dict_dbm.c b/postfix/util/dict_dbm.c index eff168a8b..64d7d346b 100644 --- a/postfix/util/dict_dbm.c +++ b/postfix/util/dict_dbm.c @@ -163,7 +163,8 @@ static void dict_dbm_update(DICT *dict, const char *name, const char *value) /* * Do the update. */ - if ((status = dbm_store(dict_dbm->dbm, dbm_key, dbm_value, DBM_INSERT)) < 0) + if ((status = dbm_store(dict_dbm->dbm, dbm_key, dbm_value, + (dict->flags & DICT_FLAG_DUP_REPLACE) ? DBM_REPLACE : DBM_INSERT)) < 0) msg_fatal("error writing DBM database %s: %m", dict_dbm->path); if (status) { if (dict->flags & DICT_FLAG_DUP_IGNORE) @@ -181,6 +182,155 @@ static void dict_dbm_update(DICT *dict, const char *name, const char *value) msg_fatal("%s: unlock dictionary: %m", dict_dbm->path); } +/* dict_dbm_delete - delete one entry from the dictionary */ + +static int dict_dbm_delete(DICT *dict, const char *key) +{ + DICT_DBM *dict_dbm = (DICT_DBM *) dict; + datum dbm_key; + int status; + int flags = 0; + + dbm_key.dptr = (void *) key; + dbm_key.dsize = strlen(key); + + /* + * If undecided about appending a null byte to key and value, choose a + * default depending on the platform. + */ + if ((dict->flags & DICT_FLAG_TRY1NULL) + && (dict->flags & DICT_FLAG_TRY0NULL)) { +#ifdef DBM_NO_TRAILING_NULL + dict->flags = DICT_FLAG_TRY0NULL; +#else + dict->flags = DICT_FLAG_TRY1NULL; +#endif + } + + /* + * Optionally append a null byte to key and value. + */ + if (dict->flags & DICT_FLAG_TRY1NULL) { + dbm_key.dsize++; + } + + /* + * Acquire an exclusive lock. + */ + if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_EXCLUSIVE) < 0) + msg_fatal("%s: lock dictionary: %m", dict_dbm->path); + + /* + * Do the delete operation. + */ + if ((status = dbm_delete(dict_dbm->dbm, dbm_key)) < 0) + msg_fatal("error deleting %s: %m", dict_dbm->path); + + /* + * Release the exclusive lock. + */ + if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_NONE) < 0) + msg_fatal("%s: unlock dictionary: %m", dict_dbm->path); + + return (status); +} + +/* traverse the dictionary */ + +static int dict_dbm_sequence(DICT *dict, const int function, + const char **key, const char **value) +{ + char *myname = "dict_dbm_sequence"; + DICT_DBM *dict_dbm = (DICT_DBM *) dict; + datum dbm_key; + datum dbm_value; + int status = 0; + static VSTRING *key_buf; + static VSTRING *value_buf; + + /* + * Acquire an exclusive lock. + */ + if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_EXCLUSIVE) < 0) + msg_fatal("%s: lock dictionary: %m", dict_dbm->path); + + /* + * Determine and execute the seek function. It returns the key. + */ + switch (function) { + case DICT_SEQ_FUN_FIRST: + dbm_key = dbm_firstkey(dict_dbm->dbm); + break; + case DICT_SEQ_FUN_NEXT: + dbm_key = dbm_nextkey(dict_dbm->dbm); + break; + default: + msg_panic("%s: invalid function: %d", myname, function); + } + + /* + * Release the exclusive lock. + */ + if ((dict->flags & DICT_FLAG_LOCK) && myflock(dict->fd, MYFLOCK_NONE) < 0) + msg_fatal("%s: unlock dictionary: %m", dict_dbm->path); + + if (dbm_key.dptr != 0 && dbm_key.dsize > 0) { + + /* + * See if this DB file was written with one null byte appended to key + * an d value or not. If necessary, copy the key. + */ + if (((char *) dbm_key.dptr)[dbm_key.dsize - 1] == 0) { + *key = dbm_key.dptr; + } else { + if (key_buf == 0) + key_buf = vstring_alloc(10); + vstring_strncpy(key_buf, dbm_key.dptr, dbm_key.dsize); + *key = vstring_str(key_buf); + } + + /* + * Fetch the corresponding value. + */ + dbm_value = dbm_fetch(dict_dbm->dbm, dbm_key); + + if (dbm_value.dptr != 0 && dbm_value.dsize > 0) { + + /* + * See if this DB file was written with one null byte appended to + * key and value or not. If necessary, copy the key. + */ + if (((char *) dbm_value.dptr)[dbm_value.dsize - 1] == 0) { + *value = dbm_value.dptr; + } else { + if (value_buf == 0) + value_buf = vstring_alloc(10); + vstring_strncpy(value_buf, dbm_value.dptr, dbm_value.dsize); + *value = vstring_str(value_buf); + } + } else { + + /* + * Determine if we have hit the last record or an error + * condition. + */ + if (dbm_error(dict_dbm->dbm)) + msg_fatal("error seeking %s: %m", dict_dbm->path); + return (1); /* no error: eof/not found + * (should not happen!) */ + } + } else { + + /* + * Determine if we have hit the last record or an error condition. + */ + if (dbm_error(dict_dbm->dbm)) + msg_fatal("error seeking %s: %m", dict_dbm->path); + return (1); /* no error: eof/not found */ + } + return (0); +} + /* dict_dbm_close - disassociate from data base */ static void dict_dbm_close(DICT *dict) @@ -209,6 +359,8 @@ DICT *dict_dbm_open(const char *path, int open_flags, int dict_flags) dict_dbm = (DICT_DBM *) mymalloc(sizeof(*dict_dbm)); dict_dbm->dict.lookup = dict_dbm_lookup; dict_dbm->dict.update = dict_dbm_update; + dict_dbm->dict.delete = dict_dbm_delete; + dict_dbm->dict.sequence = dict_dbm_sequence; dict_dbm->dict.close = dict_dbm_close; dict_dbm->dict.fd = dbm_pagfno(dbm); if (fstat(dict_dbm->dict.fd, &st) < 0) diff --git a/postfix/util/dict_open.c b/postfix/util/dict_open.c index e5912b6ca..87f00c59b 100644 --- a/postfix/util/dict_open.c +++ b/postfix/util/dict_open.c @@ -26,6 +26,16 @@ /* DICT *dict; /* const char *key; /* +/* char *dict_del(dict, key) +/* DICT *dict; +/* const char *key; +/* +/* void dict_seq(dict, func, key, value) +/* DICT *dict; +/* int func; +/* const char **key; +/* const char **value; +/* /* void dict_close(dict) /* DICT *dict; /* @@ -48,6 +58,9 @@ /* Ignore duplicate keys if the underlying database does not /* support duplicate keys. The default is to terminate with a fatal /* error. +/* .IP DICT_FLAG_DUP_REPLACE +/* Replace duplicate keys if the underlying database supports such +/* an operation. The default is to terminate with a fatal error. /* .IP DICT_FLAG_TRY0NULL /* With maps where this is appropriate, append no null byte to /* keys and values. @@ -97,6 +110,14 @@ /* dict_put() stores the specified key and value into the named /* dictionary. /* +/* dict_del() removes a dictionary entry, and returns non-zero +/* in case of problems. +/* +/* dict_seq() iterates over all members in the named dictionary. +/* func is define DICT_SEQ_FUN_FIRST (select first member) or +/* DICT_SEQ_FUN_NEXT (select next member). A null result means +/* there is more. +/* /* dict_close() closes the specified dictionary and cleans up the /* associated data structures. /* diff --git a/postfix/util/mac_expand.c b/postfix/util/mac_expand.c index 23c239b05..9dc0dd84e 100644 --- a/postfix/util/mac_expand.c +++ b/postfix/util/mac_expand.c @@ -16,11 +16,10 @@ /* 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 considered "undefined" when its -/* value is a null pointer. In all other cases the attribute is -/* considered "defined". +/* In this text, an attribute is considered "undefined" when its value +/* is a null pointer. Otherwise, the attribute is considered "defined" +/* and is expected to have as value a null-terminated string. /* /* The following expansions are implemented: /* .IP "$name, ${name}, $(name)" @@ -47,8 +46,9 @@ /* .RS /* .IP MAC_EXP_FLAG_RECURSE /* Expand $name recursively. -/* .RE +/* .PP /* The constant MAC_EXP_FLAG_NONE specifies a manifest null value. +/* .RE /* .IP filter /* A null pointer, or a null-terminated array of characters that /* are allowed to appear in an expansion. Illegal characters are @@ -134,14 +134,13 @@ static int mac_expand_callback(int type, VSTRING *buf, char *ptr) return (mc->status); /* - * $Name reference. + * $Name etc. reference. */ if (type == MAC_PARSE_VARNAME) { /* * Look for the ? or : delimiter. In case of a syntax error, return - * without doing damage. We do not have enough context to produce an - * understandable error message, so don't try. + * without doing damage, and issue a warning instead. */ for (cp = vstring_str(buf); /* void */ ; cp++) { if ((ch = *cp) == 0) {