]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.3-RC3 v2.3-RC3
authorWietse Venema <wietse@porcupine.org>
Sun, 2 Jul 2006 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <postfix-users@dukhovni.org>
Sat, 10 Feb 2018 20:36:36 +0000 (15:36 -0500)
postfix/HISTORY
postfix/RELEASE_NOTES
postfix/src/cleanup/cleanup_milter.c
postfix/src/global/mail_version.h
postfix/src/milter/milter.c
postfix/src/milter/milter.h
postfix/src/milter/milter8.c
postfix/src/milter/test-milter.c

index 2b522c4769e305ff163c0105137f394f645d2572..e4a68d886aa30f7a683d48289423bd6a16a099dc 100644 (file)
@@ -12430,6 +12430,12 @@ Apologies for any names omitted.
        header and the rest of the message, violating the draft
        domainkeys spec.
 
+20070602
+
+       Cleanup: more graceful handling of queue file read/write
+       errors while processing milter message modification requests.
+       Files: cleanup/cleanup_milter.c, milter/milter8.c.
+
 Wish list:
 
        In the SMTPD policy client (encode or strip) non-printable
index 28d69517a6315a3d312f14dbdea8838ae0355ec8..d4f5d7e859f714132df095a708c4610aebf201ec 100644 (file)
@@ -26,11 +26,12 @@ only anonymous ciphers, and will not interoperate with most clients.
 
 The SMTP server supports anonymous ciphers when client certificates
 are not requested or required, and the administrator has not excluded
-the "aNULL" OpenSSL cipher type.
+the "aNULL" OpenSSL cipher type with smtpd_tls_exclude_ciphers.
 
 The SMTP client supports anonymous ciphers when no server certificate
 is required (notably Postfix 2.3 in "opportunistic" mode) and the
-administrator has not excluded the "aNULL" OpenSSL cipher type.
+administrator has not excluded the "aNULL" OpenSSL cipher type with
+smtp_tls_exclude_ciphers.
 
 Instead of cipher lists you can now specify cipher grades.  The
 smtp_tls_mandatory_ciphers, lmtp_tls_mandatory_ciphers and
index 656f5a5316f5eb6fdf2de71a51755cf1f4b3d6e4..4c08799b40789836b01f981841257ecec723fb4c 100644 (file)
@@ -65,6 +65,7 @@
 /* DIAGNOSTICS
 /*     Fatal errors: memory allocation problem.
 /*     Panic: interface violation.
+/*     state->errs is updated in case of I/O errors.
 /* LICENSE
 /* .ad
 /* .fi
@@ -81,6 +82,7 @@
 #include <sys_defs.h>
 #include <sys/socket.h>                        /* AF_INET */
 #include <string.h>
+#include <errno.h>
 
 #ifdef STRCASECMP_IN_STRINGS_H
 #include <strings.h>
 #define STR(x)         vstring_str(x)
 #define LEN(x)         VSTRING_LEN(x)
 
+/* cleanup_milter_set_error - set error flag from errno */
+
+static void cleanup_milter_set_error(CLEANUP_STATE *state, int err)
+{
+    if (err == EFBIG)
+       state->errs |= CLEANUP_STAT_SIZE;
+    else
+       state->errs |= CLEANUP_STAT_WRITE;
+}
+
+/* cleanup_milter_error - return dummy error description */
+
+static const char *cleanup_milter_error(CLEANUP_STATE *state, int err)
+{
+
+    /*
+     * This error text will be ignored by cleanup_milter_apply(). It exists
+     * only to maintain a consistent error reporting interface to the milter
+     * infrastructure.
+     */
+    if (err)
+       cleanup_milter_set_error(state, err);
+    return ("451 4.3.0 Server internal error");
+}
+
 /* cleanup_add_header - append message header */
 
-static void cleanup_add_header(void *context, char *name, char *value)
+static const char *cleanup_add_header(void *context, char *name, char *value)
 {
     const char *myname = "cleanup_add_header";
     CLEANUP_STATE *state = (CLEANUP_STATE *) context;
@@ -237,14 +264,18 @@ static void cleanup_add_header(void *context, char *name, char *value)
      * target of the old "header append" pointer record. This reverse pointer
      * record becomes the new "header append" pointer record.
      */
-    if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0)
-       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) {
+       msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+       return (cleanup_milter_error(state, errno));
+    }
     buf = vstring_alloc(100);
     vstring_sprintf(buf, "%s: %s", name, value);
     cleanup_out_header(state, buf);
     vstring_free(buf);
-    if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0)
-       msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+    if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) {
+       msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+       return (cleanup_milter_error(state, errno));
+    }
     cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
                       (long) state->append_hdr_pt_target);
 
@@ -252,8 +283,10 @@ static void cleanup_add_header(void *context, char *name, char *value)
      * Pointer flipping: update the old "header append" pointer record value
      * with the location of the new header record.
      */
-    if (vstream_fseek(state->dst, state->append_hdr_pt_offset, SEEK_SET) < 0)
-       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    if (vstream_fseek(state->dst, state->append_hdr_pt_offset, SEEK_SET) < 0) {
+       msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+       return (cleanup_milter_error(state, errno));
+    }
     cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
                       (long) new_hdr_offset);
 
@@ -265,6 +298,11 @@ static void cleanup_add_header(void *context, char *name, char *value)
      * written while Postfix received the message.
      */
     state->append_hdr_pt_offset = reverse_ptr_offset;
+
+    /*
+     * In case of error while doing record output.
+     */
+    return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, 0));
 }
 
 /* cleanup_find_header - find specific header instance */
@@ -278,7 +316,7 @@ static off_t cleanup_find_header(CLEANUP_STATE *state, ssize_t index,
     const char *myname = "cleanup_find_header";
     off_t   curr_offset;               /* offset after found record */
     off_t   ptr_offset;                        /* pointer to found record */
-    VSTRING *ptr_buf;
+    VSTRING *ptr_buf = 0;
     int     rec_type;
     int     last_type;
     ssize_t len;
@@ -353,9 +391,21 @@ static off_t cleanup_find_header(CLEANUP_STATE *state, ssize_t index,
      * Thus, header insert operations are relative to the content as delivered,
      * that is, the content including our own Received: header.
      */
-#define GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset) \
-    if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0) \
-       msg_fatal("%s: read file %s: %m", myname, cleanup_path); \
+#define CLEANUP_FIND_HEADER_NOTFOUND   (-1)
+#define CLEANUP_FIND_HEADER_IOERROR    (-2)
+
+#define CLEANUP_FIND_HEADER_RETURN(offs) do { \
+       if (ptr_buf) \
+           vstring_free(ptr_buf); \
+       return (offs); \
+    } while (0)
+
+#define GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset, quit) \
+    if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0) { \
+       msg_warn("%s: read file %s: %m", myname, cleanup_path); \
+       cleanup_milter_set_error(state, errno); \
+       quit; \
+    } \
     if (msg_verbose > 1) \
        msg_info("%s: read: %ld: %.*s", myname, (long) curr_offset, \
                 LEN(buf) > 30 ? 30 : LEN(buf), STR(buf)); \
