-TBOUNCE_LOG_DSN_BUF
-TBOUNCE_LOG_RCPT_BUF
-TBOUNCE_STAT
+-TBOUNCE_TEMPLATE
+-TBOUNCE_TIME_DIVISOR
+-TBOUNCE_TIME_PARAMETER
-TCFG_PARSER
-TCIDR_MATCH
-TCLEANUP_STATE
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
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,
*.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
# (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
$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
The maximal amount of original message text that is
sent in a non-delivery notification.
+ <b>bounce_template_file (empty)</b>
+ Pathname of a configuration file with bounce mes-
+ sage templates.
+
<b><a href="postconf.5.html#config_directory">config_directory</a> (see 'postconf -d' output)</b>
- The default location of the Postfix main.cf and
+ The default location of the Postfix main.cf and
master.cf configuration files.
<b><a href="postconf.5.html#daemon_timeout">daemon_timeout</a> (18000s)</b>
- 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.
<b><a href="postconf.5.html#delay_notice_recipient">delay_notice_recipient</a> (postmaster)</b>
- 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 $<a href="postconf.5.html#delay_warning_time">delay_warning_time</a> time units.
<b><a href="postconf.5.html#deliver_lock_attempts">deliver_lock_attempts</a> (20)</b>
sive lock on a mailbox file or <a href="bounce.8.html"><b>bounce</b>(8)</a> logfile.
<b><a href="postconf.5.html#deliver_lock_delay">deliver_lock_delay</a> (1s)</b>
- The time between attempts to acquire an exclusive
+ The time between attempts to acquire an exclusive
lock on a mailbox file or <a href="bounce.8.html"><b>bounce</b>(8)</a> logfile.
<b><a href="postconf.5.html#ipc_timeout">ipc_timeout</a> (3600s)</b>
bounced mail.
<b><a href="postconf.5.html#max_idle">max_idle</a> (100s)</b>
- 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.
<b><a href="postconf.5.html#max_use">max_use</a> (100)</b>
- The maximal number of connection requests before a
+ The maximal number of connection requests before a
Postfix daemon process terminates.
<b><a href="postconf.5.html#notify_classes">notify_classes</a> (resource, software)</b>
- The list of error classes that are reported to the
+ The list of error classes that are reported to the
postmaster.
<b><a href="postconf.5.html#process_id">process_id</a> (read-only)</b>
- The process ID of a Postfix command or daemon
+ The process ID of a Postfix command or daemon
process.
<b><a href="postconf.5.html#process_name">process_name</a> (read-only)</b>
- The process name of a Postfix command or daemon
+ The process name of a Postfix command or daemon
process.
<b><a href="postconf.5.html#queue_directory">queue_directory</a> (see 'postconf -d' output)</b>
- The location of the Postfix top-level queue direc-
+ The location of the Postfix top-level queue direc-
tory.
<b><a href="postconf.5.html#syslog_facility">syslog_facility</a> (mail)</b>
The syslog facility of Postfix logging.
<b><a href="postconf.5.html#syslog_name">syslog_name</a> (postfix)</b>
- 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".
<b>FILES</b>
syslogd(8), system logging
<b>LICENSE</b>
- The Secure Mailer license must be distributed with this
+ The Secure Mailer license must be distributed with this
software.
<b>AUTHOR(S)</b>
proportionally. </p>
+</DD>
+
+<DT><b><a name="bounce_template_file">bounce_template_file</a>
+(default: empty)</b></DT><DD>
+
+<p> Pathname of a configuration file with bounce message templates.
+</p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
+
</DD>
<DT><b><a name="broken_sasl_auth_clients">broken_sasl_auth_clients</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
.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.
</ul>
<p> This feature is available in Postfix 2.3 and later. </p>
+
+%PARAM bounce_template_file empty
+
+<p> Pathname of a configuration file with bounce message templates.
+</p>
+
+<p> This feature is available in Postfix 2.3 and later. </p>
+
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
$(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
test: $(TESTPROG)
-tests: test
+tests: update template_test
-update: ../../libexec/$(PROG)
+update: ../../libexec/$(PROG) $(SAMPLES)
../../libexec/$(PROG): $(PROG)
cp $(PROG) ../../libexec
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 \
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
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
/* .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.
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.
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.
}
}
+/* 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)
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,
};
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);
* 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
/* } 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)
/* 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().
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;
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;
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;
&& 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);
}
*/
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);
}
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
*/
#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)));
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));
{
/*
- * 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));
}
*/
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 */
}
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);
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));
*/
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) {
/*
* 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");
* 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
#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
*/
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 */
/* */
-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 *);
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
--- /dev/null
+/*++
+/* 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 <sys_defs.h>
+#include <string.h>
+#include <ctype.h>
+
+#ifdef STRCASECMP_IN_STRINGS_H
+#include <strings.h>
+#endif
+
+/* Utility library. */
+
+#include <msg.h>
+#include <mac_expand.h>
+#include <split_at.h>
+#include <stringops.h>
+#include <mymalloc.h>
+#include <dict_ml.h>
+
+/* Global library. */
+
+#include <mail_params.h>
+#include <mail_conf.h>
+#include <mail_addr.h>
+#include <post_mail.h>
+#include <is_header.h>
+#include <mail_proto.h>
+
+/* Application-specific. */
+
+#include <bounce_service.h>
+
+ /*
+ * 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
+ * $<parameter_name>_days ($<parameter_name>_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 = <<EOF\n", tp->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);
+}
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
* 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
--- /dev/null
+/*++
+/* NAME
+/* dict 3
+/* SUMMARY
+/* dictionary manager, multi-line entry support
+/* SYNOPSIS
+/* #include <dict_ml.h>
+/*
+/* 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 = <<EOF
+/* text here...
+/* EOF
+/* .in -4
+/* .ad
+/* .fi
+/*
+/* Leading or trailing white space is not stripped from
+/* multi-line values.
+/* .IP \(bu
+/* The following input is ignored outside "<<" context: empty
+/* lines, all whitespace lines, and lines whose first
+/* non-whitespace character is "#".
+/* .PP
+/* dict_ml_load_file() enters (name, value) pairs from the
+/* specified file to the specified dictionary.
+/*
+/* dict_ml_load_fp() reads (name, value) pairs from an open
+/* stream. It has the same semantics as dict_ml_load_file().
+/* SEE ALSO
+/* dict(3)
+/* DIAGNOSTICS
+/* Fatal errors: out of memory, malformed macro name, missing
+/* or mal-formed end marker.
+/* 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 <sys_defs.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <ctype.h>
+#include <string.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <vstream.h>
+#include <vstring_vstream.h>
+#include <iostuff.h>
+#include <stringops.h>
+#include <dict.h>
+#include <dict_ml.h>
+
+#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);
+ }
+}
--- /dev/null
+#ifndef _DICT_ML_H_INCLUDED_
+#define _DICT_ML_H_INCLUDED_
+
+/*++
+/* NAME
+/* dict_ml 3h
+/* SUMMARY
+/* dictionary manager, multi-line entry support
+/* SYNOPSIS
+/* #include <dict_ml.h>
+/* DESCRIPTION
+/* .nf
+
+ /*
+ * Utility library.
+ */
+#include <vstream.h>
+#include <dict.h>
+
+ /*
+ * 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
--- /dev/null
+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 = <<EOF" and ends
+# with a line that contains the word "EOF" only. You can change the
+# word EOF if you like, but you can't do shell/perl/etc like things
+# such as enclosing it in quotes (template_name = <<'EOF').
+#
+# IMPORTANT:
+#
+# If you add non-ASCII text then you MUST change the CHARSET: value,
+# otherwise Postfix will not use your template. You must specify a
+# character set that is a superset of US-ASCII, because Postfix
+# appends ASCII text after the message template.
+#
+EOF
+;
+
+while (<>) {
+ 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 $_;
+}
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
#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
* 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
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 \
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 \
@$(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
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
--- /dev/null
+/*++
+/* NAME
+/* allascii 3
+/* SUMMARY
+/* predicate if string is all ASCII
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* 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 <sys_defs.h>
+#include <ctype.h>
+
+/* 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);
+}
--- /dev/null
+/*++
+/* NAME
+/* allspace 3
+/* SUMMARY
+/* predicate if string is all space
+/* SYNOPSIS
+/* #include <stringops.h>
+/*
+/* 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 <sys_defs.h>
+#include <ctype.h>
+
+/* 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);
+}
/*
/* 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
}
/*
- * 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) {
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