static int deliver_switch(LOCAL_STATE state, USER_ATTR usr_attr) { const char *myname = "deliver_switch"; int status = 0; struct stat st; struct mypasswd *mypwd; /* * Make verbose logging easier to understand. */ state.level++; if (msg_verbose) MSG_LOG_STATE(myname, state); /* * \user is special: it means don't do any alias or forward expansion. * * XXX This code currently does not work due to revision of the RFC822 * address parser. \user should be permitted only in locally specified * aliases, includes or forward files. * * XXX Should test for presence of user home directory. */ if (state.msg_attr.rcpt.address[0] == '\\') { state.msg_attr.rcpt.address++, state.msg_attr.local++, state.msg_attr.user++; if (deliver_mailbox(state, usr_attr, &status) == 0) status = deliver_unknown(state, usr_attr); return (status); } /* * Otherwise, alias expansion has highest precedence. First look up the * full localpart, then the bare user. Obey the address extension * propagation policy. */ state.msg_attr.unmatched = 0; if (deliver_alias(state, usr_attr, state.msg_attr.local, &status)) return (status); if (state.msg_attr.extension != 0) { if (local_ext_prop_mask & EXT_PROP_ALIAS) state.msg_attr.unmatched = state.msg_attr.extension; if (deliver_alias(state, usr_attr, state.msg_attr.user, &status)) return (status); state.msg_attr.unmatched = state.msg_attr.extension; } /* * Special case for mail locally forwarded or aliased to a different * local address. Resubmit the message via the cleanup service, so that * each recipient gets a separate delivery queue file status record in * the new queue file. The downside of this approach is that mutually * recursive .forward files cause a mail forwarding loop. Fortunately, * the loop can be broken by the use of the Delivered-To: message header. * * The code below must not trigger on mail sent to an alias that has no * owner- companion, so that mail for an alias first.last->username is * delivered directly, instead of going through username->first.last * canonical mappings in the cleanup service. The downside of this * approach is that recipients in the expansion of an alias without * owner- won't have separate delivery queue file status records, because * for them, the message won't be resubmitted as a new queue file. * * Do something sensible on systems that receive mail for multiple domains, * such as primary.name and secondary.name. Don't resubmit the message * when mail for `[email protected]' is delivered to a .forward file * that lists `user' or `[email protected]'. We already know that the * recipient domain is local, so we only have to compare local parts. */ if (state.msg_attr.owner != 0 && strcasecmp(state.msg_attr.owner, state.msg_attr.user) != 0) return (deliver_indirect(state)); /* * Always forward recipients in :include: files. */ if (state.msg_attr.exp_type == EXPAND_TYPE_INCL) return (deliver_indirect(state)); /* * Delivery to local user. First try expansion of the recipient's * $HOME/.forward file, then mailbox delivery. Back off when the user's * home directory does not exist. */ if (var_stat_home_dir && (mypwd = mypwnam(state.msg_attr.user)) != 0 && stat_as(mypwd->pw_dir, &st, mypwd->pw_uid, mypwd->pw_gid) < 0) { dsb_simple(state.msg_attr.why, "4.3.0", "cannot access home directory %s: %m", mypwd->pw_dir); return (defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr))); } if (deliver_dotforward(state, usr_attr, &status) == 0 && deliver_mailbox(state, usr_attr, &status) == 0) status = deliver_unknown(state, usr_attr); return (status); }
int deliver_resolve_tree(LOCAL_STATE state, USER_ATTR usr_attr, TOK822 *addr) { const char *myname = "deliver_resolve_tree"; RESOLVE_REPLY reply; int status; ssize_t ext_len; char *ratsign; int rcpt_delim; /* * Make verbose logging easier to understand. */ state.level++; if (msg_verbose) MSG_LOG_STATE(myname, state); /* * Initialize. */ resolve_clnt_init(&reply); /* * Rewrite the address to canonical form, just like the cleanup service * does. Then, resolve the address to (transport, nexhop, recipient), * just like the queue manager does. The only part missing here is the * virtual address substitution. Message forwarding fixes most of that. */ tok822_rewrite(addr, REWRITE_CANON); tok822_resolve(addr, &reply); /* * First, a healthy portion of error handling. */ if (reply.flags & RESOLVE_FLAG_FAIL) { dsb_simple(state.msg_attr.why, "4.3.0", "address resolver failure"); status = defer_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); } else if (reply.flags & RESOLVE_FLAG_ERROR) { dsb_simple(state.msg_attr.why, "5.1.3", "bad recipient address syntax: %s", STR(reply.recipient)); status = bounce_append(BOUNCE_FLAGS(state.request), BOUNCE_ATTR(state.msg_attr)); } else { /* * Splice in the optional unmatched address extension. */ if (state.msg_attr.unmatched) { rcpt_delim = state.msg_attr.local[strlen(state.msg_attr.user)]; if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) { VSTRING_ADDCH(reply.recipient, rcpt_delim); vstring_strcat(reply.recipient, state.msg_attr.unmatched); } else { ext_len = strlen(state.msg_attr.unmatched); VSTRING_SPACE(reply.recipient, ext_len + 2); if ((ratsign = strrchr(STR(reply.recipient), '@')) == 0) msg_panic("%s: recipient @ botch", myname); memmove(ratsign + ext_len + 1, ratsign, strlen(ratsign) + 1); *ratsign = rcpt_delim; memcpy(ratsign + 1, state.msg_attr.unmatched, ext_len); VSTRING_SKIP(reply.recipient); } } state.msg_attr.rcpt.address = STR(reply.recipient); /* * Delivery to a local or non-local address. For a while there was * some ugly code to force local recursive alias expansions on a host * with no authority over the local domain, but that code was just * too unclean. */ if (strcmp(state.msg_attr.relay, STR(reply.transport)) == 0) { status = deliver_recipient(state, usr_attr); } else { status = deliver_indirect(state); } } /* * Cleanup. */ resolve_clnt_free(&reply); return (status); }