rsalz@uunet.uu.net (Rich Salz) (10/16/89)
Submitted-by: Chip Salzenberg <chip@ateng.com> Posting-number: Volume 20, Issue 25 Archive-name: deliver2.0/part03 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of shell archive." # Contents: debug.c dest.c dfile.c lock.c main.c # Wrapped by network@ateng on Fri Jun 9 13:21:01 1989 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'debug.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'debug.c'\" else echo shar: Extracting \"'debug.c'\" \(1063 characters\) sed "s/^X//" >'debug.c' <<'END_OF_FILE' X/* $Header: debug.c,v 2.1 89/06/09 12:25:17 network Exp $ X * X * Debugging output. X * X * $Log: debug.c,v $ X * Revision 2.1 89/06/09 12:25:17 network X * Update RCS revisions. X * X * Revision 1.4 89/06/09 12:23:42 network X * Baseline for 2.0 release. X * X */ X X#include "deliver.h" X X/*---------------------------------------------------------------------- X * Print out a complete dump of all destinations X */ X Xdumpdests(when) Xchar *when; X{ X DEST *d; X X message("Destinations %s:\n", when); X for (d = first_dest(); d; d = next_dest(d)) X { X message("\t%s", d->d_name); X X switch (d->d_class) X { X case CL_USER: X /* it's understood */ X break; X case CL_MBOX: X message(", mailbox='%s'", d->d_mailbox); X break; X case CL_UUCP: X message(" (UUCP)"); X break; X } X message("; "); X switch (d->d_state) X { X case ST_WORKING: X message("Working"); X break; X case ST_HOLD: X message("Hold"); X break; X case ST_DONE: X message("Done"); X break; X case ST_ERROR: X message("Error (%s)", derrmsg(d->d_error)); X break; X } X message("\n"); X } X} END_OF_FILE if test 1063 -ne `wc -c <'debug.c'`; then echo shar: \"'debug.c'\" unpacked with wrong size! fi # end of 'debug.c' fi if test -f 'dest.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dest.c'\" else echo shar: Extracting \"'dest.c'\" \(2938 characters\) sed "s/^X//" >'dest.c' <<'END_OF_FILE' X/* $Header: dest.c,v 2.1 89/06/09 12:25:22 network Exp $ X * X * Operations on the list of mail destinations. X * X * $Log: dest.c,v $ X * Revision 2.1 89/06/09 12:25:22 network X * Update RCS revisions. X * X * Revision 1.5 89/06/09 12:23:45 network X * Baseline for 2.0 release. X * X */ X X#include "deliver.h" X X/* X * Local data. X */ X Xstatic DEST deadhead = { &deadhead, &deadhead }; X#define HEADPTR (&deadhead) X X/*---------------------------------------------------------------------- X * Add a new destination to the list (unless it already exists). X * Return pointer to DEST. X */ X XDEST * Xdest(name, mailbox) Xchar *name; Xchar *mailbox; X{ X DEST *d; X DCLASS class; X X if (strchr(name, '!')) X class = CL_UUCP; X else if (mailbox) X class = CL_MBOX; X else X class = CL_USER; X X for (d = HEADPTR->d_next; d != HEADPTR; d = d->d_next) X { X if (d->d_class != class) X continue; X X if (strcmp(d->d_name, name) != 0) X continue; X X /* X * If this destination has a named mailbox, then X * test it for equality as well. X */ X X if (class == CL_MBOX X && strcmp(d->d_mailbox, mailbox) != 0) X continue; X X /* X * Like, gnarly, dude! It's already in the chain! X */ X X return d; X } X X /* X * The given dest isn't in the list, so we have to add it. X */ X X d = (DEST *) zalloc(sizeof(DEST)); X d->d_class = class; X d->d_state = ST_WORKING; X d->d_name = copystr(name); X if (class == CL_MBOX) X d->d_mailbox = copystr(mailbox); X X /* X * Check address for validity. X */ X X if (!valid_address(name)) X dest_err(d, E_IVADDR); X else if (class != CL_UUCP && name_context(name) == NULL) X dest_err(d, E_NSUSER); X X /* X * Put new address at the end of of the chain. X * (This is important! Other code depends on it.) X */ X X d->d_prev = HEADPTR->d_prev; X d->d_next = HEADPTR; X d->d_prev->d_next = d; X d->d_next->d_prev = d; X X return d; X} X X/*---------------------------------------------------------------------- X * Return pointer to first DEST in the list. X */ X XDEST * Xfirst_dest() X{ X if (HEADPTR->d_next != HEADPTR) X return HEADPTR->d_next; X X return NULL; X} X X/*---------------------------------------------------------------------- X * Return pointer to next DEST in the list, or NULL. X */ X XDEST * Xnext_dest(d) XDEST *d; X{ X if (d && (d = d->d_next) != HEADPTR) X return d; X X return NULL; X} X X/*---------------------------------------------------------------------- X * Return an error message given a DERROR. X */ X Xchar * Xderrmsg(e) XDERROR e; X{ X static char unknown_buf[40]; X X switch (e) X { X case E_IVADDR: X return "Invalid address string"; X case E_NSUSER: X return "No such user"; X case E_NSHOST: X return "No such host (UUCP addresses)"; X case E_CTPERM: X return "No permissions for that context"; X case E_CTLOST: X return "Context lost (should never happen)"; X case E_MBOX: X return "Can't write to mailbox"; X case E_UUX: X return "Can't pipe to uux"; X } X X (void) sprintf(unknown_buf, "Unknown error %d", e); X return unknown_buf; X} END_OF_FILE if test 2938 -ne `wc -c <'dest.c'`; then echo shar: \"'dest.c'\" unpacked with wrong size! fi # end of 'dest.c' fi if test -f 'dfile.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'dfile.c'\" else echo shar: Extracting \"'dfile.c'\" \(8068 characters\) sed "s/^X//" >'dfile.c' <<'END_OF_FILE' X/* $Header: dfile.c,v 2.1 89/06/09 12:25:24 network Exp $ X * X * Filter destination(s) through delivery file(s). X * X * $Log: dfile.c,v $ X * Revision 2.1 89/06/09 12:25:24 network X * Update RCS revisions. X * X * Revision 1.10 89/06/09 12:23:49 network X * Baseline for 2.0 release. X * X */ X X#include "deliver.h" X#include <sys/stat.h> X X/*---------------------------------------------------------------------- X * Filter all valid destinations through the global delivery file. X */ X Xsys_dfile(dac, dav) Xint dac; Xchar **dav; X{ X char **fav; X int fac, a; X struct stat st; X X /* X * If there is no global delivery file, forget it. X */ X X if (stat(sys_deliver, &st) == -1) X { X if (verbose) X message("no system delivery file\n"); X return -1; X } X X /* X * If we've been asked not to run delivery files, forget it. X */ X X if (!rundfiles) X { X if (verbose) X message("system delivery file disabled\n"); X return -1; X } X X /* X * Collect the arguments for the delivery file. X */ X X fav = (char **) zalloc((dac + 3) * sizeof(char **)); X fav[0] = shell; X fav[1] = sys_deliver; X fac = 2; X X for (a = 0; a < dac; ++a) X { X char *addr; X X addr = dav[a]; X if (valid_address(addr)) X { X /* Let the delivery file handle valid addresses. */ X X fav[fac++] = addr; X } X else X { X /* Note invalid address(es); report them later. */ X X (void) dest(addr, (char *) NULL); X } X } X X fav[fac] = NULL; X X /* X * If there were any good names found, let loose the delivery X * file. Note the meaning of "good" is "well-formed", not "valid". X * Thus the system delivery file has control over the handling of X * all local deliveries, not just those to valid users. X */ X X if (fac > 2) X (void) do_dfile(eff_ct, fav, (DEST *)NULL); X X free((char *) fav); X X return 0; X} X X/*---------------------------------------------------------------------- X * Filter some undelivered destinations through the post-user X * delivery file. X */ X Xpost_dfile() X{ X DEST *d; X char **fav; X int num_dests, fac; X struct stat st; X X /* X * If there is no post-user delivery file, forget it. X */ X X if (stat(post_deliver, &st) == -1) X { X if (verbose) X message("no post-user delivery file\n"); X return -1; X } X X /* X * If we've been asked not to run delivery files, forget it. X */ X X if (!rundfiles) X { X if (verbose) X message("post-user delivery file disabled\n"); X return -1; X } X X /* X * Generate the delivery file argument list. X */ X X num_dests = 0; X for (d = first_dest(); d; d = next_dest(d)) X ++num_dests; X X fav = (char **) zalloc((num_dests + 3) * sizeof(char **)); X fav[0] = shell; X fav[1] = post_deliver; X fac = 2; X X for (d = first_dest(); d; d = next_dest(d)) X { X if ((d->d_class == CL_USER || d->d_class == CL_UUCP) X && (d->d_state == ST_WORKING X || (d->d_state == ST_ERROR && d->d_error == E_NSUSER))) X { X fav[fac++] = d->d_name; X d->d_state = ST_HOLD; X } X } X X fav[fac] = NULL; X X if (fac > 2) X (void) do_dfile(eff_ct, fav, (DEST *)NULL); X X free((char *) fav); X X return 0; X} X X/*---------------------------------------------------------------------- X * Filter all user destinations through their local delivery files. X */ X Xuser_dfiles() X{ X DEST *d; X int nfound; X X /* X * If we've been asked not to run delivery files, forget it. X */ X X if (!rundfiles) X { X if (verbose) X message("user delivery files disabled\n"); X X return -1; X } X X X /* X * Continue to loop through all addresses until no destination X * that needs expanding can be found. X */ X X do { X nfound = 0; X for (d = first_dest(); d; d = next_dest(d)) X { X if (d->d_class == CL_USER X && d->d_state == ST_WORKING X && !d->d_dfdone) X { X one_dfile(d); X d->d_dfdone = TRUE; X } X } X } while (nfound > 0); X X return 0; X} X X/*---------------------------------------------------------------------- X * Run the delivery file (if any) for the specified destination. X */ X Xone_dfile(d) XDEST *d; X{ X CONTEXT *ct; X char *fav[4]; X char udel_path[100]; X struct stat st; X X if ((ct = name_context(d->d_name)) == NULL) X { X dest_err(d, E_CTLOST); X return; X } X X /* X * If user's home directory is missing, forget it. X * If user's home directory is writable to the world, X * executing the delivery file would allow a security breach! X * Thanks to Jon Zeeff for this hint... X */ X X if (stat(ct->ct_home, &st) == -1 X || (st.st_mode & S_IFMT) != S_IFDIR) X { X if (verbose) X message("user %s: home directory %s is missing!\n", X ct->ct_name, ct->ct_home); X return; X } X X if (st.st_mode & 02) X { X if (verbose) X message("user %s: home directory is writable to the world!\n", X ct->ct_name); X return; X } X X /* X * If there is no delivery file to execute, just return. X */ X X (void) sprintf(udel_path, "%s/%s", ct->ct_home, user_deliver); X if (stat(udel_path, &st) == -1) X { X if (verbose) X message("%s has no delivery file\n", d->d_name); X return; X } X X /* X * Time to run the file! X * We put this dest on hold, so that it will be ignored unless X * the delivery file names it. X */ X X d->d_state = ST_HOLD; X X fav[0] = shell; X fav[1] = udel_path; X fav[2] = d->d_name; X fav[3] = NULL; X (void) do_dfile(ct, fav, d); X} X X/*---------------------------------------------------------------------- X * Process a delivery file. X */ X Xint Xdo_dfile(ct, av, d) XCONTEXT *ct; Xchar **av; XDEST *d; X{ X FILE *fp; X char *name, *mailbox; X X if (!ct) X return -1; X X if (! ok_context(ct)) X { X if (d) X dest_err(d, E_CTPERM); X else X message("No permissions to run as %s\n", ct->ct_name); X X return -1; X } X X /* Copy the temp files again */ X X if (copy_again() < 0) X return -1; X X /* Allow the given user to own and read the copies */ X X if (give_temps(ct) < 0) X return -1; X X /* Here we go! */ X X if (verbose) X message("Processing delivery file as %s\n", ct->ct_name); X X if ((fp = ct_popenv(ct, shell, av, "r")) == NULL) X { X error("can't execute delivery file as %s\n", ct->ct_name); X return -1; X } X X /* X * Read the standard output of the delivery file. X */ X X while (dfile_gets(fp, &name, &mailbox) >= 0) X { X DEST *nd; X X nd = dest(name, mailbox); X if (nd->d_state == ST_HOLD) X nd->d_state = ST_WORKING; X X /* X * If the delivery file specified a mailbox, verify X * that the user whose delivery file is running has X * permissions for the requested context. X */ X X if ((nd->d_state == ST_WORKING) && (mailbox != NULL)) X { X CONTEXT *nct; X X if ((nct = name_context(name)) == NULL) X dest_err(nd, E_CTLOST); X else if (! ok_context(nct)) X dest_err(nd, E_CTPERM); X } X } X X return ct_pclose(fp); X} X X/*---------------------------------------------------------------------- X * Get and parse a single delivery file output line. X */ X Xint Xdfile_gets(fp, namep, mailboxp) XFILE *fp; Xchar **namep; Xchar **mailboxp; X{ X char *p, *q; X static char buf[BUFSIZ]; X X if (fgets(buf, GETSIZE(buf), fp) == NULL) X return -1; X X if ((p = strchr(buf, '\n')) != NULL) X *p = 0; X else X { X int c; X X while ((c = fgetc(fp)) != '\n' && c != EOF) X ; /* keep reading */ X X error("invalid line from delivery file: '%s'\n", buf); X return -1; X } X X /* Strip out all whitespace and eliminate duplicated slashes */ X X p = q = buf; X while (*p) X { X if (isspace(*p)) X ++p; X else if ((*q++ = *p++) == '/') X { X while (*p == '/') X ++p; X } X } X *q = 0; X X /* Debugging message: display input line */ X X if (verbose) X message("\t'%s'\n", buf); X X if ((p = strchr(buf, ':')) != NULL) X { X *p++ = 0; X if ((q = strchr(p, ':')) != NULL) X *q = 0; X } X X *namep = buf; X *mailboxp = p; X return 0; X} X X/*---------------------------------------------------------------------- X * Make the temp files readable in the given context. X * This is needed because the temps are readable by owner only. X */ X Xint Xgive_temps(ct) XCONTEXT *ct; X{ X int err, t; X X if (!ct) X return -1; X X err = 0; X for (t = T_HDRCOPY; t <= T_BODYCOPY; ++t) X { X if (chmod(tfile[t], 0600) == -1) X { X syserr("can't chmod %s", tfile[t]); X ++err; X } X if (chown(tfile[t], ct->ct_uid, ct->ct_gid) == -1) X { X syserr("can't chown %s to %d/%d", X tfile[t], ct->ct_uid, ct->ct_gid); X ++err; X } X } X X return err ? -1 : 0; X} END_OF_FILE if test 8068 -ne `wc -c <'dfile.c'`; then echo shar: \"'dfile.c'\" unpacked with wrong size! fi # end of 'dfile.c' fi if test -f 'lock.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'lock.c'\" else echo shar: Extracting \"'lock.c'\" \(7155 characters\) sed "s/^X//" >'lock.c' <<'END_OF_FILE' X/* $Header: lock.c,v 2.1 89/06/09 12:25:30 network Exp $ X * X * Mailbox locking. X * Local hacks for mailbox access should be grafted here. X * X * $Log: lock.c,v $ X * Revision 2.1 89/06/09 12:25:30 network X * Update RCS revisions. X * X * Revision 1.6 89/06/09 12:23:52 network X * Baseline for 2.0 release. X * X */ X X#include "deliver.h" X X/* X * Validate the locking configuration. X */ X X#if (defined(ML_FCNTL) + defined(ML_LOCKF) + defined(ML_LOCKING)) > 1 X lose! "Only one of ML_FCNTL, ML_LOCKF and ML_LOCKING may be defined."; X#endif X X/* X * Support for the lockf() system call. X */ X X#ifdef ML_LOCKF X#include <unistd.h> X#define SIMPLE_LOCK "lockf" X#define LOCKFD(fd, size) lockf(fd, F_LOCK, size) X#define UNLOCKFD(fd, size) lockf(fd, F_ULOCK, size) X#endif /* ML_LOCKF */ X X/* X * Setup for the locking() system call. X */ X X#ifdef ML_LOCKING X#include <sys/locking.h> X#define SIMPLE_LOCK "locking" X#define LOCKFD(fd, size) locking(fd, LK_LOCK, size) X#define UNLOCKFD(fd, size) locking(fd, LK_UNLOCK, size) X#endif X X/* X * Local functions. X */ X X#ifdef ML_DOTLOCK Xstatic char *dotlock_name(); X#endif X#ifdef ML_DOTMLK Xstatic char *dotmlk_name(); X#endif X X/*---------------------------------------------------------------------- X * Lock a mailbox by name. X * X * This code looks quite hairy with all the ifdefs. In fact, the only X * somewhat strange thing here is that neither, either, or both of X * ML_DOTLOCK and ML_DOTMLK may be defined, and we have to allow for it. X */ X Xint Xname_lock(name) Xchar *name; X{ X#ifdef ML_DOTLOCK X char *dotlock; X#endif X#ifdef ML_DOTMLK X char *dotmlk; X#endif X X#ifdef ML_DOTLOCK X if ((dotlock = dotlock_name(name)) == NULL X || create_lockfile(dotlock) < 0) X return -1; X#endif /* ML_DOTLOCK */ X X#ifdef ML_DOTMLK X if ((dotmlk = dotmlk_name(name)) == NULL X || create_lockfile(dotmlk) < 0) X { X#ifdef ML_DOTLOCK X (void) remove_lockfile(dotlock); /* don't leave me hanging */ X#endif X return -1; X } X#endif /* ML_DOTMLK */ X X return 0; X} X X/*---------------------------------------------------------------------- X * Unlock a mailbox by name. X */ X Xint Xname_unlock(name) Xchar *name; X{ X int ret = 0; X X#ifdef ML_DOTLOCK X char *dotlock; X#endif X#ifdef ML_DOTMLK X char *dotmlk; X#endif X X#ifdef ML_DOTLOCK X if ((dotlock = dotlock_name(name)) == NULL X || remove_lockfile(dotlock) < 0) X ret = -1; X#endif /* ML_DOTLOCK */ X X#ifdef ML_DOTMLK X if ((dotmlk = dotmlk_name(name)) == NULL X || remove_lockfile(dotmlk) < 0) X ret = -1; X#endif /* ML_DOTMLK */ X X return ret; X} X X/*---------------------------------------------------------------------- X * Lock a file descriptor. X */ X Xint Xfd_lock(fd) Xint fd; X{ X#ifdef ML_FCNTL X struct flock fl; X X fl.l_type = F_WRLCK; X fl.l_whence = 0; X fl.l_start = 0L; X fl.l_len = 0L; X X if (fcntl(fd, F_SETLKW, &fl) == -1) X { X syserr("can't lock with fcntl()"); X return -1; X } X X if (verbose) X message("locked mailbox with fcntl()\n"); X#endif /* ML_FCNTL */ X X#ifdef SIMPLE_LOCK X long pos; X X if ((pos = lseek(fd, 0L, 0)) == -1) X { X syserr("can't seek in mailbox"); X return -1; X } X if (LOCKFD(fd, 0L) == -1) X { X syserr("can't lock with %s()", SIMPLE_LOCK); X return -1; X } X if (lseek(fd, pos, 0) == -1) X { X syserr("can't seek in mailbox"); X return -1; X } X X if (verbose) X message("locked mailbox with %s()\n", SIMPLE_LOCK); X#endif /* SIMPLE_LOCK */ X X /* Default: success */ X return 0; X} X X/*---------------------------------------------------------------------- X * Unlock a file descriptor. X */ X Xint Xfd_unlock(fd) Xint fd; X{ X#ifdef ML_FCNTL X struct flock fl; X X fl.l_type = F_UNLCK; X fl.l_whence = 0; X fl.l_start = 0L; X fl.l_len = 0L; X X if (fcntl(fd, F_SETLKW, &fl) == -1) X { X syserr("can't unlock with fcntl()"); X return -1; X } X X if (verbose) X message("unlocked mailbox with fcntl()\n"); X#endif /* ML_FCNTL */ X X#ifdef SIMPLE_LOCK X long pos; X X if ((pos = lseek(fd, 0L, 0)) == -1) X { X syserr("can't seek in mailbox"); X return -1; X } X if (LOCKFD(fd, 0L) == -1) X { X syserr("can't unlock with %s()", SIMPLE_LOCK); X return -1; X } X if (lseek(fd, pos, 0) == -1) X { X syserr("can't seek in mailbox"); X return -1; X } X X if (verbose) X message("unlocked mailbox with %s()\n", SIMPLE_LOCK); X#endif /* SIMPLE_LOCK */ X X /* Default: success */ X return 0; X} X X/*---------------------------------------------------------------------- X * Return the name of the appropriate ".lock" file for a mailbox. X */ X X#ifdef ML_DOTLOCK X Xstatic char * Xdotlock_name(name) Xchar *name; X{ X static char *lname = NULL; X static int lsize = 0; X char *p; X int n, i; X X n = strlen(name); X if (lsize < n + 8) X { X if (lname) X free(lname); X lsize = n + 32; X lname = zalloc(lsize); X } X X (void) strcpy(lname, name); X X /* X * We want as much of `basename.lock' as will fit in a string X * MAX_NAMESIZE long. X */ X for (i = 0, p = basename(lname); (i < MAX_NAMESIZE - 5) && (*p); ++i) X ++p; X (void) strcpy(p, ".lock"); X X return lname; X} X X#endif /* ML_DOTLOCK */ X X/*---------------------------------------------------------------------- X * Return the name of the appropriate ".mlk" file for a mailbox. X */ X X#ifdef ML_DOTMLK X Xstatic char * Xdotmlk_name(name) Xchar *name; X{ X static char lname[MAX_NAMESIZE + 16]; X char *p, *d; X int i; X X /* X * To explain the below: If we ass_u_me that MAX_NAMESIZE is 14, X * then this code is like `printf(lname, "/tmp/%.10s.mlk", ...)'. X * In other words, we want as much of `basename.mlk' as will fit X * in a string MAX_NAMESIZE long. X */ X d = lname; X for (p = "/tmp/"; *p; ) X *d++ = *p++; X for (i = 0, p = basename(name); (i < MAX_NAMESIZE - 4) && (*p); ++i) X *d++ = *p++; X (void) strcpy(d, ".mlk"); X X return lname; X} X X#endif /* ML_DOTMLK */ X X/*---------------------------------------------------------------------- X * Create a lockfile. X */ X Xint Xcreate_lockfile(name) Xchar *name; X{ X#ifndef O_CREAT X char *othername, *p; X#endif X int fd, tries; X X#ifndef O_CREAT X othername = zalloc(strlen(name) + 20); /* fudge (???) */ X (void) strcpy(othername, name); X (void) sprintf(basename(othername), ".dl.%d", getpid()); X if ((fd = creat(othername, 0)) == -1) X { X syserr("can't create %s", othername); X return -1; X } X (void) close(fd); X if (verbose) X message("created pre-lockfile %s\n", name); X#endif X X for (tries = 0; tries < 10; ++tries) X { X if (tries) X snooze(3); X X#ifdef O_CREAT X X if ((fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0)) >= 0) X { X (void) close(fd); X if (verbose) X message("created lockfile %s\n", name); X return 0; X } X X#else /* not O_CREAT */ X X if (link(othername, name) == 0) X { X if (unlink(othername) == -1) X syserr("can't remove %s", othername); X free(othername); X if (verbose) X message("created lockfile %s\n", name); X return 0; X } X X#endif /* not O_CREAT */ X X if (verbose && (tries == 0)) X message("Waiting to create %s\n", name); X } X X syserr("can't create lockfile %s", name); X return -1; X} X X/*---------------------------------------------------------------------- X * Remove a lockfile. X */ X Xint Xremove_lockfile(name) Xchar *name; X{ X if (unlink(name) == -1) X { X syserr("can't remove lockfile %s", name); X return -1; X } X X if (verbose) X message("removed lockfile %s\n", name); X X return 0; X} END_OF_FILE if test 7155 -ne `wc -c <'lock.c'`; then echo shar: \"'lock.c'\" unpacked with wrong size! fi # end of 'lock.c' fi if test -f 'main.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'main.c'\" else echo shar: Extracting \"'main.c'\" \(12025 characters\) sed "s/^X//" >'main.c' <<'END_OF_FILE' X/* $Header: main.c,v 2.1 89/06/09 12:25:32 network Exp $ X * X * A program to deliver local mail with some flexibility. X * X * $Log: main.c,v $ X * Revision 2.1 89/06/09 12:25:32 network X * Update RCS revisions. X * X * Revision 1.13 89/06/09 12:23:53 network X * Baseline for 2.0 release. X * X */ X X#include "deliver.h" X#include "patchlevel.h" X#include <signal.h> X X/* X * External data. X */ X X/* Variables set by getopt() [blech] */ X Xextern int optind, opterr; Xextern char *optarg; X X/* X * Local data X */ X Xstatic char sys_dfl[] = SYS_DELIVER; Xstatic char post_dfl[] = POST_DELIVER; Xstatic char user_dfl[] = USER_DELIVER; X X/* X * Global data X */ X Xint verbose = FALSE; Xint dryrun = FALSE; Xint rundfiles = TRUE; Xint printaddrs = FALSE; Xint leavetemps = FALSE; Xint boxdelivery = FALSE; X Xchar *progname = "deliver"; Xchar version[32] = "2.0"; Xchar *shell = SHELL; X Xchar *sys_deliver = sys_dfl; Xchar *post_deliver = post_dfl; Xchar *user_deliver = user_dfl; Xchar *sender = NULL; Xchar *hostname = NULL; X Xint eff_uid = -1; Xint eff_gid = -1; Xint real_uid = -1; Xint real_gid = -1; X XCONTEXT *eff_ct = NULL; XCONTEXT *real_ct = NULL; X Xint tty_input = FALSE; XSIGFLAG got_sig = FALSE; X Xint trust_user = FALSE; Xint trust_delfiles = FALSE; X Xchar *ttype[T_MAX] = { "header", "body", "header copy", "body copy" }; Xchar *tfile[T_MAX] = { NULL, NULL, NULL, NULL }; Xchar *tenv[T_MAX] = { NULL, NULL, ENV_HEADER, ENV_BODY }; Xint tfd[T_MAX] = { -1, -1, -1, -1 }; X X/* X * Local functions. X */ X Xstatic SIGTYPE sighup(), sigint(), sigquit(); X X/*---------------------------------------------------------------------- X * The Program. X */ X Xmain(argc, argv) Xint argc; Xchar **argv; X{ X char *p; X int u, c, errcount, copy; X X /* Make sure that stdout and stderr are interleaved correctly */ X X Linebuf(stdout); X Linebuf(stderr); X X /* Figure out the name used to invoke this program. */ X X progname = basename(argv[0]); X X /* What version of the program is this? */ X X (void) sprintf(version + strlen(version), ".%02d", PATCHLEVEL); X X /* Figure out the name of this host */ X X if ((hostname = gethost()) == NULL) X { X hostname = "unknown"; X error("unable to determine host name; using \"%s\"\n", X hostname); X } X X /* Find effective and real uids and gids. */ X X eff_uid = geteuid(); X eff_gid = getegid(); X real_uid = getuid(); X real_gid = getgid(); X X if (eff_uid != real_uid && eff_uid != 0) X { X error("if setuid, must be setuid root\n"); X leave(1); X } X X /* Process environment: handle recursive invocation */ X X if ((p = getenv(ENV_DFLAGS)) != NULL) X { X while (*p) X { X switch (*p++) X { X case 'v': X verbose = TRUE; X break; X case 'd': X verbose = TRUE; X dryrun = TRUE; X break; X case 'A': X printaddrs = TRUE; X dryrun = TRUE; X break; X case 'n': X rundfiles = FALSE; X break; X case 't': X leavetemps = TRUE; X break; X } X } X } X X if ((p = getenv(ENV_SYSDEL)) != NULL && *p) X sys_deliver = p; X if ((p = getenv(ENV_POSTDEL)) != NULL && *p) X post_deliver = p; X if ((p = getenv(ENV_USERDEL)) != NULL && *p) X user_deliver = p; X if ((p = getenv(ENV_SENDER)) != NULL && *p) X sender = p; X if ((p = getenv(ENV_HOSTNAME)) != NULL && *p) X hostname = p; X X /* Parse command line arguments */ X X while ((c = getopt(argc, argv, "vdAntbs:p:u:r:h:")) != EOF) X { X switch (c) X { X case 'v': X verbose = TRUE; X break; X case 'd': X verbose = TRUE; X dryrun = TRUE; X break; X case 'A': X printaddrs = TRUE; X dryrun = TRUE; X break; X case 'n': X rundfiles = FALSE; X break; X case 't': X leavetemps = TRUE; X break; X case 'b': X boxdelivery = TRUE; X break; X case 's': X if (*optarg) X sys_deliver = optarg; X break; X case 'p': X if (*optarg) X post_deliver = optarg; X break; X case 'u': X if (*optarg) X user_deliver = optarg; X break; X case 'r': X if (*optarg) X sender = optarg; X break; X case 'h': X if (*optarg) X hostname = optarg; X break; X case '?': X usage(); X } X } X X /* If no destinations were given, forget it. */ X X if (optind >= argc) X { X error("no recipients specified\n"); X usage(); X } X X /* Print a debugging message */ X X if (verbose) X { X message("%s %s running on host %s\n", X progname, version, hostname); X } X X /* Do we trust our caller? */ X X if (trusted_uid(real_uid)) X trust_user = TRUE; X X /* Do we trust our delivery files? */ X X if (strcmp(sys_dfl, sys_deliver) == 0 X && strcmp(post_dfl, post_deliver) == 0 X && strcmp(user_dfl, user_deliver) == 0) X trust_delfiles = TRUE; X X /* Renounce special privileges if something insecure was requested. */ X X if (!trust_user && !trust_delfiles) X { X if (setgid(eff_gid = real_gid) == -1 X || setuid(eff_uid = real_uid) == -1) X { X syserr("can't renounce setuid privileges"); X leave(1); X } X } X X /* Get the contexts of our effective and real uids. */ X X if ((eff_ct = uid_context(eff_uid)) == NULL) X error("invalid effective uid %d!?\n", eff_uid); X X if ((real_ct = uid_context(real_uid)) == NULL) X error("invalid real uid %d!?\n", real_uid); X X if (!eff_ct || !real_ct) X leave(1); X X if (verbose) X { X message("effective uid = %s (%d/%d); real uid = %s (%d/%d)\n", X eff_ct->ct_name, eff_ct->ct_uid, eff_ct->ct_gid, X real_ct->ct_name, real_ct->ct_uid, real_ct->ct_gid); X } X X /* Let's be sane about the file creation mask. */ X X u = umask(0); X u &= ~0700; /* Let's not deprive ourselves of permissions. */ X u |= 022; /* Let's be reasonably paranoid about writing. */ X (void) umask(u); X X /* X * Where is the message coming from? X */ X X if (isatty(0)) X tty_input = TRUE; X X /* X * If we are not going to deliver, or if we are receiving the X * message from a tty, catch signals so we can remove temp files. X * Otherwise, ignore signals. X */ X X if (dryrun || tty_input) X catch_sigs(); X else X ignore_sigs(); X X /* X * Create the temporary files and write the message to them. X */ X X copy = copy_message(); X X /* X * No more signals... X */ X X ignore_sigs(); X X /* X * ... but if we had already caught a signal, X * or if copy_msg() had a problem, leave. X */ X X if ((copy < 0) || got_sig) X { X if (got_sig) X error("caught signal - exiting\n"); X leave(1); X } X X /* X * Set up useful environment variables. X * Note that this must be done _after_ copy_message(), X * since that's where the temp files are created. X */ X X setup_environ(); X X /* X * Perhaps we should consider all arguments as mailbox names... X */ X X if (boxdelivery) X { X int a; X X if (verbose) X message("mailbox delivery as %s\n", real_ct->ct_name); X X /* X * Consider all arguments as mailbox filenames. X */ X X for (a = optind; a < argc; ++a) X (void) dest(real_ct->ct_name, argv[a]); X X if (verbose) X dumpdests("(should all be mailboxes)"); X } X X /* X * They're not mailbox names, so they should be mail addresses. X */ X X else X { X /* Run all destinations though the system delivery file. */ X X if (sys_dfile(argc - optind, argv + optind) >= 0) X { X if (verbose) X dumpdests("after running system delivery file"); X } X else X { X int a; X X /* X * System delivery file is missing or ignored. X * Use the argument list verbatim. X */ X X for (a = optind; a < argc; ++a) X (void) dest(argv[a], (char *) NULL); X X if (verbose) X dumpdests("as taken from argument list"); X } X X /* X * Run each user destination through his delivery file. X */ X X if (user_dfiles() >= 0) X { X if (verbose) X dumpdests("after running user delivery files"); X } X X /* X * Run each remaining destination though the post-user X * delivery file. X */ X X if (post_dfile() >= 0) X { X if (verbose) X dumpdests("after running post-user delivery file"); X } X } X X /* X * Drop mail in mailbox(es). X */ X X mbox_deliver(); X X if (verbose) X dumpdests("after delivery to all mailboxes"); X X /* X * Send mail to UUCP address(es). X */ X X uucp_deliver(); X X if (verbose) X dumpdests("after delivery to UUCP addresses"); X X /* X * Report any errors, and leave. X */ X X errcount = report_errors(); X X /* X * All done. X */ X X leave(errcount ? 1 : 0); X /* NOTREACHED */ X} X X/*---------------------------------------------------------------------- X * Print a usage message and exit. X */ X Xusage() X{ X message("Usage: %s [-b][-A][-d][-v][-n][-t][-r from][-h host] args\n", progname); X message("-b All arguments are mailbox filenames.\n"); X message(" (Default: arguments are user names.)\n"); X message("-A Resolve addresses but do not deliver.\n"); X message("-d Be verbose but do not deliver.\n"); X message("-v Be verbose and deliver.\n"); X message("-n Do not run any delivery files.\n"); X message("-t Do not remote temp files before exiting.\n"); X message("-s file Specify the system delivery filename.\n"); X message("-p file Specify the post-user delivery filename.\n"); X message("-u file Specify the user delivery filename.\n"); X message("-r from Specify the address to appear in the \"From \" line.\n"); X message("-h host Specify the host name.\n"); X message("args Either user addresses or mailboxes (-b).\n"); X leave(1); X} X X/*---------------------------------------------------------------------- X * Clean up and exit. X */ X Xleave(code) Xint code; X{ X if (! leavetemps) X { X int t; X X for (t = 0; t < T_MAX; ++t) X { X if (tfd[t] != -1) X (void) close(tfd[t]); X if (tfile[t] && unlink(tfile[t]) == -1) X syserr("can't unlink %s", tfile[t]); X } X } X X exit(code); X} X X/*---------------------------------------------------------------------- X * Catch signals. X */ X Xcatch_sigs() X{ X if (signal(SIGHUP, SIG_IGN) != SIG_IGN) X (void) signal(SIGHUP, sighup); X if (signal(SIGINT, SIG_IGN) != SIG_IGN) X (void) signal(SIGINT, sigint); X if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) X (void) signal(SIGQUIT, sigquit); X} X X/*---------------------------------------------------------------------- X * Ignore signals. X */ X Xignore_sigs() X{ X (void) signal(SIGHUP, SIG_IGN); X (void) signal(SIGINT, SIG_IGN); X (void) signal(SIGQUIT, SIG_IGN); X} X Xstatic SIGTYPE Xsighup() X{ X (void) signal(SIGHUP, sighup); X got_sig = TRUE; X} X Xstatic SIGTYPE Xsigint() X{ X (void) signal(SIGINT, sigint); X got_sig = TRUE; X} X Xstatic SIGTYPE Xsigquit() X{ X (void) signal(SIGQUIT, sigquit); X got_sig = TRUE; X} X X/*---------------------------------------------------------------------- X * Report any errors to stderr. X * Return an error count. X */ X Xint Xreport_errors() X{ X DEST *d; X int count = 0; X X for (d = first_dest(); d; d = next_dest(d)) X { X if (d->d_state != ST_ERROR) X continue; X X if (++count == 1) X { X error( X "delivery to the following address(es) failed on host %s\n", X hostname); X } X X message("\t\"%s\"", d->d_name); X if (d->d_class == CL_MBOX) X message(", mailbox \"%s\"", d->d_mailbox); X message(": %s\n", derrmsg(d->d_error)); X } X X return count; X} X X/*---------------------------------------------------------------------- X * Is the given uid trusted? X */ X Xint Xtrusted_uid(uid) Xint uid; X{ X CONTEXT *ct; X char **n; X static char *t[] = { TRUSTED_USERS, 0 }; X X for (n = t; *n; ++n) X { X if ((ct = name_context(*n)) != NULL && uid == ct->ct_uid) X return TRUE; X } X X return FALSE; X} X X/*---------------------------------------------------------------------- X * Set up useful environment variables. X */ X Xsetup_environ() X{ X char flags[8]; X int f = 0; X X flags[f++] = '-'; X if (verbose) X flags[f++] = (dryrun ? 'd' : 'v'); X if (printaddrs) X flags[f++] = 'A'; X if (leavetemps) X flags[f++] = 't'; X flags[f] = 0; X X alloc_env(ENV_DFLAGS, (f > 1) ? flags : ""); X if (sys_deliver && *sys_deliver) X alloc_env(ENV_SYSDEL, sys_deliver); X if (user_deliver && *user_deliver) X alloc_env(ENV_USERDEL, user_deliver); X if (hostname && *hostname) X alloc_env(ENV_HOSTNAME, hostname); X if (sender && *sender) X alloc_env(ENV_SENDER, sender); X X alloc_env("IFS", " \t\n"); X del_env("ENV"); /* in case SHELL is ksh */ X} END_OF_FILE if test 12025 -ne `wc -c <'main.c'`; then echo shar: \"'main.c'\" unpacked with wrong size! fi # end of 'main.c' fi echo shar: End of shell archive. exit 0 -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.