@@ -365,19 +415,28 @@ static off_t cleanup_find_header(CLEANUP_STATE *state, ssize_t index,
        && rec_type != REC_TYPE_PTR) \
        break;
 
-    if (vstream_fseek(state->dst, state->data_offset, SEEK_SET) < 0)
-       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
-    for (ptr_buf = 0, ptr_offset = 0, last_type = 0; /* void */ ; /* void */ ) {
-       if ((curr_offset = vstream_ftell(state->dst)) < 0)
-           msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+    if (vstream_fseek(state->dst, state->data_offset, SEEK_SET) < 0) {
+       msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+       cleanup_milter_set_error(state, errno);
+       CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR);
+    }
+    for (ptr_offset = 0, last_type = 0; /* void */ ; /* void */ ) {
+       if ((curr_offset = vstream_ftell(state->dst)) < 0) {
+           msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+           cleanup_milter_set_error(state, errno);
+           CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR);
+       }
        /* Caution: this macro terminates the loop at end-of-message. */
        /* Don't do complex processing while breaking out of this loop. */
-       GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset);
+       GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, buf, curr_offset,
+                  CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR));
        /* Caution: don't assume ptr->header. This may be header-ptr->body. */
        if (rec_type == REC_TYPE_PTR) {
-           if (rec_goto(state->dst, STR(buf)) < 0)
-               msg_fatal("%s: read file %s: %m",
-                         myname, cleanup_path);
+           if (rec_goto(state->dst, STR(buf)) < 0) {
+               msg_warn("%s: read file %s: %m", myname, cleanup_path);
+               cleanup_milter_set_error(state, errno);
+               CLEANUP_FIND_HEADER_RETURN(CLEANUP_FIND_HEADER_IOERROR);
+           }
            /* Save PTR record, in case it points to the start of a header. */
            if (allow_ptr_backup) {
                ptr_offset = curr_offset;
@@ -415,7 +474,7 @@ static off_t cleanup_find_header(CLEANUP_STATE *state, ssize_t index,
      * In case of failure, return negative start position.
      */
     if (index > 0) {
-       curr_offset = -1;
+       curr_offset = CLEANUP_FIND_HEADER_NOTFOUND;
     }
 
     /*
@@ -431,27 +490,25 @@ static off_t cleanup_find_header(CLEANUP_STATE *state, ssize_t index,
        }
        *prec_type = rec_type;
     }
-    if (ptr_buf)
-       vstring_free(ptr_buf);
 
     if (msg_verbose)
        msg_info("%s: index %ld name %s type %d offset %ld",
                 myname, (long) index, header_label ?
                 header_label : "(none)", rec_type, (long) curr_offset);
 
-    return (curr_offset);
+    CLEANUP_FIND_HEADER_RETURN(curr_offset);
 }
 
 /* cleanup_patch_header - patch new header into an existing header */
 
-static void cleanup_patch_header(CLEANUP_STATE *state,
-                                        const char *new_hdr_name,
-                                        const char *new_hdr_value,
-                                        off_t old_rec_offset,
-                                        int rec_type,
-                                        VSTRING *old_rec_buf,
-                                        ssize_t avail_space,
-                                        off_t read_offset)
+static const char *cleanup_patch_header(CLEANUP_STATE *state,
+                                               const char *new_hdr_name,
+                                               const char *new_hdr_value,
+                                               off_t old_rec_offset,
+                                               int rec_type,
+                                               VSTRING *old_rec_buf,
+                                               ssize_t avail_space,
+                                               off_t read_offset)
 {
     const char *myname = "cleanup_patch_header";
     VSTRING *buf = vstring_alloc(100);
@@ -459,6 +516,11 @@ static void cleanup_patch_header(CLEANUP_STATE *state,
     off_t   saved_read_offset;
     off_t   write_offset;
 
+#define CLEANUP_PATCH_HEADER_RETURN(ret) do { \
+       vstring_free(buf); \
+       return (ret); \
+    } while (0)
+
     if (msg_verbose)
        msg_info("%s: \"%s\" \"%s\" at %ld",
                 myname, new_hdr_name, new_hdr_value, (long) old_rec_offset);
@@ -498,8 +560,10 @@ static void cleanup_patch_header(CLEANUP_STATE *state,
      * Write the new header to a new location after the end of the queue
      * file.
      */
-    if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0)
-       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) {
+       msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+       CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno));
+    }
     vstring_sprintf(buf, "%s: %s", new_hdr_name, new_hdr_value);
     cleanup_out_header(state, buf);
     if (msg_verbose > 1)
@@ -526,10 +590,14 @@ static void cleanup_patch_header(CLEANUP_STATE *state,
      */
     while (rec_type != REC_TYPE_PTR && avail_space < REC_TYPE_PTR_SIZE) {
        /* Read existing text or pointer record. */
-       if (vstream_fseek(state->dst, read_offset, SEEK_SET) < 0)
-           msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
-       if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0)
-           msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+       if (vstream_fseek(state->dst, read_offset, SEEK_SET) < 0) {
+           msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+           CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno));
+       }
+       if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) < 0) {
+           msg_warn("%s: read file %s: %m", myname, cleanup_path);
+           CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno));
+       }
        if (msg_verbose > 1)
            msg_info("%s: %ld: read %.*s", myname, (long) read_offset,
                     LEN(buf) > 30 ? 30 : LEN(buf), STR(buf));
@@ -538,12 +606,16 @@ static void cleanup_patch_header(CLEANUP_STATE *state,
            msg_panic("%s: non-text/ptr record type %d in header, file %s",
                      myname, rec_type, cleanup_path);
        saved_read_offset = read_offset;
-       if ((read_offset = vstream_ftell(state->dst)) < 0)
-           msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+       if ((read_offset = vstream_ftell(state->dst)) < 0) {
+           msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+           CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno));
+       }
        avail_space += (read_offset - saved_read_offset);
        /* Save the text or pointer record. */
