diff -cr /var/tmp/postfix-2.6.2/html/cleanup.8.html ./html/cleanup.8.html
*** /var/tmp/postfix-2.6.2/html/cleanup.8.html Tue Apr 28 13:49:23 2009
--- ./html/cleanup.8.html Sun Jun 7 16:43:45 2009
***************
*** 224,229 ****
--- 224,236 ----
The macros that are sent to Milter (mail filter)
applications after the end of the message header.
+ Available in Postfix version 2.7 and later:
+
+ milter_header_checks (empty)
+ Optional lookup tables for content inspection of
+ message headers that are produced by Milter appli-
+ cations.
+
MIME PROCESSING CONTROLS
Available in Postfix version 2.0 and later:
diff -cr /var/tmp/postfix-2.6.2/html/postconf.5.html ./html/postconf.5.html
*** /var/tmp/postfix-2.6.2/html/postconf.5.html Wed May 6 14:53:52 2009
--- ./html/postconf.5.html Sun Jun 7 16:43:45 2009
***************
*** 5647,5652 ****
--- 5647,5685 ----
+
milter_header_checks
+ (default: empty)
+
+ Optional lookup tables for content inspection of message headers
+ that are produced by Milter applications. See the header_checks(5)
+ manual page available actions. Currently, PREPEND is not implemented.
+
+
+ The following example sends all mail that is marked as SPAM to
+ a spam handling machine. Note that matches are case-insensitive
+ by default.
+
+
+
+ /etc/postfix/main.cf:
+ milter_header_checks = pcre:/etc/postfix/milter_header_checks
+
+
+ /etc/postfix/milter_header_checks:
+ /^X-SPAM-FLAG:\s+YES/ FILTER mysmtp:sanitizer.example.com:25
+
+
+
+ The milter_header_checks mechanism could also be used for
+ whitelisting. For example it could be used to skip heavy content
+ scanning for DKIM-signed mail from known friendly domains.
+
+ This feature is available in Postfix 2.7, and as an optional
+ patch for Postfix 2.6.
+
+
+
+
milter_helo_macros
(default: see "postconf -d" output)
diff -cr /var/tmp/postfix-2.6.2/man/man5/postconf.5 ./man/man5/postconf.5
*** /var/tmp/postfix-2.6.2/man/man5/postconf.5 Wed May 6 14:53:53 2009
--- ./man/man5/postconf.5 Sun Jun 7 16:43:45 2009
***************
*** 3138,3143 ****
--- 3138,3177 ----
of available macro names and their meanings.
.PP
This feature is available in Postfix 2.5 and later.
+ .SH milter_header_checks (default: empty)
+ Optional lookup tables for content inspection of message headers
+ that are produced by Milter applications. See the \fBheader_checks\fR(5)
+ manual page available actions. Currently, PREPEND is not implemented.
+ .PP
+ The following example sends all mail that is marked as SPAM to
+ a spam handling machine. Note that matches are case-insensitive
+ by default.
+ .sp
+ .in +4
+ .nf
+ .na
+ .ft C
+ /etc/postfix/main.cf:
+ milter_header_checks = pcre:/etc/postfix/milter_header_checks
+ .fi
+ .ad
+ .ft R
+ .nf
+ .na
+ .ft C
+ /etc/postfix/milter_header_checks:
+ /^X-SPAM-FLAG:\es+YES/ FILTER mysmtp:sanitizer.example.com:25
+ .fi
+ .ad
+ .ft R
+ .in -4
+ .PP
+ The milter_header_checks mechanism could also be used for
+ whitelisting. For example it could be used to skip heavy content
+ scanning for DKIM-signed mail from known friendly domains.
+ .PP
+ This feature is available in Postfix 2.7, and as an optional
+ patch for Postfix 2.6.
.SH milter_helo_macros (default: see "postconf -d" output)
The macros that are sent to Milter (mail filter) applications
after the SMTP HELO or EHLO command. See
diff -cr /var/tmp/postfix-2.6.2/man/man8/cleanup.8 ./man/man8/cleanup.8
*** /var/tmp/postfix-2.6.2/man/man8/cleanup.8 Tue Apr 28 13:49:23 2009
--- ./man/man8/cleanup.8 Sun Jun 7 16:43:45 2009
***************
*** 190,195 ****
--- 190,200 ----
.IP "\fBmilter_end_of_header_macros (see 'postconf -d' output)\fR"
The macros that are sent to Milter (mail filter) applications
after the end of the message header.
+ .PP
+ Available in Postfix version 2.7 and later:
+ .IP "\fBmilter_header_checks (empty)\fR"
+ Optional lookup tables for content inspection of message headers
+ that are produced by Milter applications.
.SH "MIME PROCESSING CONTROLS"
.na
.nf
diff -cr /var/tmp/postfix-2.6.2/mantools/postlink ./mantools/postlink
*** /var/tmp/postfix-2.6.2/mantools/postlink Sun Apr 26 20:35:34 2009
--- ./mantools/postlink Thu Jun 4 20:56:43 2009
***************
*** 868,873 ****
--- 868,874 ----
s;\bmilter_unknown_command_macros\b;$&;g;
s;\bmilter_end_of_data_macros\b;$&;g;
s;\bmilter_end_of_header_macros\b;$&;g;
+ s;\bmilter_header_checks\b;$&;g;
# Multi-instance support
s;\bmulti_instance_directo[-]*\n*[ ]*ries\b;$&;g;
diff -cr /var/tmp/postfix-2.6.2/proto/postconf.proto ./proto/postconf.proto
*** /var/tmp/postfix-2.6.2/proto/postconf.proto Wed May 6 14:53:29 2009
--- ./proto/postconf.proto Sun Jun 7 16:39:55 2009
***************
*** 12269,12271 ****
--- 12269,12300 ----
when clients match the local_header_rewrite_clients parameter
setting. Earlier Postfix versions always add these headers; this
may break DKIM signatures that cover non-existent headers.
+
+ %PARAM milter_header_checks
+
+ Optional lookup tables for content inspection of message headers
+ that are produced by Milter applications. See the header_checks(5)
+ manual page available actions. Currently, PREPEND is not implemented.
+
+
+ The following example sends all mail that is marked as SPAM to
+ a spam handling machine. Note that matches are case-insensitive
+ by default.
+
+
+
+ /etc/postfix/main.cf:
+ milter_header_checks = pcre:/etc/postfix/milter_header_checks
+
+
+ /etc/postfix/milter_header_checks:
+ /^X-SPAM-FLAG:\s+YES/ FILTER mysmtp:sanitizer.example.com:25
+
+
+
+ The milter_header_checks mechanism could also be used for
+ whitelisting. For example it could be used to skip heavy content
+ scanning for DKIM-signed mail from known friendly domains.
+
+ This feature is available in Postfix 2.7, and as an optional
+ patch for Postfix 2.6.
diff -cr /var/tmp/postfix-2.6.2/src/cleanup/Makefile.in ./src/cleanup/Makefile.in
*** /var/tmp/postfix-2.6.2/src/cleanup/Makefile.in Mon Apr 27 20:53:31 2009
--- ./src/cleanup/Makefile.in Sun Jun 7 16:37:46 2009
***************
*** 349,354 ****
--- 349,355 ----
cleanup.o: ../../include/been_here.h
cleanup.o: ../../include/cleanup_user.h
cleanup.o: ../../include/dict.h
+ cleanup.o: ../../include/header_body_checks.h
cleanup.o: ../../include/header_opts.h
cleanup.o: ../../include/htable.h
cleanup.o: ../../include/iostuff.h
***************
*** 385,390 ****
--- 386,392 ----
cleanup_addr.o: ../../include/dict.h
cleanup_addr.o: ../../include/dsn_mask.h
cleanup_addr.o: ../../include/ext_prop.h
+ cleanup_addr.o: ../../include/header_body_checks.h
cleanup_addr.o: ../../include/header_opts.h
cleanup_addr.o: ../../include/htable.h
cleanup_addr.o: ../../include/iostuff.h
***************
*** 422,427 ****
--- 424,430 ----
cleanup_api.o: ../../include/dict.h
cleanup_api.o: ../../include/dsn.h
cleanup_api.o: ../../include/dsn_buf.h
+ cleanup_api.o: ../../include/header_body_checks.h
cleanup_api.o: ../../include/header_opts.h
cleanup_api.o: ../../include/htable.h
cleanup_api.o: ../../include/iostuff.h
***************
*** 456,461 ****
--- 459,465 ----
cleanup_body_edit.o: ../../include/been_here.h
cleanup_body_edit.o: ../../include/cleanup_user.h
cleanup_body_edit.o: ../../include/dict.h
+ cleanup_body_edit.o: ../../include/header_body_checks.h
cleanup_body_edit.o: ../../include/header_opts.h
cleanup_body_edit.o: ../../include/htable.h
cleanup_body_edit.o: ../../include/mail_conf.h
***************
*** 490,495 ****
--- 494,500 ----
cleanup_bounce.o: ../../include/dsn_buf.h
cleanup_bounce.o: ../../include/dsn_mask.h
cleanup_bounce.o: ../../include/dsn_util.h
+ cleanup_bounce.o: ../../include/header_body_checks.h
cleanup_bounce.o: ../../include/header_opts.h
cleanup_bounce.o: ../../include/htable.h
cleanup_bounce.o: ../../include/iostuff.h
***************
*** 527,532 ****
--- 532,538 ----
cleanup_envelope.o: ../../include/cleanup_user.h
cleanup_envelope.o: ../../include/dict.h
cleanup_envelope.o: ../../include/dsn_mask.h
+ cleanup_envelope.o: ../../include/header_body_checks.h
cleanup_envelope.o: ../../include/header_opts.h
cleanup_envelope.o: ../../include/htable.h
cleanup_envelope.o: ../../include/iostuff.h
***************
*** 545,550 ****
--- 551,557 ----
cleanup_envelope.o: ../../include/qmgr_user.h
cleanup_envelope.o: ../../include/rec_attr_map.h
cleanup_envelope.o: ../../include/rec_type.h
+ cleanup_envelope.o: ../../include/recipient_list.h
cleanup_envelope.o: ../../include/record.h
cleanup_envelope.o: ../../include/resolve_clnt.h
cleanup_envelope.o: ../../include/string_list.h
***************
*** 563,568 ****
--- 570,576 ----
cleanup_extracted.o: ../../include/cleanup_user.h
cleanup_extracted.o: ../../include/dict.h
cleanup_extracted.o: ../../include/dsn_mask.h
+ cleanup_extracted.o: ../../include/header_body_checks.h
cleanup_extracted.o: ../../include/header_opts.h
cleanup_extracted.o: ../../include/htable.h
cleanup_extracted.o: ../../include/iostuff.h
***************
*** 597,602 ****
--- 605,611 ----
cleanup_final.o: ../../include/been_here.h
cleanup_final.o: ../../include/cleanup_user.h
cleanup_final.o: ../../include/dict.h
+ cleanup_final.o: ../../include/header_body_checks.h
cleanup_final.o: ../../include/header_opts.h
cleanup_final.o: ../../include/htable.h
cleanup_final.o: ../../include/mail_conf.h
***************
*** 626,631 ****
--- 635,641 ----
cleanup_init.o: ../../include/dict.h
cleanup_init.o: ../../include/ext_prop.h
cleanup_init.o: ../../include/flush_clnt.h
+ cleanup_init.o: ../../include/header_body_checks.h
cleanup_init.o: ../../include/header_opts.h
cleanup_init.o: ../../include/htable.h
cleanup_init.o: ../../include/iostuff.h
***************
*** 658,663 ****
--- 668,674 ----
cleanup_map11.o: ../../include/been_here.h
cleanup_map11.o: ../../include/cleanup_user.h
cleanup_map11.o: ../../include/dict.h
+ cleanup_map11.o: ../../include/header_body_checks.h
cleanup_map11.o: ../../include/header_opts.h
cleanup_map11.o: ../../include/htable.h
cleanup_map11.o: ../../include/mail_addr_map.h
***************
*** 687,692 ****
--- 698,704 ----
cleanup_map1n.o: ../../include/been_here.h
cleanup_map1n.o: ../../include/cleanup_user.h
cleanup_map1n.o: ../../include/dict.h
+ cleanup_map1n.o: ../../include/header_body_checks.h
cleanup_map1n.o: ../../include/header_opts.h
cleanup_map1n.o: ../../include/htable.h
cleanup_map1n.o: ../../include/mail_addr_map.h
***************
*** 717,722 ****
--- 729,735 ----
cleanup_masquerade.o: ../../include/been_here.h
cleanup_masquerade.o: ../../include/cleanup_user.h
cleanup_masquerade.o: ../../include/dict.h
+ cleanup_masquerade.o: ../../include/header_body_checks.h
cleanup_masquerade.o: ../../include/header_opts.h
cleanup_masquerade.o: ../../include/htable.h
cleanup_masquerade.o: ../../include/mail_conf.h
***************
*** 750,755 ****
--- 763,769 ----
cleanup_message.o: ../../include/dict.h
cleanup_message.o: ../../include/dsn_util.h
cleanup_message.o: ../../include/ext_prop.h
+ cleanup_message.o: ../../include/header_body_checks.h
cleanup_message.o: ../../include/header_opts.h
cleanup_message.o: ../../include/htable.h
cleanup_message.o: ../../include/iostuff.h
***************
*** 790,795 ****
--- 804,811 ----
cleanup_milter.o: ../../include/cleanup_user.h
cleanup_milter.o: ../../include/dict.h
cleanup_milter.o: ../../include/dsn_mask.h
+ cleanup_milter.o: ../../include/dsn_util.h
+ cleanup_milter.o: ../../include/header_body_checks.h
cleanup_milter.o: ../../include/header_opts.h
cleanup_milter.o: ../../include/htable.h
cleanup_milter.o: ../../include/iostuff.h
***************
*** 828,833 ****
--- 844,850 ----
cleanup_out.o: ../../include/been_here.h
cleanup_out.o: ../../include/cleanup_user.h
cleanup_out.o: ../../include/dict.h
+ cleanup_out.o: ../../include/header_body_checks.h
cleanup_out.o: ../../include/header_opts.h
cleanup_out.o: ../../include/htable.h
cleanup_out.o: ../../include/lex_822.h
***************
*** 865,870 ****
--- 882,888 ----
cleanup_out_recipient.o: ../../include/dsn_buf.h
cleanup_out_recipient.o: ../../include/dsn_mask.h
cleanup_out_recipient.o: ../../include/ext_prop.h
+ cleanup_out_recipient.o: ../../include/header_body_checks.h
cleanup_out_recipient.o: ../../include/header_opts.h
cleanup_out_recipient.o: ../../include/htable.h
cleanup_out_recipient.o: ../../include/iostuff.h
***************
*** 899,904 ****
--- 917,923 ----
cleanup_region.o: ../../include/been_here.h
cleanup_region.o: ../../include/cleanup_user.h
cleanup_region.o: ../../include/dict.h
+ cleanup_region.o: ../../include/header_body_checks.h
cleanup_region.o: ../../include/header_opts.h
cleanup_region.o: ../../include/htable.h
cleanup_region.o: ../../include/mail_conf.h
***************
*** 925,930 ****
--- 944,950 ----
cleanup_rewrite.o: ../../include/been_here.h
cleanup_rewrite.o: ../../include/cleanup_user.h
cleanup_rewrite.o: ../../include/dict.h
+ cleanup_rewrite.o: ../../include/header_body_checks.h
cleanup_rewrite.o: ../../include/header_opts.h
cleanup_rewrite.o: ../../include/htable.h
cleanup_rewrite.o: ../../include/iostuff.h
***************
*** 956,961 ****
--- 976,982 ----
cleanup_state.o: ../../include/been_here.h
cleanup_state.o: ../../include/cleanup_user.h
cleanup_state.o: ../../include/dict.h
+ cleanup_state.o: ../../include/header_body_checks.h
cleanup_state.o: ../../include/header_opts.h
cleanup_state.o: ../../include/htable.h
cleanup_state.o: ../../include/iostuff.h
diff -cr /var/tmp/postfix-2.6.2/src/cleanup/cleanup.c ./src/cleanup/cleanup.c
*** /var/tmp/postfix-2.6.2/src/cleanup/cleanup.c Tue Apr 28 13:49:23 2009
--- ./src/cleanup/cleanup.c Sun Jun 7 16:37:46 2009
***************
*** 170,175 ****
--- 170,180 ----
/* .IP "\fBmilter_end_of_header_macros (see 'postconf -d' output)\fR"
/* The macros that are sent to Milter (mail filter) applications
/* after the end of the message header.
+ /* .PP
+ /* Available in Postfix version 2.7 and later:
+ /* .IP "\fBmilter_header_checks (empty)\fR"
+ /* Optional lookup tables for content inspection of message headers
+ /* that are produced by Milter applications.
/* MIME PROCESSING CONTROLS
/* .ad
/* .fi
diff -cr /var/tmp/postfix-2.6.2/src/cleanup/cleanup.h ./src/cleanup/cleanup.h
*** /var/tmp/postfix-2.6.2/src/cleanup/cleanup.h Mon Apr 27 14:36:57 2009
--- ./src/cleanup/cleanup.h Sun Jun 7 16:37:46 2009
***************
*** 32,37 ****
--- 32,38 ----
#include
#include
#include
+ #include
/*
* Milter library.
***************
*** 78,83 ****
--- 79,86 ----
off_t append_rcpt_pt_target; /* target of above record */
off_t append_hdr_pt_offset; /* append header here */
off_t append_hdr_pt_target; /* target of above record */
+ off_t append_meta_pt_offset; /* append meta record here */
+ off_t append_meta_pt_target; /* target of above record */
ssize_t rcpt_count; /* recipient count */
char *reason; /* failure reason */
char *smtp_reply; /* failure reason, SMTP-style */
***************
*** 108,113 ****
--- 111,118 ----
VSTRING *milter_ext_from; /* externalized sender */
VSTRING *milter_ext_rcpt; /* externalized recipient */
VSTRING *milter_err_text; /* milter call-back reply */
+ HBC_CHECKS *milter_hbc_checks; /* Milter header checks */
+ VSTRING *milter_hbc_reply; /* Milter header checks reply */
/*
* Support for Milter body replacement requests.
diff -cr /var/tmp/postfix-2.6.2/src/cleanup/cleanup_extracted.c ./src/cleanup/cleanup_extracted.c
*** /var/tmp/postfix-2.6.2/src/cleanup/cleanup_extracted.c Sun May 10 14:50:52 2009
--- ./src/cleanup/cleanup_extracted.c Sun Jun 7 16:37:46 2009
***************
*** 188,193 ****
--- 188,201 ----
cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s",
MAIL_ATTR_ENCODING, encoding);
state->flags |= CLEANUP_FLAG_INRCPT;
+ /* Make room to append more meta records. */
+ if (state->milters || cleanup_milters) {
+ if ((state->append_meta_pt_offset = vstream_ftell(state->dst)) < 0)
+ msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
+ cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
+ if ((state->append_meta_pt_target = vstream_ftell(state->dst)) < 0)
+ msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
+ }
}
/*
diff -cr /var/tmp/postfix-2.6.2/src/cleanup/cleanup_init.c ./src/cleanup/cleanup_init.c
*** /var/tmp/postfix-2.6.2/src/cleanup/cleanup_init.c Mon Mar 30 16:52:34 2009
--- ./src/cleanup/cleanup_init.c Sun Jun 7 16:37:46 2009
***************
*** 161,166 ****
--- 161,167 ----
char *var_milt_eod_macros; /* end-of-data macros */
char *var_milt_unk_macros; /* unknown command macros */
char *var_cleanup_milters; /* non-SMTP mail */
+ char *var_milt_head_checks; /* post-Milter header checks */
int var_auto_8bit_enc_hdr; /* auto-detect 8bit encoding header */
int var_always_add_hdrs; /* always add missing headers */
***************
*** 227,232 ****
--- 228,234 ----
VAR_MILT_EOD_MACROS, DEF_MILT_EOD_MACROS, &var_milt_eod_macros, 0, 0,
VAR_MILT_UNK_MACROS, DEF_MILT_UNK_MACROS, &var_milt_unk_macros, 0, 0,
VAR_CLEANUP_MILTERS, DEF_CLEANUP_MILTERS, &var_cleanup_milters, 0, 0,
+ VAR_MILT_HEAD_CHECKS, DEF_MILT_HEAD_CHECKS, &var_milt_head_checks, 0, 0,
0,
};
diff -cr /var/tmp/postfix-2.6.2/src/cleanup/cleanup_milter.c ./src/cleanup/cleanup_milter.c
*** /var/tmp/postfix-2.6.2/src/cleanup/cleanup_milter.c Tue Apr 28 15:43:45 2009
--- ./src/cleanup/cleanup_milter.c Sun Jun 7 16:37:46 2009
***************
*** 105,110 ****
--- 105,111 ----
#include
#include
#include
+ #include
/* Application-specific. */
***************
*** 216,221 ****
--- 217,523 ----
#define STR(x) vstring_str(x)
#define LEN(x) VSTRING_LEN(x)
+ /* cleanup_milter_hbc_log - log post-milter header/body_checks action */
+
+ static void cleanup_milter_hbc_log(void *context, const char *action,
+ const char *where, const char *line,
+ const char *optional_text)
+ {
+ const CLEANUP_STATE *state = (CLEANUP_STATE *) context;
+ const char *attr;
+
+ vstring_sprintf(state->temp1, "%s: milter-%s-%s: %s %.60s from %s[%s];",
+ state->queue_id, where, action, where, line,
+ state->client_name, state->client_addr);
+ if (state->sender)
+ vstring_sprintf_append(state->temp1, " from=<%s>", state->sender);
+ if (state->recip)
+ vstring_sprintf_append(state->temp1, " to=<%s>", state->recip);
+ if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_PROTO_NAME)) != 0)
+ vstring_sprintf_append(state->temp1, " proto=%s", attr);
+ if ((attr = nvtable_find(state->attr, MAIL_ATTR_LOG_HELO_NAME)) != 0)
+ vstring_sprintf_append(state->temp1, " helo=<%s>", attr);
+ if (optional_text)
+ vstring_sprintf_append(state->temp1, ": %s", optional_text);
+ msg_info("%s", vstring_str(state->temp1));
+ }
+
+ /* cleanup_milter_header_prepend - prepend header to milter-generated header */
+
+ static void cleanup_milter_header_prepend(void *context, int rec_type,
+ const char *buf, ssize_t len, off_t offset)
+ {
+ msg_warn("the milter_header/body_checks prepend action is not implemented");
+ }
+
+ /* cleanup_milter_hbc_extend - additional header/body_checks actions */
+
+ static char *cleanup_milter_hbc_extend(void *context, const char *command,
+ int cmd_len, const char *optional_text,
+ const char *where, const char *buf,
+ ssize_t buf_len, off_t offset)
+ {
+ CLEANUP_STATE *state = (CLEANUP_STATE *) context;
+ const char *map_class = VAR_MILT_HEAD_CHECKS; /* XXX */
+
+ #define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0)
+
+ /*
+ * These are currently our mutually-exclusive ways of not receiving mail:
+ * "reject" and "discard". Only these can be reported to the up-stream
+ * Postfix libmilter code, because sending any reply there causes Postfix
+ * libmilter to skip further "edit" requests. By way of safety net, each
+ * of these must also reset CLEANUP_FLAG_FILTER_ALL.
+ */
+ #define CLEANUP_MILTER_REJECTING_OR_DISCARDING_MESSAGE(state) \
+ ((state->flags & CLEANUP_FLAG_DISCARD) || (state->errs & CLEANUP_STAT_CONT))
+
+ /*
+ * We log all header/body-checks actions here, because we know the
+ * details of the message content that triggered the action. We report
+ * detail-free milter-reply values (reject/discard, stored in the
+ * milter_hbc_reply state member) to the Postfix libmilter code, so that
+ * Postfix libmilter can stop sending requests.
+ *
+ * We also set all applicable cleanup flags here, because there is no
+ * guarantee that Postfix libmilter will propagate our own milter-reply
+ * value to cleanup_milter_inspect() which calls cleanup_milter_apply().
+ * The latter translates responses from Milter applications into cleanup
+ * flags, and logs the response text. Postfix libmilter can convey only
+ * one milter-reply value per email message, and that reply may even come
+ * from outside Postfix.
+ *
+ * To suppress redundant logging, cleanup_milter_apply() does nothing when
+ * the milter-reply value matches the saved text in the milter_hbc_reply
+ * state member. As we remember only one milter-reply value, we can't
+ * report multiple milter-reply values per email message. We satisfy this
+ * constraint, because we already clear the CLEANUP_FLAG_FILTER_ALL flags
+ * to terminate further header inspection.
+ */
+ if ((state->flags & CLEANUP_FLAG_FILTER_ALL) == 0)
+ return ((char *) buf);
+
+ if (STREQUAL(command, "REJECT", cmd_len)) {
+ const CLEANUP_STAT_DETAIL *detail;
+
+ if (state->reason)
+ myfree(state->reason);
+ detail = cleanup_stat_detail(CLEANUP_STAT_CONT);
+ if (*optional_text) {
+ state->reason = dsn_prepend(detail->dsn, optional_text);
+ if (*state->reason != '4' && *state->reason != '5') {
+ msg_warn("bad DSN action in %s -- need 4.x.x or 5.x.x",
+ optional_text);
+ *state->reason = '4';
+ }
+ } else {
+ state->reason = dsn_prepend(detail->dsn, detail->text);
+ }
+ if (*state->reason == '4')
+ state->errs |= CLEANUP_STAT_DEFER;
+ else
+ state->errs |= CLEANUP_STAT_CONT;
+ state->flags &= ~CLEANUP_FLAG_FILTER_ALL;
+ cleanup_milter_hbc_log(context, "reject", where, buf, state->reason);
+ vstring_sprintf(state->milter_hbc_reply, "%d %s",
+ detail->smtp, state->reason);
+ STR(state->milter_hbc_reply)[0] = *state->reason;
+ return ((char *) buf);
+ }
+ if (STREQUAL(command, "FILTER", cmd_len)) {
+ if (*optional_text == 0) {
+ msg_warn("missing FILTER command argument in %s map", map_class);
+ } else if (strchr(optional_text, ':') == 0) {
+ msg_warn("bad FILTER command %s in %s -- "
+ "need transport:destination",
+ optional_text, map_class);
+ } else {
+ if (state->filter)
+ myfree(state->filter);
+ state->filter = mystrdup(optional_text);
+ cleanup_milter_hbc_log(context, "filter", where, buf,
+ optional_text);
+ }
+ return ((char *) buf);
+ }
+ if (STREQUAL(command, "DISCARD", cmd_len)) {
+ cleanup_milter_hbc_log(context, "discard", where, buf, optional_text);
+ vstring_strcpy(state->milter_hbc_reply, "D");
+ state->flags |= CLEANUP_FLAG_DISCARD;
+ state->flags &= ~CLEANUP_FLAG_FILTER_ALL;
+ return ((char *) buf);
+ }
+ if (STREQUAL(command, "HOLD", cmd_len)) {
+ if ((state->flags & (CLEANUP_FLAG_HOLD | CLEANUP_FLAG_DISCARD)) == 0) {
+ cleanup_milter_hbc_log(context, "hold", where, buf, optional_text);
+ state->flags |= CLEANUP_FLAG_HOLD;
+ }
+ return ((char *) buf);
+ }
+ if (STREQUAL(command, "REDIRECT", cmd_len)) {
+ if (strchr(optional_text, '@') == 0) {
+ msg_warn("bad REDIRECT target \"%s\" in %s map -- "
+ "need user@domain",
+ optional_text, map_class);
+ } else {
+ if (state->redirect)
+ myfree(state->redirect);
+ state->redirect = mystrdup(optional_text);
+ cleanup_milter_hbc_log(context, "redirect", where, buf,
+ optional_text);
+ state->flags &= ~CLEANUP_FLAG_FILTER_ALL;
+ }
+ return ((char *) buf);
+ }
+ msg_warn("unknown command in %s map: %s", map_class, command);
+ return ((char *) buf);
+ }
+
+ /* cleanup_milter_header_checks - inspect Milter-generated header */
+
+ static int cleanup_milter_header_checks(CLEANUP_STATE *state, VSTRING *buf)
+ {
+ char *ret;
+
+ /*
+ * Milter application "add/insert/replace header" requests happen at the
+ * end-of-message stage, therefore all the header operations are relative
+ * to the primary message header.
+ */
+ ret = hbc_header_checks((void *) state, state->milter_hbc_checks,
+ MIME_HDR_PRIMARY, (HEADER_OPTS *) 0,
+ buf, (off_t) 0);
+ if (ret == 0) {
+ return (0);
+ } else {
+ if (ret != STR(buf)) {
+ vstring_strcpy(buf, ret);
+ myfree(ret);
+ }
+ return (1);
+ }
+ }
+
+ /* cleanup_milter_hbc_add_meta_records - add REDIRECT or FILTER meta records */
+
+ static void cleanup_milter_hbc_add_meta_records(CLEANUP_STATE *state)
+ {
+ const char *myname = "cleanup_milter_hbc_add_meta_records";
+ off_t reverse_ptr_offset;
+ off_t new_meta_offset;
+
+ /*
+ * Note: this code runs while the Milter infrastructure is being torn
+ * down. For this reason we handle all I/O errors here on the spot,
+ * instead of reporting them back through the Milter infrastructure.
+ */
+
+ /*
+ * Sanity check.
+ */
+ if (state->append_meta_pt_offset < 0)
+ msg_panic("%s: no meta append pointer location", myname);
+ if (state->append_meta_pt_target < 0)
+ msg_panic("%s: no meta append pointer target", myname);
+
+ /*
+ * Allocate space after the end of the queue file, and write the meta
+ * record(s), followed by a reverse pointer record that points to the
+ * target of the old "meta record append" pointer record. This reverse
+ * pointer record becomes the new "meta record append" pointer record.
+ * Although the new "meta record append" pointer record will never be
+ * used, we update it here to make the code more similar to other code
+ * that inserts/appends content, so that common code can be factored out
+ * later.
+ */
+ if ((new_meta_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) {
+ msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+ state->errs |= CLEANUP_STAT_WRITE;
+ return;
+ }
+ if (state->filter != 0)
+ cleanup_out_string(state, REC_TYPE_FILT, state->filter);
+ if (state->redirect != 0)
+ cleanup_out_string(state, REC_TYPE_RDR, state->redirect);
+ if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) {
+ msg_warn("%s: vstream_ftell file %s: %m", myname, cleanup_path);
+ state->errs |= CLEANUP_STAT_WRITE;
+ return;
+ }
+ cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
+ (long) state->append_meta_pt_target);
+
+ /*
+ * Pointer flipping: update the old "meta record append" pointer record
+ * value with the location of the new meta record.
+ */
+ if (vstream_fseek(state->dst, state->append_meta_pt_offset, SEEK_SET) < 0) {
+ msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+ state->errs |= CLEANUP_STAT_WRITE;
+ return;
+ }
+ cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT,
+ (long) new_meta_offset);
+
+ /*
+ * Update the in-memory "meta append" pointer record location with the
+ * location of the reverse pointer record that follows the new meta
+ * record. The target of the "meta append" pointer record does not
+ * change; it's always the record that follows the dummy pointer record
+ * that was written while Postfix received the message.
+ */
+ state->append_meta_pt_offset = reverse_ptr_offset;
+
+ /*
+ * Note: state->append_meta_pt_target never changes.
+ */
+ }
+
+ /* cleanup_milter_header_checks_init - initialize post-Milter header checks */
+
+ static void cleanup_milter_header_checks_init(CLEANUP_STATE *state)
+ {
+ #define NO_NESTED_HDR_NAME ""
+ #define NO_NESTED_HDR_VALUE ""
+ #define NO_MIME_HDR_NAME ""
+ #define NO_MIME_HDR_VALUE ""
+
+ static /* XXX not const */ HBC_CALL_BACKS call_backs = {
+ cleanup_milter_hbc_log,
+ cleanup_milter_header_prepend,
+ cleanup_milter_hbc_extend,
+ };
+
+ state->milter_hbc_checks =
+ hbc_header_checks_create(VAR_MILT_HEAD_CHECKS, var_milt_head_checks,
+ NO_MIME_HDR_NAME, NO_MIME_HDR_VALUE,
+ NO_NESTED_HDR_NAME, NO_NESTED_HDR_VALUE,
+ &call_backs);
+ state->milter_hbc_reply = vstring_alloc(100);
+ if (state->filter)
+ myfree(state->filter);
+ state->filter = 0;
+ if (state->redirect)
+ myfree(state->redirect);
+ state->redirect = 0;
+ }
+
+ /* cleanup_milter_hbc_finish - finalize post-Milter header checks */
+
+ static void cleanup_milter_hbc_finish(CLEANUP_STATE *state)
+ {
+ if (state->milter_hbc_checks)
+ hbc_header_checks_free(state->milter_hbc_checks);
+ state->milter_hbc_checks = 0;
+ if (state->milter_hbc_reply)
+ vstring_free(state->milter_hbc_reply);
+ state->milter_hbc_reply = 0;
+ if (CLEANUP_OUT_OK(state)
+ && !CLEANUP_MILTER_REJECTING_OR_DISCARDING_MESSAGE(state)
+ && (state->filter || state->redirect))
+ cleanup_milter_hbc_add_meta_records(state);
+ }
+
/*
* Milter replies.
*/
***************
*** 306,311 ****
--- 608,625 ----
msg_panic("%s: no header append pointer target", myname);
/*
+ * Return early when Milter header checks request that this header record
+ * be dropped.
+ */
+ buf = vstring_alloc(100);
+ vstring_sprintf(buf, "%s:%s%s", name, space, value);
+ if (state->milter_hbc_checks
+ && cleanup_milter_header_checks(state, buf) == 0) {
+ vstring_free(buf);
+ return (0);
+ }
+
+ /*
* Allocate space after the end of the queue file, and write the header
* record(s), followed by a reverse pointer record that points to the
* target of the old "header append" pointer record. This reverse pointer
***************
*** 313,322 ****
*/
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%s", name, space, value);
cleanup_out_header(state, buf); /* Includes padding */
vstring_free(buf);
if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) {
--- 627,635 ----
*/
if ((new_hdr_offset = vstream_fseek(state->dst, (off_t) 0, SEEK_END)) < 0) {
msg_warn("%s: seek file %s: %m", myname, cleanup_path);
+ vstring_free(buf);
return (cleanup_milter_error(state, errno));
}
cleanup_out_header(state, buf); /* Includes padding */
vstring_free(buf);
if ((reverse_ptr_offset = vstream_ftell(state->dst)) < 0) {
***************
*** 355,361 ****
/*
* In case of error while doing record output.
*/
! return (CLEANUP_OUT_OK(state) ? 0 : cleanup_milter_error(state, 0));
}
/* cleanup_find_header_start - find specific header instance */
--- 668,680 ----
/*
* In case of error while doing record output.
*/
! return (CLEANUP_OUT_OK(state) == 0 ? cleanup_milter_error(state, 0) :
! state->milter_hbc_reply && LEN(state->milter_hbc_reply) ?
! STR(state->milter_hbc_reply) : 0);
!
! /*
! * Note: state->append_hdr_pt_target never changes.
! */
}
/* cleanup_find_header_start - find specific header instance */
***************
*** 672,677 ****
--- 991,1005 ----
*/
/*
+ * Return early when Milter header checks request that this header record
+ * be dropped.
+ */
+ vstring_sprintf(buf, "%s:%s%s", new_hdr_name, hdr_space, new_hdr_value);
+ if (state->milter_hbc_checks
+ && cleanup_milter_header_checks(state, buf) == 0)
+ CLEANUP_PATCH_HEADER_RETURN(0);
+
+ /*
* Write the new header to a new location after the end of the queue
* file.
*/
***************
*** 679,685 ****
msg_warn("%s: seek file %s: %m", myname, cleanup_path);
CLEANUP_PATCH_HEADER_RETURN(cleanup_milter_error(state, errno));
}
- vstring_sprintf(buf, "%s:%s%s", new_hdr_name, hdr_space, new_hdr_value);
cleanup_out_header(state, buf); /* Includes padding */
if (msg_verbose > 1)
msg_info("%s: %ld: write %.*s", myname, (long) new_hdr_offset,
--- 1007,1012 ----
***************
*** 734,741 ****
/*
* 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.
--- 1061,1070 ----
/*
* In case of error while doing record output.
*/
! CLEANUP_PATCH_HEADER_RETURN(
! CLEANUP_OUT_OK(state) == 0 ? cleanup_milter_error(state, 0) :
! state->milter_hbc_reply && LEN(state->milter_hbc_reply) ?
! STR(state->milter_hbc_reply) : 0);
/*
* Note: state->append_hdr_pt_target never changes.
***************
*** 1493,1498 ****
--- 1822,1850 ----
msg_info("%s: %s", myname, resp);
/*
+ * Don't process our own milter_header/body checks replies. See comments
+ * in cleanup_milter_hbc_extend().
+ */
+ if (state->milter_hbc_reply &&
+ strcmp(resp, STR(state->milter_hbc_reply)) == 0)
+ return (0);
+
+ /*
+ * Don't process Milter replies that are redundant because header/body
+ * checks already decided that we will not receive the message; or Milter
+ * replies that would have conflicting effect with the outcome of
+ * header/body checks (for example, header_checks "discard" action
+ * followed by Milter "reject" reply). Logging both actions would look
+ * silly.
+ */
+ if (CLEANUP_MILTER_REJECTING_OR_DISCARDING_MESSAGE(state)) {
+ if (msg_verbose)
+ msg_info("%s: ignoring redundant or conflicting milter reply: %s",
+ state->queue_id, resp);
+ return (0);
+ }
+
+ /*
* Sanity check.
*/
if (state->client_name == 0)
***************
*** 1619,1630 ****
--- 1971,1995 ----
cleanup_milter_client_init(state);
/*
+ * Prologue: prepare for Milter header/body checks.
+ */
+ if (*var_milt_head_checks)
+ cleanup_milter_header_checks_init(state);
+
+ /*
* Process mail filter replies. The reply format is verified by the mail
* filter library.
*/
if ((resp = milter_message(milters, state->handle->stream,
state->data_offset)) != 0)
cleanup_milter_apply(state, "END-OF-MESSAGE", resp);
+
+ /*
+ * Epilogue: finalize Milter header/body checks.
+ */
+ if (*var_milt_head_checks)
+ cleanup_milter_hbc_finish(state);
+
if (msg_verbose)
msg_info("leave %s", myname);
}
***************
*** 1779,1784 ****
--- 2144,2150 ----
char *var_milt_daemon_name = "host.example.com";
char *var_milt_v = DEF_MILT_V;
MILTERS *cleanup_milters = (MILTERS *) ((char *) sizeof(*cleanup_milters));
+ char *var_milt_head_checks = "";
/* Dummies to satisfy unused external references. */
***************
*** 1822,1829 ****
--- 2188,2199 ----
msg_warn(" ins_header index name [value]");
msg_warn(" upd_header index name [value]");
msg_warn(" del_header index name");
+ msg_warn(" chg_from addr parameters");
msg_warn(" add_rcpt addr");
+ msg_warn(" add_rcpt_par addr parameters");
msg_warn(" del_rcpt addr");
+ msg_warn(" replbody pathname");
+ msg_warn(" header_checks type:name");
}
/* flatten_args - unparse partial command line */
***************
*** 1898,1903 ****
--- 2268,2282 ----
if ((state->append_rcpt_pt_target =
vstream_ftell(state->dst)) < 0)
msg_fatal("file %s: vstream_ftell: %m", cleanup_path);
+ } else if (curr_offset > state->xtra_offset
+ && state->append_meta_pt_offset < 0) {
+ state->append_meta_pt_offset = curr_offset;
+ if (atol(STR(buf)) != 0)
+ msg_fatal("file %s: bad dummy meta PTR record: %s",
+ cleanup_path, STR(buf));
+ if ((state->append_meta_pt_target =
+ vstream_ftell(state->dst)) < 0)
+ msg_fatal("file %s: vstream_ftell: %m", cleanup_path);
}
} else {
if (state->append_hdr_pt_offset < 0) {
***************
*** 1912,1918 ****
}
}
if (state->append_rcpt_pt_offset > 0
! && state->append_hdr_pt_offset > 0)
break;
}
if (msg_verbose) {
--- 2291,2299 ----
}
}
if (state->append_rcpt_pt_offset > 0
! && state->append_hdr_pt_offset > 0
! && (rec_type == REC_TYPE_END
! || state->append_meta_pt_offset > 0))
break;
}
if (msg_verbose) {
***************
*** 1944,1949 ****
--- 2325,2335 ----
CLEANUP_STATE *state = cleanup_state_alloc((VSTREAM *) 0);
state->queue_id = mystrdup("NOQUEUE");
+ state->sender = mystrdup("sender");
+ state->recip = mystrdup("recipient");
+ state->client_name = "client_name";
+ state->client_addr = "client_addr";
+ state->flags |= CLEANUP_FLAG_FILTER_ALL;
msg_vstream_init(argv[0], VSTREAM_ERR);
var_line_limit = DEF_LINE_LIMIT;
***************
*** 1952,1957 ****
--- 2338,2344 ----
for (;;) {
ARGV *argv;
ssize_t index;
+ const char *resp = 0;
if (istty) {
vstream_printf("- ");
***************
*** 1995,2007 ****
} else if (state->dst == 0) {
msg_warn("no open queue file");
} else if (strcmp(argv->argv[0], "close") == 0) {
close_queue_file(state);
} else if (strcmp(argv->argv[0], "add_header") == 0) {
if (argv->argc < 2) {
msg_warn("bad add_header argument count: %d", argv->argc);
} else {
flatten_args(arg_buf, argv->argv + 2);
! cleanup_add_header(state, argv->argv[1], " ", STR(arg_buf));
}
} else if (strcmp(argv->argv[0], "ins_header") == 0) {
if (argv->argc < 3) {
--- 2382,2404 ----
} else if (state->dst == 0) {
msg_warn("no open queue file");
} else if (strcmp(argv->argv[0], "close") == 0) {
+ if (*var_milt_head_checks) {
+ cleanup_milter_hbc_finish(state);
+ var_milt_head_checks = "";
+ }
close_queue_file(state);
+ } else if (state->milter_hbc_reply && LEN(state->milter_hbc_reply)) {
+ /* Postfix libmilter would skip further requests. */
+ msg_info("ignoring: %s %s %s", argv->argv[0],
+ argv->argc > 1 ? argv->argv[1] : "",
+ argv->argc > 2 ? argv->argv[2] : "");
+ continue;
} else if (strcmp(argv->argv[0], "add_header") == 0) {
if (argv->argc < 2) {
msg_warn("bad add_header argument count: %d", argv->argc);
} else {
flatten_args(arg_buf, argv->argv + 2);
! resp = cleanup_add_header(state, argv->argv[1], " ", STR(arg_buf));
}
} else if (strcmp(argv->argv[0], "ins_header") == 0) {
if (argv->argc < 3) {
***************
*** 2010,2016 ****
msg_warn("bad ins_header index value");
} else {
flatten_args(arg_buf, argv->argv + 3);
! cleanup_ins_header(state, index, argv->argv[2], " ", STR(arg_buf));
}
} else if (strcmp(argv->argv[0], "upd_header") == 0) {
if (argv->argc < 3) {
--- 2407,2413 ----
msg_warn("bad ins_header index value");
} else {
flatten_args(arg_buf, argv->argv + 3);
! resp = cleanup_ins_header(state, index, argv->argv[2], " ", STR(arg_buf));
}
} else if (strcmp(argv->argv[0], "upd_header") == 0) {
if (argv->argc < 3) {
***************
*** 2019,2025 ****
msg_warn("bad upd_header index value");
} else {
flatten_args(arg_buf, argv->argv + 3);
! cleanup_upd_header(state, index, argv->argv[2], " ", STR(arg_buf));
}
} else if (strcmp(argv->argv[0], "del_header") == 0) {
if (argv->argc != 3) {
--- 2416,2422 ----
msg_warn("bad upd_header index value");
} else {
flatten_args(arg_buf, argv->argv + 3);
! resp = cleanup_upd_header(state, index, argv->argv[2], " ", STR(arg_buf));
}
} else if (strcmp(argv->argv[0], "del_header") == 0) {
if (argv->argc != 3) {
***************
*** 2072,2084 ****
--- 2469,2498 ----
vstream_fclose(fp);
}
}
+ } else if (strcmp(argv->argv[0], "header_checks") == 0) {
+ if (argv->argc != 2) {
+ msg_warn("bad header_checks argument count: %d", argv->argc);
+ } else if (*var_milt_head_checks) {
+ msg_warn("can't change header checks");
+ } else {
+ var_milt_head_checks = mystrdup(argv->argv[1]);
+ cleanup_milter_header_checks_init(state);
+ }
} else {
msg_warn("bad command: %s", argv->argv[0]);
}
argv_free(argv);
+ if (resp)
+ cleanup_milter_apply(state, "END-OF-MESSAGE", resp);
}
vstring_free(inbuf);
vstring_free(arg_buf);
+ if (state->append_meta_pt_offset >= 0) {
+ if (state->flags)
+ msg_info("flags = %s", cleanup_strflags(state->flags));
+ if (state->errs)
+ msg_info("errs = %s", cleanup_strerror(state->errs));
+ }
cleanup_state_free(state);
return (0);
diff -cr /var/tmp/postfix-2.6.2/src/cleanup/cleanup_state.c ./src/cleanup/cleanup_state.c
*** /var/tmp/postfix-2.6.2/src/cleanup/cleanup_state.c Mon Apr 27 14:37:16 2009
--- ./src/cleanup/cleanup_state.c Sun Jun 7 16:37:46 2009
***************
*** 97,102 ****
--- 97,106 ----
state->append_rcpt_pt_target = -1;
state->append_hdr_pt_offset = -1;
state->append_hdr_pt_target = -1;
+ state->append_meta_pt_offset = -1;
+ state->append_meta_pt_target = -1;
+ state->milter_hbc_checks = 0;
+ state->milter_hbc_reply = 0;
state->rcpt_count = 0;
state->reason = 0;
state->smtp_reply = 0;
diff -cr /var/tmp/postfix-2.6.2/src/global/mail_params.h ./src/global/mail_params.h
*** /var/tmp/postfix-2.6.2/src/global/mail_params.h Mon May 11 10:48:42 2009
--- ./src/global/mail_params.h Thu Jun 4 18:31:03 2009
***************
*** 2982,2987 ****
--- 2982,2991 ----
#define DEF_MILT_V "$" VAR_MAIL_NAME " $" VAR_MAIL_VERSION
extern char *var_milt_v;
+ #define VAR_MILT_HEAD_CHECKS "milter_header_checks"
+ #define DEF_MILT_HEAD_CHECKS ""
+ extern char *var_milt_head_checks;
+
/*
* What internal mail do we inspect/stamp/etc.? This is not yet safe enough
* to enable world-wide.