This is a minimal patch to add support for delivery status filtering in the Postfix 2.11 SMTP client, originally developed to change some soft TLS-related into hard errors. smtp_delivery_status_filter (default: empty) Optional filter for the smtp(8) delivery agent to change the delivery status code or explanatory text of successful or unsuccessful deliver- ies. Specify zero or more "type:table" lookup table names, separated by comma or whitespace. For each successful or unsuccessful delivery to a recipient, the tables are queried in the specified order with one line of text that is structured as follows: enhanced-status-code SPACE explanatory-text The first table match wins. The lookup result must have the same struc- ture as the query, a successful status code (2.X.X) must be replaced with a successful status code, an unsuccessful status code (4.X.X or 5.X.X) must be replaced with an unsuccessful status code, and the explanatory text field must be non-empty. Other results will result in a warning. Example: convert specific soft TLS errors into hard errors, by overrid- ing the first number in the enhanced status code. /etc/postfix/main.cf: smtp_delivery_status_filter = pcre:/etc/postfix/smtp_dsn_filter /etc/postfix/smtp_dsn_filter: /^4(\.\d+\.\d+ TLS is required, but host \S+ refused to start TLS: .+)/ 5$1 /^4(\.\d+\.\d+ TLS is required, but was not offered by host .+)/ 5$1 # Do not change the following into hard bounces. They may # result from a local configuration problem. # 4.\d+.\d+ TLS is required, but our TLS engine is unavailable # 4.\d+.\d+ TLS is required, but unavailable # 4.\d+.\d+ Cannot start TLS: handshake failure Notes: o This feature will NOT override the soft_bounce safety net. o This feature will change the enhanced status code and text that is logged to the maillog file, and that is reported to the sender in delivery confirmation or non-delivery notifications. o NOTE: This feature modifies Postfix SMTP client error or non- error messages that may or may not be derived from remote SMTP server responses. In contrast, the smtp_reply_filter feature modifies remote SMTP server responses only. diff -cr --new-file ../postfix-2.11.0/html/lmtp.8.html ./html/lmtp.8.html *** ../postfix-2.11.0/html/lmtp.8.html Fri Dec 20 19:37:50 2013 --- ./html/lmtp.8.html Sat Mar 22 19:32:09 2014 *************** *** 289,294 **** --- 289,301 ---- smtp_dns_support_level (empty) Level of DNS support in the Postfix SMTP client. + Imported from Postfix version 2.12: + + smtp_delivery_status_filter (empty) + Optional filter for the smtp(8) delivery agent to change the + delivery status code or explanatory text of successful or unsuc- + cessful deliveries. + MIME PROCESSING CONTROLS Available in Postfix version 2.0 and later: diff -cr --new-file ../postfix-2.11.0/html/postconf.5.html ./html/postconf.5.html *** ../postfix-2.11.0/html/postconf.5.html Sun Jan 12 13:01:05 2014 --- ./html/postconf.5.html Sat Mar 22 19:33:33 2014 *************** *** 9751,9756 **** --- 9751,9827 ---- +
smtp_delivery_status_filter + (default: empty)
+ +

Optional filter for the smtp(8) delivery agent to change the + delivery status code or explanatory text of successful or unsuccessful + deliveries.

+ +

Specify zero or more "type:table" lookup table names, separated + by comma or whitespace. For each successful or unsuccessful delivery + to a recipient, the tables are queried in the specified order with + one line of text that is structured as follows:

+ +
+ enhanced-status-code SPACE explanatory-text +
+ +

The first table match wins. The lookup result must have the + same structure as the query, a successful status code (2.X.X) must + be replaced with a successful status code, an unsuccessful status + code (4.X.X or 5.X.X) must be replaced with an unsuccessful status + code, and the explanatory text field must be non-empty. Other results + will result in a warning.

+ +

Example: convert specific soft TLS errors into hard errors, + by overriding the first number in the enhanced status code.

+ +
+
+ /etc/postfix/main.cf:
+     smtp_delivery_status_filter = pcre:/etc/postfix/smtp_dsn_filter
+ 
+
+ +
+
+ /etc/postfix/smtp_dsn_filter:
+     /^4(\.\d+\.\d+ TLS is required, but host \S+ refused to start TLS: .+)/
+         5$1
+     /^4(\.\d+\.\d+ TLS is required, but was not offered by host .+)/
+         5$1
+     # Do not change the following into hard bounces. They may
+     # result from a local configuration problem.
+     # 4.\d+.\d+ TLS is required, but our TLS engine is unavailable
+     # 4.\d+.\d+ TLS is required, but unavailable
+     # 4.\d+.\d+ Cannot start TLS: handshake failure
+ 
+
+ +

Notes:

+ + + +

This feature is imported from Postfix 2.12.

