Path: utzoo!utgpu!attcan!uunet!ateng!chip From: chip@ateng.ateng.com (Chip Salzenberg) Newsgroups: comp.sources.bugs Subject: Deliver 1.00 (patchlevel 8) Part 2/3 Message-ID: <1989Feb23.114709.13861@ateng.ateng.com> Date: 23 Feb 89 16:47:09 GMT Organization: A T Engineering, Tampa, FL Lines: 1762 #! /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 'context.c' <<'END_OF_FILE' X/* $Header: context.c,v 1.4 89/02/10 15:46:09 network Exp $ X * X * User context manager. X * This module exists for efficiency reasons; I could just call getpwnam() X * every time I need context info. X * X * $Log: context.c,v $ X * Revision 1.4 89/02/10 15:46:09 network X * V7 support. X * X * Revision 1.3 88/09/14 19:41:40 network X * Portability to System V and BSD. X * General fixup. X * X * Revision 1.2 88/08/30 16:12:28 network X * Use savestr() instead of strdup(). X * X * Revision 1.1 88/06/06 09:38:05 chip X * Initial revision X * X */ X X#include "deliver.h" X#include X#include X Xextern struct passwd *getpwnam(); Xextern struct passwd *getpwuid(); Xextern struct group *getgrnam(); Xextern struct group *getgrgid(); X X/* X * Local functions. X */ X Xstatic CONTEXT *new_context(); X X/* X * Local data. X */ X Xstatic CONTEXT *ctlist; /* Chain of CONTEXT structures. */ X X/*---------------------------------------------------------------------- X * Look up a context by user name. X */ X XCONTEXT * Xname_context(name) Xchar *name; X{ X struct passwd *pw; X CONTEXT *ct; X X for (ct = ctlist; ct; ct = ct->ct_next) X { X if (strcmp(ct->ct_name, name) == 0) X return ct; X } X X if ((pw = getpwnam(name)) == NULL) X return NULL; X X return new_context(pw); X} X X/*---------------------------------------------------------------------- X * Look up a context by user ID. X */ X XCONTEXT * Xuid_context(uid) Xint uid; X{ X struct passwd *pw; X CONTEXT *ct; X X for (ct = ctlist; ct; ct = ct->ct_next) X { X if (ct->ct_uid == uid) X return ct; X } X X if ((pw = getpwuid(uid)) == NULL) X return NULL; X X return new_context(pw); X} X X/*---------------------------------------------------------------------- X * Local function -- create a new context structure and return X * its address. X */ X Xstatic CONTEXT * Xnew_context(pw) Xstruct passwd *pw; X{ X CONTEXT *ct; X X ct = (CONTEXT *) zalloc(sizeof(CONTEXT)); X ct->ct_uid = pw->pw_uid; X ct->ct_gid = pw->pw_gid; X ct->ct_name = copystr(pw->pw_name); X ct->ct_home = copystr(pw->pw_dir); X X ct->ct_next = ctlist; X ctlist = ct; X X return ct; X} X X/*---------------------------------------------------------------------- X * Report whether is is possible or not to enter the given context. X */ X Xint Xok_context(ct) XCONTEXT *ct; X{ X if (! ct) X return FALSE; X X if (eff_uid == 0 X || ((real_uid == ct->ct_uid) && (real_gid == ct->ct_gid))) X return TRUE; X else X return FALSE; X} X X/*---------------------------------------------------------------------- X * Look up a group ID by name. X */ X X#ifdef MBX_GROUP X Xint Xgroup_id(name) Xchar *name; X{ X struct group *grp; X X if ((grp = getgrnam(name)) == NULL) X return -1; X X return grp->gr_gid; X} X X#endif /* MBX_GROUP */ END_OF_FILE if test 2685 -ne `wc -c <'context.c'`; then echo shar: \"'context.c'\" unpacked with wrong size! fi # end of 'context.c' fi if test -f 'copymsg.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'copymsg.c'\" else echo shar: Extracting \"'copymsg.c'\" \(8157 characters\) sed "s/^X//" >'copymsg.c' <<'END_OF_FILE' X/* $Header: copymsg.c,v 1.4 89/02/10 15:46:17 network Exp $ X * X * Take the message from standard input and write it to two temp files, X * one for the header (including the empty line) and one for the body. X * X * $Log: copymsg.c,v $ X * Revision 1.4 89/02/10 15:46:17 network X * V7 support. X * X * Revision 1.3 88/11/28 18:07:46 network X * patch5: Copy temp files before handing them to delivery files. X * patch5: Introduce "uid" program. X * X * Revision 1.2 88/11/18 12:17:40 network X * patch2: Improved signal handling. X * patch2: Make sure all environment variables are always provided. X * patch2: Some users can be trusted to specify delivery files. X * X * Revision 1.1 88/06/06 09:38:13 chip X * Initial revision X * X */ X X#include "deliver.h" X X/* X * Macros. X */ X X/* Does a string start with "From "? */ X X#define ISFROM(p) ((p)[0] == 'F' && (p)[1] == 'r' && (p)[2] == 'o' \ X && (p)[3] == 'm' && (p)[4] == ' ') X X/* X * Local functions. X */ X Xstatic char *tempfile(); Xstatic int tcreate(); X X/*---------------------------------------------------------------------- X * Copy the message on the standard input to two temp files: X * one for the header and one for the body. X */ X Xint Xcopy_message() X{ X char buf[BUFSIZ]; X FILE *dfp[T_MAX]; X char *p, *fsender, *fremote; X long now; X int t, b, empty_line; X int ret = 0; X X /* X * Create temporary files to hold the header and message body. X */ X X for (t = T_HDR; t <= T_BODY; ++t) X { X int fd; X X tfile[t] = tempfile(); X if ((tfd[t] = tcreate(tfile[t])) == -1) X return -1; X X if ((fd = dup(tfd[t])) == -1) X { X syserr("dup %s fd", ttype[t]); X return -1; X } X (void) lseek(fd, 0L, 0); X if ((dfp[t] = fdopen(fd, "r+")) == NULL) X { X error("can't fdopen %s fd", ttype[t]); X return -1; X } X } X X /* Debugging message for later examination of temp files. */ X X if (verbose) X { X message("header=%s, body=%s\n", X tfile[T_HDR], tfile[T_BODY]); X } X X /* X * If there is a From_ line, find the sender name therein. X */ X X fsender = fremote = NULL; X X b = (fgets(buf, GETSIZE(buf), stdin) ? TRUE : FALSE); X X if (b && ISFROM(buf) && (p = strchr(buf, '\n')) != NULL) X { X b = FALSE; /* Don't output two From_ lines */ X *p = 0; X X /* Find sender */ X X for (fsender = buf + 5; isspace(*fsender); ++fsender) X ; /* until sender */ X for (p = fsender; *p && !isspace(*p); ++p) X ; /* until end of sender */ X if (*p) X *p++ = '\0'; X X /* Find 'remote from' phrase (if any) */ X X for (fremote = p; X (fremote = strchr(fremote, 'r')) != NULL; X ++fremote) X { X if (strncmp(fremote, "remote from", 11) == 0) X { X fremote += 11; X while (isspace(*fremote)) X ++fremote; X break; X } X } X } X X /* X * Write a From_ line to the header file. X */ X X /* if caller specified sender, use it */ X if (sender) X ; /* fine */ X X /* else if we found a From_ line, use it */ X else if (fsender) X { X if (fremote) X { X sender = zalloc(strlen(fremote) + sizeof("!") X + strlen(fsender)); X (void) sprintf(sender, "%s!%s", fremote, fsender); X } X else X sender = copystr(fsender); X } X X /* else use our real ID */ X else X sender = real_ct->ct_name; X X /* debugging message */ X X if (verbose) X message("copy_msg: sender is \"%s\"\n", sender); X X /* X * Finally! Write the From_ line. X */ X X (void) fputs("From ", dfp[T_HDR]); X (void) fputs(sender, dfp[T_HDR]); X (void) fputc(' ', dfp[T_HDR]); X (void) time(&now); X (void) fputs(ctime(&now), dfp[T_HDR]); X X /* X * Copy the rest of the header (if any). X */ X X for (; !feof(stdin) && !ferror(stdin); b = FALSE) X { X if (!b) X { X if (fgets(buf, GETSIZE(buf), stdin)) X b = TRUE; X else X break; X } X X /* Empty line means "end of header" */ X X if (buf[0] == '\n') X { X b = FALSE; /* Don't put this line in the body. */ X break; X } X X /* X * A line too long to fit in buf[] can't be a header line. X * At least, that's my opinion... :-) X */ X X if (!strchr(buf, '\n')) X break; X X /* X * If line begins with whitespace, it's a continuation. X * Else if line begins with From_ or '>', prepend '>'. X * Else if line doesn't look like a header, this must X * be the beginning of the body. X */ X X if (isspace(buf[0])) X ; /* continuation */ X else if (ISFROM(buf) || (buf[0] == '>')) X (void) fputc('>', dfp[T_HDR]); X else X { X /* look for the colon on a header label */ X X p = buf; X while (isalpha(*p) || *p == '-') X ++p; X if ((p == buf) || (*p != ':')) X break; /* Not a header line! */ X } X X /* Write the line to the header file. */ X X (void) fputs(buf, dfp[T_HDR]); X } X X /* X * End the header file with a blank line. X * This enables us to simply concatenate it with the body file X * to produce a valid message. X */ X X (void) fputc('\n', dfp[T_HDR]); X X /* X * Copy the body (if any). X */ X X empty_line = FALSE; X for (; !feof(stdin) && !ferror(stdin); b = FALSE) X { X if (!b) X { X if (fgets(buf, GETSIZE(buf), stdin)) X b = TRUE; X else X break; X } X X if (ISFROM(buf)) X (void) fputc('>', dfp[T_BODY]); X (void) fputs(buf, dfp[T_BODY]); X X empty_line = (buf[0] == '\n'); X X /* X * Output the rest of a very long line. X * We do this here, instead of going around the loop, X * in order to avoid misinterpreting From_ strings X * that may be found in long lines. X */ X X while (!strchr(buf, '\n') X && !feof(stdin) X && !ferror(stdin) X && fgets(buf, GETSIZE(buf), stdin)) X (void) fputs(buf, dfp[T_BODY]); X } X X /* Ensure that the body ends with a blank line. */ X X if (! empty_line) X (void) fputc('\n', dfp[T_BODY]); X X /* X * If we encountered any trouble writing to the temp files, X * let's not keep it secret. X */ X X for (t = T_HDR; t <= T_BODY; ++t) X { X if (ferror(dfp[t])) X { X error("error writing to %s file %s\n", X ttype[t], tfile[t]); X ret = -1; X } X X (void) fclose(dfp[t]); X } X X /* Return error/success. */ X X return ret; X} X X/*---------------------------------------------------------------------- X * Create another copy of each temp file, for security reasons. X * Also, put their names in the environment. X */ X Xint Xcopy_again() X{ X int r, t; X X for (r = T_HDR, t = T_HDRCOPY; r <= T_BODY; ++r, ++t) X { X /* X * If the file exists, remove it but keep its name. X * Otherwise, make a new name and put that name in X * the environment. X */ X X if (tfile[t]) X (void) unlink(tfile[t]); X else X { X tfile[t] = tempfile(); X if (tenv[t]) X alloc_env(tenv[t], tfile[t]); X } X X /* X * Create the file and copy the contents of the X * original file to it. X */ X X if (tfd[t] != -1) X (void) close(tfd[t]); X X if ((tfd[t] = tcreate(tfile[t])) == -1) X return -1; X X (void) lseek(tfd[r], 0L, 0); X if (copyfd(tfd[r], tfd[t]) < 0) X return -1; X } X X if (verbose) X { X message("copy_again: header to %s, body to %s\n", X tfile[T_HDRCOPY], tfile[T_BODYCOPY]); X } X X return 0; X} X X/*---------------------------------------------------------------------- X * Copy a file via file descriptors. X */ X Xint Xcopyfd(src_fd, dest_fd) Xint src_fd; Xint dest_fd; X{ X char buf[BUFSIZ]; X int rd, wr; X X while ((rd = read(src_fd, buf, sizeof(buf))) > 0) X { X if ((wr = write(dest_fd, buf, (unsigned) rd)) != rd) X { X if (wr == -1) X syserr("can't write in copyfd"); X else X error("write error -- disk full?\n"); X return -1; X } X } X X return 0; X} X X/*---------------------------------------------------------------------- X * Return a pointer to a temporary filename, or NULL if error. X */ X Xstatic char * Xtempfile() X{ X static char template[] = "/tmp/dl.XXXXXX"; X char *f; X X f = zalloc(32); X (void) strcpy(f, template); X if (mktemp(f) == NULL) X { X error("can't create temporary file"); X return NULL; X } X return f; X} X X/*---------------------------------------------------------------------- X * Create a file, or complain if it doesn't work. X */ X Xstatic int Xtcreate(name) Xchar *name; X{ X int fd; X X#ifdef O_CREAT X fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0); X#else X fd = creat(name, 0); X#endif X if (fd == -1) X { X syserr("can't create %s", name); X return -1; X } X X#ifndef O_CREAT X (void) close(fd); X if ((fd = open(name, 2)) == -1) X { X syserr("can't re-open %s", name); X return -1; X } X#endif X X return fd; X} X END_OF_FILE if test 8157 -ne `wc -c <'copymsg.c'`; then echo shar: \"'copymsg.c'\" unpacked with wrong size! fi # end of 'copymsg.c' fi if test -f 'debug.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'debug.c'\" else echo shar: Extracting \"'debug.c'\" \(1332 characters\) sed "s/^X//" >'debug.c' <<'END_OF_FILE' X/* $Header: debug.c,v 1.3 89/02/10 15:46:24 network Exp $ X * X * Debugging output. X * X * $Log: debug.c,v $ X * Revision 1.3 89/02/10 15:46:24 network X * V7 support. X * X * Revision 1.2 88/11/26 13:20:38 network X * patch4: Add return type of signal handlers to config.h. X * patch4: Provide a version of getopt() for systems that lack it. X * patch4: Call va_end() in routines that use varargs. X * patch4: Make consistent checks for valid address strings. X * X * Revision 1.1 88/06/06 09:38:23 chip X * Initial revision 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 1332 -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'\" \(3294 characters\) sed "s/^X//" >'dest.c' <<'END_OF_FILE' X/* $Header: dest.c,v 1.4 89/02/10 15:46:31 network Exp $ X * X * Operations on the list of mail destinations. X * X * $Log: dest.c,v $ X * Revision 1.4 89/02/10 15:46:31 network X * V7 support. X * X * Revision 1.3 88/11/26 13:20:42 network X * patch4: Add return type of signal handlers to config.h. X * patch4: Provide a version of getopt() for systems that lack it. X * patch4: Call va_end() in routines that use varargs. X * patch4: Make consistent checks for valid address strings. X * X * Revision 1.2 88/08/30 16:12:40 network X * Use savestr() instead of strdup(). X * X * Revision 1.1 88/06/06 09:38:29 chip X * Initial revision 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 3294 -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'\" \(9318 characters\) sed "s/^X//" >'dfile.c' <<'END_OF_FILE' X/* $Header: dfile.c,v 1.8 89/02/15 19:11:12 network Exp $ X * X * Filter destination(s) through delivery file(s). X * X * $Log: dfile.c,v $ X * Revision 1.8 89/02/15 19:11:12 network X * Provide second system-wide delivery file, executed after user delivery X * files but before any deliveries take place. Useful for implementing X * system-wide aliases. X * Also, fix bug in do_dfile() that caused infinite loops if delivery files X * output any lines containing white space. (!) X * X * Revision 1.7 89/02/10 15:46:42 network X * V7 support. X * X * Revision 1.6 88/11/28 18:07:57 network X * patch5: Copy temp files before handing them to delivery files. X * patch5: Introduce "uid" program. X * X * Revision 1.5 88/11/26 13:20:45 network X * patch4: Add return type of signal handlers to config.h. X * patch4: Provide a version of getopt() for systems that lack it. X * patch4: Call va_end() in routines that use varargs. X * patch4: Make consistent checks for valid address strings. X * X * Revision 1.4 88/11/21 13:12:19 network X * patch3: Fix Makefile for GNU Make. X * patch3: Remove hard-coded shell name. X * patch3: Fix call to setvbuf() for System V. X * X * Revision 1.3 88/10/13 12:19:22 network X * patch1: add "-n" option, and general bug fixes. X * X * Revision 1.2 88/08/25 15:23:56 network X * Add third parameter to do_dfile(), so that if the delivery file cannot X * be executed, the given destination can be recorded as an error. X * X * Revision 1.1 88/06/06 09:38:38 chip X * Initial revision X * X */ X X#include "deliver.h" X#include X#include 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 9318 -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'\" \(7328 characters\) sed "s/^X//" >'lock.c' <<'END_OF_FILE' X/* $Header: lock.c,v 1.4 89/02/10 17:59:46 network Exp $ X * X * Mailbox locking. X * Local hacks for mailbox access should be grafted here. X * X * $Log: lock.c,v $ X * Revision 1.4 89/02/10 17:59:46 network X * Fix typo. X * X * Revision 1.3 89/02/10 15:46:52 network X * V7 support. X * X * Revision 1.2 88/08/30 16:13:14 network X * Portability fixes from Ronald Karr . X * X * Revision 1.1 88/06/06 09:38:48 chip X * Initial revision 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 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 X#include 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 7328 -ne `wc -c <'lock.c'`; then echo shar: \"'lock.c'\" unpacked with wrong size! fi # end of 'lock.c' fi echo shar: End of shell archive. exit 0 -- Chip Salzenberg or A T Engineering Me? Speak for my company? Surely you jest! "It's no good. They're tapping the lines."