1 /* $NetBSD: cleanup_envelope.c,v 1.1.1.1 2009/06/23 10:08:43 tron Exp $ */
9 /* process envelope segment
11 /* #include <cleanup.h>
13 /* void cleanup_envelope(state, type, buf, len)
14 /* CLEANUP_STATE *state;
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.
27 /* Queue file and message processing state. This state is updated
28 /* as records are processed and as errors happen.
34 /* Record content length.
38 /* The Secure Mailer license must be distributed with this software.
41 /* IBM T.J. Watson Research
43 /* Yorktown Heights, NY 10598, USA
53 /* Utility library. */
59 #include <stringops.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>
72 #include <rec_attr_map.h>
74 /* Application-specific. */
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";
117 const char *error_text
;
120 int mapped_type
= type
;
121 const char *mapped_buf
= buf
;
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
);
139 state
->flags
|= extra_opts
;
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
);
149 state
->defer_delay
= defer_delay
;
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
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
);
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
;
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
);
189 if ((junk
= rec_attr_map(attr_name
)) != 0) {
190 mapped_buf
= attr_value
;
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
;
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",
234 state
->errs
|= CLEANUP_STAT_BAD
;
237 if (state
->arrival_time
.tv_sec
== 0) {
238 msg_warn("%s: message rejected: missing time envelope record",
240 state
->errs
|= CLEANUP_STAT_BAD
;
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",
265 state
->errs
|= CLEANUP_STAT_BAD
;
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;
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;
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
);
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
);
317 QMGR_READ_FLAG_FROM_DSN(state
->dsn_notify
= junk
);
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
);
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
;
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. */
354 if (mapped_type
== REC_TYPE_CTIME
)
355 /* Use our own expiration time base record instead. */
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));
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
);
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",
381 state
->errs
|= CLEANUP_STAT_BAD
;
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
);
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",
408 state
->errs
|= CLEANUP_STAT_BAD
;
411 if (!allprint(mapped_buf
)) {
412 msg_warn("%s: message rejected: bad DSN envelope ID record",
414 state
->errs
|= CLEANUP_STAT_BAD
;
417 state
->dsn_envid
= mystrdup(mapped_buf
);
418 cleanup_out(state
, type
, buf
, len
);
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",
426 state
->errs
|= CLEANUP_STAT_BAD
;
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
;
436 state
->dsn_ret
= junk
;
437 cleanup_out(state
, type
, buf
, len
);
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
);
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",
454 } else if (verp_delims_verify(buf
) != 0) {
455 msg_warn("%s: ignoring bad VERP request: \"%.100s\"",
456 state
->queue_id
, buf
);
458 state
->verp_delims
= mystrdup(buf
);
459 cleanup_out(state
, type
, buf
, len
);
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
;
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);
479 msg_warn("%s: message rejected: bad rewriting context: %.100s",
480 state
->queue_id
, attr_value
);
481 state
->errs
|= CLEANUP_STAT_BAD
;
485 nvtable_update(state
->attr
, attr_name
, attr_value
);
486 cleanup_out(state
, type
, buf
, len
);
489 cleanup_out(state
, type
, buf
, len
);