summaryrefslogtreecommitdiffstats
path: root/src/cleanup/cleanup_api.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/cleanup/cleanup_api.c395
1 files changed, 395 insertions, 0 deletions
diff --git a/src/cleanup/cleanup_api.c b/src/cleanup/cleanup_api.c
new file mode 100644
index 0000000..4fc5e2e
--- /dev/null
+++ b/src/cleanup/cleanup_api.c
@@ -0,0 +1,395 @@
+/*++
+/* NAME
+/* cleanup_api 3
+/* SUMMARY
+/* cleanup callable interface, message processing
+/* SYNOPSIS
+/* #include "cleanup.h"
+/*
+/* CLEANUP_STATE *cleanup_open(src)
+/* VSTREAM *src;
+/*
+/* void cleanup_control(state, flags)
+/* CLEANUP_STATE *state;
+/* int flags;
+/*
+/* void CLEANUP_RECORD(state, type, buf, len)
+/* CLEANUP_STATE *state;
+/* int type;
+/* char *buf;
+/* int len;
+/*
+/* int cleanup_flush(state)
+/* CLEANUP_STATE *state;
+/*
+/* int cleanup_free(state)
+/* CLEANUP_STATE *state;
+/* DESCRIPTION
+/* 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_open() creates a new queue file and performs other
+/* per-message initialization. The result is a handle that should be
+/* given to the cleanup_control(), cleanup_record(), cleanup_flush()
+/* and cleanup_free() routines. The name of the queue file is in the
+/* queue_id result structure member.
+/*
+/* 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.
+/* .IP CLEANUP_FLAG_BOUNCE
+/* The cleanup server is responsible for returning undeliverable
+/* mail (too many hops, message too large) to the sender.
+/* .IP CLEANUP_FLAG_BCC_OK
+/* It is OK to add automatic BCC recipient addresses.
+/* .IP CLEANUP_FLAG_FILTER
+/* Enable header/body filtering. This should be enabled only with mail
+/* that enters Postfix, not with locally forwarded mail or with bounce
+/* messages.
+/* .IP CLEANUP_FLAG_MILTER
+/* Enable Milter applications. This should be enabled only with mail
+/* that enters Postfix, not with locally forwarded mail or with bounce
+/* messages.
+/* .IP CLEANUP_FLAG_MAP_OK
+/* Enable canonical and virtual mapping, and address masquerading.
+/* .PP
+/* For convenience the CLEANUP_FLAG_MASK_EXTERNAL macro specifies
+/* the options that are normally needed for mail that enters
+/* Postfix from outside, and CLEANUP_FLAG_MASK_INTERNAL specifies
+/* the options that are normally needed for internally generated or
+/* forwarded mail.
+/*
+/* 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_flush() immediately.
+/*
+/* cleanup_flush() closes a queue file. In case of any errors,
+/* the file is removed. The result value is non-zero in case of
+/* problems. In some cases a human-readable text can be found in
+/* the state->reason member. In all other cases, use cleanup_strerror()
+/* to translate the result into human-readable text.
+/*
+/* cleanup_free() destroys its argument.
+/* .IP CLEANUP_FLAG_SMTPUTF8
+/* Request SMTPUTF8 support when delivering mail.
+/* .IP CLEANUP_FLAG_AUTOUTF8
+/* Autodetection: request SMTPUTF8 support if the message
+/* contains an UTF8 message header, sender, or recipient.
+/* DIAGNOSTICS
+/* Problems and transactions are logged to \fBsyslogd\fR(8)
+/* or \fBpostlogd\fR(8).
+/* SEE ALSO
+/* cleanup(8) cleanup service description.
+/* cleanup_init(8) cleanup callable interface, initialization
+/* 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
+/*
+/* Wietse Venema
+/* Google, Inc.
+/* 111 8th Avenue
+/* New York, NY 10011, USA
+/*--*/
+
+/* System library. */
+
+#include <sys_defs.h>
+#include <errno.h>
+
+/* Utility library. */
+
+#include <msg.h>
+#include <vstring.h>
+#include <mymalloc.h>
+
+/* Global library. */
+
+#include <cleanup_user.h>
+#include <mail_queue.h>
+#include <mail_proto.h>
+#include <bounce.h>
+#include <mail_params.h>
+#include <mail_stream.h>
+#include <mail_flow.h>
+#include <rec_type.h>
+#include <smtputf8.h>
+
+/* Milter library. */
+
+#include <milter.h>
+
+/* Application-specific. */
+
+#include "cleanup.h"
+
+/* cleanup_open - open queue file and initialize */
+
+CLEANUP_STATE *cleanup_open(VSTREAM *src)
+{
+ CLEANUP_STATE *state;
+ static const char *log_queues[] = {
+ MAIL_QUEUE_DEFER,
+ MAIL_QUEUE_BOUNCE,
+ MAIL_QUEUE_TRACE,
+ 0,
+ };
+ const char **cpp;
+
+ /*
+ * Initialize private state.
+ */
+ state = cleanup_state_alloc(src);
+
+ /*
+ * 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.
+ *
+ * XXX For now, a lot of detail is frozen that could be more useful if it
+ * were made configurable.
+ */
+ state->queue_name = mystrdup(MAIL_QUEUE_INCOMING);
+ state->handle = mail_stream_file(state->queue_name,
+ MAIL_CLASS_PUBLIC, var_queue_service, 0);
+ state->dst = state->handle->stream;
+ cleanup_path = mystrdup(VSTREAM_PATH(state->dst));
+ state->queue_id = mystrdup(state->handle->id);
+ if (msg_verbose)
+ msg_info("cleanup_open: open %s", cleanup_path);
+
+ /*
+ * If there is a time to get rid of spurious log files, this is it. The
+ * down side is that this costs performance for every message, while the
+ * probability of spurious log files is quite low.
+ *
+ * XXX The defer logfile is deleted when the message is moved into the
+ * active queue. We must also remove it now, otherwise mailq produces
+ * nonsense.
+ */
+ for (cpp = log_queues; *cpp; cpp++) {
+ if (mail_queue_remove(*cpp, state->queue_id) == 0)
+ msg_warn("%s: removed spurious %s log", *cpp, state->queue_id);
+ else if (errno != ENOENT)
+ msg_fatal("%s: remove %s log: %m", *cpp, state->queue_id);
+ }
+ return (state);
+}
+
+/* cleanup_control - process client options */
+
+void cleanup_control(CLEANUP_STATE *state, int flags)
+{
+
+ /*
+ * If the client requests us to do the bouncing in case of problems,
+ * throw away the input only in case of real show-stopper errors, such as
+ * 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() macro
+ * definition.
+ */
+ if (msg_verbose)
+ msg_info("cleanup flags = %s", cleanup_strflags(flags));
+ if ((state->flags = flags) & CLEANUP_FLAG_BOUNCE) {
+ state->err_mask = CLEANUP_STAT_MASK_INCOMPLETE;
+ } else {
+ state->err_mask = ~0;
+ }
+ if (state->flags & CLEANUP_FLAG_SMTPUTF8)
+ state->smtputf8 = SMTPUTF8_FLAG_REQUESTED;
+}
+
+/* cleanup_flush - finish queue file */
+
+int cleanup_flush(CLEANUP_STATE *state)
+{
+ int status;
+ char *junk;
+ VSTRING *trace_junk;
+
+ /*
+ * Raise these errors only if we examined all queue file records.
+ */
+ if (CLEANUP_OUT_OK(state)) {
+ if (state->recip == 0)
+ state->errs |= CLEANUP_STAT_RCPT;
+ if ((state->flags & CLEANUP_FLAG_END_SEEN) == 0)
+ state->errs |= CLEANUP_STAT_BAD;
+ }
+
+ /*
+ * Status sanitization. Always report success when the discard flag was
+ * raised by some user-specified access rule.
+ */
+ if (state->flags & CLEANUP_FLAG_DISCARD)
+ state->errs = 0;
+
+ /*
+ * Apply external mail filter.
+ *
+ * XXX Include test for a built-in action to tempfail this message.
+ */
+ if (CLEANUP_MILTER_OK(state)) {
+ if (state->milters)
+ cleanup_milter_inspect(state, state->milters);
+ else if (cleanup_milters) {
+ cleanup_milter_emul_data(state, cleanup_milters);
+ if (CLEANUP_MILTER_OK(state))
+ cleanup_milter_inspect(state, cleanup_milters);
+ }
+ }
+
+ /*
+ * Update the preliminary message size and count fields with the actual
+ * values.
+ */
+ if (CLEANUP_OUT_OK(state))
+ cleanup_final(state);
+
+ /*
+ * If there was an error that requires us to generate a bounce message
+ * (mail submitted with the Postfix sendmail command, mail forwarded by
+ * the local(8) delivery agent, or mail re-queued with "postsuper -r"),
+ * send a bounce notification, reset the error flags in case of success,
+ * and request deletion of the the incoming queue file and of the
+ * optional DSN SUCCESS records from virtual alias expansion.
+ *
+ * XXX It would make no sense to knowingly report success after we already
+ * have bounced all recipients, especially because the information in the
+ * DSN SUCCESS notice is completely redundant compared to the information
+ * in the bounce notice (however, both may be incomplete when the queue
+ * file size would exceed the safety limit).
+ *
+ * An alternative is to keep the DSN SUCCESS records and to delegate bounce
+ * notification to the queue manager, just like we already delegate
+ * success notification. This requires that we leave the undeliverable
+ * message in the incoming queue; versions up to 20050726 did exactly
+ * that. Unfortunately, this broke with over-size queue files, because
+ * the queue manager cannot handle incomplete queue files (and it should
+ * not try to do so).
+ */
+#define CAN_BOUNCE() \
+ ((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \
+ && state->sender != 0 \
+ && (state->flags & CLEANUP_FLAG_BOUNCE) != 0)
+
+ if (state->errs != 0 && CAN_BOUNCE())
+ cleanup_bounce(state);
+
+ /*
+ * Optionally, place the message on hold, but only if the message was
+ * received successfully and only if it's not being discarded for other
+ * reasons. This involves renaming the queue file before "finishing" it
+ * (or else the queue manager would grab it too early) and updating our
+ * own idea of the queue file name for error recovery and for error
+ * reporting purposes.
+ *
+ * XXX Include test for a built-in action to tempfail this message.
+ */
+ if (state->errs == 0 && (state->flags & CLEANUP_FLAG_DISCARD) == 0) {
+ if ((state->flags & CLEANUP_FLAG_HOLD) != 0
+#ifdef DELAY_ACTION
+ || state->defer_delay > 0
+#endif
+ ) {
+ myfree(state->queue_name);
+#ifdef DELAY_ACTION
+ state->queue_name = mystrdup((state->flags & CLEANUP_FLAG_HOLD) ?
+ MAIL_QUEUE_HOLD : MAIL_QUEUE_DEFERRED);
+#else
+ state->queue_name = mystrdup(MAIL_QUEUE_HOLD);
+#endif
+ mail_stream_ctl(state->handle,
+ CA_MAIL_STREAM_CTL_QUEUE(state->queue_name),
+ CA_MAIL_STREAM_CTL_CLASS((char *) 0),
+ CA_MAIL_STREAM_CTL_SERVICE((char *) 0),
+#ifdef DELAY_ACTION
+ CA_MAIL_STREAM_CTL_DELAY(state->defer_delay),
+#endif
+ CA_MAIL_STREAM_CTL_END);
+ junk = cleanup_path;
+ cleanup_path = mystrdup(VSTREAM_PATH(state->handle->stream));
+ myfree(junk);
+
+ /*
+ * XXX: When delivering to a non-incoming queue, do not consume
+ * in_flow tokens. Unfortunately we can't move the code that
+ * consumes tokens until after the mail is received, because that
+ * would increase the risk of duplicate deliveries (RFC 1047).
+ */
+ (void) mail_flow_put(1);
+ }
+ state->errs = mail_stream_finish(state->handle, (VSTRING *) 0);
+ } else {
+
+ /*
+ * XXX: When discarding mail, should we consume in_flow tokens? See
+ * also the comments above for mail that is placed on hold.
+ */
+#if 0
+ (void) mail_flow_put(1);
+#endif
+ mail_stream_cleanup(state->handle);
+ }
+ state->handle = 0;
+ state->dst = 0;
+
+ /*
+ * If there was an error, or if the message must be discarded for other
+ * reasons, remove the queue file and the optional trace file with DSN
+ * SUCCESS records from virtual alias expansion.
+ */
+ if (state->errs != 0 || (state->flags & CLEANUP_FLAG_DISCARD) != 0) {
+ if (cleanup_trace_path)
+ (void) REMOVE(vstring_str(cleanup_trace_path));
+ if (REMOVE(cleanup_path))
+ msg_warn("remove %s: %m", cleanup_path);
+ }
+
+ /*
+ * Make sure that our queue file will not be deleted by the error handler
+ * AFTER we have taken responsibility for delivery. Better to deliver
+ * twice than to lose mail.
+ */
+ trace_junk = cleanup_trace_path;
+ cleanup_trace_path = 0; /* don't delete upon error */
+ junk = cleanup_path;
+ cleanup_path = 0; /* don't delete upon error */
+
+ if (trace_junk)
+ vstring_free(trace_junk);
+ myfree(junk);
+
+ /*
+ * Cleanup internal state. This is simply complementary to the
+ * initializations at the beginning of cleanup_open().
+ */
+ if (msg_verbose)
+ msg_info("cleanup_flush: status %d", state->errs);
+ status = state->errs;
+ return (status);
+}
+
+/* cleanup_free - pay the last respects */
+
+void cleanup_free(CLEANUP_STATE *state)
+{
+
+ /*
+ * Emulate disconnect event. CLEANUP_FLAG_MILTER may be turned off after
+ * we have started.
+ */
+ if (cleanup_milters != 0 && state->milters == 0)
+ milter_disc_event(cleanup_milters);
+ cleanup_state_free(state);
+}