+ + +
+
smtp_destination_concurrency_limit (default: $default_destination_concurrency_limit)
diff -cr --new-file ../postfix-2.11.0/html/smtp.8.html ./html/smtp.8.html *** ../postfix-2.11.0/html/smtp.8.html Fri Dec 20 19:37:50 2013 --- ./html/smtp.8.html Sat Mar 22 19:32:09 2014 *************** *** 289,294 **** --- 289,301 ---- smtp_dns_support_level (empty) Level of DNS support in the Postfix SMTP client. + Imported from Postfix version 2.12: + + smtp_delivery_status_filter (empty) + Optional filter for the smtp(8) delivery agent to change the + delivery status code or explanatory text of successful or unsuc- + cessful deliveries. + MIME PROCESSING CONTROLS Available in Postfix version 2.0 and later: diff -cr --new-file ../postfix-2.11.0/man/man5/postconf.5 ./man/man5/postconf.5 *** ../postfix-2.11.0/man/man5/postconf.5 Sun Jan 12 13:01:05 2014 --- ./man/man5/postconf.5 Sat Mar 22 19:33:33 2014 *************** *** 5935,5940 **** --- 5935,6009 ---- than the local MTA itself. .PP This feature is available in Postfix 2.1 and later. + .SH smtp_delivery_status_filter (default: empty) + Optional filter for the \fBsmtp\fR(8) delivery agent to change the + delivery status code or explanatory text of successful or unsuccessful + deliveries. + .PP + Specify zero or more "type:table" lookup table names, separated + by comma or whitespace. For each successful or unsuccessful delivery + to a recipient, the tables are queried in the specified order with + one line of text that is structured as follows: + .sp + .in +4 + enhanced-status-code SPACE explanatory-text + .in -4 + .PP + The first table match wins. The lookup result must have the + same structure as the query, a successful status code (2.X.X) must + be replaced with a successful status code, an unsuccessful status + code (4.X.X or 5.X.X) must be replaced with an unsuccessful status + code, and the explanatory text field must be non-empty. Other results + will result in a warning. + .PP + Example: convert specific soft TLS errors into hard errors, + by overriding the first number in the enhanced status code. + .sp + .in +4 + .nf + .na + .ft C + /etc/postfix/main.cf: + smtp_delivery_status_filter = pcre:/etc/postfix/smtp_dsn_filter + .fi + .ad + .ft R + .in -4 + .sp + .in +4 + .nf + .na + .ft C + /etc/postfix/smtp_dsn_filter: + /^4(\e.\ed+\e.\ed+ TLS is required, but host \eS+ refused to start TLS: .+)/ + 5$1 + /^4(\e.\ed+\e.\ed+ TLS is required, but was not offered by host .+)/ + 5$1 + # Do not change the following into hard bounces. They may + # result from a local configuration problem. + # 4.\ed+.\ed+ TLS is required, but our TLS engine is unavailable + # 4.\ed+.\ed+ TLS is required, but unavailable + # 4.\ed+.\ed+ Cannot start TLS: handshake failure + .fi + .ad + .ft R + .in -4 + .PP + Notes: + .IP \(bu + This feature will NOT override the soft_bounce safety net. + .IP \(bu + This feature will change the enhanced status code and text + that is logged to the maillog file, and that is reported to the + sender in delivery confirmation or non-delivery notifications. + .IP \(bu + NOTE: This feature modifies Postfix SMTP client error or + non-error messages that may or may not be derived from remote SMTP + server responses. In contrast, the smtp_reply_filter feature + modifies remote SMTP server responses only. + .br + .PP + This feature is imported from Postfix 2.12. .SH smtp_destination_concurrency_limit (default: $default_destination_concurrency_limit) The maximal number of parallel deliveries to the same destination via the smtp message delivery transport. This limit is enforced by diff -cr --new-file ../postfix-2.11.0/man/man8/smtp.8 ./man/man8/smtp.8 *** ../postfix-2.11.0/man/man8/smtp.8 Tue Nov 26 17:59:16 2013 --- ./man/man8/smtp.8 Sat Mar 22 19:31:57 2014 *************** *** 273,278 **** --- 273,284 ---- Available in Postfix version 2.11 and later: .IP "\fBsmtp_dns_support_level (empty)\fR" Level of DNS support in the Postfix SMTP client. + .PP + Imported from Postfix version 2.12: + .IP "\fBsmtp_delivery_status_filter (empty)\fR" + Optional filter for the \fBsmtp\fR(8) delivery agent to change the + delivery status code or explanatory text of successful or unsuccessful + deliveries. .SH "MIME PROCESSING CONTROLS" .na .nf diff -cr --new-file ../postfix-2.11.0/mantools/postlink ./mantools/postlink *** ../postfix-2.11.0/mantools/postlink Fri Dec 20 17:20:01 2013 --- ./mantools/postlink Sat Mar 22 18:20:56 2014 *************** *** 451,456 **** --- 451,457 ---- s;\bsmtp_connection_cache_time_limit\b;$&;g; s;\bsmtp_connection_cache_destinations\b;$&;g; + s;\bsmtp_delivery_status_filter\b;$&;g; s;\bsmtp_data_done_timeout\b;$&;g; s;\bsmtp_data_init_timeout\b;$&;g; s;\bsmtp_data_xfer_timeout\b;$&;g; diff -cr --new-file ../postfix-2.11.0/proto/postconf.proto ./proto/postconf.proto *** ../postfix-2.11.0/proto/postconf.proto Sun Jan 12 13:00:56 2014 --- ./proto/postconf.proto Sat Mar 22 19:33:28 2014 *************** *** 15092,15097 **** --- 15092,15164 ----

This feature is available in Postfix 2.9 and later.

+ %PARAM smtp_delivery_status_filter + +

Optional filter for the smtp(8) delivery agent to change the + delivery status code or explanatory text of successful or unsuccessful + deliveries.

