]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
snapshot-19990510
authorWietse Venema <wietse@porcupine.org>
Mon, 10 May 1999 05:00:00 +0000 (00:00 -0500)
committerWietse Venema <wietse@porcupine.org>
Thu, 17 Jan 2013 03:34:25 +0000 (22:34 -0500)
27 files changed:
postfix/HISTORY
postfix/RELEASE_NOTES
postfix/bounce/bounce_notify_service.c
postfix/cleanup/cleanup_extracted.c
postfix/conf/main.cf.default
postfix/conf/transport
postfix/error/error.c
postfix/global/mail_params.h
postfix/global/mail_version.h
postfix/html/local.8.html
postfix/local/dotforward.c
postfix/local/local.c
postfix/local/recipient.c
postfix/man/man8/local.8
postfix/pickup/pickup.c
postfix/smtpd/smtpd.c
postfix/smtpd/smtpd_check.c
postfix/smtpd/smtpd_check.in
postfix/smtpd/smtpd_check.in2
postfix/smtpd/smtpd_check.ref
postfix/smtpd/smtpd_check.ref2
postfix/util/dict.c
postfix/util/dict.h
postfix/util/dict_db.c
postfix/util/dict_dbm.c
postfix/util/dict_open.c
postfix/util/mac_expand.c

index 005c7eacb032a5b7708b7a625361822c80774d25..79eb92e260fbb49c16b6eb5cd4001858ac06a644 100644 (file)
@@ -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.
index 00f11089406e92fd4f2e8c5260646afea5489936..cbd9aa25688f07dc7b80d88af8178c8f38d43556 100644 (file)
@@ -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,
index 47c9d4f3f2a6a8fe147ea70f4b56f4734a505690..53ef22c7d5925f3bb27552ef70147d3a4af2332c 100644 (file)
@@ -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
index 783e93b597d72d28cd8266d579f6f7e208207937..3bd442b79bdfe95c986ef0868a4489a5d4b23e91 100644 (file)
 #include <cleanup_user.h>
 #include <record.h>
 #include <rec_type.h>
+#include <mail_params.h>
+#include <ext_prop.h>
 
 /* 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;
+       }
     }
 
     /*
index c0a62a28ef43de6ebe06d50494a4c89b67b811c5..fed943f162398ea455e5b502208de324225a3776 100644 (file)
@@ -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
index 524c84d003357db5bbf657dea250789b6c2f54f2..2a1333e0c2c6d4f6efc6277189b415f06a8d174f 100644 (file)
 #      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
index 4e3c09da2f4e78c3dbc1361e22b3460c5a9cf227..3b43f519b05a7148f2ee3efb1384f0a54a2910eb 100644 (file)
@@ -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");
index a51fc0e1731e2aa231c320e2faa366a5f7dbf350..7e9ebb4930a4ae05d08dc7c04449b26aa88e7ca5 100644 (file)
@@ -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.
   */
index f52bb08cb522f95891f6c592738ed56d84cab39b..648c4f85759343a1718a2220cbfbb4f8f46d1776 100644 (file)
@@ -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
index 99a318052bda16e67cf0c082acdc90d53f073738..ae92f3a6c958f3025f8f6180d2a5f5c7817d2939 100644 (file)
@@ -44,11 +44,7 @@ LOCAL(8)                                                 LOCAL(8)
        address  extension),  <b>$domain</b>  (recipient domain), <b>mailbox</b>
        (entire recipient address localpart) and <b>$recipient</b><i>_</i><b>delim-</b>
        <b>iter.</b>  The  forms  <i>${name?value}</i>  and <i>${name:value}</i> expand
-       conditionally to <i>value</i> when <i>$name</i> is (is not) defined.  In
-       the result of <i>name</i> expansion, characters that have special
-       meaning to the shell are replaced by underscores. The list
-       of  legal  characters is specified with the <b>forward</b><i>_</i><b>expan-</b>
-       <b>sion</b><i>_</i><b>filter</b> configuration parameter.
+       conditionally to <i>value</i> when <i>$name</i> is (is not) defined.
 
        An alias or ~/.<b>forward</b> file may list  any  combination  of
        external   commands,  destination  file  names,  <b>:include:</b>
@@ -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 ~/.<b>forward</b> file, delivery is made
+       to  the  user's mailbox instead.  An empty ~/.<b>forward</b> file
+       means do not forward mail.
+
+       In  order  to  prevent  the  mail  system  from  using  up
 
 
 
