1/*	$NetBSD$	*/
2
3/*++
4/* NAME
5/*	cleanup_api 3
6/* SUMMARY
7/*	cleanup callable interface, message processing
8/* SYNOPSIS
9/*	#include "cleanup.h"
10/*
11/*	CLEANUP_STATE *cleanup_open(src)
12/*	VSTREAM	*src;
13/*
14/*	void	cleanup_control(state, flags)
15/*	CLEANUP_STATE *state;
16/*	int	flags;
17/*
18/*	void	CLEANUP_RECORD(state, type, buf, len)
19/*	CLEANUP_STATE *state;
20/*	int	type;
21/*	char	*buf;
22/*	int	len;
23/*
24/*	int	cleanup_flush(state)
25/*	CLEANUP_STATE *state;
26/*
27/*	int	cleanup_free(state)
28/*	CLEANUP_STATE *state;
29/* DESCRIPTION
30/*	This module implements a callable interface to the cleanup service
31/*	for processing one message and for writing it to queue file.
32/*	For a description of the cleanup service, see cleanup(8).
33/*
34/*	cleanup_open() creates a new queue file and performs other
35/*	per-message initialization. The result is a handle that should be
36/*	given to the cleanup_control(), cleanup_record(), cleanup_flush()
37/*	and cleanup_free() routines. The name of the queue file is in the
38/*	queue_id result structure member.
39/*
40/*	cleanup_control() processes per-message flags specified by the caller.
41/*	These flags control the handling of data errors, and must be set
42/*	before processing the first message record.
43/* .IP CLEANUP_FLAG_BOUNCE
44/*	The cleanup server is responsible for returning undeliverable
45/*	mail (too many hops, message too large) to the sender.
46/* .IP CLEANUP_FLAG_BCC_OK
47/*	It is OK to add automatic BCC recipient addresses.
48/* .IP CLEANUP_FLAG_FILTER
49/*	Enable header/body filtering. This should be enabled only with mail
50/*	that enters Postfix, not with locally forwarded mail or with bounce
51/*	messages.
52/* .IP CLEANUP_FLAG_MILTER
53/*	Enable Milter applications. This should be enabled only with mail
54/*	that enters Postfix, not with locally forwarded mail or with bounce
55/*	messages.
56/* .IP CLEANUP_FLAG_MAP_OK
57/*	Enable canonical and virtual mapping, and address masquerading.
58/* .PP
59/*	For convenience the CLEANUP_FLAG_MASK_EXTERNAL macro specifies
60/*	the options that are normally needed for mail that enters
61/*	Postfix from outside, and CLEANUP_FLAG_MASK_INTERNAL specifies
62/*	the options that are normally needed for internally generated or
63/*	forwarded mail.
64/*
65/*	CLEANUP_RECORD() is a macro that processes one message record,
66/*	that copies the result to the queue file, and that maintains a
67/*	little state machine. The last record in a valid message has type
68/*	REC_TYPE_END.  In order to find out if a message is corrupted,
69/*	the caller is encouraged to test the CLEANUP_OUT_OK(state) macro.
70/*	The result is false when further message processing is futile.
71/*	In that case, it is safe to call cleanup_flush() immediately.
72/*
73/*	cleanup_flush() closes a queue file. In case of any errors,
74/*	the file is removed. The result value is non-zero in case of
75/*	problems. In some cases a human-readable text can be found in
76/*	the state->reason member. In all other cases, use cleanup_strerror()
77/*	to translate the result into human-readable text.
78/*
79/*	cleanup_free() destroys its argument.
80/* DIAGNOSTICS
81/*	Problems and transactions are logged to \fBsyslogd\fR(8).
82/* SEE ALSO
83/*	cleanup(8) cleanup service description.
84/*	cleanup_init(8) cleanup callable interface, initialization
85/* LICENSE
86/* .ad
87/* .fi
88/*	The Secure Mailer license must be distributed with this software.
89/* AUTHOR(S)
90/*	Wietse Venema
91/*	IBM T.J. Watson Research
92/*	P.O. Box 704
93/*	Yorktown Heights, NY 10598, USA
94/*--*/
95
96/* System library. */
97
98#include <sys_defs.h>
99#include <errno.h>
100
101/* Utility library. */
102
103#include <msg.h>
104#include <vstring.h>
105#include <mymalloc.h>
106
107/* Global library. */
108
109#include <cleanup_user.h>
110#include <mail_queue.h>
111#include <mail_proto.h>
112#include <bounce.h>
113#include <mail_params.h>
114#include <mail_stream.h>
115#include <mail_flow.h>
116#include <rec_type.h>
117
118/* Milter library. */
119
120#include <milter.h>
121
122/* Application-specific. */
123
124#include "cleanup.h"
125
126/* cleanup_open - open queue file and initialize */
127
128CLEANUP_STATE *cleanup_open(VSTREAM *src)
129{
130    CLEANUP_STATE *state;
131    static const char *log_queues[] = {
132	MAIL_QUEUE_DEFER,
133	MAIL_QUEUE_BOUNCE,
134	MAIL_QUEUE_TRACE,
135	0,
136    };
137    const char **cpp;
138
139    /*
140     * Initialize private state.
141     */
142    state = cleanup_state_alloc(src);
143
144    /*
145     * Open the queue file. Save the queue file name in a global variable, so
146     * that the runtime error handler can clean up in case of problems.
147     *
148     * XXX For now, a lot of detail is frozen that could be more useful if it
149     * were made configurable.
150     */
151    state->queue_name = mystrdup(MAIL_QUEUE_INCOMING);
152    state->handle = mail_stream_file(state->queue_name,
153				   MAIL_CLASS_PUBLIC, var_queue_service, 0);
154    state->dst = state->handle->stream;
155    cleanup_path = mystrdup(VSTREAM_PATH(state->dst));
156    state->queue_id = mystrdup(state->handle->id);
157    if (msg_verbose)
158	msg_info("cleanup_open: open %s", cleanup_path);
159
160    /*
161     * If there is a time to get rid of spurious log files, this is it. The
162     * down side is that this costs performance for every message, while the
163     * probability of spurious log files is quite low.
164     *
165     * XXX The defer logfile is deleted when the message is moved into the
166     * active queue. We must also remove it now, otherwise mailq produces
167     * nonsense.
168     */
169    for (cpp = log_queues; *cpp; cpp++) {
170	if (mail_queue_remove(*cpp, state->queue_id) == 0)
171	    msg_warn("%s: removed spurious %s log", *cpp, state->queue_id);
172	else if (errno != ENOENT)
173	    msg_fatal("%s: remove %s log: %m", *cpp, state->queue_id);
174    }
175    return (state);
176}
177
178/* cleanup_control - process client options */
179
180void    cleanup_control(CLEANUP_STATE *state, int flags)
181{
182
183    /*
184     * If the client requests us to do the bouncing in case of problems,
185     * throw away the input only in case of real show-stopper errors, such as
186     * unrecognizable data (which should never happen) or insufficient space
187     * for the queue file (which will happen occasionally). Otherwise,
188     * discard input after any lethal error. See the CLEANUP_OUT_OK() macro
189     * definition.
190     */
191    if (msg_verbose)
192	msg_info("cleanup flags = %s", cleanup_strflags(flags));
193    if ((state->flags = flags) & CLEANUP_FLAG_BOUNCE) {
194	state->err_mask = CLEANUP_STAT_MASK_INCOMPLETE;
195    } else {
196	state->err_mask = ~0;
197    }
198}
199
200/* cleanup_flush - finish queue file */
201
202int     cleanup_flush(CLEANUP_STATE *state)
203{
204    int     status;
205    char   *junk;
206    VSTRING *trace_junk;
207
208    /*
209     * Raise these errors only if we examined all queue file records.
210     */
211    if (CLEANUP_OUT_OK(state)) {
212	if (state->recip == 0)
213	    state->errs |= CLEANUP_STAT_RCPT;
214	if ((state->flags & CLEANUP_FLAG_END_SEEN) == 0)
215	    state->errs |= CLEANUP_STAT_BAD;
216    }
217
218    /*
219     * Status sanitization. Always report success when the discard flag was
220     * raised by some user-specified access rule.
221     */
222    if (state->flags & CLEANUP_FLAG_DISCARD)
223	state->errs = 0;
224
225    /*
226     * Apply external mail filter.
227     *
228     * XXX Include test for a built-in action to tempfail this message.
229     */
230    if (CLEANUP_MILTER_OK(state)) {
231	if (state->milters)
232	    cleanup_milter_inspect(state, state->milters);
233	else if (cleanup_milters) {
234	    cleanup_milter_emul_data(state, cleanup_milters);
235	    if (CLEANUP_MILTER_OK(state))
236		cleanup_milter_inspect(state, cleanup_milters);
237	}
238    }
239
240    /*
241     * Update the preliminary message size and count fields with the actual
242     * values.
243     */
244    if (CLEANUP_OUT_OK(state))
245	cleanup_final(state);
246
247    /*
248     * If there was an error that requires us to generate a bounce message
249     * (mail submitted with the Postfix sendmail command, mail forwarded by
250     * the local(8) delivery agent, or mail re-queued with "postsuper -r"),
251     * send a bounce notification, reset the error flags in case of success,
252     * and request deletion of the the incoming queue file and of the
253     * optional DSN SUCCESS records from virtual alias expansion.
254     *
255     * XXX It would make no sense to knowingly report success after we already
256     * have bounced all recipients, especially because the information in the
257     * DSN SUCCESS notice is completely redundant compared to the information
258     * in the bounce notice (however, both may be incomplete when the queue
259     * file size would exceed the safety limit).
260     *
261     * An alternative is to keep the DSN SUCCESS records and to delegate bounce
262     * notification to the queue manager, just like we already delegate
263     * success notification. This requires that we leave the undeliverable
264     * message in the incoming queue; versions up to 20050726 did exactly
265     * that. Unfortunately, this broke with over-size queue files, because
266     * the queue manager cannot handle incomplete queue files (and it should
267     * not try to do so).
268     */
269#define CAN_BOUNCE() \
270	((state->errs & CLEANUP_STAT_MASK_CANT_BOUNCE) == 0 \
271	    && state->sender != 0 \
272	    && (state->flags & CLEANUP_FLAG_BOUNCE) != 0)
273
274    if (state->errs != 0 && CAN_BOUNCE())
275	cleanup_bounce(state);
276
277    /*
278     * Optionally, place the message on hold, but only if the message was
279     * received successfully and only if it's not being discarded for other
280     * reasons. This involves renaming the queue file before "finishing" it
281     * (or else the queue manager would grab it too early) and updating our
282     * own idea of the queue file name for error recovery and for error
283     * reporting purposes.
284     *
285     * XXX Include test for a built-in action to tempfail this message.
286     */
287    if (state->errs == 0 && (state->flags & CLEANUP_FLAG_DISCARD) == 0) {
288	if ((state->flags & CLEANUP_FLAG_HOLD) != 0
289#ifdef DELAY_ACTION
290	    || state->defer_delay > 0
291#endif
292	    ) {
293	    myfree(state->queue_name);
294#ifdef DELAY_ACTION
295	    state->queue_name = mystrdup((state->flags & CLEANUP_FLAG_HOLD) ?
296				     MAIL_QUEUE_HOLD : MAIL_QUEUE_DEFERRED);
297#else
298	    state->queue_name = mystrdup(MAIL_QUEUE_HOLD);
299#endif
300	    mail_stream_ctl(state->handle,
301			    MAIL_STREAM_CTL_QUEUE, state->queue_name,
302			    MAIL_STREAM_CTL_CLASS, (char *) 0,
303			    MAIL_STREAM_CTL_SERVICE, (char *) 0,
304#ifdef DELAY_ACTION
305			    MAIL_STREAM_CTL_DELAY, state->defer_delay,
306#endif
307			    MAIL_STREAM_CTL_END);
308	    junk = cleanup_path;
309	    cleanup_path = mystrdup(VSTREAM_PATH(state->handle->stream));
310	    myfree(junk);
311
312	    /*
313	     * XXX: When delivering to a non-incoming queue, do not consume
314	     * in_flow tokens. Unfortunately we can't move the code that
315	     * consumes tokens until after the mail is received, because that
316	     * would increase the risk of duplicate deliveries (RFC 1047).
317	     */
318	    (void) mail_flow_put(1);
319	}
320	state->errs = mail_stream_finish(state->handle, (VSTRING *) 0);
321    } else {
322
323	/*
324	 * XXX: When discarding mail, should we consume in_flow tokens? See
325	 * also the comments above for mail that is placed on hold.
326	 */
327#if 0
328	(void) mail_flow_put(1);
329#endif
330	mail_stream_cleanup(state->handle);
331    }
332    state->handle = 0;
333    state->dst = 0;
334
335    /*
336     * If there was an error, or if the message must be discarded for other
337     * reasons, remove the queue file and the optional trace file with DSN
338     * SUCCESS records from virtual alias expansion.
339     */
340    if (state->errs != 0 || (state->flags & CLEANUP_FLAG_DISCARD) != 0) {
341	if (cleanup_trace_path)
342	    (void) REMOVE(vstring_str(cleanup_trace_path));
343	if (REMOVE(cleanup_path))
344	    msg_warn("remove %s: %m", cleanup_path);
345    }
346
347    /*
348     * Make sure that our queue file will not be deleted by the error handler
349     * AFTER we have taken responsibility for delivery. Better to deliver
350     * twice than to lose mail.
351     */
352    trace_junk = cleanup_trace_path;
353    cleanup_trace_path = 0;			/* don't delete upon error */
354    junk = cleanup_path;
355    cleanup_path = 0;				/* don't delete upon error */
356
357    if (trace_junk)
358	vstring_free(trace_junk);
359    myfree(junk);
360
361    /*
362     * Cleanup internal state. This is simply complementary to the
363     * initializations at the beginning of cleanup_open().
364     */
365    if (msg_verbose)
366	msg_info("cleanup_flush: status %d", state->errs);
367    status = state->errs;
368    return (status);
369}
370
371/* cleanup_free - pay the last respects */
372
373void    cleanup_free(CLEANUP_STATE *state)
374{
375
376    /*
377     * Emulate disconnect event. CLEANUP_FLAG_MILTER may be turned off after
378     * we have started.
379     */
380    if (cleanup_milters != 0 && state->milters == 0)
381	milter_disc_event(cleanup_milters);
382    cleanup_state_free(state);
383}
384