void cleanup_final(CLEANUP_STATE *state) { const char *myname = "cleanup_final"; /* * vstream_fseek() would flush the buffer anyway, but the code just reads * better if we flush first, because it makes seek error handling more * straightforward. */ if (vstream_fflush(state->dst)) { if (errno == EFBIG) { msg_warn("%s: queue file size limit exceeded", state->queue_id); state->errs |= CLEANUP_STAT_SIZE; } else { msg_warn("%s: write queue file: %m", state->queue_id); state->errs |= CLEANUP_STAT_WRITE; } return; } /* * Update the preliminary message size and count fields with the actual * values. */ if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0) msg_fatal("%s: vstream_fseek %s: %m", myname, cleanup_path); cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT, (REC_TYPE_SIZE_CAST1) (state->xtra_offset - state->data_offset), (REC_TYPE_SIZE_CAST2) state->data_offset, (REC_TYPE_SIZE_CAST3) state->rcpt_count, (REC_TYPE_SIZE_CAST4) state->qmgr_opts, (REC_TYPE_SIZE_CAST5) state->cont_length); }
void cleanup_extracted_finish(CLEANUP_STATE *state) { const char myname[] = "cleanup_extracted_finish"; /* * On the way out, add the optional automatic BCC recipient. */ if ((state->flags & CLEANUP_FLAG_BCC_OK) && state->recip != 0 && *var_always_bcc) cleanup_addr_bcc(state, var_always_bcc); /* * Terminate the extracted segment. */ cleanup_out_string(state, REC_TYPE_END, ""); /* * vstream_fseek() would flush the buffer anyway, but the code just reads * better if we flush first, because it makes seek error handling more * straightforward. */ if (vstream_fflush(state->dst)) { if (errno == EFBIG) { msg_warn("%s: queue file size limit exceeded", state->queue_id); state->errs |= CLEANUP_STAT_SIZE; } else { msg_warn("%s: write queue file: %m", state->queue_id); state->errs |= CLEANUP_STAT_WRITE; } return; } /* * Update the preliminary message size and count fields with the actual * values. */ if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0) msg_fatal("%s: vstream_fseek %s: %m", myname, cleanup_path); cleanup_out_format(state, REC_TYPE_SIZE, REC_TYPE_SIZE_FORMAT, (REC_TYPE_SIZE_CAST1) (state->xtra_offset - state->data_offset), (REC_TYPE_SIZE_CAST2) state->data_offset, (REC_TYPE_SIZE_CAST3) state->rcpt_count, (REC_TYPE_SIZE_CAST4) state->qmgr_opts); }
void cleanup_extracted_process(CLEANUP_STATE *state, int type, const char *buf, ssize_t len) { const char *myname = "cleanup_extracted_process"; const char *encoding; char *attr_name; char *attr_value; const char *error_text; int extra_opts; int junk; #ifdef DELAY_ACTION int defer_delay; #endif if (msg_verbose) msg_info("extracted envelope %c %.*s", type, (int) len, buf); if (type == REC_TYPE_FLGS) { /* Not part of queue file format. */ extra_opts = atoi(buf); if (extra_opts & ~CLEANUP_FLAG_MASK_EXTRA) msg_warn("%s: ignoring bad extra flags: 0x%x", state->queue_id, extra_opts); else state->flags |= extra_opts; return; } #ifdef DELAY_ACTION if (type == REC_TYPE_DELAY) { /* Not part of queue file format. */ defer_delay = atoi(buf); if (defer_delay <= 0) msg_warn("%s: ignoring bad delay time: %s", state->queue_id, buf); else state->defer_delay = defer_delay; return; } #endif if (strchr(REC_TYPE_EXTRACT, type) == 0) { msg_warn("%s: message rejected: " "unexpected record type %d in extracted envelope", state->queue_id, type); state->errs |= CLEANUP_STAT_BAD; return; } /* * Map DSN attribute name to pseudo record type so that we don't have to * pollute the queue file with records that are incompatible with past * Postfix versions. Preferably, people should be able to back out from * an upgrade without losing mail. */ if (type == REC_TYPE_ATTR) { vstring_strcpy(state->attr_buf, buf); error_text = split_nameval(STR(state->attr_buf), &attr_name, &attr_value); if (error_text != 0) { msg_warn("%s: message rejected: malformed attribute: %s: %.100s", state->queue_id, error_text, buf); state->errs |= CLEANUP_STAT_BAD; return; } /* Zero-length values are place holders for unavailable values. */ if (*attr_value == 0) { msg_warn("%s: spurious null attribute value for \"%s\" -- ignored", state->queue_id, attr_name); return; } if ((junk = rec_attr_map(attr_name)) != 0) { buf = attr_value; type = junk; } } /* * On the transition from non-recipient records to recipient records, * emit optional information from header/body content. */ if ((state->flags & CLEANUP_FLAG_INRCPT) == 0 && strchr(REC_TYPE_EXT_RECIPIENT, type) != 0) { if (state->filter != 0) cleanup_out_string(state, REC_TYPE_FILT, state->filter); if (state->redirect != 0) cleanup_out_string(state, REC_TYPE_RDR, state->redirect); if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) != 0) cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_ENCODING, encoding); state->flags |= CLEANUP_FLAG_INRCPT; /* Make room to append more meta records. */ if (state->milters || cleanup_milters) { if ((state->append_meta_pt_offset = vstream_ftell(state->dst)) < 0) msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); if ((state->append_meta_pt_target = vstream_ftell(state->dst)) < 0) msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); } } /* * Extracted envelope recipient record processing. */ if (type == REC_TYPE_RCPT) { if (state->sender == 0) { /* protect showq */ msg_warn("%s: message rejected: envelope recipient precedes sender", state->queue_id); state->errs |= CLEANUP_STAT_BAD; return; } if (state->orig_rcpt == 0) state->orig_rcpt = mystrdup(buf); cleanup_addr_recipient(state, buf); if (cleanup_milters != 0 && state->milters == 0 && CLEANUP_MILTER_OK(state)) cleanup_milter_emul_rcpt(state, cleanup_milters, state->recip); myfree(state->orig_rcpt); state->orig_rcpt = 0; if (state->dsn_orcpt != 0) { myfree(state->dsn_orcpt); state->dsn_orcpt = 0; } state->dsn_notify = 0; return; } if (type == REC_TYPE_DONE || type == REC_TYPE_DRCP) { if (state->orig_rcpt != 0) { myfree(state->orig_rcpt); state->orig_rcpt = 0; } if (state->dsn_orcpt != 0) { myfree(state->dsn_orcpt); state->dsn_orcpt = 0; } state->dsn_notify = 0; return; } if (type == REC_TYPE_DSN_ORCPT) { if (state->dsn_orcpt) { msg_warn("%s: ignoring out-of-order DSN original recipient record <%.200s>", state->queue_id, state->dsn_orcpt); myfree(state->dsn_orcpt); } state->dsn_orcpt = mystrdup(buf); return; } if (type == REC_TYPE_DSN_NOTIFY) { if (state->dsn_notify) { msg_warn("%s: ignoring out-of-order DSN notify record <%d>", state->queue_id, state->dsn_notify); state->dsn_notify = 0; } if (!alldig(buf) || (junk = atoi(buf)) == 0 || DSN_NOTIFY_OK(junk) == 0) msg_warn("%s: ignoring malformed dsn notify record <%.200s>", state->queue_id, buf); else state->qmgr_opts |= QMGR_READ_FLAG_FROM_DSN(state->dsn_notify = junk); return; } if (type == REC_TYPE_ORCP) { if (state->orig_rcpt != 0) { msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", state->queue_id, buf); myfree(state->orig_rcpt); } state->orig_rcpt = mystrdup(buf); return; } if (type == REC_TYPE_END) { /* Make room to append recipient. */ if ((state->milters || cleanup_milters) && state->append_rcpt_pt_offset < 0) { if ((state->append_rcpt_pt_offset = vstream_ftell(state->dst)) < 0) msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); cleanup_out_format(state, REC_TYPE_PTR, REC_TYPE_PTR_FORMAT, 0L); if ((state->append_rcpt_pt_target = vstream_ftell(state->dst)) < 0) msg_fatal("%s: vstream_ftell %s: %m:", myname, cleanup_path); } state->flags &= ~CLEANUP_FLAG_INRCPT; state->flags |= CLEANUP_FLAG_END_SEEN; cleanup_extracted_finish(state); return; } /* * Extracted envelope non-recipient record processing. */ if (state->flags & CLEANUP_FLAG_INRCPT) /* Tell qmgr that recipient records are mixed with other information. */ state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER; cleanup_out(state, type, buf, len); return; }
void cleanup_out_recipient(CLEANUP_STATE *state, const char *dsn_orcpt, int dsn_notify, const char *orcpt, const char *recip) { ARGV *argv; char **cpp; /* * XXX Not elegant, but eliminates complexity in the record reading loop. */ if (!var_enable_orcpt) orcpt = ""; if (dsn_orcpt == 0) dsn_orcpt = ""; /* * Distinguish between different original recipient addresses that map * onto the same mailbox. The recipient will use our original recipient * message header to figure things out. * * Postfix 2.2 compatibility: when ignoring differences in Postfix original * recipient information, also ignore differences in DSN attributes. We * do, however, keep the DSN attributes of the recipient that survives * duplicate elimination. */ #define STREQ(x, y) (strcmp((x), (y)) == 0) if ((state->flags & CLEANUP_FLAG_MAP_OK) == 0 || cleanup_virt_alias_maps == 0) { if ((var_enable_orcpt ? been_here(state->dups, "%s\n%d\n%s\n%s", dsn_orcpt, dsn_notify, orcpt, recip) : been_here_fixed(state->dups, recip)) == 0) { if (dsn_notify) cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d", MAIL_ATTR_DSN_NOTIFY, dsn_notify); if (*dsn_orcpt) cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_DSN_ORCPT, dsn_orcpt); cleanup_out_string(state, REC_TYPE_ORCP, orcpt); cleanup_out_string(state, REC_TYPE_RCPT, recip); state->rcpt_count++; } } /* * XXX DSN. RFC 3461 gives us three options for multi-recipient aliases * (we're treating single recipient aliases as a special case of * multi-recipient aliases, one argument being that it is none of the * sender's business). * * (a) Don't propagate ENVID, NOTIFY, RET, or ORCPT. If NOTIFY specified * SUCCESS, send a "relayed" DSN. * * (b) Propagate ENVID, (NOTIFY minus SUCCESS), RET, and ORCPT. If NOTIFY * specified SUCCESS, send an "expanded" DSN. * * (c) Propagate ENVID, NOTIFY, RET, and ORCPT to one recipient only. Send * no DSN. * * In all three cases we are modifying at least one NOTIFY value. Either we * have to record explicit dsn_notify records, or we must not allow the * use of a per-message non-default NOTIFY value that applies to all * recipient records. * * Alternatives (a) and (c) require that we store explicit per-recipient RET * and ENVID records, at least for the recipients that are excluded from * RET and ENVID propagation. This means storing explicit ENVID records * to indicate that the information does not exist. All this makes * alternative (b) more and more attractive. It is no surprise that we * use (b) here and in the local delivery agent. * * In order to generate a SUCCESS notification from the cleanup server we * have to write the trace logfile record now. We're NOT going to flush * the trace file from the cleanup server; if we need to write bounce * logfile records, and the bounce service fails, we must be able to * cancel the entire cleanup request including any success or failure * notifications. The queue manager will flush the trace (and bounce) * logfile, possibly after it has generated its own success or failure * notification records. * * Postfix 2.2 compatibility: when ignoring differences in Postfix original * recipient information, also ignore differences in DSN attributes. We * do, however, keep the DSN attributes of the recipient that survives * duplicate elimination. */ else { RECIPIENT rcpt; DSN dsn; argv = cleanup_map1n_internal(state, recip, cleanup_virt_alias_maps, cleanup_ext_prop_mask & EXT_PROP_VIRTUAL); if ((dsn_notify & DSN_NOTIFY_SUCCESS) && (argv->argc > 1 || strcmp(recip, argv->argv[0]) != 0)) { (void) DSN_SIMPLE(&dsn, "2.0.0", "alias expanded"); dsn.action = "expanded"; RECIPIENT_ASSIGN(&rcpt, 0, dsn_orcpt, dsn_notify, orcpt, recip); cleanup_trace_append(state, &rcpt, &dsn); dsn_notify = (dsn_notify == DSN_NOTIFY_SUCCESS ? DSN_NOTIFY_NEVER : dsn_notify & ~DSN_NOTIFY_SUCCESS); } for (cpp = argv->argv; *cpp; cpp++) { if ((var_enable_orcpt ? been_here(state->dups, "%s\n%d\n%s\n%s", dsn_orcpt, dsn_notify, orcpt, *cpp) : been_here_fixed(state->dups, *cpp)) == 0) { if (dsn_notify) cleanup_out_format(state, REC_TYPE_ATTR, "%s=%d", MAIL_ATTR_DSN_NOTIFY, dsn_notify); if (*dsn_orcpt) cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_DSN_ORCPT, dsn_orcpt); cleanup_out_string(state, REC_TYPE_ORCP, orcpt); cleanup_out_string(state, REC_TYPE_RCPT, *cpp); state->rcpt_count++; } } argv_free(argv); } }
void cleanup_extracted_process(CLEANUP_STATE *state, int type, const char *buf, int len) { const char *encoding; const char generated_by_cleanup[] = { REC_TYPE_FILT, REC_TYPE_RDR, REC_TYPE_ATTR, REC_TYPE_RRTO, REC_TYPE_ERTO, 0, }; if (msg_verbose) msg_info("extracted envelope %c %.*s", type, len, buf); if (strchr(REC_TYPE_EXTRACT, type) == 0) { msg_warn("%s: message rejected: " "unexpected record type %d in extracted envelope", state->queue_id, type); state->errs |= CLEANUP_STAT_BAD; return; } /* * On the transition from non-recipient records to recipient records, * emit optional information from header/body content. */ if ((state->flags & CLEANUP_FLAG_INRCPT) == 0 && strchr(REC_TYPE_EXT_RECIPIENT, type) != 0) { if (state->filter != 0) cleanup_out_string(state, REC_TYPE_FILT, state->filter); if (state->redirect != 0) cleanup_out_string(state, REC_TYPE_RDR, state->redirect); if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) != 0) cleanup_out_format(state, REC_TYPE_ATTR, "%s=%s", MAIL_ATTR_ENCODING, encoding); if (state->return_receipt) cleanup_out_string(state, REC_TYPE_RRTO, state->return_receipt); if (state->errors_to) cleanup_out_string(state, REC_TYPE_ERTO, state->errors_to); state->flags |= CLEANUP_FLAG_INRCPT; } /* * Extracted envelope recipient record processing. */ if (type == REC_TYPE_RCPT) { if (state->sender == 0) { /* protect showq */ msg_warn("%s: message rejected: envelope recipient precedes sender", state->queue_id); state->errs |= CLEANUP_STAT_BAD; return; } if (state->orig_rcpt == 0) state->orig_rcpt = mystrdup(buf); cleanup_addr_recipient(state, buf); myfree(state->orig_rcpt); state->orig_rcpt = 0; return; } if (type == REC_TYPE_DONE) { if (state->orig_rcpt != 0) { myfree(state->orig_rcpt); state->orig_rcpt = 0; } return; } if (state->orig_rcpt != 0) { /* REC_TYPE_ORCP must be followed by REC_TYPE_RCPT or REC_TYPE DONE. */ msg_warn("%s: ignoring out-of-order original recipient record <%.200s>", state->queue_id, buf); myfree(state->orig_rcpt); state->orig_rcpt = 0; } if (type == REC_TYPE_ORCP) { state->orig_rcpt = mystrdup(buf); return; } if (type == REC_TYPE_END) { state->flags &= ~CLEANUP_FLAG_INRCPT; state->flags |= CLEANUP_FLAG_END_SEEN; cleanup_extracted_finish(state); return; } /* * Extracted envelope non-recipient record processing. */ if (state->flags & CLEANUP_FLAG_INRCPT) /* Tell qmgr that recipient records are mixed with other information. */ state->qmgr_opts |= QMGR_READ_FLAG_MIXED_RCPT_OTHER; if (strchr(generated_by_cleanup, type) != 0) { /* Use our own header/body info instead. */ return; } else { /* Pass on other non-recipient record. */ cleanup_out(state, type, buf, len); return; } }