@@ -71,11 +71,7 @@ LOCAL(8)                                                 LOCAL(8)
 LOCAL(8)                                                 LOCAL(8)
 
 
-       to  the  user's mailbox instead.  An empty ~/.<b>forward</b> file
-       means do not forward mail.
-
-       In order to prevent the mail system from using  up  unrea-
-       sonable   amounts  of  memory,  input  records  read  from
+       unreasonable  amounts  of  memory, input records read from
        <b>:include:</b> or from ~/.<b>forward</b>  files  are  broken  up  into
        chunks of length <b>line</b><i>_</i><b>length</b><i>_</i><b>limit</b>.
 
@@ -125,6 +121,10 @@ LOCAL(8)                                                 LOCAL(8)
        In the case of UNIX-style mailbox delivery, the <b>local</b> dae-
        mon prepends a "<b>From</b> <i>sender</i> <i>time_stamp</i>" envelope header to
        each  message,  prepends  a  <b>Delivered-To:</b> header with the
+       envelope recipient address, prepends a <b>Return-Path:</b> header
+       with  the  envelope sender address, prepends a &gt; character
+       to lines beginning with "<b>From</b>  ",  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 <b>Return-Path:</b> header
-       with  the  envelope sender address, prepends a &gt; character
-       to lines beginning with "<b>From</b>  ",  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 <b>TZ</b> (time zone) environment
        variable is always passed on without change.
 
+       The current working directory is the mail queue directory.
+
+       The <b>local</b> daemon prepends a "<b>From</b> <i>sender</i> <i>time_stamp</i>" enve-
+       lope  header  to  each  message,  prepends a <b>Delivered-To:</b>
 
 
 
@@ -203,10 +203,6 @@ LOCAL(8)                                                 LOCAL(8)
 LOCAL(8)                                                 LOCAL(8)
 
 
-       The current working directory is the mail queue directory.
-
-       The <b>local</b> daemon prepends a "<b>From</b> <i>sender</i> <i>time_stamp</i>" enve-
-       lope  header  to  each  message,  prepends a <b>Delivered-To:</b>
        header with the recipient  envelope  address,  prepends  a
        <b>Return-Path:</b>  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 <b>default</b><i>_</i><b>privs</b> configuration parameter.
 
+<b>STANDARDS</b>
+       RFC 822 (ARPA Internet Text Messages)
 
+<b>DIAGNOSTICS</b>
+       Problems  and  transactions  are  logged  to   <b>syslogd</b>(8).
 
 
 
@@ -269,13 +269,8 @@ LOCAL(8)                                                 LOCAL(8)
 LOCAL(8)                                                 LOCAL(8)
 
 
-<b>STANDARDS</b>
-       RFC 822 (ARPA Internet Text Messages)
-
-<b>DIAGNOSTICS</b>
-       Problems and transactions are logged to <b>syslogd</b>(8).   Cor-
-       rupted  message files are marked so that the queue manager
-       can move them to the <b>corrupt</b> queue afterwards.
+       Corrupted  message files are marked so that the queue man-
+       ager can move them to the <b>corrupt</b> queue afterwards.
 
        Depending on the setting of the <b>notify</b><i>_</i><b>classes</b>  parameter,
        the  postmaster  is notified of bounces and of other trou-
@@ -323,6 +318,11 @@ LOCAL(8)                                                 LOCAL(8)
 <b>Mailbox</b> <b>delivery</b>
        <b>fallback</b><i>_</i><b>transport</b>
               Message transport for recipients that are not found
+              in the UNIX passwd database.  This parameter  over-
+              rides <b>luser</b><i>_</i><b>relay</b>.
+
+       <b>home</b><i>_</i><b>mailbox</b>
+              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 <b>luser</b><i>_</i><b>relay</b>.
-
-       <b>home</b><i>_</i><b>mailbox</b>
-              Pathname  of  a  mailbox  relative to a user's home
               directory.  Specify a path ending in <b>/</b> for maildir-
               style delivery.
 
@@ -390,6 +385,11 @@ LOCAL(8)                                                 LOCAL(8)
               Limit the amount of memory used  for  processing  a
               partial input line.
 
+       <b>local</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
+              Limit the number of parallel deliveries to the same
+              user.   The  default  limit  is  taken   from   the
+              <b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter.
+
 
 
                                                                 6
@@ -401,11 +401,6 @@ LOCAL(8)                                                 LOCAL(8)
 LOCAL(8)                                                 LOCAL(8)
 
 
