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