+ +

Specify zero or more "type:table" lookup table names, separated + by comma or whitespace. For each successful or unsuccessful delivery + to a recipient, the tables are queried in the specified order with + one line of text that is structured as follows:

+ +
+ enhanced-status-code SPACE explanatory-text +
+ +

The first table match wins. The lookup result must have the + same structure as the query, a successful status code (2.X.X) must + be replaced with a successful status code, an unsuccessful status + code (4.X.X or 5.X.X) must be replaced with an unsuccessful status + code, and the explanatory text field must be non-empty. Other results + will result in a warning.

+ +

Example: convert specific soft TLS errors into hard errors, + by overriding the first number in the enhanced status code.

+ +
+
+ /etc/postfix/main.cf:
+     smtp_delivery_status_filter = pcre:/etc/postfix/smtp_dsn_filter
+ 
+
+ +
+
+ /etc/postfix/smtp_dsn_filter:
+     /^4(\.\d+\.\d+ TLS is required, but host \S+ refused to start TLS: .+)/
+         5$1
+     /^4(\.\d+\.\d+ TLS is required, but was not offered by host .+)/
+         5$1
+     # Do not change the following into hard bounces. They may
+     # result from a local configuration problem.
+     # 4.\d+.\d+ TLS is required, but our TLS engine is unavailable
+     # 4.\d+.\d+ TLS is required, but unavailable
+     # 4.\d+.\d+ Cannot start TLS: handshake failure
+ 
+
+ +

Notes:

+ + + +

This feature is imported from Postfix 2.12.

+ %PARAM smtp_send_dummy_mail_auth no