-       <b>local</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b>
-              Limit the number of parallel deliveries to the same
-              user.   The  default  limit  is  taken   from   the
-              <b>default</b><i>_</i><b>destination</b><i>_</i><b>concurrency</b><i>_</i><b>limit</b> parameter.
-
        <b>local</b><i>_</i><b>destination</b><i>_</i><b>recipient</b><i>_</i><b>limit</b>
               Limit  the  number of recipients per message deliv-
               ery.   The  default  limit  is   taken   from   the
@@ -429,11 +424,6 @@ LOCAL(8)                                                 LOCAL(8)
               Default  rights  for  delivery  to external file or
               command.
 
-       <b>forward</b><i>_</i><b>expansion</b><i>_</i><b>filter</b>
-              What characters are  allowed  to  appear  in  $name
-              expansions  of forward_path. Illegal characters are
-              replaced by underscores.
-
 <b>HISTORY</b>
        The <b>Delivered-To:</b> header appears in the  <b>qmail</b>  system  by
        Daniel Bernstein.
@@ -455,18 +445,6 @@ LOCAL(8)                                                 LOCAL(8)
 <b>AUTHOR(S)</b>
        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
 
 
 </pre> </body> </html>
index b2250d9cca60c3172bd65581af68dd93456cb34e..a5ae42efe669437762af15e23856b1e74801553c 100644 (file)
@@ -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);
index 90c61da71b0d8492d1f0d0aac5f47dda501a0756..9e529b346681ce35c2dc0c33e73a5e78460dc5e0 100644 (file)
 /*     \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
 /*     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,
     };
index 252071f8461a2b9dcbc0ca030edc1b5bc0a62cf3..2b443006fe8de740bc0611c6141bb13f7806ba69 100644 (file)
@@ -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.
index bb41b82b048d1c8e4e983d3e9454d5bf77bdd130..d87023edf50ffaab91510da602416b41e9746369 100644 (file)
@@ -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
index 7359d4ce8fcd7ce1566a2ee4699df4359b692cf9..c7c6f5ff3f11aac9ad0a43343cb03aa28ab1b28b 100644 (file)
@@ -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);
 
     /*
index 44322368bb43fcfae2ff3fa126df05744ef3dba0..c5939921eae157bfa7475643462d79fad159b92e 100644 (file)
@@ -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[] = {
index 677fa8ee55bc53b511fc3c64fefd74effe271209..3bc3d11a4ad4a3b67a33b3976c63c76f112ab12e 100644 (file)
@@ -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,
 };
 
index a2777351d55e1bf916f6f7fc7a65908fa029332d..a395b94e64f8cebd97fe3f15caef1128fc3e01a6 100644 (file)
@@ -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
 #
index 039a9cfea413bc562b0de8a54038c617db3f9423..ba539f00ee0c1700a6ebf32470db972be691423c 100644 (file)
@@ -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
 #
index 7f26c23aad32ac2645b40f1ced90cb6e37e80cf6..c6c6eec83a8005eb5120f6f06a92774ac2e67788 100644 (file)
@@ -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
index 12b3b352a0cc62204453aa3e2c10d3c2114a625b..a396d73293347c1461c1c91d39606f8669252060 100644 (file)
@@ -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
index 4a836ba42806dd33a4a03d02ad1692237eb14086..292dab003211f60b2f9a3d74ecba8c1ef89796e2 100644 (file)
 /*     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;
 /*     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 */
index 2dd4d5dc0da6c14d07ff3f66d7cd7035eff8bee2..6dd6c9d71e70aa7e94006371dbab3807aba5e4de 100644 (file)
@@ -20,6 +20,7 @@
   * Utility library.
   */
 #include <vstream.h>
+#include <vstring.h>
 
  /*
   * 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 *);
index 36e3bb96909ac8eb47b51274958eb865ea3a490d..60e86fe0b9661da3540db5c68179d22a3405e6a3 100644 (file)
@@ -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)
index eff168a8bb36dce28c656e0ee2c2a2b8c13e8ba3..64d7d346b29e7873160acb19acfa184b0c851c54 100644 (file)
@@ -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)
index e5912b6ca5594bae63be646d94afd924a089efc5..87f00c59ba6123e25359443df8551ffbc53423b6 100644 (file)
 /*     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.
 /*     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.
 /*
index 23c239b05606245c6ade833d5273a1b4b0cfae66..9dc0dd84ec7f0d1b7c2e61a88d40b2c0a02512ba 100644 (file)
 /* 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) {