Ejemplo n.º 1
0
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);
}
Ejemplo n.º 2
0
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);
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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);
    }
}
Ejemplo n.º 5
0
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;
    }
}