From: Wietse Venema Date: Sat, 12 Nov 2005 05:00:00 +0000 (-0500) Subject: postfix-2.3-20051112 X-Git-Tag: v2.3-RC1~48 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=efd8a7a29ef229cfab6eef6b57841e259cb581e1;p=thirdparty%2Fpostfix.git postfix-2.3-20051112 --- diff --git a/postfix/.indent.pro b/postfix/.indent.pro index df2a7dea4..00e85e8cd 100644 --- a/postfix/.indent.pro +++ b/postfix/.indent.pro @@ -21,6 +21,9 @@ -TBOUNCE_LOG_DSN_BUF -TBOUNCE_LOG_RCPT_BUF -TBOUNCE_STAT +-TBOUNCE_TEMPLATE +-TBOUNCE_TIME_DIVISOR +-TBOUNCE_TIME_PARAMETER -TCFG_PARSER -TCIDR_MATCH -TCLEANUP_STATE diff --git a/postfix/HISTORY b/postfix/HISTORY index 82380f622..8c4e6961e 100644 --- a/postfix/HISTORY +++ b/postfix/HISTORY @@ -11338,9 +11338,11 @@ Apologies for any names omitted. delay_logging_resolution_limit parameter, which specifies the maximal number of digits after the decimal point. - Bugfix: under unlikely conditions two messages could get - the same message ID. Found by Victor. Files: global/mail_stream.c, - global/mail_queue.c. + Bugfix: two messages could get the same message ID due to + a race condition. This time window was increased when queue + file creation was postponed from MAIL FROM until the first + accepted RCPT TO. The window is closed again. Found by + Victor. Files: global/mail_stream.c, global/mail_queue.c. 20051109 @@ -11353,6 +11355,27 @@ Apologies for any names omitted. Bugfix: XCLIENT broke when reverse hostname support was added. Fix by Tomoyuki Sakurai. File: smtpd/smtpd.c. +20051110 + + Workaround: don't set the delay warning timer for messages + from inside or from outside that have the null sender as + recipient. This was a waste of time, because the warning + would always be discarded. File: cleanup/cleanup_envelope.c. + + Feature: the built-in mail delivery status notification + text is now implemented by built-in templates. Files: + bounce/bounce_template.c, bounce/bounce_notify_util.c. + +20051112 + + Feature (grumble): configurable bounce message templates + based on contribution by Nicolas Riendeau. I kept the general + format of his templates, but placed them together in one + file to reduce process initialization overhead. Files: + bounce/bounce_template.c, bounce/dict_ml.c (to be moved to + library if useful enough). A sample bounce message template + file is installed as $config_directory/bounce.cf.default. + Open problems: "postsuper -r" no longer resets the message arrival time, diff --git a/postfix/Makefile.in b/postfix/Makefile.in index fde9ca2a2..6ff54224d 100644 --- a/postfix/Makefile.in +++ b/postfix/Makefile.in @@ -74,7 +74,7 @@ tidy: clean *.bak */*.bak */*/*.bak \ make.err */make.err */*/make.err \ *.gmon */*.gmon */*/*.gmon \ - conf/main.cf.default + conf/main.cf.default conf/bounce.cf.default find . -type s -print | xargs rm -f find . -type d -print | xargs chmod 755 find . -type f -print | xargs chmod a+r diff --git a/postfix/conf/master.cf b/postfix/conf/master.cf index 3833ad6b4..b597e1a37 100644 --- a/postfix/conf/master.cf +++ b/postfix/conf/master.cf @@ -7,14 +7,14 @@ # (yes) (yes) (yes) (never) (100) # ========================================================================== smtp inet n - n - - smtpd -#submission inet n - n - - smtpd -# -o smtpd_etrn_restrictions=reject -# -o smtpd_client_restrictions=permit_sasl_authenticated,reject -#smtps inet n - n - - smtpd -# -o smtpd_tls_wrappermode=yes -o smtpd_sasl_auth_enable=yes -#submission inet n - n - - smtpd -# -o smtpd_etrn_restrictions=reject -# -o smtpd_enforce_tls=yes -o smtpd_sasl_auth_enable=yes +#submission inet n - n - - smtpd +# -o smtpd_enforce_tls=yes +# -o smtpd_sasl_auth_enable=yes +# -o smtpd_client_restrictions=permit_sasl_authenticated,reject +#smtps inet n - n - - smtpd +# -o smtpd_tls_wrappermode=yes +# -o smtpd_sasl_auth_enable=yes +# -o smtpd_client_restrictions=permit_sasl_authenticated,reject #628 inet n - n - - qmqpd pickup fifo n - n 60 1 pickup cleanup unix n - n - 0 cleanup diff --git a/postfix/conf/postfix-files b/postfix/conf/postfix-files index 73492dd8d..0ff7c876e 100644 --- a/postfix/conf/postfix-files +++ b/postfix/conf/postfix-files @@ -104,14 +104,15 @@ $config_directory/LICENSE:f:root:-:644 $config_directory/TLS_LICENSE:f:root:-:644 $config_directory/access:f:root:-:644:p $config_directory/aliases:f:root:-:644:p +$config_directory/bounce.cf.default:f:root:-:644 $config_directory/canonical:f:root:-:644:p $config_directory/cidr_table:f:root:-:644:o -$config_directory/generics:f:root:-:644:o $config_directory/generic:f:root:-:644:p +$config_directory/generics:f:root:-:644:o $config_directory/header_checks:f:root:-:644:p $config_directory/install.cf:f:root:-:644:o -$config_directory/main.cf:f:root:-:644:p $config_directory/main.cf.default:f:root:-:644 +$config_directory/main.cf:f:root:-:644:p $config_directory/makedefs.out:f:root:-:644 $config_directory/master.cf:f:root:-:644:p $config_directory/pcre_table:f:root:-:644:o diff --git a/postfix/html/bounce.8.html b/postfix/html/bounce.8.html index 8211f059b..7fa056803 100644 --- a/postfix/html/bounce.8.html +++ b/postfix/html/bounce.8.html @@ -78,18 +78,22 @@ BOUNCE(8) BOUNCE(8) The maximal amount of original message text that is sent in a non-delivery notification. + bounce_template_file (empty) + Pathname of a configuration file with bounce mes- + sage templates. + config_directory (see 'postconf -d' output) - The default location of the Postfix main.cf and + The default location of the Postfix main.cf and master.cf configuration files. daemon_timeout (18000s) - How much time a Postfix daemon process may take to - handle a request before it is terminated by a + How much time a Postfix daemon process may take to + handle a request before it is terminated by a built-in watchdog timer. delay_notice_recipient (postmaster) - The recipient of postmaster notifications with the - message headers of mail that cannot be delivered + The recipient of postmaster notifications with the + message headers of mail that cannot be delivered within $delay_warning_time time units. deliver_lock_attempts (20) @@ -97,7 +101,7 @@ BOUNCE(8) BOUNCE(8) sive lock on a mailbox file or bounce(8) logfile. deliver_lock_delay (1s) - The time between attempts to acquire an exclusive + The time between attempts to acquire an exclusive lock on a mailbox file or bounce(8) logfile. ipc_timeout (3600s) @@ -110,36 +114,36 @@ BOUNCE(8) BOUNCE(8) bounced mail. max_idle (100s) - The maximum amount of time that an idle Postfix - daemon process waits for the next service request + The maximum amount of time that an idle Postfix + daemon process waits for the next service request before exiting. max_use (100) - The maximal number of connection requests before a + The maximal number of connection requests before a Postfix daemon process terminates. notify_classes (resource, software) - The list of error classes that are reported to the + The list of error classes that are reported to the postmaster. process_id (read-only) - The process ID of a Postfix command or daemon + The process ID of a Postfix command or daemon process. process_name (read-only) - The process name of a Postfix command or daemon + The process name of a Postfix command or daemon process. queue_directory (see 'postconf -d' output) - The location of the Postfix top-level queue direc- + The location of the Postfix top-level queue direc- tory. syslog_facility (mail) The syslog facility of Postfix logging. syslog_name (postfix) - The mail system name that is prepended to the - process name in syslog records, so that "smtpd" + The mail system name that is prepended to the + process name in syslog records, so that "smtpd" becomes, for example, "postfix/smtpd". FILES @@ -155,7 +159,7 @@ BOUNCE(8) BOUNCE(8) syslogd(8), system logging LICENSE - The Secure Mailer license must be distributed with this + The Secure Mailer license must be distributed with this software. AUTHOR(S) diff --git a/postfix/html/postconf.5.html b/postfix/html/postconf.5.html index 7bea38b3a..1d721f539 100644 --- a/postfix/html/postconf.5.html +++ b/postfix/html/postconf.5.html @@ -1102,6 +1102,17 @@ this limit, then you should increase the bounce_template_file +(default: empty)
+ +

Pathname of a configuration file with bounce message templates. +

+ +

This feature is available in Postfix 2.3 and later.