-       if ((write_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0)
-           msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+       if ((write_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) {
+           msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+           CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno));
+       }
        CLEANUP_OUT_BUF(state, rec_type, buf);
        if (msg_verbose > 1)
            msg_info("%s: %ld: write %.*s", myname, (long) write_offset,
@@ -570,25 +642,32 @@ static void cleanup_patch_header(CLEANUP_STATE *state,
      * the queue file before the next record. In other words, we must always
      * follow pointer records otherwise we get out of sync with the data.
      */
-    if (vstream_fseek(state->dst, old_rec_offset, SEEK_SET) < 0)
-       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    if (vstream_fseek(state->dst, old_rec_offset, SEEK_SET) < 0) {
+       msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+       CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno));
+    }
     cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
                       (long) new_hdr_offset);
     if (msg_verbose > 1)
        msg_info("%s: %ld: write PTR %ld", myname, (long) old_rec_offset,
                 (long) new_hdr_offset);
 
+    /*
+     * In case of error while doing record output.
+     */
+    CLEANUP_PATCH_HEADER_RETURN(CLEANUP_OUT_OK(state) ? 0 :
+                               cleanup_milter_error(state, 0));
+
     /*
      * Note: state->append_hdr_pt_target never changes.
      */
-    vstring_free(buf);
 }
 
 /* cleanup_ins_header - insert message header */
 
-static void cleanup_ins_header(void *context, ssize_t index,
-                                      char *new_hdr_name,
-                                      char *new_hdr_value)
+static const char *cleanup_ins_header(void *context, ssize_t index,
+                                             char *new_hdr_name,
+                                             char *new_hdr_value)
 {
     const char *myname = "cleanup_ins_header";
     CLEANUP_STATE *state = (CLEANUP_STATE *) context;
@@ -597,6 +676,12 @@ static void cleanup_ins_header(void *context, ssize_t index,
     int     old_rec_type;
     off_t   read_offset;
     ssize_t avail_space;
+    const char *ret;
+
+#define CLEANUP_INS_HEADER_RETURN(ret) do { \
+       vstring_free(old_rec_buf); \
+       return (ret); \
+    } while (0)
 
     if (msg_verbose)
        msg_info("%s: %ld \"%s\" \"%s\"",
@@ -625,29 +710,35 @@ static void cleanup_ins_header(void *context, ssize_t index,
                                         old_rec_buf, &old_rec_type,
                                         ALLOW_PTR_BACKUP,
                                         DONT_SKIP_HEADERS);
+    if (old_rec_offset == CLEANUP_FIND_HEADER_IOERROR)
+       /* Warning and errno->error mapping are done elsewhere. */
+       CLEANUP_INS_HEADER_RETURN(cleanup_milter_error(state, 0));
     if (old_rec_offset < 0) {
-       cleanup_add_header(context, new_hdr_name, new_hdr_value);
+       CLEANUP_INS_HEADER_RETURN(cleanup_add_header(context, new_hdr_name,
+                                                    new_hdr_value));
     } else {
        if (old_rec_type == REC_TYPE_PTR) {
            read_offset = -1;
            avail_space = -1;
        } else {
-           if ((read_offset = vstream_ftell(state->dst)) < 0)
-               msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+           if ((read_offset = vstream_ftell(state->dst)) < 0) {
+               msg_warn("%s: read file %s: %m", myname, cleanup_path);
+               CLEANUP_INS_HEADER_RETURN(cleanup_milter_error(state, errno));
+           }
            avail_space = LEN(old_rec_buf);
        }
-       cleanup_patch_header(state, new_hdr_name, new_hdr_value,
-                            old_rec_offset, old_rec_type, old_rec_buf,
-                            avail_space, read_offset);
+       ret = cleanup_patch_header(state, new_hdr_name, new_hdr_value,
+                                  old_rec_offset, old_rec_type, old_rec_buf,
+                                  avail_space, read_offset);
+       CLEANUP_INS_HEADER_RETURN(ret);
     }
-    vstring_free(old_rec_buf);
 }
 
 /* cleanup_upd_header - modify or append message header */
 
-static void cleanup_upd_header(void *context, ssize_t index,
-                                      char *new_hdr_name,
-                                      char *new_hdr_value)
+static const char *cleanup_upd_header(void *context, ssize_t index,
+                                             char *new_hdr_name,
+                                             char *new_hdr_value)
 {
     const char *myname = "cleanup_upd_header";
     CLEANUP_STATE *state = (CLEANUP_STATE *) context;
@@ -659,6 +750,7 @@ static void cleanup_upd_header(void *context, ssize_t index,
     int     rec_type;
     int     last_type;
     int     jumped;
+    const char *ret;
 
     if (msg_verbose)
        msg_info("%s: %ld \"%s\" \"%s\"",
@@ -684,33 +776,54 @@ static void cleanup_upd_header(void *context, ssize_t index,
 #define DONT_SAVE_RECORD       0
 #define NO_PTR_BACKUP          0
 
+#define CLEANUP_UPD_HEADER_RETURN(ret) do { \
+       vstring_free(rec_buf); \
+       return (ret); \
+    } while (0)
+
     rec_buf = vstring_alloc(100);
     old_rec_offset = cleanup_find_header(state, index, new_hdr_name,
                                         rec_buf, &last_type,
                                         NO_PTR_BACKUP,
                                         SKIP_ONE_HEADER);
+    if (old_rec_offset == CLEANUP_FIND_HEADER_IOERROR)
+       /* Warning and errno->error mapping are done elsewhere. */
+       CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, 0));
     if (old_rec_offset < 0) {
-       cleanup_add_header(context, new_hdr_name, new_hdr_value);
+       CLEANUP_UPD_HEADER_RETURN(cleanup_add_header(context, new_hdr_name,
+                                                    new_hdr_value));
     } else {
        /* Find the end of this header. */
        avail_space = LEN(rec_buf);
-       if ((read_offset = vstream_ftell(state->dst)) < 0)
-           msg_fatal("%s: read file %s: %m", myname, cleanup_path);
-       for (jumped = 0; /* void */ ; /* void */ ) {
+       if ((read_offset = vstream_ftell(state->dst)) < 0) {
+           msg_warn("%s: read file %s: %m", myname, cleanup_path);
+           CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, errno));
+       }
+       for (jumped = 0, ret = 0; ret == 0; /* void */ ) {
+           if (CLEANUP_OUT_OK(state) == 0)
+               /* Warning and errno->error mapping are done elsewhere. */
+               CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, 0));
            saved_read_offset = read_offset;
            /* Caution: this macro terminates the loop at end-of-message. */
            /* Don't do complex processing while breaking out of this loop. */
-           GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, rec_buf, read_offset);
-           if ((read_offset = vstream_ftell(state->dst)) < 0)
-               msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+           GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, rec_buf, read_offset,
+           /* Warning and errno->error mapping are done elsewhere. */
+                CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, 0)));
+           if ((read_offset = vstream_ftell(state->dst)) < 0) {
+               msg_warn("%s: read file %s: %m", myname, cleanup_path);
+               CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state, errno));
+           }
            if (rec_type == REC_TYPE_PTR) {
                if (jumped == 0) {
                    /* Enough contiguous space for writing a PTR record. */
                    avail_space += read_offset - saved_read_offset;
                    jumped = 1;
                }
-               if (rec_goto(state->dst, STR(rec_buf)) < 0)
-                   msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+               if (rec_goto(state->dst, STR(rec_buf)) < 0) {
+                   msg_warn("%s: read file %s: %m", myname, cleanup_path);
+                   CLEANUP_UPD_HEADER_RETURN(cleanup_milter_error(state,
+                                                                  errno));
+               }
                /* Don't update last_type; PTR may follow REC_TYPE_CONT. */
                continue;
            }
