1 /* $NetBSD: cleanup_extracted.c,v 1.2 2017/02/14 01:16:44 christos 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
cleanup_extracted(CLEANUP_STATE * state,int type,const char * buf,ssize_t len)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
cleanup_extracted_process(CLEANUP_STATE * state,int type,const char * buf,ssize_t len)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
cleanup_extracted_finish(CLEANUP_STATE * state)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 * Flush non-Milter header/body_checks BCC recipients. Clear hbc_rcpt
313 * so that it can be used for other purposes.
314 */
315 if (state->hbc_rcpt) {
316 if (CLEANUP_OUT_OK(state) && state->recip != 0) {
317 char **cpp;
318
319 for (cpp = state->hbc_rcpt->argv; *cpp; cpp++)
320 cleanup_addr_bcc(state, *cpp);
321 }
322 argv_free(state->hbc_rcpt);
323 state->hbc_rcpt = 0;
324 }
325
326 /*
327 * Terminate the extracted segment.
328 */
329 cleanup_out_string(state, REC_TYPE_END, "");
330 }
331