int main(int argc, char **argv) { struct mypasswd **mypwd; int i; msg_vstream_init(argv[0], VSTREAM_ERR); if (argc == 1) msg_fatal("usage: %s name or uid ...", argv[0]); mypwd = (struct mypasswd **) mymalloc((argc + 2) * sizeof(*mypwd)); /* * Do a sequence of lookups. */ for (i = 1; i < argc; i++) { if (ISDIGIT(argv[i][0])) mypwd[i] = mypwuid(atoi(argv[i])); else mypwd[i] = mypwnam(argv[i]); if (mypwd[i] == 0) msg_fatal("%s: not found", argv[i]); msg_info("lookup %s %s/%d refcount=%d name_cache=%d uid_cache=%d", argv[i], mypwd[i]->pw_name, mypwd[i]->pw_uid, mypwd[i]->refcount, mypwcache_name->used, mypwcache_uid->used); } mypwd[argc] = last_pwd; /* * The following should free all entries. */ for (i = 1; i < argc + 1; i++) { msg_info("free %s/%d refcount=%d name_cache=%d uid_cache=%d", mypwd[i]->pw_name, mypwd[i]->pw_uid, mypwd[i]->refcount, mypwcache_name->used, mypwcache_uid->used); mypwfree(mypwd[i]); } msg_info("name_cache=%d uid_cache=%d", mypwcache_name->used, mypwcache_uid->used); myfree((char *) mypwd); return (0); }
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_mailbox(LOCAL_STATE state, USER_ATTR usr_attr, int *statusp) { const char *myname = "deliver_mailbox"; int status; struct mypasswd *mbox_pwd; char *path; static MAPS *transp_maps; const char *map_transport; static MAPS *cmd_maps; const char *map_command; /* * Make verbose logging easier to understand. */ state.level++; if (msg_verbose) MSG_LOG_STATE(myname, state); /* * DUPLICATE ELIMINATION * * Don't come here more than once, whether or not the recipient exists. */ if (been_here(state.dup_filter, "mailbox %s", state.msg_attr.local)) return (YES); /* * Delegate mailbox delivery to another message transport. */ if (*var_mbox_transp_maps && transp_maps == 0) transp_maps = maps_create(VAR_MBOX_TRANSP_MAPS, var_mbox_transp_maps, DICT_FLAG_LOCK | DICT_FLAG_NO_REGSUB); /* The -1 is a hint for the down-stream deliver_completed() function. */ if (*var_mbox_transp_maps && (map_transport = maps_find(transp_maps, state.msg_attr.user, DICT_FLAG_NONE)) != 0) { state.msg_attr.rcpt.offset = -1L; *statusp = deliver_pass(MAIL_CLASS_PRIVATE, map_transport, state.request, &state.msg_attr.rcpt); return (YES); } if (*var_mailbox_transport) { state.msg_attr.rcpt.offset = -1L; *statusp = deliver_pass(MAIL_CLASS_PRIVATE, var_mailbox_transport, state.request, &state.msg_attr.rcpt); return (YES); } /* * Skip delivery when this recipient does not exist. */ if ((mbox_pwd = mypwnam(state.msg_attr.user)) == 0) return (NO); /* * No early returns or we have a memory leak. */ /* * DELIVERY RIGHTS * * Use the rights of the recipient user. */ SET_USER_ATTR(usr_attr, mbox_pwd, state.level); /* * Deliver to mailbox, maildir or to external command. */ #define LAST_CHAR(s) (s[strlen(s) - 1]) if (*var_mailbox_cmd_maps && cmd_maps == 0) cmd_maps = maps_create(VAR_MAILBOX_CMD_MAPS, var_mailbox_cmd_maps, DICT_FLAG_LOCK | DICT_FLAG_PARANOID); if (*var_mailbox_cmd_maps && (map_command = maps_find(cmd_maps, state.msg_attr.user, DICT_FLAG_NONE)) != 0) { status = deliver_command(state, usr_attr, map_command); } else if (*var_mailbox_command) { status = deliver_command(state, usr_attr, var_mailbox_command); } else if (*var_home_mailbox && LAST_CHAR(var_home_mailbox) == '/') { path = concatenate(usr_attr.home, "/", var_home_mailbox, (char *) 0); status = deliver_maildir(state, usr_attr, path); myfree(path); } else if (*var_mail_spool_dir && LAST_CHAR(var_mail_spool_dir) == '/') { path = concatenate(var_mail_spool_dir, state.msg_attr.user, "/", (char *) 0); status = deliver_maildir(state, usr_attr, path); myfree(path); } else status = deliver_mailbox_file(state, usr_attr); /* * Cleanup. */ mypwfree(mbox_pwd); *statusp = status; return (YES); }