Example #1
0
void    deliver_attr_init(DELIVER_ATTR *attrp)
{
    attrp->level = 0;
    attrp->fp = 0;
    attrp->queue_name = 0;
    attrp->queue_id = 0;
    attrp->offset = 0;
    attrp->sender = 0;
    RECIPIENT_ASSIGN(&(attrp->rcpt), 0, 0, 0, 0, 0);
    attrp->domain = 0;
    attrp->local = 0;
    attrp->user = 0;
    attrp->extension = 0;
    attrp->unmatched = 0;
    attrp->owner = 0;
    attrp->delivered = 0;
    attrp->relay = 0;
    attrp->exp_type = 0;
    attrp->exp_from = 0;
    attrp->why = dsb_create();
}
Example #2
0
int     cleanup_bounce(CLEANUP_STATE *state)
{
    const char *myname = "cleanup_bounce";
    VSTRING *buf = vstring_alloc(100);
    const CLEANUP_STAT_DETAIL *detail;
    DSN_SPLIT dp;
    const char *dsn_status;
    const char *dsn_text;
    char   *rcpt = 0;
    RECIPIENT recipient;
    DSN     dsn;
    char   *attr_name;
    char   *attr_value;
    char   *dsn_orcpt = 0;
    int     dsn_notify = 0;
    char   *orig_rcpt = 0;
    char   *start;
    int     rec_type;
    int     junk;
    long    curr_offset;
    const char *encoding;
    const char *dsn_envid;
    int     dsn_ret;
    int     bounce_err;

    /*
     * Parse the failure reason if one was given, otherwise use a generic
     * mapping from cleanup-internal error code to (DSN + text).
     */
    if (state->reason) {
	dsn_split(&dp, "5.0.0", state->reason);
	dsn_status = DSN_STATUS(dp.dsn);
	dsn_text = dp.text;
    } else {
	detail = cleanup_stat_detail(state->errs);
	dsn_status = detail->dsn;
	dsn_text = detail->text;
    }

    /*
     * Create a bounce logfile with one entry for each final recipient.
     * Degrade gracefully in case of no recipients or no queue file.
     * 
     * Victor Duchovni observes that the number of recipients in the queue file
     * can potentially be very large due to virtual alias expansion. This can
     * expand the recipient count by virtual_alias_expansion_limit (default:
     * 1000) times.
     * 
     * After a queue file write error, purge any unwritten data (so that
     * vstream_fseek() won't fail while trying to flush it) and reset the
     * stream error flags to avoid false alarms.
     */
    if (vstream_ferror(state->dst) || vstream_fflush(state->dst)) {
	(void) vstream_fpurge(state->dst, VSTREAM_PURGE_BOTH);
	vstream_clearerr(state->dst);
    }
    if (vstream_fseek(state->dst, 0L, SEEK_SET) < 0)
	msg_fatal("%s: seek %s: %m", myname, cleanup_path);

    while ((state->errs & CLEANUP_STAT_WRITE) == 0) {
	if ((curr_offset = vstream_ftell(state->dst)) < 0)
	    msg_fatal("%s: vstream_ftell %s: %m", myname, cleanup_path);
	if ((rec_type = rec_get(state->dst, buf, 0)) <= 0
	    || rec_type == REC_TYPE_END)
	    break;
	start = STR(buf);
	if (rec_type == REC_TYPE_ATTR) {
	    if (split_nameval(STR(buf), &attr_name, &attr_value) != 0
		|| *attr_value == 0)
		continue;
	    /* Map attribute names to pseudo record type. */
	    if ((junk = rec_attr_map(attr_name)) != 0) {
		start = attr_value;
		rec_type = junk;
	    }
	}
	switch (rec_type) {
	case REC_TYPE_DSN_ORCPT:		/* RCPT TO ORCPT parameter */
	    if (dsn_orcpt != 0)			/* can't happen */
		myfree(dsn_orcpt);
	    dsn_orcpt = mystrdup(start);
	    break;
	case REC_TYPE_DSN_NOTIFY:		/* RCPT TO NOTIFY parameter */
	    if (alldig(start) && (junk = atoi(start)) > 0
		&& DSN_NOTIFY_OK(junk))
		dsn_notify = junk;
	    else
		dsn_notify = 0;
	    break;
	case REC_TYPE_ORCP:			/* unmodified RCPT TO address */
	    if (orig_rcpt != 0)			/* can't happen */
		myfree(orig_rcpt);
	    orig_rcpt = mystrdup(start);
	    break;
	case REC_TYPE_RCPT:			/* rewritten RCPT TO address */
	    rcpt = start;
	    RECIPIENT_ASSIGN(&recipient, curr_offset,
			     dsn_orcpt ? dsn_orcpt : "", dsn_notify,
			     orig_rcpt ? orig_rcpt : rcpt, rcpt);
	    (void) DSN_SIMPLE(&dsn, dsn_status, dsn_text);
	    cleanup_bounce_append(state, &recipient, &dsn);
	    /* FALLTHROUGH */
	case REC_TYPE_DRCP:			/* canceled recipient */
	case REC_TYPE_DONE:			/* can't happen */
	    if (orig_rcpt != 0) {
		myfree(orig_rcpt);
		orig_rcpt = 0;
	    }
	    if (dsn_orcpt != 0) {
		myfree(dsn_orcpt);
		dsn_orcpt = 0;
	    }
	    dsn_notify = 0;
	    break;
	}
    }
    if (orig_rcpt != 0)				/* can't happen */
	myfree(orig_rcpt);
    if (dsn_orcpt != 0)				/* can't happen */
	myfree(dsn_orcpt);

    /*
     * No recipients. Yes, this can happen.
     */
    if ((state->errs & CLEANUP_STAT_WRITE) == 0 && rcpt == 0) {
	RECIPIENT_ASSIGN(&recipient, 0, "", 0, "", "unknown");
	(void) DSN_SIMPLE(&dsn, dsn_status, dsn_text);
	cleanup_bounce_append(state, &recipient, &dsn);
    }
    vstring_free(buf);

    /*
     * Flush the bounce logfile to the sender. See also qmgr_active.c.
     */
    if ((state->errs & CLEANUP_STAT_WRITE) == 0) {
	if ((encoding = nvtable_find(state->attr, MAIL_ATTR_ENCODING)) == 0)
	    encoding = MAIL_ATTR_ENC_NONE;
	dsn_envid = state->dsn_envid ?
	    state->dsn_envid : "";
	/* Do not send unfiltered (body) content. */
	dsn_ret = (state->errs & (CLEANUP_STAT_CONT | CLEANUP_STAT_SIZE)) ?
	    DSN_RET_HDRS : state->dsn_ret;

	if (state->verp_delims == 0 || var_verp_bounce_off) {
	    bounce_err =
		bounce_flush(BOUNCE_FLAG_CLEAN,
			     state->queue_name, state->queue_id,
			     encoding, state->sender, dsn_envid,
			     dsn_ret);
	} else {
	    bounce_err =
		bounce_flush_verp(BOUNCE_FLAG_CLEAN,
				  state->queue_name, state->queue_id,
				  encoding, state->sender, dsn_envid,
				  dsn_ret, state->verp_delims);
	}
	if (bounce_err != 0) {
	    msg_warn("%s: bounce message failure", state->queue_id);
	    state->errs |= CLEANUP_STAT_WRITE;
	}
    }

    /*
     * Schedule this message (and trace logfile) for deletion when all is
     * well. When all is not well these files would be deleted too, but the
     * client would get a different completion status so we have to carefully
     * maintain the bits anyway.
     */
    if ((state->errs &= CLEANUP_STAT_WRITE) == 0)
	state->flags |= CLEANUP_FLAG_DISCARD;

    return (state->errs);
}
Example #3
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);
    }
}