+ +
broken_sasl_auth_clients diff --git a/postfix/man/man5/postconf.5 b/postfix/man/man5/postconf.5 index b1b5b6768..fcc3accaa 100644 --- a/postfix/man/man5/postconf.5 +++ b/postfix/man/man5/postconf.5 @@ -599,6 +599,10 @@ The maximal amount of original message text that is sent in a non-delivery notification. Specify a byte count. If you increase this limit, then you should increase the mime_nesting_limit value proportionally. +.SH bounce_template_file (default: empty) +Pathname of a configuration file with bounce message templates. +.PP +This feature is available in Postfix 2.3 and later. .SH broken_sasl_auth_clients (default: no) Enable inter-operability with SMTP clients that implement an obsolete version of the AUTH command (RFC 2554). Examples of such clients diff --git a/postfix/man/man8/bounce.8 b/postfix/man/man8/bounce.8 index adf9df566..de843697f 100644 --- a/postfix/man/man8/bounce.8 +++ b/postfix/man/man8/bounce.8 @@ -73,6 +73,8 @@ transcripts of mail that Postfix did not receive. .IP "\fBbounce_size_limit (50000)\fR" The maximal amount of original message text that is sent in a non-delivery notification. +.IP "\fBbounce_template_file (empty)\fR" +Pathname of a configuration file with bounce message templates. .IP "\fBconfig_directory (see 'postconf -d' output)\fR" The default location of the Postfix main.cf and master.cf configuration files. diff --git a/postfix/proto/postconf.proto b/postfix/proto/postconf.proto index 802f413b1..80119f197 100644 --- a/postfix/proto/postconf.proto +++ b/postfix/proto/postconf.proto @@ -8675,3 +8675,11 @@ precision.

This feature is available in Postfix 2.3 and later.

+ +%PARAM bounce_template_file empty + +

Pathname of a configuration file with bounce message templates. +

+ +

This feature is available in Postfix 2.3 and later.

