1/*++
2/* NAME
3/*	cleanup_extracted 3
4/* SUMMARY
5/*	process extracted segment
6/* SYNOPSIS
7/*	#include "cleanup.h"
8/*
9/*	void	cleanup_extracted(state, type, buf, len)
10/*	CLEANUP_STATE *state;
11/*	int	type;
12/*	const char *buf;
13/*	ssize_t	len;
14/* DESCRIPTION
15/*	This module processes message records with information extracted
16/*	from message content, or with recipients that are stored after the
17/*	message content. It updates recipient records, writes extracted
18/*	information records to the output, and writes the queue
19/*	file end marker.  The queue file is left in a state that
20/*	is suitable for Milter inspection, but the size record still
21/*	contains dummy values.
22/*
23/*	Arguments:
24/* .IP state
25/*	Queue file and message processing state. This state is updated
26/*	as records are processed and as errors happen.
27/* .IP type
28/*	Record type.
29/* .IP buf
30/*	Record content.
31/* .IP len
32/*	Record content length.
33/* LICENSE
34/* .ad
35/* .fi
36/*	The Secure Mailer license must be distributed with this software.
37/* AUTHOR(S)
38/*	Wietse Venema
39/*	IBM T.J. Watson Research
40/*	P.O. Box 704
41/*	Yorktown Heights, NY 10598, USA
42/*--*/
43
44/* System library. */
45
46#include <sys_defs.h>
47#include <unistd.h>
48#include <errno.h>
49#include <string.h>
50#include <stdlib.h>
51
52/* Utility library. */
53
54#include <msg.h>
55#include <vstring.h>
56#include <vstream.h>
57#include <mymalloc.h>
58#include <nvtable.h>
59#include <stringops.h>
60
61/* Global library. */
62
63#include <cleanup_user.h>
64#include <qmgr_user.h>
65#include <record.h>
66#include <rec_type.h>
67#include <mail_params.h>
68#include <mail_proto.h>
69#include <dsn_mask.h>
70#include <rec_attr_map.h>
71
72/* Application-specific. */
73
74#include "cleanup.h"
75
76#define STR(x)	vstring_str(x)
77
78static void cleanup_extracted_process(CLEANUP_STATE *, int, const char *, ssize_t);
79static void cleanup_extracted_finish(CLEANUP_STATE *);
80
81/* cleanup_extracted - initialize extracted segment */
82
83void    cleanup_extracted(CLEANUP_STATE *state, int type,
84			          const char *buf, ssize_t len)
85{
86
87    /*
88     * Start the extracted segment.
89     */
90    cleanup_out_string(state, REC_TYPE_XTRA, "");
91
92    /*
93     * Pass control to the actual envelope processing routine.
94     */
95    state->action = cleanup_extracted_process;
96    cleanup_extracted_process(state, type, buf, len);
97}
98
99/* cleanup_extracted_process - process one extracted envelope record */
100
101void    cleanup_extracted_process(CLEANUP_STATE *state, int type,
102				          const char *buf, ssize_t len)
103{
104    const char *myname = "cleanup_extracted_process";
105    const char *encoding;
106    char   *attr_name;
107    char   *attr_value;
108    const char *error_text;
109    int     extra_opts;
110    int     junk;
111
112#ifdef DELAY_ACTION
113    int     defer_delay;
114
115#endif
116
117    if (msg_verbose)
118	msg_info("extracted envelope %c %.*s", type, (int) len, buf);
119
120    if (type == REC_TYPE_FLGS) {
121	/* Not part of queue file format. */
122	extra_opts = atoi(buf);
123	if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA)
124	    msg_warn("%s: ignoring bad extra flags: 0x%x",
125		     state->queue_id, extra_opts);
126	else
127	    state->flags |= extra_opts;
128	return;
129    }
130#ifdef DELAY_ACTION
131    if (type == REC_TYPE_DELAY) {
132	/* Not part of queue file format. */
133	defer_delay = atoi(buf);
134	if (defer_delay <= 0)
135	    msg_warn("%s: ignoring bad delay time: %s", state->queue_id, buf);
136	else
137	    state->defer_delay = defer_delay;
138	return;
139    }
140#endif
141
142    if (strchr(REC_TYPE_EXTRACT, type) == 0) {
143	msg_warn("%s: message rejected: "
144		 "unexpected record type %d in extracted envelope",
145		 state->queue_id, type);
146	state->errs |= CLEANUP_STAT_BAD;
147	return;
148    }
149
150    /*
151     * Map DSN attribute name to pseudo record type so that we don't have to
152     * pollute the queue file with records that are incompatible with past
153     * Postfix versions. Preferably, people should be able to back out from
154     * an upgrade without losing mail.
155     */
156    if (type == REC_TYPE_ATTR) {
157	vstring_strcpy(state->attr_buf, buf);
158	error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value);
159	if (error_text != 0) {
160	    msg_warn("%s: message rejected: malformed attribute: %s: %.100s",
161		     state->queue_id, error_text, buf);
162	    state->errs |= CLEANUP_STAT_BAD;
163	    return;
164	}
165	/* Zero-length values are place holders for unavailable values. */
166	if (*attr_value == 0) {
167	    msg_warn("%s: spurious null attribute value for \"%s\" -- ignored",
168		     state->queue_id, attr_name);
169	    return;
170	}
171	if ((junk = rec_attr_map(attr_name)) != 0) {
172	    buf = attr_value;
173	    type = junk;
174	}
175    }
176
177    /*
178     * On the transition from non-recipient records to recipient records,
179     * emit optional information from header/body content.
180     */
181    if ((state->flags & CLEANUP_FLAG_INRCPT) == 0
182	&& strchr(REC_TYPE_EXT_RECIPIENT, type) != 0) {
183	if (state->filter != 0)
184	    cleanup_out_string(state, REC_TYPE_FILT, state->filter);
185	if (state->redirect != 0)
186	    cleanup_out_string(state, REC_TYPE_RDR, state->redirect);
187	if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) != 0)
188	    cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s",
189			       MAIL_ATTR_ENCODING, encoding);
190	state->flags |= CLEANUP_FLAG_INRCPT;
191	/* Make room to append more meta records. */
192	if (state->milters || cleanup_milters) {
193	    if ((state->append_meta_pt_offset = vstream_ftell(state->dst)) < 0)
194		msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
195	    cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
196	    if ((state->append_meta_pt_target = vstream_ftell(state->dst)) < 0)
197		msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
198	}
199    }
200
201    /*
202     * Extracted envelope recipient record processing.
203     */
204    if (type == REC_TYPE_RCPT) {
205	if (state->sender == 0) {		/* protect showq */
206	    msg_warn("%s: message rejected: envelope recipient precedes sender",
207		     state->queue_id);
208	    state->errs |= CLEANUP_STAT_BAD;
209	    return;
210	}
211	if (state->orig_rcpt == 0)
212	    state->orig_rcpt = mystrdup(buf);
213	cleanup_addr_recipient(state, buf);
214	if (cleanup_milters != 0
215	    && state->milters == 0
216	    && CLEANUP_MILTER_OK(state))
217	    cleanup_milter_emul_rcpt(state, cleanup_milters, state->recip);
218	myfree(state->orig_rcpt);
219	state->orig_rcpt = 0;
220	if (state->dsn_orcpt != 0) {
221	    myfree(state->dsn_orcpt);
222	    state->dsn_orcpt = 0;
223	}
224	state->dsn_notify = 0;
225	return;
226    }
227    if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) {
228	if (state->orig_rcpt != 0) {
229	    myfree(state->orig_rcpt);
230	    state->orig_rcpt = 0;
231	}
232	if (state->dsn_orcpt != 0) {
233	    myfree(state->dsn_orcpt);
234	    state->dsn_orcpt = 0;
235	}
236	state->dsn_notify = 0;
237	return;
238    }
239    if (type == REC_TYPE_DSN_ORCPT) {
240	if (state->dsn_orcpt) {
241	    msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>",
242		     state->queue_id, state->dsn_orcpt);
243	    myfree(state->dsn_orcpt);
244	}
245	state->dsn_orcpt = mystrdup(buf);
246	return;
247    }
248    if (type == REC_TYPE_DSN_NOTIFY) {
249	if (state->dsn_notify) {
250	    msg_warn("%s: ignoring out-of-order DSN notify record <%d>",
251		     state->queue_id, state->dsn_notify);
252	    state->dsn_notify = 0;
253	}
254	if (!alldig(buf) || (junk = atoi(buf)) == 0 || DSN_NOTIFY_OK(junk) == 0)
255	    msg_warn("%s: ignoring malformed dsn notify record <%.200s>",
256		     state->queue_id, buf);
257	else
258	    state->qmgr_opts |=
259		QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk);
260	return;
261    }
262    if (type == REC_TYPE_ORCP) {
263	if (state->orig_rcpt != 0) {
264	    msg_warn("%s: ignoring out-of-order original recipient record <%.200s>",
265		     state->queue_id, buf);
266	    myfree(state->orig_rcpt);
267	}
268	state->orig_rcpt = mystrdup(buf);
269	return;
270    }
271    if (type == REC_TYPE_END) {
272	/* Make room to append recipient. */
273	if ((state->milters || cleanup_milters)
274	    && state->append_rcpt_pt_offset < 0) {
275	    if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0)
276		msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
277	    cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
278	    if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0)
279		msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
280	}
281	state->flags &= ~CLEANUP_FLAG_INRCPT;
282	state->flags |= CLEANUP_FLAG_END_SEEN;
283	cleanup_extracted_finish(state);
284	return;
285    }
286
287    /*
288     * Extracted envelope non-recipient record processing.
289     */
290    if (state->flags & CLEANUP_FLAG_INRCPT)
291	/* Tell qmgr that recipient records are mixed with other information. */
292	state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER;
293    cleanup_out(state, type, buf, len);
294    return;
295}
296
297/* cleanup_extracted_finish - complete the third message segment */
298
299void    cleanup_extracted_finish(CLEANUP_STATE *state)
300{
301
302    /*
303     * On the way out, add the optional automatic BCC recipient.
304     */
305    if ((state->flags & CLEANUP_FLAG_BCC_OK)
306	&& state->recip != 0 && *var_always_bcc)
307	cleanup_addr_bcc(state, var_always_bcc);
308
309    /*
310     * Terminate the extracted segment.
311     */
312    cleanup_out_string(state, REC_TYPE_END, "");
313}
314