Expand PMF_FN_* macros.
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / cleanup / cleanup_envelope.c
blob3f9eb9298751ecba45ac26066bd50d81a321d939
1 /* $NetBSD: cleanup_envelope.c,v 1.1.1.1 2009/06/23 10:08:43 tron Exp $ */
3 /* $NetBSD$ */
5 /*++
6 /* NAME
7 /* cleanup_envelope 3
8 /* SUMMARY
9 /* process envelope segment
10 /* SYNOPSIS
11 /* #include <cleanup.h>
13 /* void cleanup_envelope(state, type, buf, len)
14 /* CLEANUP_STATE *state;
15 /* int type;
16 /* const char *buf;
17 /* ssize_t len;
18 /* DESCRIPTION
19 /* This module processes envelope records and writes the result
20 /* to the queue file. It validates the message structure, rewrites
21 /* sender/recipient addresses to canonical form, and expands recipients
22 /* according to entries in the virtual table. This routine absorbs but
23 /* does not emit the envelope to content boundary record.
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 /*--*/
46 /* System library. */
48 #include <sys_defs.h>
49 #include <string.h>
50 #include <stdlib.h>
51 #include <ctype.h>
53 /* Utility library. */
55 #include <msg.h>
56 #include <vstring.h>
57 #include <vstream.h>
58 #include <mymalloc.h>
59 #include <stringops.h>
60 #include <nvtable.h>
62 /* Global library. */
64 #include <record.h>
65 #include <rec_type.h>
66 #include <cleanup_user.h>
67 #include <qmgr_user.h>
68 #include <mail_params.h>
69 #include <verp_sender.h>
70 #include <mail_proto.h>
71 #include <dsn_mask.h>
72 #include <rec_attr_map.h>
74 /* Application-specific. */
76 #include "cleanup.h"
78 #define STR vstring_str
79 #define STREQ(x,y) (strcmp((x), (y)) == 0)
81 static void cleanup_envelope_process(CLEANUP_STATE *, int, const char *, ssize_t);
83 /* cleanup_envelope - initialize message envelope */
85 void cleanup_envelope(CLEANUP_STATE *state, int type,
86 const char *str, ssize_t len)
90 * The message size and count record goes first, so it can easily be
91 * updated in place. This information takes precedence over any size
92 * estimate provided by the client. It's all in one record, data size
93 * first, for backwards compatibility reasons.
95 cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT,
96 (REC_TYPE_SIZE_CAST1) 0, /* extra offs - content offs */
97 (REC_TYPE_SIZE_CAST2) 0, /* content offset */
98 (REC_TYPE_SIZE_CAST3) 0, /* recipient count */
99 (REC_TYPE_SIZE_CAST4) 0, /* qmgr options */
100 (REC_TYPE_SIZE_CAST5) 0); /* content length */
103 * Pass control to the actual envelope processing routine.
105 state->action = cleanup_envelope_process;
106 cleanup_envelope_process(state, type, str, len);
109 /* cleanup_envelope_process - process one envelope record */
111 static void cleanup_envelope_process(CLEANUP_STATE *state, int type,
112 const char *buf, ssize_t len)
114 const char *myname = "cleanup_envelope_process";
115 char *attr_name;
116 char *attr_value;
117 const char *error_text;
118 int extra_opts;
119 int junk;
120 int mapped_type = type;
121 const char *mapped_buf = buf;
122 int milter_count;
124 #ifdef DELAY_ACTION
125 int defer_delay;
127 #endif
129 if (msg_verbose)
130 msg_info("initial envelope %c %.*s", type, (int) len, buf);
132 if (type == REC_TYPE_FLGS) {
133 /* Not part of queue file format. */
134 extra_opts = atoi(buf);
135 if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA)
136 msg_warn("%s: ignoring bad extra flags: 0x%x",
137 state->queue_id, extra_opts);
138 else
139 state->flags |= extra_opts;
140 return;
142 #ifdef DELAY_ACTION
143 if (type == REC_TYPE_DELAY) {
144 /* Not part of queue file format. */
145 defer_delay = atoi(buf);
146 if (defer_delay <= 0)
147 msg_warn("%s: ignoring bad delay time: %s", state->queue_id, buf);
148 else
149 state->defer_delay = defer_delay;
150 return;
152 #endif
155 * XXX We instantiate a MILTERS structure even when the filter count is
156 * zero (for example, all filters are in ACCEPT state, or the SMTP server
157 * sends a dummy MILTERS structure without any filters), otherwise the
158 * cleanup server would apply the non_smtpd_milters setting
159 * inappropriately.
161 if (type == REC_TYPE_MILT_COUNT) {
162 /* Not part of queue file format. */
163 if ((milter_count = atoi(buf)) >= 0)
164 cleanup_milter_receive(state, milter_count);
165 return;
169 * Map DSN attribute name to pseudo record type so that we don't have to
170 * pollute the queue file with records that are incompatible with past
171 * Postfix versions. Preferably, people should be able to back out from
172 * an upgrade without losing mail.
174 if (type == REC_TYPE_ATTR) {
175 vstring_strcpy(state->attr_buf, buf);
176 error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value);
177 if (error_text != 0) {
178 msg_warn("%s: message rejected: malformed attribute: %s: %.100s",
179 state->queue_id, error_text, buf);
180 state->errs |= CLEANUP_STAT_BAD;
181 return;
183 /* Zero-length values are place holders for unavailable values. */
184 if (*attr_value == 0) {
185 msg_warn("%s: spurious null attribute value for \"%s\" -- ignored",
186 state->queue_id, attr_name);
187 return;
189 if ((junk = rec_attr_map(attr_name)) != 0) {
190 mapped_buf = attr_value;
191 mapped_type = junk;
196 * Sanity check.
198 if (strchr(REC_TYPE_ENVELOPE, type) == 0) {
199 msg_warn("%s: message rejected: unexpected record type %d in envelope",
200 state->queue_id, type);
201 state->errs |= CLEANUP_STAT_BAD;
202 return;
206 * Although recipient records appear at the end of the initial or
207 * extracted envelope, the code for processing recipient records is first
208 * because there can be lots of them.
210 * Recipient records may be mixed with other information (such as FILTER or
211 * REDIRECT actions from SMTPD). In that case the queue manager needs to
212 * examine all queue file records before it can start delivery. This is
213 * not a problem when SMTPD recipient lists are small.
215 * However, if recipient records are not mixed with other records
216 * (typically, mailing list mail) then we can make an optimization: the
217 * queue manager does not need to examine every envelope record before it
218 * can start deliveries. This can help with very large mailing lists.
222 * On the transition from non-recipient records to recipient records,
223 * emit some records and do some sanity checks.
225 * XXX Moving the envelope sender (and the test for its presence) to the
226 * extracted segment can reduce qmqpd memory requirements because it no
227 * longer needs to read the entire message into main memory.
229 if ((state->flags & CLEANUP_FLAG_INRCPT) == 0
230 && strchr(REC_TYPE_ENV_RECIPIENT, type) != 0) {
231 if (state->sender == 0) {
232 msg_warn("%s: message rejected: missing sender envelope record",
233 state->queue_id);
234 state->errs |= CLEANUP_STAT_BAD;
235 return;
237 if (state->arrival_time.tv_sec == 0) {
238 msg_warn("%s: message rejected: missing time envelope record",
239 state->queue_id);
240 state->errs |= CLEANUP_STAT_BAD;
241 return;
245 * XXX This works by accident, because the sender is recorded at the
246 * beginning of the envelope segment.
248 if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0
249 && state->sender && *state->sender
250 && var_delay_warn_time > 0) {
251 cleanup_out_format(state, REC_TYPE_WARN, REC_TYPE_WARN_FORMAT,
252 REC_TYPE_WARN_ARG(state->arrival_time.tv_sec
253 + var_delay_warn_time));
255 state->flags |= CLEANUP_FLAG_INRCPT;
259 * Initial envelope recipient record processing.
261 if (type == REC_TYPE_RCPT) {
262 if (state->sender == 0) { /* protect showq */
263 msg_warn("%s: message rejected: envelope recipient precedes sender",
264 state->queue_id);
265 state->errs |= CLEANUP_STAT_BAD;
266 return;
268 if (state->orig_rcpt == 0)
269 state->orig_rcpt = mystrdup(buf);
270 cleanup_addr_recipient(state, buf);
271 if (cleanup_milters != 0
272 && state->milters == 0
273 && CLEANUP_MILTER_OK(state))
274 cleanup_milter_emul_rcpt(state, cleanup_milters, state->recip);
275 myfree(state->orig_rcpt);
276 state->orig_rcpt = 0;
277 if (state->dsn_orcpt != 0) {
278 myfree(state->dsn_orcpt);
279 state->dsn_orcpt = 0;
281 state->dsn_notify = 0;
282 return;
284 if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) {
285 if (state->orig_rcpt != 0) {
286 myfree(state->orig_rcpt);
287 state->orig_rcpt = 0;
289 if (state->dsn_orcpt != 0) {
290 myfree(state->dsn_orcpt);
291 state->dsn_orcpt = 0;
293 state->dsn_notify = 0;
294 return;
296 if (mapped_type == REC_TYPE_DSN_ORCPT) {
297 if (state->dsn_orcpt) {
298 msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>",
299 state->queue_id, state->dsn_orcpt);
300 myfree(state->dsn_orcpt);
302 state->dsn_orcpt = mystrdup(mapped_buf);
303 return;
305 if (mapped_type == REC_TYPE_DSN_NOTIFY) {
306 if (state->dsn_notify) {
307 msg_warn("%s: ignoring out-of-order DSN notify record <%d>",
308 state->queue_id, state->dsn_notify);
309 state->dsn_notify = 0;
311 if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0
312 || DSN_NOTIFY_OK(junk) == 0)
313 msg_warn("%s: ignoring malformed DSN notify record <%.200s>",
314 state->queue_id, buf);
315 else
316 state->qmgr_opts |=
317 QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk);
318 return;
320 if (type == REC_TYPE_ORCP) {
321 if (state->orig_rcpt != 0) {
322 msg_warn("%s: ignoring out-of-order original recipient record <%.200s>",
323 state->queue_id, state->orig_rcpt);
324 myfree(state->orig_rcpt);
326 state->orig_rcpt = mystrdup(buf);
327 return;
329 if (type == REC_TYPE_MESG) {
330 state->action = cleanup_message;
331 if (state->flags & CLEANUP_FLAG_INRCPT) {
332 if (state->milters || cleanup_milters) {
333 /* Make room to append recipient. */
334 if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0)
335 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
336 cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L);
337 if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0)
338 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
340 state->flags &= ~CLEANUP_FLAG_INRCPT;
342 return;
346 * Initial envelope non-recipient record processing.
348 if (state->flags & CLEANUP_FLAG_INRCPT)
349 /* Tell qmgr that recipient records are mixed with other information. */
350 state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER;
351 if (type == REC_TYPE_SIZE)
352 /* Use our own SIZE record instead. */
353 return;
354 if (mapped_type == REC_TYPE_CTIME)
355 /* Use our own expiration time base record instead. */
356 return;
357 if (type == REC_TYPE_TIME) {
358 /* First instance wins. */
359 if (state->arrival_time.tv_sec == 0) {
360 REC_TYPE_TIME_SCAN(buf, state->arrival_time);
361 cleanup_out(state, type, buf, len);
363 /* Generate our own expiration time base record. */
364 cleanup_out_format(state, REC_TYPE_ATTR, "%s=%ld",
365 MAIL_ATTR_CREATE_TIME, (long) time((time_t *) 0));
366 return;
368 if (type == REC_TYPE_FULL) {
369 /* First instance wins. */
370 if (state->fullname == 0) {
371 state->fullname = mystrdup(buf);
372 cleanup_out(state, type, buf, len);
374 return;
376 if (type == REC_TYPE_FROM) {
377 /* Allow only one instance. */
378 if (state->sender != 0) {
379 msg_warn("%s: message rejected: multiple envelope sender records",
380 state->queue_id);
381 state->errs |= CLEANUP_STAT_BAD;
382 return;
384 if (state->milters || cleanup_milters) {
385 /* Remember the sender record offset. */
386 if ((state->sender_pt_offset = vstream_ftell(state->dst)) < 0)
387 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
389 cleanup_addr_sender(state, buf);
390 if (state->milters || cleanup_milters) {
391 /* Make room to replace sender. */
392 rec_pad(state->dst, REC_TYPE_PTR, REC_TYPE_PTR_PAYL_SIZE);
393 /* Remember the after-sender record offset. */
394 if ((state->sender_pt_target = vstream_ftell(state->dst)) < 0)
395 msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path);
397 if (cleanup_milters != 0
398 && state->milters == 0
399 && CLEANUP_MILTER_OK(state))
400 cleanup_milter_emul_mail(state, cleanup_milters, state->sender);
401 return;
403 if (mapped_type == REC_TYPE_DSN_ENVID) {
404 /* Allow only one instance. */
405 if (state->dsn_envid != 0) {
406 msg_warn("%s: message rejected: multiple DSN envelope ID records",
407 state->queue_id);
408 state->errs |= CLEANUP_STAT_BAD;
409 return;
411 if (!allprint(mapped_buf)) {
412 msg_warn("%s: message rejected: bad DSN envelope ID record",
413 state->queue_id);
414 state->errs |= CLEANUP_STAT_BAD;
415 return;
417 state->dsn_envid = mystrdup(mapped_buf);
418 cleanup_out(state, type, buf, len);
419 return;
421 if (mapped_type == REC_TYPE_DSN_RET) {
422 /* Allow only one instance. */
423 if (state->dsn_ret != 0) {
424 msg_warn("%s: message rejected: multiple DSN RET records",
425 state->queue_id);
426 state->errs |= CLEANUP_STAT_BAD;
427 return;
429 if (!alldig(mapped_buf) || (junk = atoi(mapped_buf)) == 0
430 || DSN_RET_OK(junk) == 0) {
431 msg_warn("%s: message rejected: bad DSN RET record <%.200s>",
432 state->queue_id, buf);
433 state->errs |= CLEANUP_STAT_BAD;
434 return;
436 state->dsn_ret = junk;
437 cleanup_out(state, type, buf, len);
438 return;
440 if (type == REC_TYPE_WARN) {
441 /* First instance wins. */
442 if ((state->flags & CLEANUP_FLAG_WARN_SEEN) == 0) {
443 state->flags |= CLEANUP_FLAG_WARN_SEEN;
444 cleanup_out(state, type, buf, len);
446 return;
448 /* XXX Needed for cleanup_bounce(); sanity check usage. */
449 if (type == REC_TYPE_VERP) {
450 if (state->verp_delims == 0) {
451 if (state->sender == 0 || state->sender[0] == 0) {
452 msg_warn("%s: ignoring VERP request for null sender",
453 state->queue_id);
454 } else if (verp_delims_verify(buf) != 0) {
455 msg_warn("%s: ignoring bad VERP request: \"%.100s\"",
456 state->queue_id, buf);
457 } else {
458 state->verp_delims = mystrdup(buf);
459 cleanup_out(state, type, buf, len);
462 return;
464 if (type == REC_TYPE_ATTR) {
465 if (state->attr->used >= var_qattr_count_limit) {
466 msg_warn("%s: message rejected: attribute count exceeds limit %d",
467 state->queue_id, var_qattr_count_limit);
468 state->errs |= CLEANUP_STAT_BAD;
469 return;
471 if (strcmp(attr_name, MAIL_ATTR_RWR_CONTEXT) == 0) {
472 /* Choose header rewriting context. See also cleanup_addr.c. */
473 if (STREQ(attr_value, MAIL_ATTR_RWR_LOCAL)) {
474 state->hdr_rewrite_context = MAIL_ATTR_RWR_LOCAL;
475 } else if (STREQ(attr_value, MAIL_ATTR_RWR_REMOTE)) {
476 state->hdr_rewrite_context =
477 (*var_remote_rwr_domain ? MAIL_ATTR_RWR_REMOTE : 0);
478 } else {
479 msg_warn("%s: message rejected: bad rewriting context: %.100s",
480 state->queue_id, attr_value);
481 state->errs |= CLEANUP_STAT_BAD;
482 return;
485 nvtable_update(state->attr, attr_name, attr_value);
486 cleanup_out(state, type, buf, len);
487 return;
488 } else {
489 cleanup_out(state, type, buf, len);
490 return;