+ diff --git a/postfix/src/bounce/Makefile.in b/postfix/src/bounce/Makefile.in index f8b0a6e43..f6f3c6141 100644 --- a/postfix/src/bounce/Makefile.in +++ b/postfix/src/bounce/Makefile.in @@ -1,16 +1,19 @@ SHELL = /bin/sh SRCS = bounce.c bounce_append_service.c bounce_notify_service.c \ bounce_cleanup.c bounce_notify_util.c bounce_notify_verp.c \ - bounce_one_service.c bounce_warn_service.c bounce_trace_service.c + bounce_one_service.c bounce_warn_service.c bounce_trace_service.c \ + bounce_template.c dict_ml.c OBJS = bounce.o bounce_append_service.o bounce_notify_service.o \ bounce_cleanup.o bounce_notify_util.o bounce_notify_verp.o \ - bounce_one_service.o bounce_warn_service.o bounce_trace_service.o + bounce_one_service.o bounce_warn_service.o bounce_trace_service.o \ + bounce_template.o dict_ml.o HDRS = TESTSRC = DEFS = -I. -I$(INC_DIR) -D$(SYSTYPE) CFLAGS = $(DEBUG) $(OPT) $(DEFS) TESTPROG= PROG = bounce +SAMPLES = ../../conf/bounce.cf.default INC_DIR = ../../include LIBS = ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libutil.a @@ -19,6 +22,10 @@ LIBS = ../../lib/libmaster.a ../../lib/libglobal.a ../../lib/libutil.a $(PROG): $(OBJS) $(LIBS) $(CC) $(CFLAGS) -o $@ $(OBJS) $(LIBS) $(SYSLIBS) +../../conf/bounce.cf.default: $(PROG) fix-template.pl + rm -f $@ + ./$(PROG) -Szn default | perl fix-template.pl >$@ + $(OBJS): ../../conf/makedefs.out Makefile: Makefile.in @@ -26,9 +33,9 @@ Makefile: Makefile.in test: $(TESTPROG) -tests: test +tests: update template_test -update: ../../libexec/$(PROG) +update: ../../libexec/$(PROG) $(SAMPLES) ../../libexec/$(PROG): $(PROG) cp $(PROG) ../../libexec @@ -50,6 +57,11 @@ clean: tidy: clean +template_test: + ./bounce -Szn default >junk + ./bounce -Szn actual -o bounce_template_file=`pwd`/junk | diff junk - + rm -f junk + depend: $(MAKES) (sed '1,/^# do not edit/!d' Makefile.in; \ set -e; for i in [a-z][a-z0-9]*.c; do \ @@ -230,6 +242,34 @@ bounce_one_service.o: ../../include/vstream.h bounce_one_service.o: ../../include/vstring.h bounce_one_service.o: bounce_one_service.c bounce_one_service.o: bounce_service.h +bounce_template.o: ../../include/argv.h +bounce_template.o: ../../include/attr.h +bounce_template.o: ../../include/bounce_log.h +bounce_template.o: ../../include/cleanup_user.h +bounce_template.o: ../../include/dict.h +bounce_template.o: ../../include/dsn.h +bounce_template.o: ../../include/dsn_buf.h +bounce_template.o: ../../include/iostuff.h +bounce_template.o: ../../include/is_header.h +bounce_template.o: ../../include/mac_expand.h +bounce_template.o: ../../include/mac_parse.h +bounce_template.o: ../../include/mail_addr.h +bounce_template.o: ../../include/mail_conf.h +bounce_template.o: ../../include/mail_params.h +bounce_template.o: ../../include/mail_proto.h +bounce_template.o: ../../include/msg.h +bounce_template.o: ../../include/mymalloc.h +bounce_template.o: ../../include/post_mail.h +bounce_template.o: ../../include/rcpt_buf.h +bounce_template.o: ../../include/recipient_list.h +bounce_template.o: ../../include/stringops.h +bounce_template.o: ../../include/sys_defs.h +bounce_template.o: ../../include/vbuf.h +bounce_template.o: ../../include/vstream.h +bounce_template.o: ../../include/vstring.h +bounce_template.o: bounce_service.h +bounce_template.o: bounce_template.c +bounce_template.o: dict_ml.h bounce_trace_service.o: ../../include/attr.h bounce_trace_service.o: ../../include/bounce_log.h bounce_trace_service.o: ../../include/cleanup_user.h @@ -274,3 +314,15 @@ bounce_warn_service.o: ../../include/vstream.h bounce_warn_service.o: ../../include/vstring.h bounce_warn_service.o: bounce_service.h bounce_warn_service.o: bounce_warn_service.c +dict_ml.o: ../../include/argv.h +dict_ml.o: ../../include/dict.h +dict_ml.o: ../../include/iostuff.h +dict_ml.o: ../../include/msg.h +dict_ml.o: ../../include/stringops.h +dict_ml.o: ../../include/sys_defs.h +dict_ml.o: ../../include/vbuf.h +dict_ml.o: ../../include/vstream.h +dict_ml.o: ../../include/vstring.h +dict_ml.o: ../../include/vstring_vstream.h +dict_ml.o: dict_ml.c +dict_ml.o: dict_ml.h diff --git a/postfix/src/bounce/bounce.c b/postfix/src/bounce/bounce.c index 14ed9d6ca..de4219a06 100644 --- a/postfix/src/bounce/bounce.c +++ b/postfix/src/bounce/bounce.c @@ -61,6 +61,8 @@ /* .IP "\fBbounce_size_limit (50000)\fR" /* The maximal amount of original message text that is sent in a /* non-delivery notification. +/* .IP "\fBbounce_template_file (empty)\fR" +/* Pathname of a configuration file with bounce message templates. /* .IP "\fBconfig_directory (see 'postconf -d' output)\fR" /* The default location of the Postfix main.cf and master.cf /* configuration files. @@ -168,6 +170,7 @@ char *var_notify_classes; char *var_bounce_rcpt; char *var_2bounce_rcpt; char *var_delay_rcpt; +char *var_bounce_tmpl; /* * We're single threaded, so we can avoid some memory allocation overhead. @@ -441,6 +444,16 @@ static void bounce_service(VSTREAM *client, char *service_name, char **argv) if (mail_queue_name_ok(service_name) == 0) msg_fatal("malformed service name: %s", service_name); + /* + * Special case: dump the built-in templates. This is not part of the + * public interface. + */ + if (strcmp(service_name, "default") == 0) { + bounce_template_dump_default(VSTREAM_OUT); + vstream_fflush(VSTREAM_OUT); + exit(0); + } + /* * Read and validate the first parameter of the client request. Let the * request-specific protocol routines take care of the remainder. @@ -490,6 +503,29 @@ static void bounce_service(VSTREAM *client, char *service_name, char **argv) } } +/* pre_jail_init - pre-jail initialization */ + +static void pre_jail_init(char *service_name, char **unused_argv) +{ + + /* + * Load the alternate message files (if specified) before entering the ch + * root jail. + */ + if (*var_bounce_tmpl) + bounce_template_load(var_bounce_tmpl); + + /* + * Special case: dump the actual templates. This is not part of the + * public interface. + */ + if (strcmp(service_name, "actual") == 0) { + bounce_template_dump_actual(VSTREAM_OUT); + vstream_fflush(VSTREAM_OUT); + exit(0); + } +} + /* post_jail_init - initialize after entering chroot jail */ static void post_jail_init(char *unused_name, char **unused_argv) @@ -527,6 +563,7 @@ int main(int argc, char **argv) VAR_BOUNCE_RCPT, DEF_BOUNCE_RCPT, &var_bounce_rcpt, 1, 0, VAR_2BOUNCE_RCPT, DEF_2BOUNCE_RCPT, &var_2bounce_rcpt, 1, 0, VAR_DELAY_RCPT, DEF_DELAY_RCPT, &var_delay_rcpt, 1, 0, + VAR_BOUNCE_TMPL, DEF_BOUNCE_TMPL, &var_bounce_tmpl, 0, 0, 0, }; @@ -537,6 +574,7 @@ int main(int argc, char **argv) MAIL_SERVER_INT_TABLE, int_table, MAIL_SERVER_STR_TABLE, str_table, MAIL_SERVER_TIME_TABLE, time_table, + MAIL_SERVER_PRE_INIT, pre_jail_init, MAIL_SERVER_POST_INIT, post_jail_init, MAIL_SERVER_UNLIMITED, 0); diff --git a/postfix/src/bounce/bounce_notify_service.c b/postfix/src/bounce/bounce_notify_service.c index 1cf6aeb5d..415bdeec4 100644 --- a/postfix/src/bounce/bounce_notify_service.c +++ b/postfix/src/bounce/bounce_notify_service.c @@ -127,7 +127,7 @@ int bounce_notify_service(int flags, char *service, char *queue_name, * notification is enabled. */ bounce_info = bounce_mail_init(service, queue_name, queue_id, - encoding, dsn_envid, BOUNCE_REPORT_FAIL); + encoding, dsn_envid, FAIL_TEMPLATE()); #define NULL_SENDER MAIL_ADDR_EMPTY /* special address */ #define NULL_TRACE_FLAGS 0 diff --git a/postfix/src/bounce/bounce_notify_util.c b/postfix/src/bounce/bounce_notify_util.c index 1b5d70bf3..27a2cd7a0 100644 --- a/postfix/src/bounce/bounce_notify_util.c +++ b/postfix/src/bounce/bounce_notify_util.c @@ -13,13 +13,13 @@ /* } BOUNCE_INFO; /* /* BOUNCE_INFO *bounce_mail_init(service, queue_name, queue_id, -/* encoding, dsn_envid, report_type) +/* encoding, dsn_envid, template) /* const char *service; /* const char *queue_name; /* const char *queue_id; /* const char *encoding; /* const char *dsn_envid; -/* int report_type; +/* const BOUNCE_TEMPLATE *template; /* /* BOUNCE_INFO *bounce_mail_one_init(queue_name, queue_id, encoding, /* dsn_envid, dsn_notify, rcpt, dsn) @@ -83,14 +83,11 @@ /* bounce_mail_init() bundles up its argument and attempts to /* open the corresponding logfile and message file. A BOUNCE_INFO /* structure contains all the necessary information about an -/* undeliverable message. The report type is BOUNCE_REPORT_WARN -/* for delayed mail, BOUNCE_REPORT_FAIL for undeliverable mail, -/* BOUNCE_REPORT_SUCCESS for "success" delivery notification, -/* or BOUNCE_REPORT_OTHER for other status reports. +/* undeliverable message. /* /* bounce_mail_one_init() provides the same function for only /* one recipient that is not read from bounce logfile. It -/* assumes a report type of BOUNCE_REPORT_FAIL. +/* assumes a template type of FAIL_TEMPLATE(). /* /* bounce_mail_free() releases memory allocated by bounce_mail_init() /* and closes any files opened by bounce_mail_init(). @@ -211,7 +208,7 @@ static BOUNCE_INFO *bounce_mail_alloc(const char *service, const char *queue_id, const char *encoding, const char *dsn_envid, - int report_type, + const BOUNCE_TEMPLATE *template, BOUNCE_LOG *log_handle) { BOUNCE_INFO *bounce_info; @@ -239,7 +236,7 @@ static BOUNCE_INFO *bounce_mail_alloc(const char *service, bounce_info->dsn_envid = dsn_envid; else bounce_info->dsn_envid = 0; - bounce_info->report_type = report_type; + bounce_info->template = template; bounce_info->buf = vstring_alloc(100); bounce_info->sender = vstring_alloc(100); bounce_info->arrival_time = 0; @@ -327,7 +324,7 @@ BOUNCE_INFO *bounce_mail_init(const char *service, const char *queue_id, const char *encoding, const char *dsn_envid, - int report_type) + const BOUNCE_TEMPLATE *template) { BOUNCE_INFO *bounce_info; BOUNCE_LOG *log_handle; @@ -344,7 +341,7 @@ BOUNCE_INFO *bounce_mail_init(const char *service, && errno != ENOENT) msg_fatal("open %s %s: %m", service, queue_id); bounce_info = bounce_mail_alloc(service, queue_name, queue_id, encoding, - dsn_envid, report_type, log_handle); + dsn_envid, template, log_handle); return (bounce_info); } @@ -366,7 +363,7 @@ BOUNCE_INFO *bounce_mail_one_init(const char *queue_name, */ log_handle = bounce_log_forge(rcpt, dsn); bounce_info = bounce_mail_alloc("none", queue_name, queue_id, encoding, - dsn_envid, BOUNCE_REPORT_FAIL, log_handle); + dsn_envid, FAIL_TEMPLATE(), log_handle); return (bounce_info); } @@ -393,6 +390,7 @@ void bounce_mail_free(BOUNCE_INFO *bounce_info) int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info, const char *dest) { + int postmaster_copy; /* * Print a minimal bounce header. The cleanup service will add other @@ -400,44 +398,21 @@ int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info, */ #define STREQ(a, b) (strcasecmp((a), (b)) == 0) - post_mail_fprintf(bounce, "From: %s (Mail Delivery System)", - MAIL_ADDR_MAIL_DAEMON); - - /* - * Non-delivery subject line. - */ - if (bounce_info->report_type == BOUNCE_REPORT_FAIL) { - post_mail_fputs(bounce, dest == var_bounce_rcpt - || dest == var_2bounce_rcpt || dest == var_delay_rcpt ? - "Subject: Postmaster Copy: Undelivered Mail" : - "Subject: Undelivered Mail Returned to Sender"); - } - /* - * Delayed mail subject line. + * XXX This should be caller specified. */ - else if (bounce_info->report_type == BOUNCE_REPORT_WARN) { - post_mail_fputs(bounce, dest == var_bounce_rcpt - || dest == var_2bounce_rcpt || dest == var_delay_rcpt ? - "Subject: Postmaster Warning: Delayed Mail" : - "Subject: Delayed Mail (still being retried)"); - } - - /* - * DSN SUCCESS report. - */ - else if (bounce_info->report_type == BOUNCE_REPORT_SUCCESS) { - post_mail_fputs(bounce, - "Subject: Successful Mail Delivery Report"); - } + postmaster_copy = + (bounce_info->template->postmaster_subject != 0 + && (dest == var_bounce_rcpt || dest == var_2bounce_rcpt + || dest == var_delay_rcpt)); /* - * Address verification report, verbose delivery report. + * Generic headers. */ - else { - post_mail_fputs(bounce, "Subject: Mail Delivery Status Report"); - } - + post_mail_fprintf(bounce, "From: %s", bounce_info->template->from); + post_mail_fprintf(bounce, "Subject: %s", postmaster_copy ? + bounce_info->template->postmaster_subject : + bounce_info->template->subject); post_mail_fprintf(bounce, "To: %s", STR(quote_822_local(bounce_info->buf, dest))); @@ -460,7 +435,8 @@ int bounce_header(VSTREAM *bounce, BOUNCE_INFO *bounce_info, post_mail_fputs(bounce, ""); post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary); post_mail_fprintf(bounce, "Content-Description: %s", "Notification"); - post_mail_fprintf(bounce, "Content-Type: %s", "text/plain"); + post_mail_fprintf(bounce, "Content-Type: %s; charset=%s", + "text/plain", bounce_info->template->charset); post_mail_fputs(bounce, ""); return (vstream_ferror(bounce)); @@ -472,64 +448,9 @@ int bounce_boilerplate(VSTREAM *bounce, BOUNCE_INFO *bounce_info) { /* - * Print the message body with the problem report. XXX For now, we use a - * fixed bounce template. We could use a site-specific parametrized - * template with ${name} macros and we could do wonderful things such as - * word wrapping to make the text look nicer. No matter how hard we would - * try, receiving bounced mail will always suck. + * Print the boiler-plate text. */ -#define UNDELIVERED(type) \ - ((type) == BOUNCE_REPORT_FAIL || (type) == BOUNCE_REPORT_WARN) - - post_mail_fprintf(bounce, "This is the %s program at host %s.", - var_mail_name, var_myhostname); - post_mail_fputs(bounce, ""); - if (bounce_info->report_type == BOUNCE_REPORT_FAIL) { - post_mail_fputs(bounce, - "I'm sorry to have to inform you that your message could not"); - post_mail_fputs(bounce, - "be delivered to one or more recipients. It's attached below."); - } else if (bounce_info->report_type == BOUNCE_REPORT_WARN) { - post_mail_fputs(bounce, - "####################################################################"); - post_mail_fputs(bounce, - "# THIS IS A WARNING ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. #"); - post_mail_fputs(bounce, - "####################################################################"); - post_mail_fputs(bounce, ""); - post_mail_fprintf(bounce, - "Your message could not be delivered for %.1f hours.", - var_delay_warn_time / 3600.0); - post_mail_fprintf(bounce, - "It will be retried until it is %.1f days old.", - var_max_queue_time / 86400.0); - } else if (bounce_info->report_type == BOUNCE_REPORT_SUCCESS) { - post_mail_fputs(bounce, - "Your message was successfully delivered to the destination(s) listed"); - post_mail_fputs(bounce, - "below. In the case of delivery to mailbox you will receive no further"); - post_mail_fputs(bounce, - "notifications. In the case of other deliveries you may still"); - post_mail_fputs(bounce, - "receive notifications of mail delivery errors."); - - } else { - post_mail_fputs(bounce, - "Enclosed is the mail delivery report that you requested."); - } - if (UNDELIVERED(bounce_info->report_type)) { - post_mail_fputs(bounce, ""); - post_mail_fprintf(bounce, - "For further assistance, please send mail to <%s>", - MAIL_ADDR_POSTMASTER); - post_mail_fputs(bounce, ""); - post_mail_fprintf(bounce, - "If you do so, please include this problem report. You can"); - post_mail_fprintf(bounce, - "delete your own text from the attached returned message."); - } - post_mail_fputs(bounce, ""); - post_mail_fprintf(bounce, "\t\t\tThe %s program", var_mail_name); + bounce_template_expand(bounce, bounce_info->template); return (vstream_ferror(bounce)); } @@ -602,7 +523,7 @@ int bounce_diagnostic_log(VSTREAM *bounce, BOUNCE_INFO *bounce_info, */ if (bounce_info->log_handle == 0 || bounce_log_rewind(bounce_info->log_handle)) { - if (bounce_info->report_type == BOUNCE_REPORT_FAIL) { + if (bounce_info->template == FAIL_TEMPLATE()) { post_mail_fputs(bounce, "\t--- Delivery report unavailable ---"); count = 1; /* XXX don't abort */ } @@ -694,7 +615,7 @@ int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info) bounce_info->log_handle->rcpt.orig_addr); } post_mail_fprintf(bounce, "Action: %s", - bounce_info->report_type == BOUNCE_REPORT_FAIL ? + bounce_info->template == FAIL_TEMPLATE() ? "failed" : bounce_info->log_handle->dsn.action); post_mail_fprintf(bounce, "Status: %s", bounce_info->log_handle->dsn.status); @@ -716,7 +637,7 @@ int bounce_recipient_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info) post_mail_fprintf(bounce, "Last-Attempt-Date: %s", bounce_info->log_handle->log_time); #endif - if (bounce_info->report_type == BOUNCE_REPORT_WARN) + if (bounce_info->template == DELAY_TEMPLATE()) post_mail_fprintf(bounce, "Will-Retry-Until: %s", mail_date(bounce_info->arrival_time + var_max_queue_time)); return (vstream_ferror(bounce)); @@ -740,7 +661,7 @@ int bounce_diagnostic_dsn(VSTREAM *bounce, BOUNCE_INFO *bounce_info, */ if (bounce_info->log_handle == 0 || bounce_log_rewind(bounce_info->log_handle)) { - if (bounce_info->report_type == BOUNCE_REPORT_FAIL) + if (bounce_info->template == FAIL_TEMPLATE()) count = 1; /* XXX don't abort */ } else { while (bounce_log_read(bounce_info->log_handle) != 0) { @@ -776,10 +697,13 @@ int bounce_original(VSTREAM *bounce, BOUNCE_INFO *bounce_info, /* * MIME headers. */ +#define UNDELIVERED(template) \ + ((template) == FAIL_TEMPLATE() || (template) == DELAY_TEMPLATE()) + post_mail_fputs(bounce, ""); post_mail_fprintf(bounce, "--%s", bounce_info->mime_boundary); post_mail_fprintf(bounce, "Content-Description: %s%s", - UNDELIVERED(bounce_info->report_type) ? + UNDELIVERED(bounce_info->template) ? "Undelivered " : "", headers_only == DSN_RET_HDRS ? "Message Headers" : "Message"); diff --git a/postfix/src/bounce/bounce_notify_verp.c b/postfix/src/bounce/bounce_notify_verp.c index 97873bc5f..2c938be8d 100644 --- a/postfix/src/bounce/bounce_notify_verp.c +++ b/postfix/src/bounce/bounce_notify_verp.c @@ -109,7 +109,7 @@ int bounce_notify_verp(int flags, char *service, char *queue_name, * Initialize. Open queue file, bounce log, etc. */ bounce_info = bounce_mail_init(service, queue_name, queue_id, - encoding, dsn_envid, BOUNCE_REPORT_FAIL); + encoding, dsn_envid, FAIL_TEMPLATE()); #define NULL_SENDER MAIL_ADDR_EMPTY /* special address */ #define NULL_TRACE_FLAGS 0 diff --git a/postfix/src/bounce/bounce_service.h b/postfix/src/bounce/bounce_service.h index a47fc5db8..e0034c08f 100644 --- a/postfix/src/bounce/bounce_service.h +++ b/postfix/src/bounce/bounce_service.h @@ -58,6 +58,65 @@ extern void bounce_cleanup_unregister(void); #define bounce_cleanup_registered() (bounce_cleanup_path != 0) + /* + * bounce_template.c + */ +typedef struct { + const char *class; /* template type */ + const char *charset; /* character set */ + const char *encoding; /* 7bit or 8bit */ + const char *from; /* originator */ + const char *subject; /* general subject line */ + const char *postmaster_subject; /* postmaster subject line */ + const char **message_text; /* message text */ +} BOUNCE_TEMPLATE; + +extern void bounce_template_load(const char *); +extern void bounce_template_expand(VSTREAM *, const BOUNCE_TEMPLATE *); +extern const BOUNCE_TEMPLATE *bounce_template_find(const char *, const BOUNCE_TEMPLATE *); +extern void bounce_template_dump_default(VSTREAM *); +extern void bounce_template_dump_actual(VSTREAM *); + +#define BOUNCE_TEMPLATE_DICT "bounce_templates" +#define BOUNCE_TMPL_DICT_FAIL "fail_template" +#define BOUNCE_TMPL_DICT_DELAY "delay_template" +#define BOUNCE_TMPL_DICT_SUCCESS "success_template" +#define BOUNCE_TMPL_DICT_VERIFY "verify_template" + +#define FAIL_TEMPLATE() \ + (bounce_fail_template ? bounce_fail_template : \ + (bounce_fail_template = \ + bounce_template_find(BOUNCE_TMPL_DICT_FAIL, \ + &def_bounce_fail_template))) + +#define DELAY_TEMPLATE() \ + (bounce_delay_template ? bounce_delay_template : \ + (bounce_delay_template = \ + bounce_template_find(BOUNCE_TMPL_DICT_DELAY, \ + &def_bounce_delay_template))) + +#define SUCCESS_TEMPLATE() \ + (bounce_success_template ? bounce_success_template : \ + (bounce_success_template = \ + bounce_template_find(BOUNCE_TMPL_DICT_SUCCESS, \ + &def_bounce_success_template))) + +#define VERIFY_TEMPLATE() \ + (bounce_verify_template ? bounce_verify_template : \ + (bounce_verify_template = \ + bounce_template_find(BOUNCE_TMPL_DICT_VERIFY, \ + &def_bounce_verify_template))) + +extern const BOUNCE_TEMPLATE *bounce_fail_template; +extern const BOUNCE_TEMPLATE *bounce_delay_template; +extern const BOUNCE_TEMPLATE *bounce_success_template; +extern const BOUNCE_TEMPLATE *bounce_verify_template; + +extern const BOUNCE_TEMPLATE def_bounce_fail_template; +extern const BOUNCE_TEMPLATE def_bounce_delay_template; +extern const BOUNCE_TEMPLATE def_bounce_success_template; +extern const BOUNCE_TEMPLATE def_bounce_verify_template; + /* * bounce_notify_util.c */ @@ -68,7 +127,7 @@ typedef struct { const char *mime_encoding; /* null or encoding */ const char *dsn_envid; /* DSN envelope ID */ const char *mime_boundary; /* for MIME */ - int report_type; /* see below */ + const BOUNCE_TEMPLATE *template; /* see above */ VSTRING *buf; /* scratch pad */ VSTRING *sender; /* envelope sender */ VSTREAM *orig_fp; /* open queue file */ @@ -81,7 +140,7 @@ typedef struct { /* */ -extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, const char *, const char *, int); +extern BOUNCE_INFO *bounce_mail_init(const char *, const char *, const char *, const char *, const char *, const BOUNCE_TEMPLATE *); extern BOUNCE_INFO *bounce_mail_one_init(const char *, const char *, const char *, const char *, RECIPIENT *, DSN *); extern void bounce_mail_free(BOUNCE_INFO *); extern int bounce_header(VSTREAM *, BOUNCE_INFO *, const char *); @@ -95,14 +154,6 @@ extern int bounce_original(VSTREAM *, BOUNCE_INFO *, int); extern void bounce_delrcpt(BOUNCE_INFO *); extern void bounce_delrcpt_one(BOUNCE_INFO *); - /* - * Report types. - */ -#define BOUNCE_REPORT_FAIL 0 /* undeliverable mail */ -#define BOUNCE_REPORT_WARN 1 /* delayed mail */ -#define BOUNCE_REPORT_SUCCESS 2 /* success */ -#define BOUNCE_REPORT_OTHER 3 /* other status */ - /* LICENSE /* .ad /* .fi diff --git a/postfix/src/bounce/bounce_template.c b/postfix/src/bounce/bounce_template.c new file mode 100644 index 000000000..78ccea3a9 --- /dev/null +++ b/postfix/src/bounce/bounce_template.c @@ -0,0 +1,547 @@ +/*++ +/* NAME +/* bounce_template 3 +/* SUMMARY +/* bounce template processing +/* SYNOPSIS +/* #include "bounce_service.h" +/* +/* void bounce_template_load(path) +/* const char *path; +/* +/* const BOUNCE_TEMPLATE *FAIL_TEMPLATE() +/* +/* const BOUNCE_TEMPLATE *DELAY_TEMPLATE() +/* +/* const BOUNCE_TEMPLATE *SUCCESS_TEMPLATE() +/* +/* const BOUNCE_TEMPLATE *VERIFY_TEMPLATE() +/* +/* void bounce_template_expand(stream, template) +/* VSTREAM *stream; +/* BOUNCE_TEMPLATE *template; +/* AUXILIARY FUNCTIONS +/* void bounce_template_dump_default(stream) +/* VSTREAM *stream; +/* +/* void bounce_template_dump_actual(stream) +/* VSTREAM *stream; +/* DESCRIPTION +/* This module implements the built-in and external bounce +/* message template support. +/* +/* bounce_template_load() reads bounce templates from the +/* specified file. +/* +/* FAIL_TEMPLATE() etc. look up the corresponding bounce +/* template from file, or use a built-in template when no +/* template was specified externally. +/* +/* bounce_template_expand() expands the body text of the +/* specified template and writes the result to the specified +/* queue file record stream. +/* +/* bounce_template_dump_default() dumps the built-in default templates +/* to the specified stream. This can be used to generate input +/* for the default bounce service configuration file. +/* +/* bounce_template_dump_actual() dumps the actually-used templates +/* to the specified stream. This can be used to verify that +/* the bounce server correctly reads its own bounce_template_dump_default() +/* output. +/* DIAGNOSTICS +/* Fatal error: error opening template file, out of memory, +/* undefined macro name in template. +/* BUGS +/* 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 library. */ + +#include +#include +#include + +#ifdef STRCASECMP_IN_STRINGS_H +#include +#endif + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include + +/* Global library. */ + +#include +#include +#include +#include +#include +#include + +/* Application-specific. */ + +#include + + /* + * The fail template is for permanent failure. + */ +static const char *def_bounce_fail_body[]; + +const BOUNCE_TEMPLATE def_bounce_fail_template = { + "fail", + "us-ascii", + MAIL_ATTR_ENC_7BIT, + MAIL_ADDR_MAIL_DAEMON " (Mail Delivery System)", + "Undelivered Mail Returned to Sender", + "Postmaster Copy: Undelivered Mail", + def_bounce_fail_body, +}; + +static const char *def_bounce_fail_body[] = { + "This is the $mail_name program at host $myhostname.", + "", + "I'm sorry to have to inform you that your message could not", + "be delivered to one or more recipients. It's attached below.", + "", + "For further assistance, please send mail to <" MAIL_ADDR_POSTMASTER ">", + "", + "If you do so, please include this problem report. You can", + "delete your own text from the attached returned message.", + "", + " The $mail_name program", + 0, +}; + + /* + * The delay template is for delayed mail notifications. + */ +static const char *def_bounce_delay_body[]; + +const BOUNCE_TEMPLATE def_bounce_delay_template = { + "delay", + "us-ascii", + MAIL_ATTR_ENC_7BIT, + MAIL_ADDR_MAIL_DAEMON " (Mail Delivery System)", + "Delayed Mail (still being retried)", + "Postmaster Warning: Delayed Mail", + def_bounce_delay_body, +}; + +static const char *def_bounce_delay_body[] = { + "This is the $mail_name program at host $myhostname.", + "", + "####################################################################", + "# THIS IS A WARNING ONLY. YOU DO NOT NEED TO RESEND YOUR MESSAGE. #", + "####################################################################", + "", + "Your message could not be delivered for $delay_warning_time_hours hour(s).", + "It will be retried until it is $maximal_queue_lifetime_days day(s) old.", + "", + "For further assistance, please send mail to <" MAIL_ADDR_POSTMASTER ">", + "", + "If you do so, please include this problem report. You can", + "delete your own text from the attached returned message.", + "", + " The $mail_name program", + 0, +}; + + /* + * The success template is for "delivered", "expanded" and "relayed" success + * notifications. + */ +static const char *def_bounce_success_body[]; + +const BOUNCE_TEMPLATE def_bounce_success_template = { + "success", + "us-ascii", + MAIL_ATTR_ENC_7BIT, + MAIL_ADDR_MAIL_DAEMON " (Mail Delivery System)", + "Successful Mail Delivery Report", + 0, + def_bounce_success_body, +}; + +static const char *def_bounce_success_body[] = { + "This is the $mail_name program at host $myhostname.", + "", + "Your message was successfully delivered to the destination(s)", + "listed below. If the message was delivered to mailbox you will", + "receive no further notifications. Otherwise you may still receive", + "notifications of mail delivery errors from other systems.", + "", + " The $mail_name program", + 0, +}; + + /* + * The "verify" template is for verbose delivery (sendmail -v) and for + * address verification (sendmail -bv). + */ +static const char *def_bounce_verify_body[]; + +const BOUNCE_TEMPLATE def_bounce_verify_template = { + "verify", + "us-ascii", + MAIL_ATTR_ENC_7BIT, + MAIL_ADDR_MAIL_DAEMON " (Mail Delivery System)", + "Mail Delivery Status Report", + 0, + def_bounce_verify_body, +}; + +static const char *def_bounce_verify_body[] = { + "This is the $mail_name program at host $myhostname.", + "", + "Enclosed is the mail delivery report that you requested.", + "", + " The $mail_name program", + 0, +}; + + /* + * Pointers, so that we can override a built-in template with one from file + * without clobbering the built-in template. + */ +const BOUNCE_TEMPLATE *bounce_fail_template; +const BOUNCE_TEMPLATE *bounce_delay_template; +const BOUNCE_TEMPLATE *bounce_success_template; +const BOUNCE_TEMPLATE *bounce_verify_template; + + /* + * The following tables implement support for bounce template expansions of + * $_days ($_hours, etc.). The expansion of + * these is the actual parameter value divided by the number of seconds in a + * day (hour, etc.), so that we can produce nicely formatted bounce messages + * with time values converted into the appropriate units. + * + * Ideally, the bounce template processor would strip the _days etc. suffix + * from the parameter name, and use the parameter name to look up the actual + * parameter value and its default value (the default value specifies the + * default time unit of that parameter (seconds, minutes, etc.), and allows + * us to convert the parameter string value into the corresponding number of + * seconds). The bounce template processor would then use the _hours etc. + * suffix from the bounce template to divide this number by the number of + * seconds in an hour, etc. and produce the number that is needed for the + * template. + * + * Unfortunately, there exists no code to look up default values by parameter + * name. If such code existed, then we could do the _days, _hours, etc. + * conversion with every main.cf time parameter without having to know in + * advance what time parameter names exist. + * + * So we have to either maintain our own table of all time related main.cf + * parameter names and defaults (like the postconf command does) or we make + * a special case for a few parameters of special interest. + * + * We go for the second solution. There are only a few parameters that need + * this treatment, and there will be more special cases when individual + * queue files get support for individual expiration times, and when other + * queue file information needs to be reported in bounce template messages. + * + * A really lame implementation would simply strip the optional s, h, d, etc. + * suffix from the actual (string) parameter value and not do any conversion + * at all to hours, days or weeks. But then the information in delay warning + * notices could be seriously incorrect. + */ +typedef struct { + const char *suffix; /* days, hours, etc. */ + int suffix_len; /* byte count */ + int divisor; /* divisor */ +} BOUNCE_TIME_DIVISOR; + +#define STRING_AND_LEN(x) (x), (sizeof(x) - 1) + +static BOUNCE_TIME_DIVISOR time_divisors[] = { + STRING_AND_LEN("seconds"), 1, + STRING_AND_LEN("minutes"), 60, + STRING_AND_LEN("hours"), 60 * 60, + STRING_AND_LEN("days"), 24 * 60 * 60, + STRING_AND_LEN("weeks"), 7 * 24 * 60 * 60, + 0, 0, +}; + + /* + * The few special-case main.cf parameters that have support for _days, etc. + * suffixes for automatic conversion when expanded into a bounce template. + */ +typedef struct { + const char *param_name; /* parameter name */ + int param_name_len; /* name length */ + int *value; /* parameter value */ +} BOUNCE_TIME_PARAMETER; + +static BOUNCE_TIME_PARAMETER time_parameter[] = { + STRING_AND_LEN(VAR_DELAY_WARN_TIME), &var_delay_warn_time, + STRING_AND_LEN(VAR_MAX_QUEUE_TIME), &var_max_queue_time, + 0, 0, +}; + + /* + * SLMs. + */ +#define STR(x) vstring_str(x) + +/* bounce_template_lookup - lookup $name value */ + +static const char *bounce_template_lookup(const char *key, int unused_mode, + char *context) +{ + BOUNCE_TEMPLATE *template = (BOUNCE_TEMPLATE *) context; + BOUNCE_TIME_PARAMETER *bp; + BOUNCE_TIME_DIVISOR *bd; + static VSTRING *buf; + int result; + + /* + * Look for parameter names that can have a time unit suffix, and scale + * the time value according to the suffix. + */ + for (bp = time_parameter; bp->param_name; bp++) { + if (strncmp(key, bp->param_name, bp->param_name_len) == 0 + && key[bp->param_name_len] == '_') { + for (bd = time_divisors; bd->suffix; bd++) { + if (strcmp(key + bp->param_name_len + 1, bd->suffix) == 0) { + result = bp->value[0] / bd->divisor; + if (result > 999 && bd->divisor < 86400) { + msg_warn("excessive result \"%d\" in %s bounce " + "template conversion of parameter \"%s\"", + result, template->class, key); + msg_warn("please increase time unit \"%s\" of \"%s\" " + "in bounce template file", bd->suffix, key); + } else if (result == 0 && bd->divisor > 1) { + msg_warn("zero result in %s bounce template " + "conversion of parameter \"%s\"", + template->class, key); + msg_warn("please reduce time unit \"%s\" of \"%s\" " + "in bounce template file", bd->suffix, key); + } + if (buf == 0) + buf = vstring_alloc(10); + vstring_sprintf(buf, "%d", result); + return (STR(buf)); + } + } + msg_fatal("unrecognized suffix \"%s\" in template parameter \"%s\"", + key + bp->param_name_len + 1, key); + } + } + return (mail_conf_lookup_eval(key)); +} + +/* bounce_template_expand - expand template body */ + +void bounce_template_expand(VSTREAM *stream, const BOUNCE_TEMPLATE *template) +{ + VSTRING *buf = vstring_alloc(100); + const char **cpp; + int stat; + const char *filter = "\t !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~"; + +#define NO_CONTEXT ((char *) 0) + + for (cpp = template->message_text; *cpp; cpp++) { + stat = mac_expand(buf, *cpp, MAC_EXP_FLAG_NONE, filter, + bounce_template_lookup, (char *) template); + if (stat & MAC_PARSE_ERROR) + msg_fatal("bad $name syntax in %s template: %s", + template->class, *cpp); + if (stat & MAC_PARSE_UNDEF) + msg_fatal("undefined $name in %s template: %s", + template->class, *cpp); + post_mail_fputs(stream, STR(buf)); + } + vstring_free(buf); +} + +/* bounce_template_load - load template(s) from file */ + +void bounce_template_load(const char *path) +{ + static int once = 0; + + /* + * Split the input stream into chunks between begin/end markers, ignoring + * comment lines. + */ + if (once++ > 0) + msg_panic("bounce_template_load: multiple calls"); + dict_ml_load_file(BOUNCE_TEMPLATE_DICT, path); +} + +/* bounce_template_find - return default or user-specified template */ + +const BOUNCE_TEMPLATE *bounce_template_find(const char *template_name, + const BOUNCE_TEMPLATE *def_template) +{ + BOUNCE_TEMPLATE *tp; + char *tval; + char *cp; + char **cpp; + int cpp_len; + int cpp_used; + int hlen; + char *hval; + + /* + * Look up a non-default template. Once we found it we are going to + * destroy it; no-one will access this data again. + */ + if (*var_bounce_tmpl == 0 + || (tval = (char *) dict_lookup(BOUNCE_TEMPLATE_DICT, template_name)) == 0) + return (def_template); + + /* + * Initialize a new template. We're not going to use the message text + * from the default template. + */ + tp = (BOUNCE_TEMPLATE *) mymalloc(sizeof(*tp)); + *tp = *def_template; + +#define CLEANUP_AND_RETURN(x) do { \ + myfree((char *) tp); \ + return (x); \ + } while (0) + + + /* + * Parse pseudo-header labels and values. + */ +#define GETLINE(line, buf) \ + (((line) = (buf)) ? ((buf) = split_at((buf), '\n'), (line)) : 0) +/*#define GETLINE(line, buf) (line = mystrtok(&buf, "\n"))*/ + + while ((GETLINE(cp, tval)) != 0 && (hlen = is_header(cp)) > 0) { + for (hval = cp + hlen; *hval && (*hval == ':' || ISSPACE(*hval)); hval++) + *hval = 0; + if (*hval == 0) { + msg_warn("empty \"%s\" header value in %s template " + "-- ignoring this template", cp, template_name); + CLEANUP_AND_RETURN(def_template); + } + if (!allascii(hval)) { + msg_warn("non-ASCII \"%s\" header value in %s template " + "-- ignoring this template", cp, template_name); + CLEANUP_AND_RETURN(def_template); + } + if (strcasecmp("charset", cp) == 0) { + tp->charset = hval; + } else if (strcasecmp("from", cp) == 0) { + tp->from = hval; + } else if (strcasecmp("subject", cp) == 0) { + tp->subject = hval; + } else if (strcasecmp("postmaster-subject", cp) == 0) { + if (tp->postmaster_subject == 0) { + msg_warn("\"%s\" header label in %s template is not applicable " + "-- ignoring this template", cp, template_name); + CLEANUP_AND_RETURN(def_template); + } + tp->postmaster_subject = hval; + } else { + msg_warn("unknown \"%s\" header label in %s template " + "-- ignoring this template", cp, template_name); + CLEANUP_AND_RETURN(def_template); + } + } + + /* + * Skip blank lines between header and message text. + */ + while (cp && (*cp == 0 || allspace(cp))) + (void) GETLINE(cp, tval); + if (cp == 0) { + msg_warn("missing message text in %s template " + "-- ignoring this template", template_name); + CLEANUP_AND_RETURN(def_template); + } + + /* + * Is this 7bit or 8bit text? If the character set is US-ASCII, then + * don't allow 8bit text. + */ +#define NON_ASCII(p) (*(p) && !allascii((p))) + + if (NON_ASCII(cp) || NON_ASCII(tval)) { + if (strcasecmp(tp->charset, "us-ascii") == 0) { + msg_warn("8-bit message text in %s template", template_name); + msg_warn("please specify a charset value other than us-ascii"); + msg_warn("-- ignoring this template for now"); + CLEANUP_AND_RETURN(def_template); + } + tp->encoding = MAIL_ATTR_ENC_8BIT; + } + + /* + * Collect the message text and null-terminate the result. + */ + cpp_len = 10; + cpp_used = 0; + cpp = (char **) mymalloc(sizeof(*cpp) * cpp_len); + while (cp) { + cpp[cpp_used++] = cp; + if (cpp_used >= cpp_len) { + cpp = (char **) myrealloc((char *) cpp, + sizeof(*cpp) * 2 * cpp_len); + cpp_len *= 2; + } + (void) GETLINE(cp, tval); + } + cpp[cpp_used] = 0; + tp->message_text = (const char **) cpp; + + return (tp); +} + +/* print_template - dump one template */ + +static void print_template(VSTREAM *stream, const BOUNCE_TEMPLATE *tp) +{ + const char **cpp; + + vstream_fprintf(stream, "%s_template = <class); + vstream_fprintf(stream, "Charset: %s\n", tp->charset); + vstream_fprintf(stream, "From: %s\n", tp->from); + vstream_fprintf(stream, "Subject: %s\n", tp->subject); + if (tp->postmaster_subject) + vstream_fprintf(stream, "Postmaster-Subject: %s\n", + tp->postmaster_subject); + vstream_fprintf(stream, "\n"); + for (cpp = tp->message_text; *cpp; cpp++) + vstream_fprintf(stream, "%s\n", *cpp); + vstream_fprintf(stream, "EOF\n"); + vstream_fflush(stream); +} + +/* bounce_template_dump_actual - dump actual templates to stream */ + +void bounce_template_dump_actual(VSTREAM *stream) +{ + print_template(VSTREAM_OUT, FAIL_TEMPLATE()); + print_template(VSTREAM_OUT, DELAY_TEMPLATE()); + print_template(VSTREAM_OUT, SUCCESS_TEMPLATE()); + print_template(VSTREAM_OUT, VERIFY_TEMPLATE()); +} + +/* bounce_template_dump_default - dump built-in templates to stream */ + +void bounce_template_dump_default(VSTREAM *stream) +{ + print_template(VSTREAM_OUT, &def_bounce_fail_template); + print_template(VSTREAM_OUT, &def_bounce_delay_template); + print_template(VSTREAM_OUT, &def_bounce_success_template); + print_template(VSTREAM_OUT, &def_bounce_verify_template); +} diff --git a/postfix/src/bounce/bounce_trace_service.c b/postfix/src/bounce/bounce_trace_service.c index 0212932aa..c760f1618 100644 --- a/postfix/src/bounce/bounce_trace_service.c +++ b/postfix/src/bounce/bounce_trace_service.c @@ -106,8 +106,8 @@ int bounce_trace_service(int flags, char *service, char *queue_name, bounce_info = bounce_mail_init(service, queue_name, queue_id, encoding, dsn_envid, flags & NON_DSN_FLAGS ? - BOUNCE_REPORT_OTHER : - BOUNCE_REPORT_SUCCESS); + VERIFY_TEMPLATE() : + SUCCESS_TEMPLATE()); /* * XXX With multi-recipient mail some queue file recipients may have diff --git a/postfix/src/bounce/bounce_warn_service.c b/postfix/src/bounce/bounce_warn_service.c index 2a964ceec..229965fd8 100644 --- a/postfix/src/bounce/bounce_warn_service.c +++ b/postfix/src/bounce/bounce_warn_service.c @@ -117,7 +117,7 @@ int bounce_warn_service(int unused_flags, char *service, char *queue_name, * notify_classes restrictions. */ bounce_info = bounce_mail_init(service, queue_name, queue_id, - encoding, dsn_envid, BOUNCE_REPORT_WARN); + encoding, dsn_envid, DELAY_TEMPLATE()); #define NULL_SENDER MAIL_ADDR_EMPTY /* special address */ #define NULL_TRACE_FLAGS 0 diff --git a/postfix/src/bounce/dict_ml.c b/postfix/src/bounce/dict_ml.c new file mode 100644 index 000000000..d7be3f149 --- /dev/null +++ b/postfix/src/bounce/dict_ml.c @@ -0,0 +1,179 @@ +/*++ +/* NAME +/* dict 3 +/* SUMMARY +/* dictionary manager, multi-line entry support +/* SYNOPSIS +/* #include +/* +/* void dict_ml_load_file(dict_name, path) +/* const char *dict_name; +/* const char *path; +/* +/* void dict_ml_load_fp(dict_name, fp) +/* const char *dict_name; +/* VSTREAM *fp; +/* DESCRIPTION +/* This module implements input routines for dictionaries +/* with single-line and multi-line values. +/* .IP \(bu +/* Single-line values are specified as "name = value". +/* Like dict_load_file() and dict_load_fp(), leading and +/* trailing whitespace is stripped from name and value. +/* .IP \(bu +/* Multi-line values are specified as: +/* +/* .na +/* .nf +/* .in +4 +/* name = < +#include +#include +#include +#include + +/* Utility library. */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define STR(x) vstring_str(x) + +/* dict_ml_load_file - load table from file */ + +void dict_ml_load_file(const char *dict_name, const char *path) +{ + VSTREAM *fp; + struct stat st; + time_t before; + time_t after; + + /* + * Read the file again if it is hot. This may result in reading a partial + * parameter name when a file changes in the middle of a read. + */ + for (before = time((time_t *) 0); /* see below */ ; before = after) { + if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) + msg_fatal("open %s: %m", path); + dict_ml_load_fp(dict_name, fp); + if (fstat(vstream_fileno(fp), &st) < 0) + msg_fatal("fstat %s: %m", path); + if (vstream_ferror(fp) || vstream_fclose(fp)) + msg_fatal("read %s: %m", path); + after = time((time_t *) 0); + if (st.st_mtime < before - 1 || st.st_mtime > after) + break; + if (msg_verbose) + msg_info("pausing to let %s cool down", path); + doze(300000); + } +} + +/* dict_ml_load_fp - load table from stream */ + +void dict_ml_load_fp(const char *dict_name, VSTREAM *fp) +{ + VSTRING *line_buf; + char *member_name; + VSTRING *multi_line_buf = 0; + VSTRING *saved_member_name = 0; + VSTRING *saved_end_marker = 0; + char *value; + int lineno; + const char *err; + char *cp; + + line_buf = vstring_alloc(100); + lineno = 1; + while (vstring_get_nonl(line_buf, fp) > 0) { + lineno++; + cp = STR(line_buf) + strspn(STR(line_buf), " \t\n\v\f\r"); + if (*cp == 0 || *cp == '#') + continue; + if ((err = split_nameval(STR(line_buf), &member_name, &value)) != 0) + msg_fatal("%s, line %d: %s: \"%s\"", + VSTREAM_PATH(fp), lineno, err, STR(line_buf)); + if (value[0] == '<' && value[1] == '<') { + value += 2; + while (ISSPACE(*value)) + value++; + if (*value == 0) + msg_fatal("%s, line %d: missing end marker after <<", + VSTREAM_PATH(fp), lineno); + if (!ISALNUM(*value)) + msg_fatal("%s, line %d: malformed end marker after <<", + VSTREAM_PATH(fp), lineno); + if (multi_line_buf == 0) { + saved_member_name = vstring_alloc(100); + saved_end_marker = vstring_alloc(100); + multi_line_buf = vstring_alloc(100); + } else + VSTRING_RESET(multi_line_buf); + vstring_strcpy(saved_member_name, member_name); + vstring_strcpy(saved_end_marker, value); + while (vstring_get_nonl(line_buf, fp) > 0) { + lineno++; + if (strcmp(STR(line_buf), STR(saved_end_marker)) == 0) + break; + if (VSTRING_LEN(multi_line_buf) > 0) + vstring_strcat(multi_line_buf, "\n"); + vstring_strcat(multi_line_buf, STR(line_buf)); + } + if (vstream_feof(fp)) + msg_fatal("%s, line %d: missing \"%s\" end marker", + VSTREAM_PATH(fp), lineno, value); + member_name = STR(saved_member_name); + value = STR(multi_line_buf); + } + dict_update(dict_name, member_name, value); + } + vstring_free(line_buf); + if (multi_line_buf) { + vstring_free(saved_member_name); + vstring_free(saved_end_marker); + vstring_free(multi_line_buf); + } +} diff --git a/postfix/src/bounce/dict_ml.h b/postfix/src/bounce/dict_ml.h new file mode 100644 index 000000000..b7f47dc19 --- /dev/null +++ b/postfix/src/bounce/dict_ml.h @@ -0,0 +1,37 @@ +#ifndef _DICT_ML_H_INCLUDED_ +#define _DICT_ML_H_INCLUDED_ + +/*++ +/* NAME +/* dict_ml 3h +/* SUMMARY +/* dictionary manager, multi-line entry support +/* SYNOPSIS +/* #include +/* DESCRIPTION +/* .nf + + /* + * Utility library. + */ +#include +#include + + /* + * External interface. + */ +extern void dict_ml_load_file(const char *, const char *); +extern void dict_ml_load_fp(const char *, VSTREAM *); + +/* 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 --git a/postfix/src/bounce/fix-template.pl b/postfix/src/bounce/fix-template.pl new file mode 100644 index 000000000..5d0dde5ca --- /dev/null +++ b/postfix/src/bounce/fix-template.pl @@ -0,0 +1,99 @@ +print <<'EOF' +# +# Do not edit this file. This file shows the default delivery status +# notification (DSN) messages that are built into Postfix. +# +# To change Postfix DSN messages, perhaps to add non-English text, +# follow instructions in the BOUNCE_README document. +# +# QUICK INSTRUCTIONS: +# +# Copy this file to $config_directory/bounce.cf, edit that file, +# then specify in main.cf: +# +# bounce_template_file = $config_directory/bounce.cf +# +#-The template file can specify bounce message templates for +# failed mail, delayed mail, successful delivery, and verbose delivery. +# You don't have to specify templates for all of these. If there is +# anything about a template that Postfix does not like it logs a +# warning and keeps using its built-in template. +# +#-Each template consists of a few headers and message text. The +# headers control what the recipient sees as From: and Subject:, and +# what MIME information Postfix will generate. The message text is +# not sent in Postmaster copies of delivery status notifications. +# +#-You can specify main.cf parameters in a template message text. +# Some parameters have additional features as described below. +# +#-You cannot specify main.cf parameters in template headers. +# +#-Each template starts with "template_name = <) { + if (/^fail_template/) { print <<'EOF' + +# +# The fail template is used when mail is returned to the sender; +# either the destination rejected the message, or the destination +# could not be reached before the message expired in the queue. +# + +EOF +; + } elsif (/^delay_template/) { print <<'EOF' + +# +# The delay template is used when mail is delayed. Note a neat trick: +# the default template displays the delay_warning_time value as hours +# by appending the _hours suffix to the parameter name; it displays +# the maximal_queue_lifetime value as days by appending the _days +# suffix. +# +# Other suffixes are: _seconds, _minutes, _weeks. There are no other +# main.cf parameters that have this special behavior. +# +# You need to adjust these suffixes (and the surrounding text) if +# you have very different settings for these time parameters. +# + +EOF +; + } elsif (/^success_template/) { print <<'EOF' + +# +# The success template is used when mail is delivered to mailbox, +# when an alias or list is expanded, or when mail is delivered to a +# system that does not announce DSN support. It is an error to specify +# a Postmaster-Subject: here. +# + +EOF +; + } elsif (/^verify_template/) { print <<'EOF' + +# +# The verify template is used for address verification (sendmail -bv +# address...). or for verbose mail delivery (sendmail -v address...). +# It is an error to specify a Postmaster-Subject: here. +# + +EOF +; + } + print $_; +} diff --git a/postfix/src/cleanup/cleanup_envelope.c b/postfix/src/cleanup/cleanup_envelope.c index d7b4f0d80..3d9b2da7b 100644 --- a/postfix/src/cleanup/cleanup_envelope.c +++ b/postfix/src/cleanup/cleanup_envelope.c @@ -207,7 +207,13 @@ static void cleanup_envelope_process(CLEANUP_STATE *state, int type, state->errs |= CLEANUP_STAT_BAD; return; } + + /* + * XXX This works by accident, because the sender is recorded at the + * beginning of the envelope segment. + */ if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0 + && state->sender && *state->sender && var_delay_warn_time > 0) { cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT, REC_TYPE_WARN_ARG(state->arrival_time.tv_sec diff --git a/postfix/src/global/mail_params.h b/postfix/src/global/mail_params.h index 6b00c12ea..2bb85b0e8 100644 --- a/postfix/src/global/mail_params.h +++ b/postfix/src/global/mail_params.h @@ -2397,6 +2397,13 @@ extern bool var_frozen_delivered; #define MIN_DELAY_MAX_RES 0 extern int var_delay_max_res; + /* + * Bounce message templates. + */ +#define VAR_BOUNCE_TMPL "bounce_template_file" +#define DEF_BOUNCE_TMPL "" +extern char *var_bounce_tmpl; + /* LICENSE /* .ad /* .fi diff --git a/postfix/src/global/mail_version.h b/postfix/src/global/mail_version.h index 4b9d9215a..2cd5ce343 100644 --- a/postfix/src/global/mail_version.h +++ b/postfix/src/global/mail_version.h @@ -20,7 +20,7 @@ * Patches change both the patchlevel and the release date. Snapshots have no * patchlevel; they change the release date only. */ -#define MAIL_RELEASE_DATE "20051109" +#define MAIL_RELEASE_DATE "20051112" #define MAIL_VERSION_NUMBER "2.3" #ifdef SNAPSHOT diff --git a/postfix/src/util/Makefile.in b/postfix/src/util/Makefile.in index f337b4502..182d8eb27 100644 --- a/postfix/src/util/Makefile.in +++ b/postfix/src/util/Makefile.in @@ -29,7 +29,8 @@ SRCS = alldig.c allprint.c argv.c argv_split.c attr_clnt.c attr_print0.c \ unix_recv_fd.c unix_send_fd.c unix_trigger.c unsafe.c uppercase.c \ username.c valid_hostname.c vbuf.c vbuf_print.c vstream.c \ vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \ - write_buf.c write_wait.c sane_basename.c format_tv.c + write_buf.c write_wait.c sane_basename.c format_tv.c allspace.c \ + allascii.c OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ attr_print64.o attr_print_plain.o attr_scan0.o attr_scan64.o \ attr_scan_plain.o auto_clnt.o base64_code.o basename.o binhash.o \ @@ -60,7 +61,8 @@ OBJS = alldig.o allprint.o argv.o argv_split.o attr_clnt.o attr_print0.o \ unix_recv_fd.o unix_send_fd.o unix_trigger.o unsafe.o uppercase.o \ username.o valid_hostname.o vbuf.o vbuf_print.o vstream.o \ vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \ - write_buf.o write_wait.o sane_basename.o format_tv.o + write_buf.o write_wait.o sane_basename.o format_tv.o allspace.o \ + allascii.o HDRS = argv.h attr.h attr_clnt.h auto_clnt.h base64_code.h binhash.h \ chroot_uid.h cidr_match.h clean_env.h connect.h ctable.h dict.h \ dict_cdb.h dict_cidr.h dict_db.h dict_dbm.h dict_env.h dict_ht.h \ @@ -532,6 +534,11 @@ depend: $(MAKES) @$(EXPORT) make -f Makefile.in Makefile 1>&2 # do not edit below this line - it is generated by 'make depend' +allascii.o: allascii.c +allascii.o: stringops.h +allascii.o: sys_defs.h +allascii.o: vbuf.h +allascii.o: vstring.h alldig.o: alldig.c alldig.o: stringops.h alldig.o: sys_defs.h @@ -542,6 +549,11 @@ allprint.o: stringops.h allprint.o: sys_defs.h allprint.o: vbuf.h allprint.o: vstring.h +allspace.o: allspace.c +allspace.o: stringops.h +allspace.o: sys_defs.h +allspace.o: vbuf.h +allspace.o: vstring.h argv.o: argv.c argv.o: argv.h argv.o: msg.h diff --git a/postfix/src/util/allascii.c b/postfix/src/util/allascii.c new file mode 100644 index 000000000..aabbc8e0d --- /dev/null +++ b/postfix/src/util/allascii.c @@ -0,0 +1,50 @@ +/*++ +/* NAME +/* allascii 3 +/* SUMMARY +/* predicate if string is all ASCII +/* SYNOPSIS +/* #include +/* +/* int allascii(buffer) +/* const char *buffer; +/* DESCRIPTION +/* allascii() determines if its argument is an all-ASCII string. +/* +/* Arguments: +/* .IP buffer +/* The null-terminated input string. +/* 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 library. */ + +#include +#include + +/* Utility library. */ + +#include "stringops.h" + +/* allascii - return true if string is all ASCII */ + +int allascii(const char *string) +{ + const char *cp; + int ch; + + if (*string == 0) + return (0); + for (cp = string; (ch = *(unsigned char *) cp) != 0; cp++) + if (!ISASCII(ch)) + return (0); + return (1); +} diff --git a/postfix/src/util/allspace.c b/postfix/src/util/allspace.c new file mode 100644 index 000000000..9a0534712 --- /dev/null +++ b/postfix/src/util/allspace.c @@ -0,0 +1,50 @@ +/*++ +/* NAME +/* allspace 3 +/* SUMMARY +/* predicate if string is all space +/* SYNOPSIS +/* #include +/* +/* int allspace(buffer) +/* const char *buffer; +/* DESCRIPTION +/* allspace() determines if its argument is an all-space string. +/* +/* Arguments: +/* .IP buffer +/* The null-terminated input string. +/* 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 library. */ + +#include +#include + +/* Utility library. */ + +#include "stringops.h" + +/* allspace - return true if string is all space */ + +int allspace(const char *string) +{ + const char *cp; + int ch; + + if (*string == 0) + return (0); + for (cp = string; (ch = *(unsigned char *) cp) != 0; cp++) + if (!ISASCII(ch) || !ISSPACE(ch)) + return (0); + return (1); +} diff --git a/postfix/src/util/dict.c b/postfix/src/util/dict.c index d20eb2fd0..e57df6506 100644 --- a/postfix/src/util/dict.c +++ b/postfix/src/util/dict.c @@ -55,7 +55,7 @@ /* /* void dict_load_fp(dict_name, fp) /* const char *dict_name; -/* FILE *fp; +/* VSTREAM *fp; /* DESCRIPTION /* This module maintains a collection of name-value dictionaries. /* Each dictionary has its own name and has its own methods to read diff --git a/postfix/src/util/format_tv.c b/postfix/src/util/format_tv.c index ef06e6804..e555cb3c7 100644 --- a/postfix/src/util/format_tv.c +++ b/postfix/src/util/format_tv.c @@ -109,7 +109,7 @@ VSTRING *format_tv(VSTRING *buf, int sec, int usec, } /* - * Format the number. Truncate thrash below the resolution. + * Format the number. Truncate trailing null and thrash below resolution. */ vstring_sprintf_append(buf, "%d", sec); if (usec >= ures) { diff --git a/postfix/src/util/stringops.h b/postfix/src/util/stringops.h index 02ea3079a..04e09df00 100644 --- a/postfix/src/util/stringops.h +++ b/postfix/src/util/stringops.h @@ -37,6 +37,8 @@ extern char *sane_dirname(VSTRING *, const char *); extern VSTRING *unescape(VSTRING *, const char *); extern int alldig(const char *); extern int allprint(const char *); +extern int allspace(const char *); +extern int allascii(const char *); extern const char *split_nameval(char *, char **, char **); /* LICENSE