@@ -721,16 +834,17 @@ static void cleanup_upd_header(void *context, ssize_t index,
                avail_space += read_offset - saved_read_offset;
            last_type = rec_type;
        }
-       cleanup_patch_header(state, new_hdr_name, new_hdr_value,
-                            old_rec_offset, DONT_SAVE_RECORD, (VSTRING *) 0,
-                            avail_space, saved_read_offset);
+       ret = cleanup_patch_header(state, new_hdr_name, new_hdr_value,
+                           old_rec_offset, DONT_SAVE_RECORD, (VSTRING *) 0,
+                                  avail_space, saved_read_offset);
+       CLEANUP_UPD_HEADER_RETURN(ret);
     }
-    vstring_free(rec_buf);
 }
 
 /* cleanup_del_header - delete message header */
 
-static void cleanup_del_header(void *context, ssize_t index, char *hdr_name)
+static const char *cleanup_del_header(void *context, ssize_t index,
+                                             char *hdr_name)
 {
     const char *myname = "cleanup_del_header";
     CLEANUP_STATE *state = (CLEANUP_STATE *) context;
@@ -765,6 +879,11 @@ static void cleanup_del_header(void *context, ssize_t index, char *hdr_name)
     header_offset = cleanup_find_header(state, index, hdr_name, rec_buf,
                                        &last_type, NO_PTR_BACKUP,
                                        SKIP_ONE_HEADER);
+    if (header_offset == CLEANUP_FIND_HEADER_IOERROR) {
+       vstring_free(rec_buf);
+       /* Warning and errno->error mapping are done elsewhere. */
+       return (cleanup_milter_error(state, 0));
+    }
     /* Memory usage for header offsets is limited by header_size_limit. */
     if (header_offset > 0) {
        ssize_t off_len = 1;
@@ -772,15 +891,26 @@ static void cleanup_del_header(void *context, ssize_t index, char *hdr_name)
        off_t  *off_list = (off_t *) mymalloc(off_len * sizeof(*off_list));
        int     n;
 
+#define CLEANUP_DEL_HEADER_RETURN(ret) do { \
+       vstring_free(rec_buf); \
+       myfree((char *) off_list); \
+       return (ret); \
+    } while (0)
+
        off_list[0] = header_offset;
        for (;;) {
            curr_offset = vstream_ftell(state->dst);
            /* Caution: this macro terminates the loop at end-of-message. */
            /* Don't do complex processing while breaking out of this loop. */
-           GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, rec_buf, curr_offset);
+           GET_NEXT_TEXT_OR_PTR_RECORD(rec_type, state, rec_buf, curr_offset,
+           /* Warning and errno->error mapping are done elsewhere. */
+                CLEANUP_DEL_HEADER_RETURN(cleanup_milter_error(state, 0)));
            if (rec_type == REC_TYPE_PTR) {
-               if (rec_goto(state->dst, STR(rec_buf)) < 0)
-                   msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+               if (rec_goto(state->dst, STR(rec_buf)) < 0) {
+                   msg_warn("%s: read file %s: %m", myname, cleanup_path);
+                   CLEANUP_DEL_HEADER_RETURN(cleanup_milter_error(state,
+                                                                  errno));
+               }
                /* Don't update last_type; PTR may follow REC_TYPE_CONT. */
                continue;
            }
@@ -797,17 +927,25 @@ static void cleanup_del_header(void *context, ssize_t index, char *hdr_name)
            last_type = rec_type;
        }
        /* Mark the header text records as deleted. */
-       for (n = 0; n < off_used; n++)
-           if (rec_put_type(state->dst, REC_TYPE_DTXT, off_list[n]) < 0)
-               msg_fatal("%s: write file %s: %m", myname, cleanup_path);
+       for (n = 0; n < off_used; n++) {
+           if (rec_put_type(state->dst, REC_TYPE_DTXT, off_list[n]) < 0) {
+               msg_warn("%s: write file %s: %m", myname, cleanup_path);
+               CLEANUP_DEL_HEADER_RETURN(cleanup_milter_error(state, errno));
+           }
+       }
        myfree((char *) off_list);
     }
     vstring_free(rec_buf);
+
+    /*
+     * In case of error while doing record output.
+     */
+    return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, 0));
 }
 
 /* cleanup_add_rcpt - append recipient address */
 
-static void cleanup_add_rcpt(void *context, char *rcpt)
+static const char *cleanup_add_rcpt(void *context, char *rcpt)
 {
     const char *myname = "cleanup_add_rcpt";
     CLEANUP_STATE *state = (CLEANUP_STATE *) context;
@@ -841,11 +979,15 @@ static void cleanup_add_rcpt(void *context, char *rcpt)
      */
 #define NO_DSN_ORCPT   ((char *) 0)
 
-    if ((new_rcpt_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0)
-       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    if ((new_rcpt_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) {
+       msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+       return (cleanup_milter_error(state, errno));
+    }
     cleanup_addr_bcc(state, rcpt);
-    if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0)
-       msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+    if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) {
+       msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+       return (cleanup_milter_error(state, errno));
+    }
     cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
                       (long) state->append_rcpt_pt_target);
 
