Knox @ vushta.com.
Performance: FreeBSD has bidirectional pipes that are faster
- than socketpairs. Anticpiating on more platform-specific
+ than socketpairs. Anticipating on more platform-specific
optimizations, all duplex pipe plumbing is now isolated in
a duplex_pipe.c module that provides a system-independent
interface.
excess mail so that one site with a large backlog does not
block other deliveries.
+20000129
+
+ Bugfix: extracted recipients were misfiled when a message
+ was moved back to the maildrop queue. But they still worked
+ due to a coincidence.
+
+ Feature: bounce_recip() bounces a recipient immediately
+ without accessing a bounce logfile. This is necessary for
+ VERP bounces, for bounces by delivery agents that change
+ the sender address, and for bounces that for some reason
+ must not use temporary logfiles. Files: global/bounce.c,
+ bounce/bounce_recip_service.c.
+
+20000130
+
+ Bugfix: the too long header fix of 20000115-22 lost mail
+ with too long headers that didn't need to extract recipients
+ from message headers.
+
+ Bugfix: the too long header fix of 20000115-22 lost mail
+ without (blank line + message body).
+
+ Code rewrite: reorganized the cleanup daemon source code
+ so that the cleanup service can be called one record at a
+ time (see cleanup/cleanup_api.c); also got rid of the global
+ state variables and fixed a couple bugs that were introduced
+ with 20000115-22.
/* int flush;
/* DESCRIPTION
/* This module implements the server side of the bounce_recip()
-/* (send bounce message) request. If flush is zero, the logfile
-/* is not removed, and a warning is sent instead of a bounce.
+/* (send bounce message) request. If flush is zero, a warning is
+/* sent instead of a bounce.
/*
/* When a message bounces, a full copy is sent to the originator,
/* and an optional copy of the diagnostics with message headers is
SHELL = /bin/sh
SRCS = cleanup.c cleanup_out.c cleanup_envelope.c cleanup_message.c \
- cleanup_extracted.c cleanup_state.c cleanup_api.c cleanup_rewrite.c \
+ cleanup_extracted.c cleanup_state.c cleanup_rewrite.c \
cleanup_map11.c cleanup_map1n.c cleanup_masquerade.c \
- cleanup_out_recipient.c
+ cleanup_out_recipient.c cleanup_init.c cleanup_api.c
OBJS = cleanup.o cleanup_out.o cleanup_envelope.o cleanup_message.o \
- cleanup_extracted.o cleanup_state.o cleanup_api.o cleanup_rewrite.o \
+ cleanup_extracted.o cleanup_state.o cleanup_rewrite.o \
cleanup_map11.o cleanup_map1n.o cleanup_masquerade.o \
- cleanup_out_recipient.o
+ cleanup_out_recipient.o cleanup_init.o cleanup_api.o
HDRS =
TESTSRC =
WARN = -W -Wformat -Wimplicit -Wmissing-prototypes \
cleanup.o: ../include/msg.h
cleanup.o: ../include/vstring.h
cleanup.o: ../include/vbuf.h
-cleanup.o: ../include/vstream.h
-cleanup.o: ../include/mymalloc.h
-cleanup.o: ../include/iostuff.h
cleanup.o: ../include/dict.h
+cleanup.o: ../include/vstream.h
cleanup.o: ../include/argv.h
cleanup.o: ../include/mail_conf.h
cleanup.o: ../include/cleanup_user.h
-cleanup.o: ../include/mail_queue.h
cleanup.o: ../include/mail_proto.h
-cleanup.o: ../include/opened.h
-cleanup.o: ../include/bounce.h
+cleanup.o: ../include/iostuff.h
cleanup.o: ../include/mail_params.h
-cleanup.o: ../include/mail_stream.h
-cleanup.o: ../include/mail_addr.h
-cleanup.o: ../include/ext_prop.h
cleanup.o: ../include/record.h
cleanup.o: ../include/rec_type.h
cleanup.o: ../include/mail_server.h
cleanup.o: ../include/tok822.h
cleanup.o: ../include/resolve_clnt.h
cleanup.o: ../include/been_here.h
+cleanup.o: ../include/mail_stream.h
cleanup_api.o: cleanup_api.c
cleanup_api.o: ../include/sys_defs.h
cleanup_api.o: ../include/msg.h
cleanup_api.o: ../include/vstream.h
cleanup_api.o: ../include/mail_proto.h
cleanup_api.o: ../include/iostuff.h
-cleanup_api.o: ../include/opened.h
cleanup_api.o: ../include/bounce.h
cleanup_api.o: ../include/mail_params.h
cleanup_api.o: ../include/mail_stream.h
-cleanup_api.o: ../include/mail_addr.h
cleanup_api.o: cleanup.h
cleanup_api.o: ../include/argv.h
cleanup_api.o: ../include/maps.h
cleanup_api.o: ../include/tok822.h
cleanup_api.o: ../include/resolve_clnt.h
cleanup_api.o: ../include/been_here.h
+cleanup_api.o: ../include/mail_conf.h
cleanup_envelope.o: cleanup_envelope.c
cleanup_envelope.o: ../include/sys_defs.h
cleanup_envelope.o: ../include/msg.h
cleanup_envelope.o: ../include/dict.h
cleanup_envelope.o: ../include/been_here.h
cleanup_envelope.o: ../include/mail_stream.h
+cleanup_envelope.o: ../include/mail_conf.h
cleanup_extracted.o: cleanup_extracted.c
cleanup_extracted.o: ../include/sys_defs.h
cleanup_extracted.o: ../include/msg.h
cleanup_extracted.o: ../include/resolve_clnt.h
cleanup_extracted.o: ../include/been_here.h
cleanup_extracted.o: ../include/mail_stream.h
+cleanup_extracted.o: ../include/mail_conf.h
+cleanup_init.o: cleanup_init.c
+cleanup_init.o: ../include/sys_defs.h
+cleanup_init.o: ../include/msg.h
+cleanup_init.o: ../include/iostuff.h
+cleanup_init.o: ../include/mail_addr.h
+cleanup_init.o: ../include/mail_params.h
+cleanup_init.o: ../include/ext_prop.h
+cleanup_init.o: cleanup.h
+cleanup_init.o: ../include/vstring.h
+cleanup_init.o: ../include/vbuf.h
+cleanup_init.o: ../include/vstream.h
+cleanup_init.o: ../include/argv.h
+cleanup_init.o: ../include/maps.h
+cleanup_init.o: ../include/dict.h
+cleanup_init.o: ../include/tok822.h
+cleanup_init.o: ../include/resolve_clnt.h
+cleanup_init.o: ../include/been_here.h
+cleanup_init.o: ../include/mail_stream.h
+cleanup_init.o: ../include/mail_conf.h
cleanup_map11.o: cleanup_map11.c
cleanup_map11.o: ../include/sys_defs.h
cleanup_map11.o: ../include/msg.h
cleanup_map11.o: ../include/resolve_clnt.h
cleanup_map11.o: ../include/been_here.h
cleanup_map11.o: ../include/mail_stream.h
+cleanup_map11.o: ../include/mail_conf.h
cleanup_map1n.o: cleanup_map1n.c
cleanup_map1n.o: ../include/sys_defs.h
cleanup_map1n.o: ../include/mymalloc.h
cleanup_map1n.o: ../include/resolve_clnt.h
cleanup_map1n.o: ../include/been_here.h
cleanup_map1n.o: ../include/mail_stream.h
+cleanup_map1n.o: ../include/mail_conf.h
cleanup_masquerade.o: cleanup_masquerade.c
cleanup_masquerade.o: ../include/sys_defs.h
cleanup_masquerade.o: ../include/msg.h
cleanup_masquerade.o: ../include/dict.h
cleanup_masquerade.o: ../include/been_here.h
cleanup_masquerade.o: ../include/mail_stream.h
+cleanup_masquerade.o: ../include/mail_conf.h
cleanup_message.o: cleanup_message.c
cleanup_message.o: ../include/sys_defs.h
cleanup_message.o: ../include/msg.h
cleanup_message.o: ../include/dict.h
cleanup_message.o: ../include/been_here.h
cleanup_message.o: ../include/mail_stream.h
+cleanup_message.o: ../include/mail_conf.h
cleanup_out.o: cleanup_out.c
cleanup_out.o: ../include/sys_defs.h
cleanup_out.o: ../include/msg.h
cleanup_out.o: ../include/resolve_clnt.h
cleanup_out.o: ../include/been_here.h
cleanup_out.o: ../include/mail_stream.h
+cleanup_out.o: ../include/mail_conf.h
cleanup_out_recipient.o: cleanup_out_recipient.c
cleanup_out_recipient.o: ../include/sys_defs.h
cleanup_out_recipient.o: ../include/argv.h
cleanup_out_recipient.o: ../include/tok822.h
cleanup_out_recipient.o: ../include/resolve_clnt.h
cleanup_out_recipient.o: ../include/mail_stream.h
+cleanup_out_recipient.o: ../include/mail_conf.h
cleanup_rewrite.o: cleanup_rewrite.c
cleanup_rewrite.o: ../include/sys_defs.h
cleanup_rewrite.o: ../include/msg.h
cleanup_rewrite.o: ../include/dict.h
cleanup_rewrite.o: ../include/been_here.h
cleanup_rewrite.o: ../include/mail_stream.h
+cleanup_rewrite.o: ../include/mail_conf.h
cleanup_state.o: cleanup_state.c
cleanup_state.o: ../include/sys_defs.h
cleanup_state.o: ../include/mymalloc.h
cleanup_state.o: ../include/tok822.h
cleanup_state.o: ../include/resolve_clnt.h
cleanup_state.o: ../include/mail_stream.h
+cleanup_state.o: ../include/mail_conf.h
/* System library. */
#include <sys_defs.h>
-#include <sys/stat.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
-#include <errno.h>
/* Utility library. */
#include <msg.h>
#include <vstring.h>
-#include <vstream.h>
-#include <mymalloc.h>
-#include <iostuff.h>
#include <dict.h>
/* Global library. */
#include <mail_conf.h>
#include <cleanup_user.h>
-#include <mail_queue.h>
#include <mail_proto.h>
-#include <opened.h>
-#include <bounce.h>
#include <mail_params.h>
-#include <mail_stream.h>
-#include <mail_addr.h>
-#include <ext_prop.h>
#include <record.h>
#include <rec_type.h>
#include "cleanup.h"
- /*
- * Tunable parameters.
- */
-int var_hopcount_limit; /* max mailer hop count */
-int var_header_limit; /* max header length */
-char *var_canonical_maps; /* common canonical maps */
-char *var_send_canon_maps; /* sender canonical maps */
-char *var_rcpt_canon_maps; /* recipient canonical maps */
-char *var_virtual_maps; /* virtual maps */
-char *var_masq_domains; /* masquerade domains */
-char *var_masq_exceptions; /* users not masqueraded */
-char *var_header_checks; /* any header checks */
-int var_dup_filter_limit; /* recipient dup filter */
-char *var_empty_addr; /* destination of bounced bounces */
-int var_delay_warn_time; /* delay that triggers warning */
-char *var_prop_extension; /* propagate unmatched extension */
-char *var_always_bcc;
-
- /*
- * Mappings.
- */
-MAPS *cleanup_comm_canon_maps;
-MAPS *cleanup_send_canon_maps;
-MAPS *cleanup_rcpt_canon_maps;
-MAPS *cleanup_header_checks;
-MAPS *cleanup_virtual_maps;
-ARGV *cleanup_masq_domains;
-
- /*
- * Address extension propagation restrictions.
- */
-int cleanup_ext_prop_mask;
-
/* cleanup_service - process one request to inject a message into the queue */
static void cleanup_service(VSTREAM *src, char *unused_service, char **argv)
VSTRING *buf = vstring_alloc(100);
CLEANUP_STATE *state;
int flags;
- int type;
+ int type = 0;
/*
* Sanity check. This service takes no command-line arguments.
}
/*
- * Keep reading in case of problems, so that the sender is ready to
- * receive our status report.
+ * Keep reading in case of problems, until the sender is ready to receive
+ * our status report.
*/
- if (CLEANUP_OUT_OK(state) == 0) {
+ if (CLEANUP_OUT_OK(state) == 0 && type > 0) {
if ((state->errs & CLEANUP_STAT_CONT) == 0)
msg_warn("%s: skipping further client input", state->queue_id);
while ((type = rec_get(src, buf, 0)) > 0
/*
* Finish this message, and report the result status to the client.
*/
- mail_print(src, "%d", cleanup_close(state));/* we're committed now */
+ mail_print(src, "%d", cleanup_close(state));
/*
* Cleanup.
exit(sig);
}
-/* pre_jail_init - initialize before entering the chroot jail */
-
-static void pre_jail_init(char *unused_name, char **unused_argv)
-{
- if (*var_canonical_maps)
- cleanup_comm_canon_maps =
- maps_create(VAR_CANONICAL_MAPS, var_canonical_maps, DICT_FLAG_LOCK);
- if (*var_send_canon_maps)
- cleanup_send_canon_maps =
- maps_create(VAR_SEND_CANON_MAPS, var_send_canon_maps,
- DICT_FLAG_LOCK);
- if (*var_rcpt_canon_maps)
- cleanup_rcpt_canon_maps =
- maps_create(VAR_RCPT_CANON_MAPS, var_rcpt_canon_maps,
- DICT_FLAG_LOCK);
- if (*var_virtual_maps)
- cleanup_virtual_maps = maps_create(VAR_VIRTUAL_MAPS, var_virtual_maps,
- DICT_FLAG_LOCK);
- if (*var_masq_domains)
- cleanup_masq_domains = argv_split(var_masq_domains, " ,\t\r\n");
- if (*var_header_checks)
- cleanup_header_checks =
- maps_create(VAR_HEADER_CHECKS, var_header_checks, DICT_FLAG_LOCK);
-}
-
/* pre_accept - see if tables have changed */
static void pre_accept(char *unused_name, char **unused_argv)
}
}
-/* post_jail_init - initialize after entering the chroot jail */
-
-static void post_jail_init(char *unused_name, char **unused_argv)
-{
-
- /*
- * Optionally set the file size resource limit. XXX This limits the
- * message content to somewhat less than requested, because the total
- * queue file size also includes envelope information. Unless people set
- * really low limit, the difference is going to matter only when a queue
- * file has lots of recipients.
- */
- if (var_message_limit > 0)
- set_file_limit((off_t) var_message_limit);
-
- /*
- * Control how unmatched extensions are propagated.
- */
- cleanup_ext_prop_mask = ext_prop_mask(var_prop_extension);
-}
-
/* main - the main program */
int main(int argc, char **argv)
{
- static CONFIG_INT_TABLE int_table[] = {
- VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0,
- VAR_HEADER_LIMIT, DEF_HEADER_LIMIT, &var_header_limit, 1, 0,
- VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0,
- VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0,
- 0,
- };
- static CONFIG_STR_TABLE str_table[] = {
- VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0,
- VAR_SEND_CANON_MAPS, DEF_SEND_CANON_MAPS, &var_send_canon_maps, 0, 0,
- VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0,
- VAR_VIRTUAL_MAPS, DEF_VIRTUAL_MAPS, &var_virtual_maps, 0, 0,
- VAR_MASQ_DOMAINS, DEF_MASQ_DOMAINS, &var_masq_domains, 0, 0,
- VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0,
- VAR_MASQ_EXCEPTIONS, DEF_MASQ_EXCEPTIONS, &var_masq_exceptions, 0, 0,
- VAR_HEADER_CHECKS, DEF_HEADER_CHECKS, &var_header_checks, 0, 0,
- VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0,
- VAR_ALWAYS_BCC, DEF_ALWAYS_BCC, &var_always_bcc, 0, 0,
- 0,
- };
/*
* Clean up an incomplete queue file in case of a fatal run-time error,
* Pass control to the single-threaded service skeleton.
*/
single_server_main(argc, argv, cleanup_service,
- MAIL_SERVER_INT_TABLE, int_table,
- MAIL_SERVER_STR_TABLE, str_table,
- MAIL_SERVER_PRE_INIT, pre_jail_init,
- MAIL_SERVER_POST_INIT, post_jail_init,
+ MAIL_SERVER_INT_TABLE, cleanup_int_table,
+ MAIL_SERVER_STR_TABLE, cleanup_str_table,
+ MAIL_SERVER_PRE_INIT, cleanup_pre_jail,
+ MAIL_SERVER_POST_INIT, cleanup_post_jail,
MAIL_SERVER_PRE_ACCEPT, pre_accept,
0);
}
#include <tok822.h>
#include <been_here.h>
#include <mail_stream.h>
+#include <mail_conf.h>
/*
* These state variables are accessed by many functions, and there is only
typedef struct CLEANUP_STATE {
VSTRING *temp1; /* scratch buffer, local use only */
VSTRING *temp2; /* scratch buffer, local use only */
- VSTREAM *src; /* current input stream */
VSTREAM *dst; /* current output stream */
MAIL_STREAM *handle; /* mail stream handle */
char *queue_id; /* queue file basename */
void (*action) (struct CLEANUP_STATE *, int, char *, int);
long mesg_offset; /* start of message segment */
long data_offset; /* start of message content */
+ int end_seen; /* REC_TYPE_END seen */
} CLEANUP_STATE;
/*
extern void cleanup_control(CLEANUP_STATE *, int);
extern int cleanup_close(CLEANUP_STATE *);
extern void cleanup_all(void);
+extern void cleanup_pre_jail(char *, char **);
+extern void cleanup_post_jail(char *, char **);
+extern CONFIG_INT_TABLE cleanup_int_table[];
+extern CONFIG_STR_TABLE cleanup_str_table[];
#define CLEANUP_RECORD(s, t, b, l) ((s)->action((s), (t), (b), (l)))
/*
* cleanup_envelope.c
*/
-extern void cleanup_envelope_init(CLEANUP_STATE *, int, char *, int);
-extern void cleanup_envelope_process(CLEANUP_STATE *, int, char *, int);
+extern void cleanup_envelope(CLEANUP_STATE *, int, char *, int);
/*
* cleanup_message.c
*/
-extern void cleanup_message_init(CLEANUP_STATE *, int, char *, int);
-extern void cleanup_message_header(CLEANUP_STATE *, int, char *, int);
-extern void cleanup_message_body(CLEANUP_STATE *, int, char *, int);
+extern void cleanup_message(CLEANUP_STATE *, int, char *, int);
/*
* cleanup_extracted.c
*/
-extern void cleanup_extracted_init(CLEANUP_STATE *, int, char *, int);
-extern void cleanup_extracted_process(CLEANUP_STATE *, int, char *, int);
+extern void cleanup_extracted(CLEANUP_STATE *, int, char *, int);
/*
* cleanup_rewrite.c
/* NAME
/* cleanup_api 3
/* SUMMARY
-/* callable interface
+/* cleanup callable interface, message processing
/* SYNOPSIS
/* #include "cleanup.h"
/*
-/* char *cleanup_path;
-/*
-/* void cleanup_all()
-/*
/* CLEANUP_STATE *cleanup_open()
/*
/* void cleanup_control(state, flags)
/* int cleanup_close(state)
/* CLEANUP_STATE *state;
/* DESCRIPTION
-/* This module implements a callable interface to the cleanup service.
+/* This module implements a callable interface to the cleanup service
+/* for processing one message and for writing it to queue file.
/* For a description of the cleanup service, see cleanup(8).
/*
-/* cleanup_path is a null pointer or it is the name of the queue
-/* file that currently is being written. This information is used
-/* by cleanup_all() to clean up in case of fatal errors.
-/*
/* cleanup_open() creates a new queue file and performs other
-/* initialization. The result is a handle that should be given
-/* to the cleanup_control(), cleanup_record() and cleanup_close()
+/* per-message initialization. The result is a handle that should be
+/* given to the cleanup_control(), cleanup_record() and cleanup_close()
/* routines. The name of the queue file is in the queue_id result
/* structure member.
/*
-/* cleanup_control() processes flags specified by the caller.
-/* These flags control what happens in case of data errors.
+/* cleanup_control() processes per-message flags specified by the caller.
+/* These flags control the handling of data errors, and must be set
+/* before processing the first message record.
/*
-/* CLEANUP_RECORD() processes one queue file record and maintains
-/* a little state machine. CLEANUP_RECORD() is a macro that calls
-/* the appropriate routine depending on what section of a queue file
-/* is being processed. In order to find out if a file is corrupted,
-/* the caller can test the CLEANUP_OUT_OK(state) macro. The result is
-/* false when further message processing is futile.
+/* CLEANUP_RECORD() is a macro that processes one message record,
+/* that copies the result to the queue file, and that maintains a
+/* little state machine. The last record in a valid message has type
+/* REC_TYPE_END. In order to find out if a message is corrupted,
+/* the caller is encouraged to test the CLEANUP_OUT_OK(state) macro.
+/* The result is false when further message processing is futile.
+/* In that case, it is safe to call cleanup_close() immediately.
/*
/* cleanup_close() finishes a queue file. In case of any errors,
-/* the file is removed. The result status is non-zero in case of
-/* problems. use cleanup_strerror() to translate the result into
+/* the file is removed. The result value is non-zero in case of
+/* problems. Use cleanup_strerror() to translate the result into
/* human_readable text.
-/*
-/* cleanup_all() should be called in case of fatal error, in order
-/* to remove an incomplete queue file. Typically one registers a
-/* msg_cleanup() handler and a signal() handler that call
-/* cleanup_all() before terminating the process.
-/* STANDARDS
-/* RFC 822 (ARPA Internet Text Messages)
/* DIAGNOSTICS
/* Problems and transactions are logged to \fBsyslogd\fR(8).
/* SEE ALSO
/* cleanup(8) cleanup service description.
-/* FILES
-/* /etc/postfix/canonical*, canonical mapping table
-/* /etc/postfix/virtual*, virtual mapping table
+/* cleanup_init(8) cleanup callable interface, initialization
/* LICENSE
/* .ad
/* .fi
#include <cleanup_user.h>
#include <mail_queue.h>
#include <mail_proto.h>
-#include <opened.h>
#include <bounce.h>
#include <mail_params.h>
#include <mail_stream.h>
-#include <mail_addr.h>
/* Application-specific. */
#include "cleanup.h"
- /*
- * Global state: any queue files that we have open, so that the error
- * handler can clean up in case of trouble.
- */
-char *cleanup_path; /* queue file name */
-
/* cleanup_open - open queue file and initialize */
-CLEANUP_STATE * cleanup_open(void)
+CLEANUP_STATE *cleanup_open(void)
{
CLEANUP_STATE *state;
static char *log_queues[] = {
char **cpp;
/*
- * Initialize.
+ * Initialize private state.
*/
state = cleanup_state_alloc();
/*
- * Open the queue file. Send the queue ID to the client so they can use
- * it for logging purposes. For example, the SMTP server sends the queue
- * id to the SMTP client after completion of the DATA command; and when
- * the local delivery agent forwards a message, it logs the new queue id
- * together with the old one. All this is done to make it easier for mail
- * admins to follow a message while it hops from machine to machine.
- *
- * Save the queue file name, so that the runtime error handler can clean up
- * in case of problems.
+ * Open the queue file. Save the queue file name in a global variable, so
+ * that the runtime error handler can clean up in case of problems.
*/
state->handle = mail_stream_file(MAIL_QUEUE_INCOMING,
MAIL_CLASS_PUBLIC, MAIL_SERVICE_QUEUE);
* unrecognizable data (which should never happen) or insufficient space
* for the queue file (which will happen occasionally). Otherwise,
* discard input after any lethal error. See the CLEANUP_OUT_OK()
- * definition.
+ * macro definition.
*/
if ((state->flags = flags) & CLEANUP_FLAG_BOUNCE) {
state->err_mask =
int status;
/*
- * Now that we have captured the entire message, see if there are any
- * other errors. For example, if the message needs to be bounced for lack
- * of recipients. We want to turn on the execute bits on a file only when
- * we want the queue manager to process it.
+ * See if there are any errors. For example, the message is incomplete,
+ * or it needs to be bounced for lack of recipients. We want to turn on
+ * the execute bits on a file only when we really want the queue manager
+ * to process it.
*/
if (state->recip == 0)
state->errs |= CLEANUP_STAT_RCPT;
+ if (state->end_seen == 0)
+ state->errs |= CLEANUP_STAT_BAD;
/*
* If there are no errors, be very picky about queue file write errors
* because we are about to tell the sender that it can throw away its
* copy of the message.
*/
- if (state->errs == 0)
+ if ((state->errs & CLEANUP_STAT_LETHAL) == 0)
state->errs |= mail_stream_finish(state->handle);
else
mail_stream_cleanup(state->handle);
* message headers because we could not process all the message headers).
* However, cleanup_strerror() prioritizes errors so that it can report
* the cause (e.g., header buffer overflow), which is more useful.
- * Amazing.
*/
#define CAN_BOUNCE() \
((state->errs & (CLEANUP_STAT_BAD | CLEANUP_STAT_WRITE)) == 0 \
MAIL_QUEUE_INCOMING, state->queue_id,
state->sender, state->recip ?
state->recip : "", "cleanup", state->time,
- "Message rejected: %s",
+ "Message processing aborted: %s",
cleanup_strerror(state->errs)) == 0) {
state->errs = 0;
} else {
cleanup_state_free(state);
return (status);
}
-
-/* cleanup_all - callback for the runtime error handler */
-
-void cleanup_all(void)
-{
- if (cleanup_path && REMOVE(cleanup_path))
- msg_warn("cleanup_all: remove %s: %m", cleanup_path);
-}
/* SYNOPSIS
/* #include <cleanup.h>
/*
-/* void cleanup_envelope_init(state, type, buf, len)
+/* void cleanup_envelope(state, type, buf, len)
/* CLEANUP_STATE *state;
/* int type;
/* char *buf;
/* int len;
-/*
-/* void cleanup_envelope_process(state, type, buf, len)
-/* CLEANUP_STATE *state;
-/* int type;
-/* char *buf;
/* int len;
/* DESCRIPTION
-/* This module processes the envelope segment of a mail message.
-/* While copying records from input to output it validates the
-/* message structure, rewrites sender/recipient addresses
-/* to canonical form, expands recipients according to
-/* entries in the virtual table, and updates the state structure.
+/* This module processes envelope records and writes the result
+/* to the queue file. It validates the message structure, rewrites
+/* sender/recipient addresses to canonical form, and expands recipients
+/* according to entries in the virtual table.
+/*
+/* Arguments:
+/* .IP state
+/* Queue file and message processing state. This state is updated
+/* as records are processed and as errors happen.
+/* .IP type
+/* Record type.
+/* .IP buf
+/* Record content.
+/* .IP len
+/* Record content length.
/* LICENSE
/* .ad
/* .fi
#define STR vstring_str
-/* cleanup_envelope_init - initialization */
+static void cleanup_envelope_process(CLEANUP_STATE *, int, char *, int);
-void cleanup_envelope_init(CLEANUP_STATE *state, int type, char *str, int len)
+/* cleanup_envelope - process message envelope */
+
+void cleanup_envelope(CLEANUP_STATE *state, int type, char *str, int len)
{
/*
* produce queue file reports.
*/
cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT, 0L);
+
+ /*
+ * Pass control to the actual envelope processing routine.
+ */
state->action = cleanup_envelope_process;
cleanup_envelope_process(state, type, str, len);
}
/* cleanup_envelope_process - process one envelope record */
-void cleanup_envelope_process(CLEANUP_STATE *state, int type, char *buf, int len)
+static void cleanup_envelope_process(CLEANUP_STATE *state, int type, char *buf, int len)
{
if (type == REC_TYPE_MESG) {
if (state->sender == 0 || state->time == 0) {
if (state->warn_time)
cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT,
state->warn_time);
- state->action = cleanup_message_init;
+ state->action = cleanup_message;
}
return;
}
state->errs |= CLEANUP_STAT_BAD;
return;
}
- cleanup_rewrite_internal(clean_addr, *buf ?
- buf : var_empty_addr);
+ cleanup_rewrite_internal(clean_addr, *buf ? buf : var_empty_addr);
if (cleanup_rcpt_canon_maps)
cleanup_map11_internal(state, clean_addr, cleanup_rcpt_canon_maps,
cleanup_ext_prop_mask & EXT_PROP_CANONICAL);
/* SYNOPSIS
/* #include "cleanup.h"
/*
-/* void cleanup_extracted(void)
+/* void cleanup_extracted(state, type, buf, len)
+/* CLEANUP_STATE *state;
+/* int type;
+/* char *buf;
+/* int len;
/* DESCRIPTION
-/* This module processes message segments for information
-/* extracted from message content. It requires that the input
-/* contains no extracted information, and writes extracted
+/* This module processes message records with information extracted
+/* from message content, or with recipients that are stored after the
+/* message content. It updates recipient records, and writes extracted
/* information records to the output.
+/*
+/* Arguments:
+/* .IP state
+/* Queue file and message processing state. This state is updated
+/* as records are processed and as errors happen.
+/* .IP type
+/* Record type.
+/* .IP buf
+/* Record content.
+/* .IP len
+/* Record content length.
/* LICENSE
/* .ad
/* .fi
#define STR(x) vstring_str(x)
-/* cleanup_extracted_init - initialize extracted segment */
+static void cleanup_extracted_process(CLEANUP_STATE *, int, char *, int);
+
+/* cleanup_extracted - initialize extracted segment */
-void cleanup_extracted_init(CLEANUP_STATE *state, int type, char *buf, int len)
+void cleanup_extracted(CLEANUP_STATE *state, int type, char *buf, int len)
{
/*
* Start the extracted segment.
*/
cleanup_out_string(state, REC_TYPE_XTRA, "");
+
+ /*
+ * Always emit Return-Receipt-To and Errors-To records, and always emit
+ * them ahead of extracted recipients, so that the queue manager does not
+ * waste lots of time searching through large numbers of recipient
+ * addresses.
+ */
+ cleanup_out_string(state, REC_TYPE_RRTO, state->return_receipt ?
+ state->return_receipt : "");
+
+ cleanup_out_string(state, REC_TYPE_ERTO, state->errors_to ?
+ state->errors_to : state->sender);
+
+ /*
+ * Pass control to the routine that processes the extracted segment.
+ */
state->action = cleanup_extracted_process;
cleanup_extracted_process(state, type, buf, len);
}
/* cleanup_extracted_process - process extracted segment */
-void cleanup_extracted_process(CLEANUP_STATE *state, int type, char *buf, int unused_len)
+static void cleanup_extracted_process(CLEANUP_STATE *state, int type, char *buf, int unused_len)
{
VSTRING *clean_addr;
ARGV *rcpt;
return;
}
- /*
- * Always emit Return-Receipt-To and Errors-To records, and always try to
- * emit them ahead of extracted recipients, so that the queue manager
- * does not waste lots of time searching through large numbers of
- * recipient addresses.
- */
- cleanup_out_string(state, REC_TYPE_RRTO, state->return_receipt ?
- state->return_receipt : "");
-
- cleanup_out_string(state, REC_TYPE_ERTO, state->errors_to ?
- state->errors_to : state->sender);
-
/*
* Optionally account for missing recipient envelope records.
*
* Terminate the extracted segment.
*/
cleanup_out_string(state, REC_TYPE_END, "");
+ state->end_seen = 1;
}
--- /dev/null
+/*++
+/* NAME
+/* cleanup_init 3
+/* SUMMARY
+/* cleanup callable interface, initializations
+/* SYNOPSIS
+/* #include "cleanup.h"
+/*
+/* CONFIG_INT_TABLE cleanup_int_table[];
+/*
+/* CONFIG_STR_TABLE cleanup_str_table[];
+/*
+/* void cleanup_pre_jail(service_name, argv)
+/* char *service_name;
+/* char **argv;
+/*
+/* void cleanup_post_jail(service_name, argv)
+/* char *service_name;
+/* char **argv;
+/*
+/* char *cleanup_path;
+/*
+/* void cleanup_all()
+/* DESCRIPTION
+/* This module implements a callable interface to the cleanup service
+/* for one-time initializations that must be done before any message
+/* processing can take place.
+/*
+/* cleanup_int_table[] and cleanup_str_table[] specify configuration
+/* parameters that must be initialized before calling any functions
+/* in this module. These tables satisfy the interface as specified in
+/* single_service(3).
+/*
+/* cleanup_pre_jail() and cleanup_post_jail() perform mandatory
+/* initializations before and after the process enters the optional
+/* chroot jail. These functions satisfy the interface as specified
+/* in single_service(3).
+/*
+/* cleanup_path is either a null pointer or it is the name of a queue
+/* file that currently is being written. This information is used
+/* by cleanup_all() to remove incomplete files after a fatal error.
+/*
+/* cleanup_all() must be called in case of fatal error, in order
+/* to remove an incomplete queue file. Normally, as part of process
+/* initialization, one registers a msg_cleanup() handler and a signal()
+/* handler that both call cleanup_all() before terminating the process.
+/* DIAGNOSTICS
+/* Problems and transactions are logged to \fBsyslogd\fR(8).
+/* SEE ALSO
+/* cleanup_api(3) cleanup callable interface, message processing
+/* 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>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <iostuff.h>
+
+/* Global library. */
+
+#include <mail_addr.h>
+#include <mail_params.h>
+#include <ext_prop.h>
+
+/* Application-specific. */
+
+#include "cleanup.h"
+
+ /*
+ * Global state: any queue files that we have open, so that the error
+ * handler can clean up in case of trouble.
+ */
+char *cleanup_path; /* queue file name */
+
+ /*
+ * Tunable parameters.
+ */
+int var_hopcount_limit; /* max mailer hop count */
+int var_header_limit; /* max header length */
+char *var_canonical_maps; /* common canonical maps */
+char *var_send_canon_maps; /* sender canonical maps */
+char *var_rcpt_canon_maps; /* recipient canonical maps */
+char *var_virtual_maps; /* virtual maps */
+char *var_masq_domains; /* masquerade domains */
+char *var_masq_exceptions; /* users not masqueraded */
+char *var_header_checks; /* any header checks */
+int var_dup_filter_limit; /* recipient dup filter */
+char *var_empty_addr; /* destination of bounced bounces */
+int var_delay_warn_time; /* delay that triggers warning */
+char *var_prop_extension; /* propagate unmatched extension */
+char *var_always_bcc; /* big brother */
+
+CONFIG_INT_TABLE cleanup_int_table[] = {
+ VAR_HOPCOUNT_LIMIT, DEF_HOPCOUNT_LIMIT, &var_hopcount_limit, 1, 0,
+ VAR_HEADER_LIMIT, DEF_HEADER_LIMIT, &var_header_limit, 1, 0,
+ VAR_DUP_FILTER_LIMIT, DEF_DUP_FILTER_LIMIT, &var_dup_filter_limit, 0, 0,
+ VAR_DELAY_WARN_TIME, DEF_DELAY_WARN_TIME, &var_delay_warn_time, 0, 0,
+ 0,
+};
+CONFIG_STR_TABLE cleanup_str_table[] = {
+ VAR_CANONICAL_MAPS, DEF_CANONICAL_MAPS, &var_canonical_maps, 0, 0,
+ VAR_SEND_CANON_MAPS, DEF_SEND_CANON_MAPS, &var_send_canon_maps, 0, 0,
+ VAR_RCPT_CANON_MAPS, DEF_RCPT_CANON_MAPS, &var_rcpt_canon_maps, 0, 0,
+ VAR_VIRTUAL_MAPS, DEF_VIRTUAL_MAPS, &var_virtual_maps, 0, 0,
+ VAR_MASQ_DOMAINS, DEF_MASQ_DOMAINS, &var_masq_domains, 0, 0,
+ VAR_EMPTY_ADDR, DEF_EMPTY_ADDR, &var_empty_addr, 1, 0,
+ VAR_MASQ_EXCEPTIONS, DEF_MASQ_EXCEPTIONS, &var_masq_exceptions, 0, 0,
+ VAR_HEADER_CHECKS, DEF_HEADER_CHECKS, &var_header_checks, 0, 0,
+ VAR_PROP_EXTENSION, DEF_PROP_EXTENSION, &var_prop_extension, 0, 0,
+ VAR_ALWAYS_BCC, DEF_ALWAYS_BCC, &var_always_bcc, 0, 0,
+ 0,
+};
+
+ /*
+ * Mappings.
+ */
+MAPS *cleanup_comm_canon_maps;
+MAPS *cleanup_send_canon_maps;
+MAPS *cleanup_rcpt_canon_maps;
+MAPS *cleanup_header_checks;
+MAPS *cleanup_virtual_maps;
+ARGV *cleanup_masq_domains;
+
+ /*
+ * Address extension propagation restrictions.
+ */
+int cleanup_ext_prop_mask;
+
+/* cleanup_all - callback for the runtime error handler */
+
+void cleanup_all(void)
+{
+ if (cleanup_path && REMOVE(cleanup_path))
+ msg_warn("cleanup_all: remove %s: %m", cleanup_path);
+}
+
+/* cleanup_pre_jail - initialize before entering the chroot jail */
+
+void cleanup_pre_jail(char *unused_name, char **unused_argv)
+{
+ if (*var_canonical_maps)
+ cleanup_comm_canon_maps =
+ maps_create(VAR_CANONICAL_MAPS, var_canonical_maps, DICT_FLAG_LOCK);
+ if (*var_send_canon_maps)
+ cleanup_send_canon_maps =
+ maps_create(VAR_SEND_CANON_MAPS, var_send_canon_maps,
+ DICT_FLAG_LOCK);
+ if (*var_rcpt_canon_maps)
+ cleanup_rcpt_canon_maps =
+ maps_create(VAR_RCPT_CANON_MAPS, var_rcpt_canon_maps,
+ DICT_FLAG_LOCK);
+ if (*var_virtual_maps)
+ cleanup_virtual_maps = maps_create(VAR_VIRTUAL_MAPS, var_virtual_maps,
+ DICT_FLAG_LOCK);
+ if (*var_masq_domains)
+ cleanup_masq_domains = argv_split(var_masq_domains, " ,\t\r\n");
+ if (*var_header_checks)
+ cleanup_header_checks =
+ maps_create(VAR_HEADER_CHECKS, var_header_checks, DICT_FLAG_LOCK);
+}
+
+/* cleanup_post_jail - initialize after entering the chroot jail */
+
+void cleanup_post_jail(char *unused_name, char **unused_argv)
+{
+
+ /*
+ * Optionally set the file size resource limit. XXX This limits the
+ * message content to somewhat less than requested, because the total
+ * queue file size also includes envelope information. Unless people set
+ * really low limit, the difference is going to matter only when a queue
+ * file has lots of recipients.
+ */
+ if (var_message_limit > 0)
+ set_file_limit((off_t) var_message_limit);
+
+ /*
+ * Control how unmatched extensions are propagated.
+ */
+ cleanup_ext_prop_mask = ext_prop_mask(var_prop_extension);
+}
/* SYNOPSIS
/* #include "cleanup.h"
/*
-/* void cleanup_message_init(state, type, buf, len)
-/* CLEANUP_STATE *state;
-/* int type;
-/* char *buf;
-/*
-/* void cleanup_message_process(state, type, buf, len)
+/* void cleanup_message(state, type, buf, len)
/* CLEANUP_STATE *state;
/* int type;
/* char *buf;
/* int len;
/* DESCRIPTION
-/* This module processes message content segments.
-/* While copying records from input to output, it validates
-/* the input, rewrites sender/recipient addresses to canonical
-/* form, inserts missing message headers, and extracts information
-/* from message headers to be used later when generating the extracted
-/* output segment.
+/* This module processes message content records and copies the
+/* result to the queue file. It validates the input, rewrites
+/* sender/recipient addresses to canonical form, inserts missing
+/* message headers, and extracts information from message headers
+/* to be used later when generating the extracted output segment.
+/*
+/* Arguments:
+/* .IP state
+/* Queue file and message processing state. This state is updated
+/* as records are processed and as errors happen.
+/* .IP type
+/* Record type.
+/* .IP buf
+/* Record content.
+/* .IP len
+/* Record content length.
/* LICENSE
/* .ad
/* .fi
#include "cleanup.h"
+static void cleanup_message_header(CLEANUP_STATE *, int, char *, int);
+static void cleanup_message_body(CLEANUP_STATE *, int, char *, int);
+
/* cleanup_out_header - output one header as a bunch of records */
static void cleanup_out_header(CLEANUP_STATE *state)
/* cleanup_message - initialize message content segment */
-void cleanup_message_init(CLEANUP_STATE *state, int type, char *buf, int len)
+void cleanup_message(CLEANUP_STATE *state, int type, char *buf, int len)
{
- char *myname = "cleanup_message_init";
+ char *myname = "cleanup_message";
/*
* Write a dummy start-of-content segment marker. We'll update it with
cleanup_out_format(state, REC_TYPE_MESG, REC_TYPE_MESG_FORMAT, 0L);
if ((state->data_offset = vstream_ftell(state->dst)) < 0)
msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
+
+ /*
+ * Pass control to the header processing routine.
+ */
state->action = cleanup_message_header;
cleanup_message_header(state, type, buf, len);
}
/* cleanup_message_header - process message content, header */
-void cleanup_message_header(CLEANUP_STATE *state, int type, char *buf, int len)
+static void cleanup_message_header(CLEANUP_STATE *state, int type, char *buf, int len)
{
char *myname = "cleanup_message_header";
+ /*
+ * Sanity check.
+ */
if (strchr(REC_TYPE_CONTENT, type) == 0) {
msg_warn("%s: %s: unexpected record type %d",
state->queue_id, myname, type);
*/
if (VSTRING_LEN(state->header_buf) > 0) {
if ((VSTRING_LEN(state->header_buf) >= var_header_limit
- || type != REC_TYPE_NORM)) {
+ || type == REC_TYPE_CONT)) {
state->errs |= CLEANUP_STAT_HOVFL;
- } else if (ISSPACE(*buf)) {
+ } else if (type == REC_TYPE_NORM && ISSPACE(*buf)) {
VSTRING_ADDCH(state->header_buf, '\n');
vstring_strcat(state->header_buf, buf);
return;
* missing headers. Add one blank line when the message headers are
* immediately followed by a non-empty message body.
*/
- if (((state->errs & CLEANUP_STAT_HOVFL) || !is_header(buf))) {
+ if (((state->errs & CLEANUP_STAT_HOVFL)
+ || type != REC_TYPE_NORM
+ || !is_header(buf))) {
cleanup_missing_headers(state);
if (type != REC_TYPE_XTRA && *buf) /* output blank line */
cleanup_out_string(state, REC_TYPE_NORM, "");
/* cleanup_message_body - process message segment, body */
-void cleanup_message_body(CLEANUP_STATE *state, int type, char *buf, int len)
+static void cleanup_message_body(CLEANUP_STATE *state, int type, char *buf, int len)
{
char *myname = "cleanup_message_body";
long xtra_offset;
+ /*
+ * Sanity check.
+ */
+ if (strchr(REC_TYPE_CONTENT, type) == 0) {
+ msg_warn("%s: %s: unexpected record type %d",
+ state->queue_id, myname, type);
+ state->errs |= CLEANUP_STAT_BAD;
+ return;
+ }
+
/*
* Copy body record to the output.
*/
xtra_offset - state->data_offset);
if (vstream_fseek(state->dst, xtra_offset, SEEK_SET) < 0)
msg_fatal("%s: vstream_fseek %s: %m", myname, cleanup_path);
- state->action = cleanup_extracted_init;
+ state->action = cleanup_extracted;
}
/*
state->temp1 = vstring_alloc(10);
state->temp2 = vstring_alloc(10);
- state->hop_count = 0;
- state->headers_seen = 0;
+ state->dst = 0;
+ state->handle = 0;
+ state->queue_id = 0;
state->time = 0;
- state->errs = 0;
- state->from = 0;
state->fullname = 0;
- state->header_buf = vstring_alloc(100);
- state->queue_id = 0;
+ state->sender = 0;
+ state->from = 0;
+ state->resent_from = 0;
state->recip = 0;
state->return_receipt = 0;
state->errors_to = 0;
+ state->flags = 0;
+ state->errs = 0;
+ state->err_mask = 0;
+ state->header_buf = vstring_alloc(100);
+ state->headers_seen = 0;
+ state->hop_count = 0;
state->recipients = argv_alloc(2);
- state->resent = "";
- state->resent_from = 0;
state->resent_recip = argv_alloc(2);
- state->sender = 0;
+ state->resent = "";
state->dups = been_here_init(var_dup_filter_limit, BH_FLAG_FOLD);
state->warn_time = 0;
- state->action = cleanup_envelope_init;
-
+ state->action = cleanup_envelope;
+ state->mesg_offset = -1;
+ state->data_offset = -1;
+ state->end_seen = 0;
return (state);
}
#include <dir_forest.h>
#include <make_dirs.h>
#include <split_at.h>
+#include <sane_fsops.h>
/* Global library. */
* Version of this program.
*/
#define VAR_MAIL_VERSION "mail_version"
-#define DEF_MAIL_VERSION "Snapshot-20000129"
+#define DEF_MAIL_VERSION "Snapshot-20000130"
extern char *var_mail_version;
/* LICENSE
and of the corresponding message. When the bounce
is posted successfully, the log file is deleted.
- The software does a best effort to notify the sender that
- there was a problem. A notification is sent even when the
+ <b>o</b> Post a bounce message without accessing a per-mes-
+ sage log file.
+
+ The software does a best effort to notify the sender that
+ there was a problem. A notification is sent even when the
log file or original message cannot be read.
- Optionally, a client can request that the per-message log
- file be deleted when the requested operation fails. This
+ Optionally, a client can request that the per-message log
+ file be deleted when the requested operation fails. This
is used by clients that cannot retry transactions by them-
- selves, and that depend on retry logic in their own
+ selves, and that depend on retry logic in their own
client.
<b>STANDARDS</b>
Problems and transactions are logged to <b>syslogd</b>(8).
<b>BUGS</b>
- The log files use an ad-hoc, unstructured format. This
- will have to change in order to easily support standard
+ The log files use an ad-hoc, unstructured format. This
+ will have to change in order to easily support standard
delivery status notifications.
<b>CONFIGURATION</b> <b>PARAMETERS</b>
- The following <b>main.cf</b> parameters are especially relevant
- to this program. See the Postfix <b>main.cf</b> file for syntax
- details and for default values. Use the <b>postfix</b> <b>reload</b>
+ The following <b>main.cf</b> parameters are especially relevant
+ to this program. See the Postfix <b>main.cf</b> file for syntax
+ details and for default values. Use the <b>postfix</b> <b>reload</b>
command after a configuration change.
- <b>bounce</b><i>_</i><b>notice</b><i>_</i><b>recipient</b>
- The recipient of single bounce postmaster notices.
-
BOUNCE(8) BOUNCE(8)
+ <b>bounce</b><i>_</i><b>notice</b><i>_</i><b>recipient</b>
+ The recipient of single bounce postmaster notices.
+
<b>2bounce</b><i>_</i><b>notice</b><i>_</i><b>recipient</b>
- The recipient of double bounce postmaster notices.
+ The recipient of double bounce postmaster notices.
<b>delay</b><i>_</i><b>notice</b><i>_</i><b>recipient</b>
The recipient of "delayed mail" postmaster notices.
<b>bounce</b><i>_</i><b>size</b><i>_</i><b>limit</b>
- Limit the amount of original message context that
+ Limit the amount of original message context that
is sent in a non-delivery notification.
<b>mail</b><i>_</i><b>name</b>
- Use this mail system name in the introductory text
+ Use this mail system name in the introductory text
at the start of a bounce message.
<b>notify</b><i>_</i><b>classes</b>
- Notify the postmaster of bounced mail when this
- parameter includes the <b>bounce</b> class. For privacy
+ Notify the postmaster of bounced mail when this
+ parameter includes the <b>bounce</b> class. For privacy
reasons, the message body is not included.
<b>SEE</b> <b>ALSO</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>
-
-
-
--- /dev/null
+<html> <head> </head> <body> <pre>
+</pre> </body> </html>
--- /dev/null
+<html> <head> </head> <body> <pre>
+</pre> </body> </html>
Post a bounce message, with a copy of a log file and of the
corresponding message. When the bounce is posted successfully,
the log file is deleted.
+.IP \(bu
+Post a bounce message without accessing a per-message log file.
.PP
The software does a best effort to notify the sender that there
was a problem. A notification is sent even when the log file
#include <sys_defs.h>
#include <sys/socket.h>
+#include <unistd.h>
/* Utility library. */