Deliver 1.00 (patchlevel 8) Part 2/3
Chip Salzenberg
chip at ateng.ateng.com
Fri Feb 24 02:47:09 AEST 1989
#! /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: context.c copymsg.c debug.c dest.c dfile.c lock.c
# Wrapped by network at ateng on Wed Feb 15 20:36:23 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'context.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'context.c'\"
else
echo shar: Extracting \"'context.c'\" \(2685 characters\)
sed "s/^X//" >'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 <pwd.h>
X#include <grp.h>
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 <sys/types.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 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 <tron at uts.amdahl.com>.
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 <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/types.h>
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 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 <chip at ateng.com> or <uunet!ateng!chip>
A T Engineering Me? Speak for my company? Surely you jest!
"It's no good. They're tapping the lines."
More information about the Comp.sources.bugs
mailing list