@@ -853,8 +995,10 @@ static void cleanup_add_rcpt(void *context, char *rcpt)
      * Pointer flipping: update the old "recipient append" pointer record
      * value to the location of the new recipient record.
      */
-    if (vstream_fseek(state->dst, state->append_rcpt_pt_offset, SEEK_SET) < 0)
-       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    if (vstream_fseek(state->dst, state->append_rcpt_pt_offset, SEEK_SET) < 0) {
+       msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+       return (cleanup_milter_error(state, errno));
+    }
     cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
                       (long) new_rcpt_offset);
 
@@ -866,11 +1010,16 @@ static void cleanup_add_rcpt(void *context, char *rcpt)
      * record that was written while Postfix received the message.
      */
     state->append_rcpt_pt_offset = reverse_ptr_offset;
+
+    /*
+     * In case of error while doing record output.
+     */
+    return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, 0));
 }
 
 /* cleanup_del_rcpt - remove recipient and all its expansions */
 
-static void cleanup_del_rcpt(void *context, char *rcpt)
+static const char *cleanup_del_rcpt(void *context, char *rcpt)
 {
     const char *myname = "cleanup_del_rcpt";
     CLEANUP_STATE *state = (CLEANUP_STATE *) context;
@@ -908,26 +1057,48 @@ static void cleanup_del_rcpt(void *context, char *rcpt)
      * XXX Remove the (dsn_orcpt, dsn_notify, orcpt, recip) tuple from the
      * duplicate recipient filter.
      */
-    if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0)
-       msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+    if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0) {
+       msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+       return (cleanup_milter_error(state, errno));
+    }
+#define CLEANUP_DEL_RCPT_RETURN(ret) do { \
+       if (orig_rcpt != 0)     \
+           myfree(orig_rcpt); \
+       if (dsn_orcpt != 0) \
+           myfree(dsn_orcpt); \
+       vstring_free(buf); \
+       return (ret); \
+    } while (0)
+
     buf = vstring_alloc(100);
-    while (CLEANUP_OUT_OK(state)) {
-       if ((curr_offset = vstream_ftell(state->dst)) < 0)
-           msg_fatal("%s: vstream_ftell file %s: %m", myname, cleanup_path);
-       if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) <= 0)
-           msg_fatal("%s: read file %s: %m", myname, cleanup_path);
+    for (;;) {
+       if (CLEANUP_OUT_OK(state) == 0)
+           /* Warning and errno->error mapping are done elsewhere. */
+           CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, 0));
+       if ((curr_offset = vstream_ftell(state->dst)) < 0) {
+           msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+           CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno));
+       }
+       if ((rec_type = rec_get_raw(state->dst, buf, 0, REC_FLAG_NONE)) <= 0) {
+           msg_warn("%s: read file %s: %m", myname, cleanup_path);
+           CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno));
+       }
        if (rec_type == REC_TYPE_END)
            break;
        /* Skip over message content. */
        if (rec_type == REC_TYPE_MESG) {
-           if (vstream_fseek(state->dst, state->xtra_offset, SEEK_SET) < 0)
-               msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+           if (vstream_fseek(state->dst, state->xtra_offset, SEEK_SET) < 0) {
+               msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+               CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno));
+           }
            continue;
        }
        start = STR(buf);
        if (rec_type == REC_TYPE_PTR) {
-           if (rec_goto(state->dst, start) < 0)
-               msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+           if (rec_goto(state->dst, start) < 0) {
+               msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+               CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno));
+           }
            continue;
        }
        /* Map attribute names to pseudo record type. */
@@ -960,11 +1131,13 @@ static void cleanup_del_rcpt(void *context, char *rcpt)
            break;
        case REC_TYPE_RCPT:                     /* rewritten RCPT TO address */
            if (strcmp(orig_rcpt ? orig_rcpt : start, rcpt) == 0) {
-               if (vstream_fseek(state->dst, curr_offset, SEEK_SET) < 0)
-                   msg_fatal("%s: seek file %s: %m", myname, cleanup_path);
+               if (vstream_fseek(state->dst, curr_offset, SEEK_SET) < 0) {
+                   msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+                   CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno));
+               }
                if (REC_PUT_BUF(state->dst, REC_TYPE_DRCP, buf) < 0) {
                    msg_warn("%s: write queue file: %m", state->queue_id);
-                   state->errs |= CLEANUP_STAT_WRITE;
+                   CLEANUP_DEL_RCPT_RETURN(cleanup_milter_error(state, errno));
                }
                count++;
            }
@@ -983,20 +1156,17 @@ static void cleanup_del_rcpt(void *context, char *rcpt)
            break;
        }
     }
-    if (orig_rcpt != 0)                                /* can't happen */
-       myfree(orig_rcpt);
-    if (dsn_orcpt != 0)                                /* can't happen */
-       myfree(dsn_orcpt);
-    vstring_free(buf);
 
     if (msg_verbose)
        msg_info("%s: deleted %d records for recipient \"%s\"",
                 myname, count, rcpt);
+
+    CLEANUP_DEL_RCPT_RETURN(0);
 }
 
 /* cleanup_repl_body - replace message body */
 
