int been_here(BH_TABLE *dup_filter, const char *fmt,...) { VSTRING *buf = vstring_alloc(100); int status; va_list ap; /* * Construct the string to be checked. */ va_start(ap, fmt); vstring_vsprintf(buf, fmt, ap); va_end(ap); /* * Do the duplicate check. */ status = been_here_fixed(dup_filter, vstring_str(buf)); /* * Cleanup. */ vstring_free(buf); return (status); }
ARGV *cleanup_map1n_internal(CLEANUP_STATE *state, const char *addr, MAPS *maps, int propagate) { ARGV *argv; ARGV *lookup; int count; int i; int arg; BH_TABLE *been_here; char *saved_lhs; /* * Initialize. */ argv = argv_alloc(1); argv_add(argv, addr, ARGV_END); argv_terminate(argv); been_here = been_here_init(0, BH_FLAG_FOLD); /* * Rewrite the address vector in place. With each map lookup result, * split it into separate addresses, then rewrite and flatten each * address, and repeat the process. Beware: argv is being changed, so we * must index the array explicitly, instead of running along it with a * pointer. */ #define UPDATE(ptr,new) do { \ if (ptr) myfree(ptr); ptr = mystrdup(new); \ } while (0) #define STR vstring_str #define RETURN(x) do { \ been_here_free(been_here); return (x); \ } while (0) #define UNEXPAND(argv, addr) do { \ argv_truncate((argv), 0); argv_add((argv), (addr), (char *) 0); \ } while (0) for (arg = 0; arg < argv->argc; arg++) { if (argv->argc > var_virt_expan_limit) { msg_warn("%s: unreasonable %s map expansion size for %s -- " "message not accepted, try again later", state->queue_id, maps->title, addr); state->errs |= CLEANUP_STAT_DEFER; UPDATE(state->reason, "4.6.0 Alias expansion error"); UNEXPAND(argv, addr); RETURN(argv); } for (count = 0; /* void */ ; count++) { /* * Don't expand an address that already expanded into itself. */ if (been_here_check_fixed(been_here, argv->argv[arg]) != 0) break; if (count >= var_virt_recur_limit) { msg_warn("%s: unreasonable %s map nesting for %s -- " "message not accepted, try again later", state->queue_id, maps->title, addr); state->errs |= CLEANUP_STAT_DEFER; UPDATE(state->reason, "4.6.0 Alias expansion error"); UNEXPAND(argv, addr); RETURN(argv); } quote_822_local(state->temp1, argv->argv[arg]); if ((lookup = mail_addr_map(maps, STR(state->temp1), propagate)) != 0) { saved_lhs = mystrdup(argv->argv[arg]); for (i = 0; i < lookup->argc; i++) { unquote_822_local(state->temp1, lookup->argv[i]); if (i == 0) { UPDATE(argv->argv[arg], STR(state->temp1)); } else { argv_add(argv, STR(state->temp1), ARGV_END); argv_terminate(argv); } /* * Allow an address to expand into itself once. */ if (strcasecmp(saved_lhs, STR(state->temp1)) == 0) been_here_fixed(been_here, saved_lhs); } myfree(saved_lhs); argv_free(lookup); } else if (maps->error != 0) { msg_warn("%s: %s map lookup problem for %s -- " "message not accepted, try again later", state->queue_id, maps->title, addr); state->errs |= CLEANUP_STAT_WRITE; UPDATE(state->reason, "4.6.0 Alias expansion error"); UNEXPAND(argv, addr); RETURN(argv); } else { break; } } } RETURN(argv); }
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); } }