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(); }
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); }
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); } }