-static void cleanup_repl_body(void *context, VSTRING *body)
+static const char *cleanup_repl_body(void *context, VSTRING *body)
 {
     const char *myname = "cleanup_repl_body";
 
@@ -1088,6 +1258,13 @@ static const char *cleanup_milter_apply(CLEANUP_STATE *state, const char *resp)
 
     if (msg_verbose)
        msg_info("%s: %s", myname, resp);
+
+    /*
+     * We don't report errors that were already reported by the content
+     * editing call-back routines. See cleanup_milter_error() above.
+     */
+    if (CLEANUP_OUT_OK(state) == 0)
+       return (0);
     switch (resp[0]) {
     case 'H':
        /* XXX Should log the reason here. */
@@ -1293,7 +1470,8 @@ void    cleanup_milter_emul_data(CLEANUP_STATE *state, MILTERS *milters)
 #ifdef TEST
 
  /*
-  * Queue file editing driver for regression tests.
+  * Queue file editing driver for regression tests. In this case it is OK to
+  * report fatal errors after I/O errors.
   */
 #include <stdio.h>
 #include <msg_vstream.h>
index c42b00c766a0cc3461b6c89da2d31b7764fdc64b..ad6f60c841c42965a88e92a94bce37c7adfec221 100644 (file)
@@ -20,8 +20,8 @@
   * Patches change both the patchlevel and the release date. Snapshots have no
   * patchlevel; they change the release date only.
   */
-#define MAIL_RELEASE_DATE      "20060630"
-#define MAIL_VERSION_NUMBER    "2.3-RC2"
+#define MAIL_RELEASE_DATE      "20060702"
+#define MAIL_VERSION_NUMBER    "2.3-RC3"
 
 #define VAR_MAIL_VERSION       "mail_version"
 #define DEF_MAIL_VERSION       MAIL_VERSION_NUMBER
index 94bfa01421885056181020dce8e67e8ab7a90ba2..2ab2a516ebdc79efcb713328068f7c5bf3d73a08 100644 (file)
 /*                                     ins_header, del_header, add_rcpt,
 /*                                     del_rcpt, repl_body, context)
 /*     MILTERS *milters;
-/*     void    (*add_header) (void *context, char *name, char *value);
-/*     void    (*upd_header) (void *context, ssize_t index,
+/*     const char *(*add_header) (void *context, char *name, char *value);
+/*     const char *(*upd_header) (void *context, ssize_t index,
 /*                             char *name, char *value);
-/*     void    (*ins_header) (void *context, ssize_t index,
+/*     const char *(*ins_header) (void *context, ssize_t index,
 /*                             char *name, char *value);
-/*     void    (*del_header) (void *context, ssize_t index, char *name);
-/*     void    (*add_rcpt) (void *context, char *rcpt);
-/*     void    (*del_rcpt) (void *context, char *rcpt);
-/*     void    (*repl_body) (void *context, VSTRING *body);
+/*     const char *(*del_header) (void *context, ssize_t index, char *name);
+/*     const char *(*add_rcpt) (void *context, char *rcpt);
+/*     const char *(*del_rcpt) (void *context, char *rcpt);
+/*     const char *(*repl_body) (void *context, VSTRING *body);
 /*     void    *context;
 /*
 /*     const char *milter_conn_event(milters, client_name, client_addr,
@@ -272,13 +272,13 @@ void    milter_macro_callback(MILTERS *milters,
 /* milter_edit_callback - specify queue file edit call-back information */
 
 void    milter_edit_callback(MILTERS *milters,
-                               void (*add_header) (void *, char *, char *),
-                      void (*upd_header) (void *, ssize_t, char *, char *),
-                      void (*ins_header) (void *, ssize_t, char *, char *),
-                              void (*del_header) (void *, ssize_t, char *),
-                                    void (*add_rcpt) (void *, char *),
-                                    void (*del_rcpt) (void *, char *),
-                                    void (*repl_body) (void *, VSTRING *),
+                        const char *(*add_header) (void *, char *, char *),
+               const char *(*upd_header) (void *, ssize_t, char *, char *),
+               const char *(*ins_header) (void *, ssize_t, char *, char *),
+                       const char *(*del_header) (void *, ssize_t, char *),
+                                  const char *(*add_rcpt) (void *, char *),
+                                  const char *(*del_rcpt) (void *, char *),
+                              const char *(*repl_body) (void *, VSTRING *),
                                     void *chg_context)
 {
     milters->add_header = add_header;
@@ -838,9 +838,9 @@ int     main(int argc, char **argv)
                msg_warn("deleting existing milters");
                milter_free(milters);
            }
-           milters = milter_create(args[0], var_milt_conn_time, 
-                               var_milt_cmd_time, var_milt_msg_time, 
-                               var_milt_protocol, var_milt_def_action,
+           milters = milter_create(args[0], var_milt_conn_time,
+                                   var_milt_cmd_time, var_milt_msg_time,
+                                   var_milt_protocol, var_milt_def_action,
                                    conn_macros, helo_macros, mail_macros,
                                    rcpt_macros, data_macros, eod_macros,
                                    unk_macros);
index 2cf63e5e39faeac0b7ae50f08779566329389d53..9e480193a8d62ddeb5b10d2cd566759b83e7db62 100644 (file)
@@ -61,21 +61,21 @@ typedef struct MILTERS {
     char   *eod_macros;                        /* macros for END-OF-DATA command */
     char   *unk_macros;                        /* macros for unknown command */
     void   *chg_context;               /* context for queue file changes */
-    void    (*add_header) (void *, char *, char *);
-    void    (*upd_header) (void *, ssize_t, char *, char *);
-    void    (*del_header) (void *, ssize_t, char *);
-    void    (*ins_header) (void *, ssize_t, char *, char *);
-    void    (*add_rcpt) (void *, char *);
-    void    (*del_rcpt) (void *, char *);
-    void    (*repl_body) (void *, VSTRING *);
+    const char *(*add_header) (void *, char *, char *);
+    const char *(*upd_header) (void *, ssize_t, char *, char *);
+    const char *(*del_header) (void *, ssize_t, char *);
+    const char *(*ins_header) (void *, ssize_t, char *, char *);
+    const char *(*add_rcpt) (void *, char *);
+    const char *(*del_rcpt) (void *, char *);
+    const char *(*repl_body) (void *, VSTRING *);
 } MILTERS;
 
 typedef const char *(*MILTER_MAC_LOOKUP_FN) (const char *, void *);
-typedef void (*MILTER_ADD_HEADER_FN) (void *, char *, char *);
-typedef void (*MILTER_EDIT_HEADER_FN) (void *, ssize_t, char *, char *);
-typedef void (*MILTER_DEL_HEADER_FN) (void *, ssize_t, char *);
-typedef void (*MILTER_EDIT_RCPT_FN) (void *, char *);
-typedef void (*MILTER_EDIT_BODY_FN) (void *, VSTRING *);
+typedef const char *(*MILTER_ADD_HEADER_FN) (void *, char *, char *);
+typedef const char *(*MILTER_EDIT_HEADER_FN) (void *, ssize_t, char *, char *);
+typedef const char *(*MILTER_DEL_HEADER_FN) (void *, ssize_t, char *);
+typedef const char *(*MILTER_EDIT_RCPT_FN) (void *, char *);
+typedef const char *(*MILTER_EDIT_BODY_FN) (void *, VSTRING *);
 
 extern MILTERS *milter_create(const char *, int, int, int,
                                      const char *, const char *,
index 8b5c442206bb5abfa651a3cb4088ac9535578f2b..e926ac2d15f16917d8a6c68b2a3839e1075ff308 100644 (file)
@@ -19,7 +19,7 @@
 /*     MILTER  *milter8_receive(stream)
 /*     VSTREAM *stream;
 /* DESCRIPTION
-/*     This modulde implements the MTA side of the Sendmail 8 mail
+/*     This module implements the MTA side of the Sendmail 8 mail
 /*     filter protocol.
 /*
 /*     milter8_create() creates a MILTER data structure with virtual
@@ -625,7 +625,7 @@ static int vmilter8_read_data(MILTER8 *milter, ssize_t data_len, va_list ap)
 
     /*
      * Sanity checks. We may have excess data when the sender is confused. We
-     * may have a negative count when we're confused outselves.
+     * may have a negative count when we're confused ourselves.
      */
     if (data_left > 0) {
        msg_warn("%s: left-over data %ld bytes", myname, (long) data_left);
@@ -863,11 +863,12 @@ static const char *milter8_event(MILTER8 *milter, int event,
     const char *smfir_name;
     MILTERS *parent;
     UINT32_TYPE index;
+    const char *edit_resp;
 
 #define DONT_SKIP_REPLY        0
 
     /*
-     * Skip this event if it is not defined for my protocol version.
+     * Skip this event if it doesn't exist in the protocol that I announced.
      */
 #ifndef USE_LIBMILTER_INCLUDES
     if ((skip_event_flag & milter->np_mask) != 0) {
@@ -880,9 +881,9 @@ static const char *milter8_event(MILTER8 *milter, int event,
 #endif
 
     /*
-     * Send the macros even when the corresponding list is empty. This is not
-     * a problem because we're sending macros and event parameters in one
-     * transaction.
+     * Send the macros for this event, even when we're not reporting the
+     * event itself. This does not introduce a performance problem because
+     * we're sending macros and event parameters in one VSTREAM transaction.
      */
     if (msg_verbose) {
        VSTRING *buf = vstring_alloc(100);
@@ -921,7 +922,8 @@ static const char *milter8_event(MILTER8 *milter, int event,
     }
 
     /*
-     * Size the command data.
+     * Compute the command data size. This is necessary because the protocol
+     * sends length before content.
      */
     va_start(ap, macros);
     data_len = vmilter8_size_data(ap);
@@ -938,8 +940,8 @@ static const char *milter8_event(MILTER8 *milter, int event,
 
     /*
      * Special feature: don't wait for one reply per header. This allows us
-     * to send multiple headers in one transaction, and improves over-all
-     * performance.
+     * to send multiple headers in one VSTREAM transaction, and improves
+     * over-all performance.
      */
     if (skip_reply) {
        if (msg_verbose)
@@ -960,9 +962,9 @@ static const char *milter8_event(MILTER8 *milter, int event,
        if (milter8_read_cmd(milter, &cmd, &data_size) != 0)
            return (milter->def_reply);
        if (msg_verbose)
-           msg_info("reply: %s %d",
+           msg_info("reply: %s data %ld bytes",
                     (smfir_name = str_name_code(smfir_table, cmd)) != 0 ?
-                    smfir_name : "unknown", data_size);
+                    smfir_name : "unknown", (long) data_size);
        switch (cmd) {
 
            /*
@@ -1051,7 +1053,7 @@ static const char *milter8_event(MILTER8 *milter, int event,
 #endif
                milter->state = MILTER8_STAT_REJECT_CON;
                return (milter8_def_reply(milter,
-                      "451 4.7.1 Service unavailable - try again later"));
+                       "451 4.7.1 Service unavailable - try again later"));
            } else {
                return ("451 4.7.1 Service unavailable - try again later");
            }
@@ -1136,6 +1138,9 @@ static const char *milter8_event(MILTER8 *milter, int event,
                                          MILTER8_DATA_END) != 0)
                        return (milter->def_reply);
                    parent = milter->m.parent;
+                   /* XXX Sendmail 8 compatibility. */
+                   if (index == 0)
+                       index = 1;
                    if ((ssize_t) index < 1) {
                        msg_warn("milter %s: bad change header index: %ld",
                                 milter->m.name, (long) index);
@@ -1149,12 +1154,16 @@ static const char *milter8_event(MILTER8 *milter, int event,
                        return (milter->def_reply);
                    }
                    if (STR(milter->body)[0])
-                       parent->upd_header(parent->chg_context, (ssize_t) index,
-                                          STR(milter->buf),
-                                          STR(milter->body));
+                       edit_resp = parent->upd_header(parent->chg_context,
+                                                      (ssize_t) index,
+                                                      STR(milter->buf),
+                                                      STR(milter->body));
                    else
-                       parent->del_header(parent->chg_context, (ssize_t) index,
-                                          STR(milter->buf));
+                       edit_resp = parent->del_header(parent->chg_context,
+                                                      (ssize_t) index,
+                                                      STR(milter->buf));
+                   if (edit_resp)
+                       return (milter8_def_reply(milter, edit_resp));
                    continue;
 #endif
 
@@ -1168,8 +1177,11 @@ static const char *milter8_event(MILTER8 *milter, int event,
                                          MILTER8_DATA_END) != 0)
                        return (milter->def_reply);
                    parent = milter->m.parent;
-                   parent->add_header(parent->chg_context, STR(milter->buf),
-                                      STR(milter->body));
+                   edit_resp = parent->add_header(parent->chg_context,
+                                                  STR(milter->buf),
+                                                  STR(milter->body));
+                   if (edit_resp)
+                       return (milter8_def_reply(milter, edit_resp));
                    continue;
 
                    /*
@@ -1193,8 +1205,12 @@ static const char *milter8_event(MILTER8 *milter, int event,
                        return (milter->def_reply);
                    }
                    parent = milter->m.parent;
-                   parent->ins_header(parent->chg_context, (ssize_t) index + 1,
-                                      STR(milter->buf), STR(milter->body));
+                   edit_resp = parent->ins_header(parent->chg_context,
+                                                  (ssize_t) index + 1,
+                                                  STR(milter->buf),
+                                                  STR(milter->body));
+                   if (edit_resp)
+                       return (milter8_def_reply(milter, edit_resp));
                    continue;
 #endif
 
@@ -1207,7 +1223,10 @@ static const char *milter8_event(MILTER8 *milter, int event,
                                          MILTER8_DATA_END) != 0)
                        return (milter->def_reply);
                    parent = milter->m.parent;
-                   parent->add_rcpt(parent->chg_context, STR(milter->buf));
+                   edit_resp = parent->add_rcpt(parent->chg_context,
+                                                STR(milter->buf));
+                   if (edit_resp)
+                       return (milter8_def_reply(milter, edit_resp));
                    continue;
 
                    /*
@@ -1219,7 +1238,10 @@ static const char *milter8_event(MILTER8 *milter, int event,
                                          MILTER8_DATA_END) != 0)
                        return (milter->def_reply);
                    parent = milter->m.parent;
-                   parent->del_rcpt(parent->chg_context, STR(milter->buf));
+                   edit_resp = parent->del_rcpt(parent->chg_context,
+                                                STR(milter->buf));
+                   if (edit_resp)
+                       return (milter8_def_reply(milter, edit_resp));
                    continue;
 
                    /*
@@ -1233,7 +1255,10 @@ static const char *milter8_event(MILTER8 *milter, int event,
                                          MILTER8_DATA_END) != 0)
                        return (milter->def_reply);
                    parent = milter->m.parent;
-                   parent->repl_body(parent->chg_context, milter->body);
+                   edit_resp = parent->repl_body(parent->chg_context,
+                                                 milter->body);
+                   if (edit_resp)
+                       return (milter8_def_reply(milter, edit_resp));
                    continue;
 #endif
                }
index 3ff1586d6f298846299754271e75216ea519cd07..a954024fc1af083abf6cf1ba842545dbf6e6ef3d 100644 (file)
@@ -7,14 +7,24 @@
   * 
   * Specifies a non-default reply. The default is to always continue.
   * 
+  * -d Enable libmilter debugging.
+  * 
   * -c connect|helo|mail|rcpt|data|header|eoh|body|eom|unknown|close|abort
   * 
   * When to send the non-default reply. The default is "connect".
   * 
+  * -i "index header-label header-value"
+  * 
+  * Insert header at specified position.
+  * 
   * -p inet:port@host|unix:/path/name
   * 
   * The mail filter listen endpoint.
   * 
+  * -r "index header-label header-value"
+  * 
+  * Replace header at specified position.
+  * 
   * -C count
   * 
   * Terminate after count connections.
@@ -86,6 +96,14 @@ static char *reply_code;
 static char *reply_dsn;
 static char *reply_message;
 
+static char *ins_hdr;
+static int ins_idx;
+static char *ins_val;
+
+static char *chg_hdr;
+static int chg_idx;
+static char *chg_val;
+
 static int test_reply(SMFICTX *ctx, int code)
 {
     if (code == SMFIR_REPLYCODE) {
@@ -184,25 +202,20 @@ static sfsistat test_eoh(SMFICTX *ctx)
 
 static sfsistat test_body(SMFICTX *ctx, unsigned char *data, size_t data_len)
 {
-    printf("test_body %ld bytes\n", (long) data_len);
+    if (verbose == 0)
+       printf("test_body %ld bytes\n", (long) data_len);
+    else
+       printf("%*s", data_len, data);
     return (test_reply(ctx, test_body_reply));
 }
 
 static sfsistat test_eom(SMFICTX *ctx)
 {
     printf("test_eom\n");
-#if 0
-    if (smfi_insheader(ctx, 1, "Received", "insert at 1") == MI_FAILURE)
+    if (ins_hdr && smfi_insheader(ctx, ins_idx, ins_hdr, ins_val) == MI_FAILURE)
        fprintf(stderr, "smfi_insheader failed");
-#endif
-#if 0
-    if (smfi_chgheader(ctx, "Received", 1, "change received #1") == MI_FAILURE)
-       fprintf(stderr, "smfi_chgheader failed");
-#endif
-#if 0
-    if (smfi_chgheader(ctx, "date", 0, "change date #0") == MI_FAILURE)
+    if (chg_hdr && smfi_chgheader(ctx, chg_hdr, chg_idx, chg_val) == MI_FAILURE)
        fprintf(stderr, "smfi_chgheader failed");
-#endif
     return (test_reply(ctx, test_eom_reply));
 }
 
@@ -265,6 +278,22 @@ static struct smfiDesc smfilter =
 #endif
 };
 
+static void parse_hdr_info(const char *optarg, int *idx,
+                                  char **hdr, char **value)
+{
+    int     len;
+
+    len = strlen(optarg) + 1;
+    if ((*hdr = malloc(len)) == 0 || (*value = malloc(len)) == 0) {
+       fprintf(stderr, "out of memory\n");
+       exit(1);
+    }
+    if (sscanf(optarg, "%d %s %[^\n]", idx, *hdr, *value) != 3) {
+       fprintf(stderr, "bad header info: %s\n", optarg);
+       exit(1);
+    }
+}
+
 int     main(int argc, char **argv)
 {
     char   *action = 0;
@@ -273,7 +302,7 @@ int     main(int argc, char **argv)
     int     ch;
     int     code;
 
-    while ((ch = getopt(argc, argv, "a:c:d:p:vC:")) > 0) {
+    while ((ch = getopt(argc, argv, "a:c:d:i:p:r:vC:")) > 0) {
        switch (ch) {
        case 'a':
            action = optarg;
@@ -287,12 +316,26 @@ int     main(int argc, char **argv)
                exit(1);
            }
            break;
+       case 'i':
+           if (ins_hdr) {
+               fprintf(stderr, "too many -i options\n");
+               exit(1);
+           }
+           parse_hdr_info(optarg, &ins_idx, &ins_hdr, &ins_val);
+           break;
        case 'p':
            if (smfi_setconn(optarg) == MI_FAILURE) {
                fprintf(stderr, "smfi_setconn failed\n");
                exit(1);
            }
            break;
+       case 'r':
+           if (chg_hdr) {
+               fprintf(stderr, "too many -r options\n");
+               exit(1);
+           }
+           parse_hdr_info(optarg, &chg_idx, &chg_hdr, &chg_val);
+           break;
        case 'v':
            verbose++;
            break;
@@ -301,7 +344,13 @@ int     main(int argc, char **argv)
            break;
        default:
            fprintf(stderr,
-                   "usage: %s [-a action] [-c command] [-C conn_count] [-d debug] -p port [-v]\n",
+                   "usage: %s [-dv] \n"
+                   "\t[-a action]              non-default action\n"
+                   "\t[-c command]             non-default action trigger\n"
+                   "\t[-i 'index label value'] insert header\n"
+                   "\t-p port                  milter application\n"
+                   "\t[-r 'index label value'] replace header\n"
+                   "\t[-C conn_count]          when to exit\n",
                    argv[0]);
            exit(1);
        }