Whether or not to append the "AUTH=<>" option to the MAIL diff -cr --new-file ../postfix-2.11.0/src/global/Makefile.in ./src/global/Makefile.in *** ../postfix-2.11.0/src/global/Makefile.in Mon Dec 9 15:13:50 2013 --- ./src/global/Makefile.in Sat Mar 22 18:19:34 2014 *************** *** 32,38 **** match_service.c mail_conf_nint.c addr_match_list.c mail_conf_nbool.c \ smtp_reply_footer.c safe_ultostr.c verify_sender_addr.c \ dict_memcache.c mail_version.c memcache_proto.c server_acl.c \ ! mkmap_fail.c haproxy_srvr.c OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \ clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \ --- 32,38 ---- match_service.c mail_conf_nint.c addr_match_list.c mail_conf_nbool.c \ smtp_reply_footer.c safe_ultostr.c verify_sender_addr.c \ dict_memcache.c mail_version.c memcache_proto.c server_acl.c \ ! mkmap_fail.c haproxy_srvr.c dsn_filter.c OBJS = abounce.o anvil_clnt.o been_here.o bounce.o bounce_log.o \ canon_addr.o cfg_parser.o cleanup_strerror.o cleanup_strflags.o \ clnt_stream.o conv_time.o db_common.o debug_peer.o debug_process.o \ *************** *** 66,72 **** match_service.o mail_conf_nint.o addr_match_list.o mail_conf_nbool.o \ smtp_reply_footer.o safe_ultostr.o verify_sender_addr.o \ dict_memcache.o mail_version.o memcache_proto.o server_acl.o \ ! mkmap_fail.o haproxy_srvr.o HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \ conv_time.h db_common.h debug_peer.h debug_process.h defer.h \ --- 66,72 ---- match_service.o mail_conf_nint.o addr_match_list.o mail_conf_nbool.o \ smtp_reply_footer.o safe_ultostr.o verify_sender_addr.o \ dict_memcache.o mail_version.o memcache_proto.o server_acl.o \ ! mkmap_fail.o haproxy_srvr.o dsn_filter.o HDRS = abounce.h anvil_clnt.h been_here.h bounce.h bounce_log.h \ canon_addr.h cfg_parser.h cleanup_user.h clnt_stream.h config.h \ conv_time.h db_common.h debug_peer.h debug_process.h defer.h \ *************** *** 93,99 **** fold_addr.h header_body_checks.h data_redirect.h match_service.h \ addr_match_list.h smtp_reply_footer.h safe_ultostr.h \ verify_sender_addr.h dict_memcache.h memcache_proto.h server_acl.h \ ! haproxy_srvr.h TESTSRC = rec2stream.c stream2rec.c recdump.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) --- 93,99 ---- fold_addr.h header_body_checks.h data_redirect.h match_service.h \ addr_match_list.h smtp_reply_footer.h safe_ultostr.h \ verify_sender_addr.h dict_memcache.h memcache_proto.h server_acl.h \ ! haproxy_srvr.h dsn_filter.h TESTSRC = rec2stream.c stream2rec.c recdump.c DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) *************** *** 660,665 **** --- 660,666 ---- bounce.o: deliver_request.h bounce.o: dsn.h bounce.o: dsn_buf.h + bounce.o: dsn_filter.h bounce.o: dsn_print.h bounce.o: dsn_util.h bounce.o: log_adhoc.h *************** *** 803,808 **** --- 804,810 ---- defer.o: deliver_request.h defer.o: dsn.h defer.o: dsn_buf.h + defer.o: dsn_filter.h defer.o: dsn_print.h defer.o: dsn_util.h defer.o: flush_clnt.h *************** *** 1012,1017 **** --- 1014,1033 ---- dsn_buf.o: dsn.h dsn_buf.o: dsn_buf.c dsn_buf.o: dsn_buf.h + dsn_filter.o: ../../include/argv.h + dsn_filter.o: ../../include/dict.h + dsn_filter.o: ../../include/msg.h + dsn_filter.o: ../../include/myflock.h + dsn_filter.o: ../../include/mymalloc.h + dsn_filter.o: ../../include/sys_defs.h + dsn_filter.o: ../../include/vbuf.h + dsn_filter.o: ../../include/vstream.h + dsn_filter.o: ../../include/vstring.h + dsn_filter.o: dsn.h + dsn_filter.o: dsn_filter.c + dsn_filter.o: dsn_filter.h + dsn_filter.o: dsn_util.h + dsn_filter.o: maps.h dsn_mask.o: ../../include/msg.h dsn_mask.o: ../../include/name_code.h dsn_mask.o: ../../include/name_mask.h *************** *** 2035,2040 **** --- 2051,2057 ---- sent.o: deliver_request.h sent.o: dsn.h sent.o: dsn_buf.h + sent.o: dsn_filter.h sent.o: dsn_mask.h sent.o: dsn_util.h sent.o: log_adhoc.h diff -cr --new-file ../postfix-2.11.0/src/global/bounce.c ./src/global/bounce.c *** ../postfix-2.11.0/src/global/bounce.c Tue May 15 16:13:00 2007 --- ./src/global/bounce.c Sat Mar 22 19:00:59 2014 *************** *** 48,53 **** --- 48,66 ---- /* RECIPIENT *rcpt; /* const char *relay; /* DSN *dsn; + /* + /* void bounce_client_init(title, maps) + /* const char *title; + /* const char *maps; + /* INTERNAL API + /* DSN_FILTER *delivery_status_filter; + /* + /* int bounce_append_intern(flags, id, stats, recipient, relay, dsn) + /* int flags; + /* const char *id; + /* MSG_STATS *stats; + /* RECIPIENT *rcpt; + /* const char *relay; /* DESCRIPTION /* This module implements the client interface to the message /* bounce service, which maintains a per-message log of status *************** *** 75,80 **** --- 88,97 ---- /* return address in a manner that depends on the recipient /* address. /* + /* bounce_client_init() initializes an optional DSN filter. + /* + /* bounce_append_intern() is for use after the DSN filter. + /* /* Arguments: /* .IP flags /* The bitwise OR of zero or more of the following (specify *************** *** 131,136 **** --- 148,158 ---- /* zero value. Otherwise, the functions return a non-zero result, /* and when BOUNCE_FLAG_CLEAN is disabled, log that message /* delivery is deferred. + /* .IP title + /* The origin of the optional DSN filter lookup table names. + /* .IP maps + /* The optional "type:table" DSN filter lookup table names, + /* separated by comma or whitespace. /* BUGS /* Should be replaced by routines with an attribute-value based /* interface instead of an interface that uses a rigid argument list. *************** *** 158,163 **** --- 180,186 ---- /* Global library. */ + #define DSN_INTERN #include #include #include *************** *** 169,174 **** --- 192,201 ---- #include #include + /* Shared internally, between bounce and defer clients. */ + + DSN_FILTER *delivery_status_filter; + /* bounce_append - append dsn_text to per-message bounce log */ int bounce_append(int flags, const char *id, MSG_STATS *stats, *************** *** 176,182 **** DSN *dsn) { DSN my_dsn = *dsn; ! int status; /* * Sanity check. If we're really confident, change this into msg_panic --- 203,209 ---- DSN *dsn) { DSN my_dsn = *dsn; ! DSN *dsn_res; /* * Sanity check. If we're really confident, change this into msg_panic *************** *** 188,193 **** --- 215,241 ---- } /* + * DSN filter (Postfix 2.12). + */ + if (delivery_status_filter != 0 + && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) { + if (dsn_res->status[0] == '4') + return (defer_append_intern(flags, id, stats, rcpt, relay, dsn_res)); + my_dsn = *dsn_res; + } + return (bounce_append_intern(flags, id, stats, rcpt, relay, &my_dsn)); + } + + /* bounce_append_intern - append delivery status to per-message bounce log */ + + int bounce_append_intern(int flags, const char *id, MSG_STATS *stats, + RECIPIENT *rcpt, const char *relay, + DSN *dsn) + { + DSN my_dsn = *dsn; + int status; + + /* * MTA-requested address verification information is stored in the verify * service database. */ *************** *** 259,265 **** vstring_sprintf(junk, "%s or %s service failure", var_bounce_service, var_trace_service); my_dsn.reason = vstring_str(junk); ! status = defer_append(flags, id, stats, rcpt, relay, &my_dsn); vstring_free(junk); } else { status = -1; --- 307,313 ---- vstring_sprintf(junk, "%s or %s service failure", var_bounce_service, var_trace_service); my_dsn.reason = vstring_str(junk); ! status = defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn); vstring_free(junk); } else { status = -1; *************** *** 428,430 **** --- 476,490 ---- return (status); } } + + /* bounce_client_init - initialize bounce/defer DSN filter */ + + void bounce_client_init(const char *title, const char *maps) + { + const char myname[] = "bounce_client_init"; + + if (delivery_status_filter != 0) + msg_panic("%s: duplicate initialization", myname); + if (*maps) + delivery_status_filter = dsn_filter_create(title, maps); + } diff -cr --new-file ../postfix-2.11.0/src/global/bounce.h ./src/global/bounce.h *** ../postfix-2.11.0/src/global/bounce.h Sat Jan 7 15:49:53 2006 --- ./src/global/bounce.h Sat Mar 22 18:10:01 2014 *************** *** 35,40 **** --- 35,41 ---- const char *, const char *, int, MSG_STATS *, RECIPIENT *, const char *, DSN *); + extern void bounce_client_init(const char *, const char *); /* * Bounce/defer protocol commands. *************** *** 65,70 **** --- 66,86 ---- */ #define BOUNCE_FLAG_KEEP BOUNCE_FLAG_NONE + /* + * Start of private API. + */ + + #ifdef DSN_INTERN + + #include + + extern DSN_FILTER *delivery_status_filter; + + extern int bounce_append_intern(int, const char *, MSG_STATS *, RECIPIENT *, + const char *, DSN *); + + #endif + /* LICENSE /* .ad /* .fi diff -cr --new-file ../postfix-2.11.0/src/global/defer.c ./src/global/defer.c *** ../postfix-2.11.0/src/global/defer.c Tue May 15 16:13:13 2007 --- ./src/global/defer.c Sat Mar 22 18:36:25 2014 *************** *** 31,36 **** --- 31,43 ---- /* const char *sender; /* const char *dsn_envid; /* int dsn_ret; + /* INTERNAL API + /* int defer_append_intern(flags, id, stats, rcpt, relay, dsn) + /* int flags; + /* const char *id; + /* MSG_STATS *stats; + /* RECIPIENT *rcpt; + /* const char *relay; /* DESCRIPTION /* This module implements a client interface to the defer service, /* which maintains a per-message logfile with status records for *************** *** 56,61 **** --- 63,70 ---- /* question has been deferred. The defer log is not deleted, /* and no recipients are deleted from the original queue file. /* + /* defer_append_intern() is for use after the DSN filter. + /* /* Arguments: /* .IP flags /* The bit-wise OR of zero or more of the following (specify *************** *** 134,139 **** --- 143,149 ---- /* Global library. */ + #define DSN_INTERN #include #include #include *************** *** 154,162 **** RECIPIENT *rcpt, const char *relay, DSN *dsn) { - const char *rcpt_domain; DSN my_dsn = *dsn; ! int status; /* * Sanity check. --- 164,171 ---- RECIPIENT *rcpt, const char *relay, DSN *dsn) { DSN my_dsn = *dsn; ! DSN *dsn_res; /* * Sanity check. *************** *** 167,172 **** --- 176,203 ---- } /* + * DSN filter (Postfix 2.12). + */ + if (delivery_status_filter != 0 + && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) { + if (dsn_res->status[0] == '5') + return (bounce_append_intern(flags, id, stats, rcpt, relay, dsn_res)); + my_dsn = *dsn_res; + } + return (defer_append_intern(flags, id, stats, rcpt, relay, &my_dsn)); + } + + /* defer_append_intern - defer message delivery */ + + int defer_append_intern(int flags, const char *id, MSG_STATS *stats, + RECIPIENT *rcpt, const char *relay, + DSN *dsn) + { + const char *rcpt_domain; + DSN my_dsn = *dsn; + int status; + + /* * MTA-requested address verification information is stored in the verify * service database. */ diff -cr --new-file ../postfix-2.11.0/src/global/defer.h ./src/global/defer.h *** ../postfix-2.11.0/src/global/defer.h Tue Nov 1 15:35:05 2005 --- ./src/global/defer.h Sat Mar 22 18:37:20 2014 *************** *** 26,31 **** --- 26,41 ---- extern int defer_warn(int, const char *, const char *, const char *, const char *, int); + /* + * Start of private API. + */ + #ifdef DSN_INTERN + + extern int defer_append_intern(int, const char *, MSG_STATS *, RECIPIENT *, + const char *, DSN *); + + #endif + /* LICENSE /* .ad /* .fi diff -cr --new-file ../postfix-2.11.0/src/global/dsn_filter.c ./src/global/dsn_filter.c *** ../postfix-2.11.0/src/global/dsn_filter.c Wed Dec 31 19:00:00 1969 --- ./src/global/dsn_filter.c Sat Mar 22 18:56:46 2014 *************** *** 0 **** --- 1,193 ---- + /*++ + /* NAME + /* dsn_filter 3 + /* SUMMARY + /* filter delivery status code or text + /* SYNOPSIS + /* #include + /* + /* DSN_FILTER *dsn_filter_create( + /* const char *title, + /* const char *map_names) + /* + /* DSN *dsn_filter_lookup( + /* DSN_FILTER *fp, + /* DSN *dsn) + /* + /* void dsn_free( + /* DSN_FILTER *fp) + /* DESCRIPTION + /* This module maps (bounce or defer non-delivery status code + /* and text) into replacement (bounce or defer non-delivery + /* status code and text), or maps (success status code and + /* text) into replacement (success status code and text). Other + /* DSN attributes are passed through without modification. + /* + /* dsn_filter_create() instantiates a delivery status filter. + /* + /* dsn_filter_lookup() queries the specified filter. The input + /* DSN must be a success, bounce or defer DSN. If a match is + /* found a non-delivery status must map to a non-delivery + /* status, a success status must map to a success status, and + /* the text must be non-empty. The result is a null pointer + /* when no valid match is found. Otherwise, the result is + /* overwritten upon each call. This function must not be + /* called with the result from a dsn_filter_lookup() call. + /* + /* dsn_free() destroys the specified delivery status filter. + /* + /* Arguments: + /* .IP title + /* Origin of the mapnames argument, typically a configuration + /* parameter name. This is reported in diagnostics. + /* .IP mapnames + /* List of lookup tables, separated by whitespace or comma. + /* .IP fp + /* filter created with dsn_filter_create() + /* .IP dsn + /* A success, bounce or defer DSN data structure. The + /* dsn_filter_lookup() result value is in part a shallow copy + /* of this argument. + /* SEE ALSO + /* maps(3) multi-table search + /* DIAGNOSTICS + /* Panic: invalid dsn argument; recursive call. Fatal error: + /* memory allocation problem. Warning: invalid DSN lookup + /* result. + /* LICENSE + /* .ad + /* .fi + /* The Secure Mailer license must be distributed with this software. + /* AUTHOR(S) + /* Wietse Venema + /* IBM T.J. Watson Research + /* P.O. Box 704 + /* Yorktown Heights, NY 10598, USA + /*--*/ + + /* + * System libraries. + */ + #include + + /* + * Utility library. + */ + #include + #include + #include + + /* + * Global library. + */ + #include + #include + #include + #include + #include + + /* + * Private data structure. + */ + struct DSN_FILTER { + MAPS *maps; /* Replacement (status, text) */ + VSTRING *buffer; /* Status code and text */ + DSN_SPLIT dp; /* Parsing aid */ + DSN dsn; /* Shallow copy */ + }; + + /* + * SLMs. + */ + #define STR(x) vstring_str(x) + + /* dsn_filter_create - create delivery status filter */ + + DSN_FILTER *dsn_filter_create(const char *title, const char *map_names) + { + const char myname[] = "dsn_filter_create"; + DSN_FILTER *fp; + + if (msg_verbose) + msg_info("%s: %s %s", myname, title, map_names); + + fp = (DSN_FILTER *) mymalloc(sizeof(*fp)); + fp->buffer = vstring_alloc(100); + fp->maps = maps_create(title, map_names, DICT_FLAG_LOCK); + return (fp); + } + + /* dsn_filter_lookup - apply delivery status filter */ + + DSN *dsn_filter_lookup(DSN_FILTER *fp, DSN *dsn) + { + const char myname[] = "dsn_filter_lookup"; + const char *result; + int ndr_dsn = 0; + + if (msg_verbose) + msg_info("%s: %s %s", myname, dsn->status, dsn->reason); + + /* + * XXX Instead of hard-coded '4' etc., use some form of encapsulation + * when reading or updating the status class field. + */ + #define IS_SUCCESS_DSN(s) (dsn_valid(s) && (s)[0] == '2') + #define IS_NDR_DSN(s) (dsn_valid(s) && ((s)[0] == '4' || (s)[0] == '5')) + + /* + * Sanity check. We filter only success/bounce/defer DSNs. + */ + if (IS_SUCCESS_DSN(dsn->status)) + ndr_dsn = 0; + else if (IS_NDR_DSN(dsn->status)) + ndr_dsn = 1; + else + msg_panic("%s: dsn argument with bad status code: %s", + myname, dsn->status); + + /* + * Sanity check. A delivery status filter must not be invoked with its + * own result. + */ + if (dsn->reason == fp->dsn.reason) + msg_panic("%s: recursive call is not allowed", myname); + + /* + * Look up replacement status and text. + */ + vstring_sprintf(fp->buffer, "%s %s", dsn->status, dsn->reason); + if ((result = maps_find(fp->maps, STR(fp->buffer), 0)) != 0) { + /* Sanity check. Do not allow success<=>error mappings. */ + if ((ndr_dsn == 0 && !IS_SUCCESS_DSN(result)) + || (ndr_dsn != 0 && !IS_NDR_DSN(result))) { + msg_warn("%s: bad status code: %s", fp->maps->title, result); + return (0); + } else { + vstring_strcpy(fp->buffer, result); + dsn_split(&fp->dp, "can't happen", STR(fp->buffer)); + (void) DSN_ASSIGN(&fp->dsn, DSN_STATUS(fp->dp.dsn), + (result[0] == '4' ? "delayed" : + result[0] == '5' ? "failed" : + dsn->action), + fp->dp.text, dsn->dtype, dsn->dtext, + dsn->mtype, dsn->mname); + return (&fp->dsn); + } + } + return (0); + } + + /* dsn_filter_free - destroy delivery status filter */ + + void dsn_filter_free(DSN_FILTER *fp) + { + const char myname[] = "dsn_filter_free"; + + if (msg_verbose) + msg_info("%s: %s", myname, fp->maps->title); + + maps_free(fp->maps); + vstring_free(fp->buffer); + myfree((char *) fp); + } diff -cr --new-file ../postfix-2.11.0/src/global/dsn_filter.h ./src/global/dsn_filter.h *** ../postfix-2.11.0/src/global/dsn_filter.h Wed Dec 31 19:00:00 1969 --- ./src/global/dsn_filter.h Sat Mar 22 18:53:27 2014 *************** *** 0 **** --- 1,34 ---- + #ifndef _DSN_FILTER_H_INCLUDED_ + #define _DSN_FILTER_H_INCLUDED_ + + /*++ + /* NAME + /* dsn_filter 3h + /* SUMMARY + /* delivery status filter + /* SYNOPSIS + /* #include + /* DESCRIPTION + /* .nf + + /* + * External interface. + */ + typedef struct DSN_FILTER DSN_FILTER; + + extern DSN_FILTER *dsn_filter_create(const char *, const char *); + extern DSN *dsn_filter_lookup(DSN_FILTER *, DSN *); + extern void dsn_filter_free(DSN_FILTER *); + + /* LICENSE + /* .ad + /* .fi + /* The Secure Mailer license must be distributed with this software. + /* AUTHOR(S) + /* Wietse Venema + /* IBM T.J. Watson Research + /* P.O. Box 704 + /* Yorktown Heights, NY 10598, USA + /*--*/ + + #endif diff -cr --new-file ../postfix-2.11.0/src/global/mail_params.h ./src/global/mail_params.h *** ../postfix-2.11.0/src/global/mail_params.h Sat Dec 28 20:27:16 2013 --- ./src/global/mail_params.h Sat Mar 22 18:27:16 2014 *************** *** 3744,3749 **** --- 3744,3758 ---- #define DEF_DAEMON_OPEN_FATAL 0 extern bool var_daemon_open_fatal; + /* + * Optional delivery status filter. + */ + #define VAR_SMTP_DSN_FILTER "smtp_delivery_status_filter" + #define DEF_SMTP_DSN_FILTER "" + #define VAR_LMTP_DSN_FILTER "lmtp_delivery_status_filter" + #define DEF_LMTP_DSN_FILTER "" + extern char *var_smtp_dsn_filter; + /* LICENSE /* .ad /* .fi diff -cr --new-file ../postfix-2.11.0/src/global/sent.c ./src/global/sent.c *** ../postfix-2.11.0/src/global/sent.c Sat Dec 17 17:30:01 2005 --- ./src/global/sent.c Sat Mar 22 18:59:38 2014 *************** *** 79,84 **** --- 79,85 ---- /* Global library. */ + #define DSN_INTERN #include #include #include *************** *** 97,102 **** --- 98,104 ---- DSN *dsn) { DSN my_dsn = *dsn; + DSN *dsn_res; int status; /* *************** *** 108,113 **** --- 110,122 ---- } /* + * DSN filter (Postfix 2.12). + */ + if (delivery_status_filter != 0 + && (dsn_res = dsn_filter_lookup(delivery_status_filter, &my_dsn)) != 0) + my_dsn = *dsn_res; + + /* * MTA-requested address verification information is stored in the verify * service database. */ diff -cr --new-file ../postfix-2.11.0/src/master/mail_server.h ./src/master/mail_server.h *** ../postfix-2.11.0/src/master/mail_server.h Thu Jun 14 15:43:35 2012 --- ./src/master/mail_server.h Sat Mar 15 08:38:01 2014 *************** *** 38,43 **** --- 38,44 ---- #define MAIL_SERVER_IN_FLOW_DELAY 20 #define MAIL_SERVER_SLOW_EXIT 21 + #define MAIL_SERVER_BOUNCE_INIT 22 typedef void (*MAIL_SERVER_INIT_FN) (char *, char **); typedef int (*MAIL_SERVER_LOOP_FN) (char *, char **); diff -cr --new-file ../postfix-2.11.0/src/master/single_server.c ./src/master/single_server.c *** ../postfix-2.11.0/src/master/single_server.c Thu Jun 21 09:15:04 2012 --- ./src/master/single_server.c Thu Mar 20 19:09:45 2014 *************** *** 118,123 **** --- 118,126 ---- /* This service must be configured with process limit of 0. /* .IP MAIL_SERVER_PRIVILEGED /* This service must be configured as privileged. + /* .IP "MAIL_SERVER_BOUNCE_INIT (const char *, const char **)" + /* Initialize the DSN filter for the bounce/defer service + /* clients with the specified map source and map names. /* .PP /* The var_use_limit variable limits the number of clients that /* a server can service before it commits suicide. *************** *** 194,199 **** --- 197,203 ---- #include #include #include + #include /* Process manager. */ *************** *** 430,435 **** --- 434,441 ---- char *generation; int msg_vstream_needed = 0; int redo_syslog_init = 0; + const char *dsn_filter_title; + const char **dsn_filter_maps; /* * Process environment options as early as we can. *************** *** 631,636 **** --- 637,647 ---- msg_fatal("service %s requires privileged operation", service_name); break; + case MAIL_SERVER_BOUNCE_INIT: + dsn_filter_title = va_arg(ap, const char *); + dsn_filter_maps = va_arg(ap, const char **); + bounce_client_init(dsn_filter_title, *dsn_filter_maps); + break; default: msg_panic("%s: unknown argument type: %d", myname, key); } diff -cr --new-file ../postfix-2.11.0/src/smtp/lmtp_params.c ./src/smtp/lmtp_params.c *** ../postfix-2.11.0/src/smtp/lmtp_params.c Sun Aug 25 15:49:34 2013 --- ./src/smtp/lmtp_params.c Sat Mar 22 18:41:25 2014 *************** *** 57,62 **** --- 57,63 ---- VAR_LMTP_RESP_FILTER, DEF_LMTP_RESP_FILTER, &var_smtp_resp_filter, 0, 0, VAR_LMTP_ADDR_PREF, DEF_LMTP_ADDR_PREF, &var_smtp_addr_pref, 1, 0, VAR_LMTP_DNS_RES_OPT, DEF_LMTP_DNS_RES_OPT, &var_smtp_dns_res_opt, 0, 0, + VAR_LMTP_DSN_FILTER, DEF_LMTP_DSN_FILTER, &var_smtp_dsn_filter, 0, 0, 0, }; static const CONFIG_TIME_TABLE lmtp_time_table[] = { diff -cr --new-file ../postfix-2.11.0/src/smtp/smtp.c ./src/smtp/smtp.c *** ../postfix-2.11.0/src/smtp/smtp.c Tue Nov 26 17:58:51 2013 --- ./src/smtp/smtp.c Sat Mar 22 19:29:10 2014 *************** *** 251,256 **** --- 251,262 ---- /* Available in Postfix version 2.11 and later: /* .IP "\fBsmtp_dns_support_level (empty)\fR" /* Level of DNS support in the Postfix SMTP client. + /* .PP + /* Imported from Postfix version 2.12: + /* .IP "\fBsmtp_delivery_status_filter (empty)\fR" + /* Optional filter for the \fBsmtp\fR(8) delivery agent to change the + /* delivery status code or explanatory text of successful or unsuccessful + /* deliveries. /* MIME PROCESSING CONTROLS /* .ad /* .fi *************** *** 873,878 **** --- 879,885 ---- char *var_smtp_dns_support; bool var_smtp_rec_deadline; bool var_smtp_dummy_mail_auth; + char *var_smtp_dsn_filter; /* Special handling of 535 AUTH errors. */ char *var_smtp_sasl_auth_cache_name; *************** *** 1268,1272 **** --- 1275,1281 ---- MAIL_SERVER_PRE_INIT, pre_init, MAIL_SERVER_POST_INIT, post_init, MAIL_SERVER_PRE_ACCEPT, pre_accept, + MAIL_SERVER_BOUNCE_INIT, VAR_SMTP_DSN_FILTER, + &var_smtp_dsn_filter, 0); } diff -cr --new-file ../postfix-2.11.0/src/smtp/smtp_params.c ./src/smtp/smtp_params.c *** ../postfix-2.11.0/src/smtp/smtp_params.c Sun Aug 25 15:49:34 2013 --- ./src/smtp/smtp_params.c Sat Mar 22 18:41:25 2014 *************** *** 58,63 **** --- 58,64 ---- VAR_SMTP_RESP_FILTER, DEF_SMTP_RESP_FILTER, &var_smtp_resp_filter, 0, 0, VAR_SMTP_ADDR_PREF, DEF_SMTP_ADDR_PREF, &var_smtp_addr_pref, 1, 0, VAR_SMTP_DNS_RES_OPT, DEF_SMTP_DNS_RES_OPT, &var_smtp_dns_res_opt, 0, 0, + VAR_SMTP_DSN_FILTER, DEF_SMTP_DSN_FILTER, &var_smtp_dsn_filter, 0, 0, 0, }; static const CONFIG_TIME_TABLE smtp_time_table[] = { diff -cr --new-file ../postfix-2.11.0/src/smtp/smtp_trouble.c ./src/smtp/smtp_trouble.c *** ../postfix-2.11.0/src/smtp/smtp_trouble.c Mon Jun 11 11:25:02 2012 --- ./src/smtp/smtp_trouble.c Sat Mar 22 17:58:08 2014 *************** *** 190,195 **** --- 190,196 ---- DSN_BUF *why = state->why; RECIPIENT *rcpt; int status; + int aggregate_status; int soft_error = (STR(why->status)[0] == '4'); int soft_bounce_error = (STR(why->status)[0] == '5' && var_soft_bounce); int nrcpt; *************** *** 234,239 **** --- 235,241 ---- GETTIMEOFDAY(&request->msg_stats.deliver_done); (void) DSN_FROM_DSN_BUF(why); + aggregate_status = 0; for (nrcpt = 0; nrcpt < SMTP_RCPT_LEFT(state); nrcpt++) { rcpt = request->rcpt_list.info + nrcpt; if (SMTP_RCPT_ISMARKED(rcpt)) *************** *** 245,254 **** if (status == 0) deliver_completed(state->src, rcpt->offset); SMTP_RCPT_DROP(state, rcpt); ! state->status |= status; } if ((state->misc_flags & SMTP_MISC_FLAG_COMPLETE_SESSION) == 0 ! && throttle_queue && (soft_error || soft_bounce_error) && request->hop_status == 0) request->hop_status = DSN_COPY(&why->dsn); } --- 247,257 ---- if (status == 0) deliver_completed(state->src, rcpt->offset); SMTP_RCPT_DROP(state, rcpt); ! aggregate_status |= status; } + state->status |= aggregate_status; if ((state->misc_flags & SMTP_MISC_FLAG_COMPLETE_SESSION) == 0 ! && throttle_queue && aggregate_status && request->hop_status == 0) request->hop_status = DSN_COPY(&why->dsn); }