/* #ident "@(#)smail/src:RELEASE-3_2_0_121:direct.c,v 1.56 2005/08/26 19:59:40 woods Exp" */ /* * Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll * Copyright (C) 1992 Ronald S. Karr * * See the file COPYING, distributed with smail, for restriction * and warranty information. */ /* * direct.c: * resolve local form addresses to more addresses or to transports * * external functions: direct_local_addrs, verify_local_addrs, * cache_directors, finish_directors, * director_user_info, find_director, * find_direct_driver, read_director_file. * dump_director_config */ #include "defs.h" #include #include #include #include #include #include #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H) # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef __STDC__ # include #else # include #endif #if defined(HAVE_UNISTD_H) # include #endif #include "smail.h" #include "config.h" #include "main.h" #include "parse.h" #include "addr.h" #include "direct.h" #include "directors/user.h" #include "log.h" #include "alloc.h" #include "list.h" #include "smailstring.h" #include "dys.h" #include "exitcodes.h" #include "transport.h" #include "smailconf.h" #include "debug.h" #include "extern.h" #include "error.h" #include "smailport.h" /* enums local to this file */ enum local_form { FILE_FORM, /* filename address */ PIPE_FORM, /* shell-command address */ INCLUDE_FORM, /* mailing list address */ OTHER_FORM /* any other address form */ }; enum other_form_op { SEND_TO_NEXT, /* send addr to next director */ DROP_ADDRESS, /* ignore addr */ ADDRESS_OKAY /* reparse address */ }; /* variables exported from this file */ int cached_directors = FALSE; /* TRUE if cache_directors() called */ /* functions local to this file */ static void implicit_redirect __P((struct addr*, struct addr **, char *)); static enum other_form_op finish_direct_other_form __P((struct director *, struct addr *)); static void finish_direct_special_form __P((struct addr *, struct director *, enum local_form, struct addr **, struct addr **, struct addr **, struct addr **, struct addr **)); static void cache_director_data __P((struct director *)); static void premunge_local_addrs __P((struct addr *)); static enum local_form addr_type __P((char *)); static char *director_driv_function __P((char *, struct attribute *)); /* Configuration template information */ static struct attr_table director_generic[] = { { "driver", t_string, 0, NULL, NULL, OFFSET(director, driver) }, { "caution", t_boolean, 0, NULL, NULL, CAUTION_DIRECTOR }, { "nobody", t_boolean, 0, NULL, NULL, NOBODY_DIRECTOR }, { "sender_okay", t_boolean, 0, NULL, NULL, SENDER_OKAY }, { "ignore_alias_match", t_boolean, 0, NULL, NULL, IGNORE_ALIAS_MATCH }, { "owner", t_string, 0, NULL, NULL, OFFSET(director, owner) }, { "default_user", t_string, 0, NULL, NULL, OFFSET(director, default_user) }, { "default_group", t_string, 0, NULL, NULL, OFFSET(director, default_group) }, { "default_home", t_string, 0, NULL, NULL, OFFSET(director, default_home) }, { "domains", t_string, 0, NULL, NULL, OFFSET(director, domains) }, { "set_user", t_string, 0, NULL, NULL, OFFSET(director, set_user) }, { "set_group", t_string, 0, NULL, NULL, OFFSET(director, set_group) }, { "set_home", t_string, 0, NULL, NULL, OFFSET(director, set_home) }, }; /* * direct_local_addrs - produce finished and expanded addrs from local addrs * * this is called from resolve_addr_list to run local-form addresses * through the directors to completely resolve addr structures and * to transform them into a new list of addr structures to resolve. */ void direct_local_addrs(in, out, retry, defer, fail) struct addr *in; /* input local-form addrs */ struct addr **out; /* output resolved addrs */ struct addr **retry; /* addr structures to reparse */ struct addr **defer; /* addrs to defer to a later time */ struct addr **fail; /* unresolvable addrs */ { struct director *dp; /* temp for stepping thru directors */ struct addr *cur; /* temp for stepping through addrs */ struct addr *newaddrs; /* new addrs from a director */ struct addr *includes; /* mailing list addrs */ struct addr *next; /* next value for cur */ struct addr *other_domains; /* for domain-specific handling */ DEBUG(DBG_DIRECT_HI, "direct_local_addrs called\n"); /* do any munging of the addr structures required before directing */ premunge_local_addrs(in); /* * call each director in turn, where each director * produces an output list a list of addrs it could * not resolve, and a list of addrs it resolved to * a list. The actual return value is the list it * could not resolve. */ *retry = NULL; includes = NULL; while (in) { /* * directors are organized as subsections with the subsection * data being the director structures. */ for (dp = directors; in && dp; dp = dp->succ) { /* look up the director driver by name */ struct direct_driver *driver = find_direct_driver(dp->driver); if (driver == NULL) { /* * ERR_107 - director driver not found * * DESCRIPTION * A director driver was not found. * * ACTIONS * Defer all remaining input addresses with * configuration errors. Since it is not known if this * director would have matched any addresses, we cannot * pass the addresses on to the next director. Thus, * we must defer all addresses not matched by a * previous director. * * RESOLUTION * The postmaster must check the director and transport * configurations before delivery can be performed. */ insert_addr_list(in, defer, note_error(ERR_CONFERR|ERR_107, xprintf( "director %v: driver %s driver not found", dp->name, dp->driver))); exitvalue = EX_DATAERR; return; } /* initialize list of new addrs found in this pass */ newaddrs = NULL; /* If a domain list exists, remove from the input list all those * addresses that are not for the domains handled by this * director. Afterwards, put them back on the input list. */ other_domains = NULL; if (dp->domains) { struct addr *temp = in; struct addr **prev = ∈ while (temp != NULL) { if (!temp->local_name || !is_string_in_list(temp->local_name, dp->domains)) { *prev = temp->succ; temp->succ = other_domains; other_domains = temp; temp = *prev; } else { prev = &(temp->succ); temp = temp->succ; } } } /* call the driver if any input addresses are left to direct */ if (in != NULL) { in = (*driver->driver)(dp, in, out, &newaddrs, defer, fail); } /* put back any that were held back because they did not match the * director's domain list */ if (other_domains != NULL) { if (in == NULL) { in = other_domains; } else { insert_addr_list(other_domains, &in, (struct error *) NULL); } } /* step through newaddrs to verify them (newaddrs will be left as * NULL if the input address(es) had ADDR_VRFY_ONLY set) */ for (cur = newaddrs; cur; cur = next) { enum local_form form = addr_type(cur->work_addr); next = cur->succ; /* note which addr to examine next */ /* * read in cache data in the director structure, if this * has not yet been done. */ if (! (dp->flags & CACHED_DIRECTOR) ) { cache_director_data(dp); } /* setup the uid, gid and home directories for the addr */ if (dp->default_user) { if (cur->uid == BOGUS_USER) { cur->uid = dp->default_uid; } if (cur->gid == BOGUS_GROUP) { cur->gid = dp->default_gid; } } if (dp->default_home && cur->home == NULL) { cur->home = dp->x_default_home; } if (dp->set_user) { cur->uid = dp->set_uid; cur->gid = dp->set_gid; } if (dp->set_home) { cur->home = dp->x_set_home; } if (dp->flags & CAUTION_DIRECTOR) { /* be cautious on all addrs from this director */ cur->flags |= ADDR_CAUTION; } if (form == OTHER_FORM) { switch (finish_direct_other_form(dp, cur)) { case SEND_TO_NEXT: /* matched the parent addr, send to next director */ cur->succ = in; in = cur; break; case DROP_ADDRESS: /* matched the sender, drop the address */ break; case ADDRESS_OKAY: /* address is okay, put back on the input */ cur->succ = *retry; *retry = cur; break; } } else { finish_direct_special_form(cur, dp, form, retry, out, defer, fail, &includes); } } } /* * log addrs which were not directed anywhere */ for (; in; in = next) { next = in->succ; /* ... unless they're super-special... */ if (EQIC(in->remainder, "mailer-daemon")) { implicit_redirect(in, retry, postmaster_address); } else if (EQIC(in->remainder, "postmaster")) { implicit_redirect(in, retry, postmaster_address); } else { /* no director found the mailbox, fail the address */ exitvalue = EX_NOUSER; /* also set exit status, just in case */ /* * ERR_100 - unknown user * * DESCRIPTION * The remainder string in this addr structure was not * matched by any of the directors. * * ACTIONS * Send a message to the owner of the address, or to * the sender if there is no owner. * * RESOLUTION * A list owner should check through the list that he * or she owns. A sender may wish to send mail to the * postmaster of the machine asking about login names * or aliases for a particular user. */ in->error = note_error(ERR_NSOWNER|ERR_100, "no such user or mailbox"); in->succ = *fail; *fail = in; } } /* * copy any mailing list addrs back to the input * so they can be run back through the directors. */ in = includes; includes = NULL; } } /* * cache_directors - call cache entrypoints for all directors * * cache information used by director drivers. This can be called * when it is determined that there will be an attempt to deliver more * than one mail message, to increase the overall efficiency of the * mailer. * * Daemons can call this periodically to recache stale data. */ void cache_directors() { struct director *dp; /* temp for stepping thru directors */ struct direct_driver *driver; for (dp = directors; dp; dp = dp->succ) { driver = find_direct_driver(dp->driver); if (driver && driver->cache) { (*driver->cache)(dp); } } cached_directors = TRUE; } #ifdef notyet /* * finish_directors - free resources used by all directors * * free information that was cached by directors or used by directors * in the process of resolving local addresses. Directors can cache * data for efficiency, or can maintain state between invocations. * This function is called when directors will no longer be needed, * allowing directors to free any resources that they were using that * will no longer be needed. For example, it is a good idea for * directors to close any files that they opened, as file descriptors * are a precious resource in some machines. */ void finish_directors() { struct directors *dp; /* temp for stepping thru directors */ struct direct_driver *driver; for (dp = directors; dp; dp = dp->succ) { driver = find_direct_driver(dp->driver); if (driver && driver->finish) { (*driver->finish)(dp); } } cached_directors = FALSE; } #endif /* * implicit_redirect - an implicit director to redirect to the given address */ static void implicit_redirect(match, retry, in_addr) struct addr *match; /* addr structure to direct */ struct addr **retry; /* attach new addr to this list */ char *in_addr; /* the text for the new address */ { struct addr *new = alloc_addr(); new->in_addr = COPY_STRING(in_addr); new->work_addr = COPY_STRING(in_addr); new->succ = *retry; *retry = new; DEBUG3(DBG_DIRECT_LO, "implicity matched special mailbox <%v>\n redirected <%v> --> <%v>\n", match->in_addr, match->in_addr, in_addr); } /* * finish_direct_other_form - finish directing a simple addr * * return SEND_TO_NEXT if this addr matches the parent addr and should * thus be sent to the next director rather than being put on the * list of new addrs. * return DROP_ADDRESS if this addr is in local form and matches the * sender and should thus be ignored. This value is not returned if * the `sender_okay' attribute is set for the director, or if the * me_too flag was set in the invocation arguments. * return ADDRESS_OKAY if the addr should be put on the list of * addresses to be reparsed. * * XXX - this may change the addr structure as a side effect, so watch out. */ static enum other_form_op finish_direct_other_form(dp, addr) struct director *dp; /* director which returned addr */ struct addr *addr; /* addr struct from director */ { int parseflags; int escaped = 0; char *temp_target = NULL; char *temp_remainder; char *temp_work_addr; int form; if (!addr->parent) { panic(EX_SOFTWARE, "finish_direct_other_form(): director %v: no parent address for <%v>.", dp->name, addr->in_addr); /* NOTREACHED */ } /* not an address form we need to be worried about */ if (EQIC(addr->work_addr, addr->parent->remainder) || (addr->work_addr[0] == '\\' && EQIC(addr->work_addr + 1, addr->parent->remainder))) { /* for the form \parent-addr drop the \ */ if (addr->work_addr[0] == '\\') { escaped = 1; (void) strcpy(addr->work_addr, addr->work_addr + 1); } /* * new addr same as input addr, send to * the next director. E.g., foo aliased * to foo. However, if the address was not escaped, * and the ignore_alias_match flags is set on the * director, just drop the address. */ if (! escaped && (dp->flags & IGNORE_ALIAS_MATCH)) { DEBUG3(DBG_DIRECT_LO, " directed <%v> --> <%v> using %v ... (matched parent addr, not escaped, but ignore_alias_match set), passing to next director\n", addr->parent->remainder, addr->work_addr, dp->name); return DROP_ADDRESS; } DEBUG4(DBG_DIRECT_LO, " directed <%v> --> %s<%v> using %v ... matched parent addr, pass to next director\n", addr->parent->remainder, escaped ? "\\" : "", addr->work_addr, dp->name); addr->remainder = addr->work_addr; return SEND_TO_NEXT; } /* * XXX - reparse the address looking for references * to the the localhost with a remainder equal * to the parent address. This allows the use * of username@localhost addresses in alias * files, making it possible to share an alias * file for redirecting users around a network. * * This moves intelligence out of resolve.c that * really belongs there. */ temp_work_addr = COPY_STRING(addr->work_addr); parseflags = addr->parseflags; form = parse_address(temp_work_addr, &temp_target, &temp_remainder, &parseflags); switch (form) { case FAIL: xfree(temp_work_addr); break; case LOCAL: /* * if the me_too flag is not set, then filter the sender * from the output of directors. A director can * explicitly allow sender addresses to be produced by * including the `sender_okay' attribute. When expanding * an address list for verification purposes, the sender * may not be specified. Watch out for this case. */ xfree(temp_work_addr); if (! (dp->flags & SENDER_OKAY) && ! me_too && sender && EQIC(sender, addr->work_addr)) { DEBUG3(DBG_DIRECT_LO, " directed <%v> --> <%v> using %v ... matched sender, ignored\n", addr->parent->remainder, addr->work_addr, dp->name); return DROP_ADDRESS; } break; default: if (islocalhost(temp_target) && EQIC(temp_remainder, addr->parent->remainder) && ((addr->parent->local_name && EQIC(temp_target, addr->parent->local_name)) || (addr->parent->target && EQIC(temp_target, addr->parent->target)))) { /* * new addr same as input addr, send to * the next director. E.g., foo aliased * to foo. */ DEBUG5(DBG_DIRECT_LO, " directed <%v@%v> --> <%v> using %v ... matched localhost, passing %v to next director\n", addr->parent->remainder, addr->parent->target ? addr->parent->target : "(no-parent-target)", addr->work_addr, dp->name, temp_remainder); (void) strcpy(addr->work_addr, temp_remainder); /* XXXXXX does this clobber???? */ addr->remainder = addr->work_addr; xfree(temp_work_addr); return SEND_TO_NEXT; } /* Turning on ADDR_DONTHASH prevents accidental matching of remainder * for virtual hosts or for cases where an alias/etc. re-directs to a * localhost. */ addr->flags |= ADDR_DONTHASH; DEBUG7(DBG_DIRECT_HI, " NOT passing %v@%v (%v) to next director as <%v>%s%s%s\n", addr->parent->remainder, addr->parent->target ? addr->parent->target : "(no-parent-target)", addr->parent->local_name ? addr->parent->local_name : "(no-parent-local_name)", addr->work_addr, islocalhost(temp_target) ? "" : ", not a localhost", EQIC(temp_remainder, addr->parent->remainder) ? "" : ", not same remainder", (addr->parent->local_name && EQIC(temp_target, addr->parent->local_name)) || (addr->parent->target && EQIC(temp_target, addr->parent->target)) ? "" : ", not same target"); } /* we have a new address to reparse */ #ifndef NDEBUG if (debug) { char *in_addr = COPY_STRING(addr->in_addr); /* this is a cheap hack, but it's better than nothing! */ if ((strncmpic(in_addr, ":defer:", sizeof(":defer:")-1) == 0) || (strncmpic(in_addr, ":error:", sizeof(":error:")-1) == 0) || (strncmpic(in_addr, ":fail:", sizeof(":fail:")-1) == 0)) { *(strchr(in_addr + 1, ':') + 1) = '\0'; } DEBUG4(DBG_DIRECT_LO, " directed <%v@%v> --> <%v> using %v\n", addr->parent->remainder, addr->parent->target ? addr->parent->target : "(localhost)", in_addr, dp->name); xfree(in_addr); } #endif return ADDRESS_OKAY; } /* * finish_direct_special_form - direct a file, pipe or mailing list form addr */ /*ARGSUSED*/ static void finish_direct_special_form(addr, dp, form, new, out, defer, fail, includes) struct addr *addr; /* addr structure to finish */ struct director *dp; /* matching director */ enum local_form form; /* local address form */ struct addr **new __attribute__((unused)); /* put new addrs to retry here */ struct addr **out; /* resolved addrs */ struct addr **defer; /* put config errors here */ struct addr **fail; /* put failed addrs here */ struct addr **includes; /* include file forms */ { /* save a pointer to the pipe transport */ static struct transport *pipe_transport = NULL; /* save a pointer to the file transport */ static struct transport *file_transport = NULL; /* * not a regular address, should we keep it or drop it? * * NOTE: unsecure addresses are less secure than addresses which * are not secure. Addresses which are not secure may have * accesses checked under the nobody uid/gid. Unsecure * pipes, files and mailing lists are are always dropped. */ /* only hash normal-form addresses */ addr->flags |= ADDR_DONTHASH; if ((addr->flags & ADDR_UNSECURE) || ((addr->flags & ADDR_CAUTION) && ! (dp->flags & NOBODY_DIRECTOR))) { /* * ERR_104 - security violation * * DESCRIPTION * A questionable address was supplied from a director * that is not allowed to supply a file command or * mailing list. * * ACTIONS * mail is sent to the address owner or to the * postmaster. * * RESOLUTION * The owner or postmaster should check the source source * of addresses against any file ownership restrictions * named in the director. */ addr->error = note_error(ERR_NPOWNER|ERR_104, xprintf("director %v: security violation", dp->name)); addr->succ = *fail; *fail = addr; return; } if (addr->flags & ADDR_CAUTION) { /* be cautious of addresses, but nobody is set */ if (operation_mode != VERIFY_ADDRS && operation_mode != TEST_MODE) { write_log(WRITE_LOG_SYS, "%v ... director %v: child of %v insecure, access as 'nobody'", addr->in_addr, dp->name, addr->parent->in_addr); } addr->uid = nobody_uid; addr->gid = nobody_gid; } /* passed security checks, now what do we do? */ switch (form) { case PIPE_FORM: /* * pipe form, associate with the "pipe" transport */ if (pipe_transport == NULL) { pipe_transport = find_transport(PIPE_TRANSPORT_NM); if (pipe_transport == NULL) { char *errtxt; /* configuration error, try again later */ /* * ERR_105 - no pipe transport * * DESCRIPTION * There is no "pipe" transport in the * table of transports, and a shell command * address returned by a director. * * ACTIONS * Defer message as a configuration error. * * RESOLUTION * The postmaster should add a "pipe" * transport to the transport file. */ errtxt = xprintf("no `pipe' transport with the name `%s'", PIPE_TRANSPORT_NM); addr->error = note_error(ERR_CONFERR | ERR_105, errtxt); addr->succ = *defer; *defer = addr; break; } } addr->transport = pipe_transport; /* setup command as $user variable for transports */ addr->next_addr = COPY_STRING(addr->work_addr + 1); DEBUG4(DBG_DIRECT_LO, " directed <%v> --> <%v> using %v ... send to the %s transport\n", addr->parent->remainder, addr->in_addr, dp->name, PIPE_TRANSPORT_NM); /* addr finished link into the output queue */ addr->succ = *out; *out = addr; break; case FILE_FORM: /* * file form, associate with the "file" transport */ if (file_transport == NULL) { file_transport = find_transport(FILE_TRANSPORT_NM); if (file_transport == NULL) { char *errtxt; /* configuration error, must be fixed */ /* * ERR_106 - no file transport * * DESCRIPTION * There is no "file" transport in the * table of transports, and a file form * address was returned by a director. * * ACTIONS * Defer message as a configuration error. * * RESOLUTION * The postmaster should add a "file" * transport to the transport file. */ errtxt = xprintf("no `appendfile' transport with the name `%s'", FILE_TRANSPORT_NM); addr->error = note_error(ERR_CONFERR | ERR_106, errtxt); addr->succ = *defer; *defer = addr; break; } } addr->transport = file_transport; /* setup file as $user variable for transports */ addr->next_addr = COPY_STRING(addr->work_addr); DEBUG4(DBG_DIRECT_LO, " directed <%v> --> <%v> using %v ... send to %s transport\n", addr->parent->remainder, addr->in_addr, dp->name, FILE_TRANSPORT_NM); /* addr finished link into the output queue */ addr->succ = *out; *out = addr; break; case INCLUDE_FORM: /* mailing lists go back through all the directors */ DEBUG3(DBG_DIRECT_LO, " directed <%v> --> <%v> using %v ... mailing list\n", addr->parent->remainder, addr->in_addr, dp->name); /* set local part to the mailing list address */ addr->remainder = addr->work_addr; addr->succ = *includes; *includes = addr; break; case OTHER_FORM: panic(EX_SOFTWARE, "director %v, impossible situation: reached OTHER_FORM", dp->name); /* NOTREACHED */ break; } } /* * cache_director_data - cache in some generic director information * * perform once on each director to perform some lookup and expansion * operations that would otherwise have to be performed each time these * values are used. */ static void cache_director_data(dp) register struct director *dp; { if (dp->default_user) { struct passwd *pw = getpwbyname(FALSE, dp->default_user); if (pw) { dp->default_uid = pw->pw_uid; dp->default_gid = pw->pw_gid; } else { dp->default_user = NULL; } } if (dp->default_group) { struct group *gr = getgrbyname(dp->default_group); if (gr) { dp->default_gid = gr->gr_gid; } else { dp->default_group = NULL; } } if (dp->default_home) { char *x_home = expand_string(dp->default_home, (struct addr *)NULL, (char *)NULL, (char *)NULL); if (x_home && !EQ(x_home, dp->default_home)) { dp->x_default_home = COPY_STRING(x_home); } else { dp->x_default_home = dp->default_home; } } if (dp->set_user) { struct passwd *pw = getpwbyname(FALSE, dp->set_user); if (pw) { dp->set_uid = pw->pw_uid; dp->set_gid = pw->pw_gid; } else { dp->set_user = NULL; } } if (dp->set_group) { struct group *gr = getgrbyname(dp->set_group); if (gr) { dp->set_gid = gr->gr_gid; } else { dp->set_group = NULL; } } if (dp->set_home) { char *x_home = expand_string(dp->set_home, (struct addr *)NULL, (char *)NULL, (char *)NULL); if (x_home && !EQ(x_home, dp->set_home)) { dp->x_set_home = COPY_STRING(x_home); } else { dp->x_set_home = dp->set_home; } } } /* * premunge_local_addrs - pre-routing munging on local addr structures * * Remove any extraneously set flags. */ static void premunge_local_addrs(list) struct addr *list; /* list of remote addrs to premunge */ { register struct addr *cur; /* current address being processed */ for (cur = list; cur; cur = cur->succ) { cur->flags &= ~(ADDR_PUTDOT | ADDR_MOVEDOT | ADDR_ERROR | ADDR_FINISHED | ADDR_FULLMATCH | ADDR_NOTUSER | ADDR_ISUSER); } } /* * addr_type - return the form of a local address string * * Determine if the address is a pipe, file, mailing list, * or other. */ static enum local_form addr_type(s) register char *s; { /* * file is one of: * "/string" * "\/string" * "~string" * "\~string" * /string * \/string * ~string * \~string */ if (s[0] == '"' && ((s[1] == '/' || s[1] == '~') || (s[1] == '\\' && (s[2] == '/' || s[2] == '~')))) { /* file form, but only if the address is in local form */ register char *p = address_token(s); if (p && *p == '\0') { /* address is in local form, strip work_addr inline */ (void) strip(s); return FILE_FORM; } } if (s[0] == '/' || s[0] == '~' || (s[0] == '\\' && (s[1] == '/' || s[1] == '~'))) { /* filename not quoted, don't check for local form in this case */ (void) strip(s); return FILE_FORM; } /* * pipe is one of: * "|shell-command" * "\|shell-command" * |shell-command * \|shell-command */ if (s[0] == '"' && (s[1] == '|' || (s[1] == '\\' && s[2] == '|'))) { /* pipe form, but only if the address is in local form */ char *p = address_token(s); if (p && *p == '\0') { /* address is in local form, strip work_addr inline */ (void) strip(s); return PIPE_FORM; } } if (s[0] == '|' || (s[0] == '\\' && s[1] == '|')) { /* shell command not quoted, don't check for local form in this case */ (void) strip(s); return PIPE_FORM; } /* * mailing list is one of: * :include:anything * ":include:anything" */ if (s[0] == '"' && s[1] == ':' && strncmpic(s + 2, "include:", sizeof("include:") - 1) == 0) { /* mailing list form, but only if the address is in local form */ char *p = address_token(s); if (p && *p == '\0') { /* address is in local form, strip work_addr inline */ (void) strip(s); return INCLUDE_FORM; } } if (s[0] == ':' && strncmpic(s+1, "include:", sizeof("include:")-1) == 0) { /* shell command not quoted, don't check for local form in this case */ return INCLUDE_FORM; } return OTHER_FORM; } /* * director_user_info - fill an addr structure with user information * * XXX This function should probably be in directors/user.c, though it is also * used in directors/fwdfile.c * * if the remainder field if the addr structure matches a username, * fill in fields which pertain to the user's passwd file entry. */ void director_user_info(addr) struct addr *addr; /* addr structure to check */ { struct passwd *pw; /* passwd file entry for user */ if (addr->flags & (ADDR_NOTUSER | ADDR_ISUSER)) { /* a previous call to director_user_info() already took care of this */ return; } /* get the passwd entry if one exists */ pw = getpwbyname((int) ignore_user_case, addr->remainder); if (pw) { /* passwd entry found */ addr->flags |= ADDR_ISUSER; addr->uid = pw->pw_uid; addr->gid = pw->pw_gid; addr->home = COPY_STRING(pw->pw_dir); if (ignore_user_case) { strcpy(addr->remainder, pw->pw_name); /* preserve case */ } return; } /* no passwd entry found */ addr->flags |= ADDR_NOTUSER; return; } /* * find_director - given a director's name, return the director structure * * return NULL if no director of that name exists. */ struct director * find_director(name) register char *name; /* search key */ { register struct director *dp; /* temp for stepping thru directors */ /* loop through all the directors */ for (dp = directors; dp; dp = dp->succ) { if (EQ(dp->name, name)) { /* found the director in question */ return dp; } } return NULL; /* director not found */ } /* * find_direct_driver - given a driver's name, return the driver structure * * return NULL if driver does not exist. */ struct direct_driver * find_direct_driver(name) register char *name; /* search key */ { register struct direct_driver *ddp; /* pointer to table of drivers */ for (ddp = direct_drivers; ddp->name; ddp++) { if (EQ(ddp->name, name)) { return ddp; /* found the driver */ } } return NULL; /* driver not found */ } /* * read_director_file - read director file * * read the director file and build a director list describing the * entries. Return an error message or NULL. */ char * read_director_file() { FILE *f; /* open director file */ char *error; /* error from read_standard_file() */ struct stat statbuf; /* FIXME: avoid this ugly stuff -- use null-entry termination semantics... */ struct attr_table *end_director_generic = ENDTABLE(director_generic); static struct director director_template = { NULL, /* name */ "pathalias", /* driver, a reasonable default */ NULL, /* succ will be assigned */ NOBODY_DIRECTOR, /* flags */ NULL, /* owner */ NULL, /* default_user */ NULL, /* default_group */ NULL, /* default_home */ NULL, /* domains */ NULL, /* set_user */ NULL, /* set_group */ NULL, /* set_home */ NULL, /* private */ 0, /* cache - default_uid */ 0, /* cache - default_gid */ NULL, /* cache - x_default_home */ 0, /* cache - set_uid */ 0, /* cache - set_gid */ NULL, /* cache - x_set_home */ }; /* * try to open directory file, stat file if possible */ if (director_file == NULL || EQ(director_file, "-")) { return NULL; } f = fopen(director_file, "r"); if (f == NULL) { if (require_configs) { return xprintf("%v: %s", director_file, strerror(errno)); } add_config_stat(director_file, (struct stat *)NULL); return NULL; } (void)fstat(fileno(f), &statbuf); add_config_stat(director_file, &statbuf); /* call read_standard_file to do the real work */ error = read_standard_file(f, director_file, (char *) &director_template, sizeof(struct director), OFFSET(director, name), OFFSET(director, flags), OFFSET(director, succ), director_generic, end_director_generic, director_driv_function, (char **) &directors); /* finish up */ (void) fclose(f); /* return any error message */ if (error) { return xprintf("%v: %s", director_file, error); } return NULL; } static char * director_driv_function(struct_p, driver_attrs) char *struct_p; /* passed director structure */ struct attribute *driver_attrs; /* driver-specific attributes */ { struct director *dp = (struct director *)struct_p; struct direct_driver *drv; if (dp->driver == NULL) { return xprintf("director %v: no driver attribute", dp->name); } drv = find_direct_driver(dp->driver); if (drv == NULL) { return xprintf("director %v: unknown driver: %s", dp->name, dp->driver); } if (drv->builder) { return (*drv->builder)(dp, driver_attrs); } return NULL; } void dump_director_config(f) FILE * f; { struct director *dp; /* temp for stepping thru directors */ /* FIXME: avoid this ugly stuff -- use null-entry termination semantics... */ struct attr_table *end_director_generic = ENDTABLE(director_generic); char * name; /* name of director */ unsigned long flags; /* flags for director */ struct direct_driver *driver; /* drivers for director */ DEBUG(DBG_ROUTE_HI, "dump director configs called\n"); fputs("#\n# -- directors configuration\n", f); /* * work through each director in turn. */ for (dp = directors; dp; dp = dp->succ) { name = dp->name; flags = dp->flags; fprintf(f, "#\n%s:\n", name); /* not %v since reading back can't decode */ (void) dump_standard_config(f, (char *) dp, name, flags, director_generic, end_director_generic); fputs("\t;\n", f); driver = find_direct_driver(dp->driver); if (driver) { if (driver->dumper) { (*driver->dumper)(f, dp); } else { fprintf(f, "\t#\n\t# -- the %s director driver has no 'dumper' function!\n\t#\n", dp->driver); } } else { fprintf(f, "\t#\n\t# -- there is no %s director driver!\n\t#\n", dp->driver); } } fputs("#\n# -- end of directors\n", f); } /* * Local Variables: * c-file-style: "smail" * End: */