119304Speter/*	$NetBSD: cleanup_api.c,v 1.4 2022/10/08 16:12:45 christos Exp $	*/
219304Speter
319304Speter/*++
419304Speter/* NAME
519304Speter/*	cleanup_api 3
619304Speter/* SUMMARY
719304Speter/*	cleanup callable interface, message processing
819304Speter/* SYNOPSIS
919304Speter/*	#include "cleanup.h"
1019304Speter/*
1119304Speter/*	CLEANUP_STATE *cleanup_open(src)
1219304Speter/*	VSTREAM	*src;
13254225Speter/*
1419304Speter/*	void	cleanup_control(state, flags)
1519304Speter/*	CLEANUP_STATE *state;
1619304Speter/*	int	flags;
1719304Speter/*
18254225Speter/*	void	CLEANUP_RECORD(state, type, buf, len)
1919304Speter/*	CLEANUP_STATE *state;
2019304Speter/*	int	type;
2119304Speter/*	char	*buf;
2219304Speter/*	int	len;
2319304Speter/*
2419304Speter/*	int	cleanup_flush(state)
2519304Speter/*	CLEANUP_STATE *state;
26254225Speter/*
27170371Srafan/*	int	cleanup_free(state)
28254225Speter/*	CLEANUP_STATE *state;
2919304Speter/* DESCRIPTION
3019304Speter/*	This module implements a callable interface to the cleanup service
3119304Speter/*	for processing one message and for writing it to queue file.
3219304Speter/*	For a description of the cleanup service, see cleanup(8).
3319304Speter/*
3419304Speter/*	cleanup_open() creates a new queue file and performs other
3519304Speter/*	per-message initialization. The result is a handle that should be
3619304Speter/*	given to the cleanup_control(), cleanup_record(), cleanup_flush()
3719304Speter/*	and cleanup_free() routines. The name of the queue file is in the
3819304Speter/*	queue_id result structure member.
3919304Speter/*
4019304Speter/*	cleanup_control() processes per-message flags specified by the caller.
4119304Speter/*	These flags control the handling of data errors, and must be set
4219304Speter/*	before processing the first message record.
4319304Speter/* .IP CLEANUP_FLAG_BOUNCE
4419304Speter/*	The cleanup server is responsible for returning undeliverable
4519304Speter/*	mail (too many hops, message too large) to the sender.
4619304Speter/* .IP CLEANUP_FLAG_BCC_OK
4719304Speter/*	It is OK to add automatic BCC recipient addresses.
4819304Speter/* .IP CLEANUP_FLAG_FILTER
49254225Speter/*	Enable header/body filtering. This should be enabled only with mail
5019304Speter/*	that enters Postfix, not with locally forwarded mail or with bounce
5119304Speter/*	messages.
52254225Speter/* .IP CLEANUP_FLAG_MILTER
5319304Speter/*	Enable Milter applications. This should be enabled only with mail
5419304Speter/*	that enters Postfix, not with locally forwarded mail or with bounce
5519304Speter/*	messages.
5619304Speter/* .IP CLEANUP_FLAG_MAP_OK
57254225Speter/*	Enable canonical and virtual mapping, and address masquerading.
5819304Speter/* .PP
5919304Speter/*	For convenience the CLEANUP_FLAG_MASK_EXTERNAL macro specifies
6019304Speter/*	the options that are normally needed for mail that enters
61254225Speter/*	Postfix from outside, and CLEANUP_FLAG_MASK_INTERNAL specifies
62254225Speter/*	the options that are normally needed for internally generated or
63254225Speter/*	forwarded mail.
64254225Speter/*
6519304Speter/*	CLEANUP_RECORD() is a macro that processes one message record,
6619304Speter/*	that copies the result to the queue file, and that maintains a
6719304Speter/*	little state machine. The last record in a valid message has type
6819304Speter/*	REC_TYPE_END.  In order to find out if a message is corrupted,
6919304Speter/*	the caller is encouraged to test the CLEANUP_OUT_OK(state) macro.
7019304Speter/*	The result is false when further message processing is futile.
71254225Speter/*	In that case, it is safe to call cleanup_flush() immediately.
72254225Speter/*
7319304Speter/*	cleanup_flush() closes a queue file. In case of any errors,
7419304Speter/*	the file is removed. The result value is non-zero in case of
7519304Speter/*	problems. In some cases a human-readable text can be found in
7619304Speter/*	the state->reason member. In all other cases, use cleanup_strerror()
7719304Speter/*	to translate the result into human-readable text.
7819304Speter/*
7919304Speter/*	cleanup_free() destroys its argument.
8019304Speter/* .IP CLEANUP_FLAG_SMTPUTF8
8119304Speter/*	Request SMTPUTF8 support when delivering mail.
8219304Speter/* .IP CLEANUP_FLAG_AUTOUTF8
8319304Speter/*	Autodetection: request SMTPUTF8 support if the message
8419304Speter/*	contains an UTF8 message header, sender, or recipient.
8519304Speter/* DIAGNOSTICS
8619304Speter/*	Problems and transactions are logged to \fBsyslogd\fR(8)
8719304Speter/*	or \fBpostlogd\fR(8).
8819304Speter/* SEE ALSO
8919304Speter/*	cleanup(8) cleanup service description.
9019304Speter/*	cleanup_init(8) cleanup callable interface, initialization
9119304Speter/* LICENSE
9219304Speter/* .ad
9319304Speter/* .fi
9419304Speter/*	The Secure Mailer license must be distributed with this software.
9519304Speter/* AUTHOR(S)
9619304Speter/*	Wietse Venema
9719304Speter/*	IBM T.J. Watson Research
9819304Speter/*	P.O. Box 704
9919304Speter/*	Yorktown Heights, NY 10598, USA
100254225Speter/*
101254225Speter/*	Wietse Venema
102254225Speter/*	Google, Inc.
10319304Speter/*	111 8th Avenue
104254225Speter/*	New York, NY 10011, USA
105254225Speter/*--*/
10619304Speter
10719304Speter/* System library. */
10819304Speter
10919304Speter#include <sys_defs.h>
11019304Speter#include <errno.h>
11119304Speter
11219304Speter/* Utility library. */
11319304Speter
11419304Speter#include <msg.h>
11519304Speter#include <vstring.h>
11619304Speter#include <mymalloc.h>
11719304Speter
11819304Speter/* Global library. */
11919304Speter
12019304Speter#include <cleanup_user.h>
12119304Speter#include <mail_queue.h>
12219304Speter#include <mail_proto.h>
12319304Speter#include <bounce.h>
12419304Speter#include <mail_params.h>
12519304Speter#include <mail_stream.h>
12619304Speter#include <mail_flow.h>
12719304Speter#include <rec_type.h>
12819304Speter#include <smtputf8.h>
12919304Speter
13019304Speter/* Milter library. */
13119304Speter
13219304Speter#include <milter.h>
13319304Speter
13419304Speter/* Application-specific. */
13519304Speter
13619304Speter#include "cleanup.h"
137254225Speter
13819304Speter/* cleanup_open - open queue file and initialize */
13919304Speter
14019304SpeterCLEANUP_STATE *cleanup_open(VSTREAM *src)
14119304Speter{
14219304Speter    CLEANUP_STATE *state;
14319304Speter    static const char *log_queues[] = {
14419304Speter	MAIL_QUEUE_DEFER,
14519304Speter	MAIL_QUEUE_BOUNCE,
14619304Speter	MAIL_QUEUE_TRACE,
14719304Speter	0,
14819304Speter    };
14919304Speter    const char **cpp;
15019304Speter
15119304Speter    /*
15219304Speter     * Initialize private state.
15319304Speter     */
15419304Speter    state = cleanup_state_alloc(src);
15519304Speter
15619304Speter    /*
15719304Speter     * Open the queue file. Save the queue file name in a global variable, so
15819304Speter     * that the runtime error handler can clean up in case of problems.
15919304Speter     *
16019304Speter     * XXX For now, a lot of detail is frozen that could be more useful if it
16119304Speter     * were made configurable.
16219304Speter     */
16319304Speter    state->queue_name = mystrdup(MAIL_QUEUE_INCOMING);
16419304Speter    state->handle = mail_stream_file(state->queue_name,
16519304Speter				   MAIL_CLASS_PUBLIC, var_queue_service, 0);
16619304Speter    state->dst = state->handle->stream;
16719304Speter    cleanup_path = mystrdup(VSTREAM_PATH(state->dst));
16819304Speter    state->queue_id = mystrdup(state->handle->id);
16919304Speter    if (msg_verbose)
17019304Speter	msg_info("cleanup_open: open %s", cleanup_path);
17119304Speter
17219304Speter    /*
17319304Speter     * If there is a time to get rid of spurious log files, this is it. The
17419304Speter     * down side is that this costs performance for every message, while the
17519304Speter     * probability of spurious log files is quite low.
17619304Speter     *
17719304Speter     * XXX The defer logfile is deleted when the message is moved into the
17819304Speter     * active queue. We must also remove it now, otherwise mailq produces
17919304Speter     * nonsense.
18019304Speter     */
18119304Speter    for (cpp = log_queues; *cpp; cpp++) {
18219304Speter	if (mail_queue_remove(*cpp, state->queue_id) == 0)
18319304Speter	    msg_warn("%s: removed spurious %s log", *cpp, state->queue_id);
18419304Speter	else if (errno != ENOENT)
18519304Speter	    msg_fatal("%s: remove %s log: %m", *cpp, state->queue_id);
18619304Speter    }
187254225Speter    return (state);
18819304Speter}
18919304Speter
19019304Speter/* cleanup_control - process client options */
19119304Speter
19219304Spetervoid    cleanup_control(CLEANUP_STATE *state, int flags)
19319304Speter{
19419304Speter
19519304Speter    /*
19619304Speter     * If the client requests us to do the bouncing in case of problems,
19719304Speter     * throw away the input only in case of real show-stopper errors, such as
19819304Speter     * unrecognizable data (which should never happen) or insufficient space
19919304Speter     * for the queue file (which will happen occasionally). Otherwise,
20019304Speter     * discard input after any lethal error. See the CLEANUP_OUT_OK() macro
20119304Speter     * definition.
20219304Speter     */
20319304Speter    if (msg_verbose)
20419304Speter	msg_info("cleanup flags = %s", cleanup_strflags(flags));
20519304Speter    if ((state->flags = flags) & CLEANUP_FLAG_BOUNCE) {
20619304Speter	state->err_mask = CLEANUP_STAT_MASK_INCOMPLETE;
20719304Speter    } else {
20819304Speter	state->err_mask = ~0;
20919304Speter    }
21019304Speter    if (state->flags & CLEANUP_FLAG_SMTPUTF8)
21119304Speter	state->smtputf8 = SMTPUTF8_FLAG_REQUESTED;
21219304Speter}
21319304Speter
21419304Speter/* cleanup_flush - finish queue file */
21519304Speter
21619304Speterint     cleanup_flush(CLEANUP_STATE *state)
21719304Speter{
21819304Speter    int     status;
21919304Speter    char   *junk;
22019304Speter    VSTRING *trace_junk;
22119304Speter
22219304Speter    /*
22319304Speter     * Raise these errors only if we examined all queue file records.
22419304Speter     */
22519304Speter    if (CLEANUP_OUT_OK(state)) {
22619304Speter	if (state->recip == 0)
22719304Speter	    state->errs |= CLEANUP_STAT_RCPT;
22819304Speter	if ((state->flags & CLEANUP_FLAG_END_SEEN) == 0)
22919304Speter	    state->errs |= CLEANUP_STAT_BAD;
23019304Speter    }
23119304Speter
23219304Speter    /*
23319304Speter     * Status sanitization. Always report success when the discard flag was
23419304Speter     * raised by some user-specified access rule.
23519304Speter     */
23619304Speter    if (state->flags & CLEANUP_FLAG_DISCARD)
23719304Speter	state->errs = 0;
23819304Speter
23919304Speter    /*
24019304Speter     * Apply external mail filter.
24119304Speter     *
24219304Speter     * XXX Include test for a built-in action to tempfail this message.
24319304Speter     */
24419304Speter    if (CLEANUP_MILTER_OK(state)) {
24519304Speter	if (state->milters)
24619304Speter	    cleanup_milter_inspect(state, state->milters);
24719304Speter	else if (cleanup_milters) {
24819304Speter	    cleanup_milter_emul_data(state, cleanup_milters);
24919304Speter	    if (CLEANUP_MILTER_OK(state))
250254225Speter		cleanup_milter_inspect(state, cleanup_milters);
25119304Speter	}
25219304Speter    }
25319304Speter
25419304Speter    /*
25519304Speter     * Update the preliminary message size and count fields with the actual
25619304Speter     * values.
25719304Speter     */
25819304Speter    if (CLEANUP_OUT_OK(state))
25919304Speter	cleanup_final(state);
26019304Speter
26119304Speter    /*
26219304Speter     * If there was an error that requires us to generate a bounce message
26319304Speter     * (mail submitted with the Postfix sendmail command, mail forwarded by
26419304Speter     * the local(8) delivery agent, or mail re-queued with "postsuper -r"),
26519304Speter     * send a bounce notification, reset the error flags in case of success,
26619304Speter     * and request deletion of the incoming queue file and of the
26719304Speter     * optional DSN SUCCESS records from virtual alias expansion.
26819304Speter     *
26919304Speter     * XXX It would make no sense to knowingly report success after we already
27019304Speter     * have bounced all recipients, especially because the information in the
27119304Speter     * DSN SUCCESS notice is completely redundant compared to the information
27219304Speter     * in the bounce notice (however, both may be incomplete when the queue
27319304Speter     * file size would exceed the safety limit).
27419304Speter     *
27519304Speter     * An alternative is to keep the DSN SUCCESS records and to delegate bounce
27619304Speter     * notification to the queue manager, just like we already delegate
27719304Speter     * success notification. This requires that we leave the undeliverable
27819304Speter     * message in the incoming queue; versions up to 20050726 did exactly
27919304Speter     * that. Unfortunately, this broke with over-size queue files, because
28019304Speter     * the queue manager cannot handle incomplete queue files (and it should
28119304Speter     * not try to do so).
28219304Speter     */
28319304Speter#define CAN_BOUNCE() \
28419304Speter	((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \
28519304Speter	    && state->sender != 0 \
28619304Speter	    && (state->flags & CLEANUP_FLAG_BOUNCE) != 0)
28719304Speter
28819304Speter    if (state->errs != 0 && CAN_BOUNCE())
28919304Speter	cleanup_bounce(state);
29019304Speter
29119304Speter    /*
29219304Speter     * Optionally, place the message on hold, but only if the message was
29319304Speter     * received successfully and only if it's not being discarded for other
29419304Speter     * reasons. This involves renaming the queue file before "finishing" it
29519304Speter     * (or else the queue manager would grab it too early) and updating our
29619304Speter     * own idea of the queue file name for error recovery and for error
29719304Speter     * reporting purposes.
29819304Speter     *
29919304Speter     * XXX Include test for a built-in action to tempfail this message.
30019304Speter     */
30119304Speter    if (state->errs == 0 && (state->flags & CLEANUP_FLAG_DISCARD) == 0) {
30219304Speter	if ((state->flags & CLEANUP_FLAG_HOLD) != 0
30319304Speter#ifdef DELAY_ACTION
30419304Speter	    || state->defer_delay > 0
30519304Speter#endif
30619304Speter	    ) {
30719304Speter	    myfree(state->queue_name);
30819304Speter#ifdef DELAY_ACTION
30919304Speter	    state->queue_name = mystrdup((state->flags & CLEANUP_FLAG_HOLD) ?
31019304Speter				     MAIL_QUEUE_HOLD : MAIL_QUEUE_DEFERRED);
31119304Speter#else
31219304Speter	    state->queue_name = mystrdup(MAIL_QUEUE_HOLD);
31319304Speter#endif
31419304Speter	    mail_stream_ctl(state->handle,
31519304Speter			    CA_MAIL_STREAM_CTL_QUEUE(state->queue_name),
31619304Speter			    CA_MAIL_STREAM_CTL_CLASS((char *) 0),
31719304Speter			    CA_MAIL_STREAM_CTL_SERVICE((char *) 0),
31819304Speter#ifdef DELAY_ACTION
31919304Speter			    CA_MAIL_STREAM_CTL_DELAY(state->defer_delay),
32019304Speter#endif
32119304Speter			    CA_MAIL_STREAM_CTL_END);
32219304Speter	    junk = cleanup_path;
32319304Speter	    cleanup_path = mystrdup(VSTREAM_PATH(state->handle->stream));
32419304Speter	    myfree(junk);
32519304Speter
32619304Speter	    /*
32719304Speter	     * XXX: When delivering to a non-incoming queue, do not consume
32819304Speter	     * in_flow tokens. Unfortunately we can't move the code that
32919304Speter	     * consumes tokens until after the mail is received, because that
33019304Speter	     * would increase the risk of duplicate deliveries (RFC 1047).
33119304Speter	     */
33219304Speter	    (void) mail_flow_put(1);
33319304Speter	}
33419304Speter	state->errs = mail_stream_finish(state->handle, (VSTRING *) 0);
33519304Speter    } else {
33619304Speter
33719304Speter	/*
33819304Speter	 * XXX: When discarding mail, should we consume in_flow tokens? See
33919304Speter	 * also the comments above for mail that is placed on hold.
34019304Speter	 */
34119304Speter#if 0
34219304Speter	(void) mail_flow_put(1);
34319304Speter#endif
34419304Speter	mail_stream_cleanup(state->handle);
34519304Speter    }
34619304Speter    state->handle = 0;
34719304Speter    state->dst = 0;
34819304Speter
34919304Speter    /*
35019304Speter     * If there was an error, or if the message must be discarded for other
35119304Speter     * reasons, remove the queue file and the optional trace file with DSN
35219304Speter     * SUCCESS records from virtual alias expansion.
35319304Speter     */
35419304Speter    if (state->errs != 0 || (state->flags & CLEANUP_FLAG_DISCARD) != 0) {
35519304Speter	if (cleanup_trace_path)
35619304Speter	    (void) REMOVE(vstring_str(cleanup_trace_path));
35719304Speter	if (REMOVE(cleanup_path))
35819304Speter	    msg_warn("remove %s: %m", cleanup_path);
35919304Speter    }
36019304Speter
36119304Speter    /*
36219304Speter     * Make sure that our queue file will not be deleted by the error handler
36319304Speter     * AFTER we have taken responsibility for delivery. Better to deliver
36419304Speter     * twice than to lose mail.
36519304Speter     */
36619304Speter    trace_junk = cleanup_trace_path;
36719304Speter    cleanup_trace_path = 0;			/* don't delete upon error */
36819304Speter    junk = cleanup_path;
36919304Speter    cleanup_path = 0;				/* don't delete upon error */
37019304Speter
37119304Speter    if (trace_junk)
37219304Speter	vstring_free(trace_junk);
37319304Speter    myfree(junk);
37419304Speter
37519304Speter    /*
37619304Speter     * Cleanup internal state. This is simply complementary to the
37786201Srwatson     * initializations at the beginning of cleanup_open().
37886201Srwatson     */
37919304Speter    if (msg_verbose)
38019304Speter	msg_info("cleanup_flush: status %d", state->errs);
38119304Speter    status = state->errs;
38219304Speter    return (status);
38319304Speter}
38419304Speter
38519304Speter/* cleanup_free - pay the last respects */
38619304Speter
38719304Spetervoid    cleanup_free(CLEANUP_STATE *state)
38819304Speter{
38919304Speter
39019304Speter    /*
391254225Speter     * Emulate disconnect event. CLEANUP_FLAG_MILTER may be turned off after
39219304Speter     * we have started.
39319304Speter     */
39419304Speter    if (cleanup_milters != 0 && state->milters == 0)
39519304Speter	milter_disc_event(cleanup_milters);
39619304Speter    cleanup_state_free(state);
39719304Speter}
39819304Speter