[comp.sources.unix] v16i082: Mail delivery program, Part02/03

rsalz@uunet.uu.net (Rich Salz) (11/16/88)

Submitted-by: Chip Salzenberg <chip@ateng.uu.net>
Posting-number: Volume 16, Issue 82
Archive-name: deliver/part02

#! /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
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'\" \(2596 characters\)
sed "s/^X//" >'context.c' <<'END_OF_FILE'
X/* $Header: context.c,v 1.3 88/09/14 19:41:40 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.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->next)
X	{
X		if (strcmp(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->next)
X	{
X		if (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->uid = pw->pw_uid;
X	ct->gid = pw->pw_gid;
X	ct->name = copystr(pw->pw_name);
X	ct->home = copystr(pw->pw_dir);
X
X	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->uid) && (real_gid == 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 MAILBOX_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 /* MAILBOX_GROUP */
END_OF_FILE
if test 2596 -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'\" \(5998 characters\)
sed "s/^X//" >'copymsg.c' <<'END_OF_FILE'
X/* $Header: copymsg.c,v 1.1 88/06/06 09:38:13 chip 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.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, *osender, *oremote;
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 = 0; t < T_MAX; ++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_HEADER], 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	 * If the user specified a sender name, use it;
X	 * else if we found a From_ line, use the sender found therein;
X	 * else use the user name of our real UID.
X	 */
X
X	if (sender && *sender)
X	{
X		osender = sender;
X		oremote = NULL;
X	}
X	else if (fsender)
X	{
X		osender = fsender;
X		oremote = fremote;
X	}
X	else
X	{
X		osender = real_ct->name;
X		oremote = NULL;
X	}
X
X	(void) fputs("From ", dfp[T_HEADER]);
X	if (oremote)
X	{
X		(void) fputs(oremote, dfp[T_HEADER]);
X		(void) fputc('!', dfp[T_HEADER]);
X	}
X	(void) fputs(osender, dfp[T_HEADER]);
X	(void) fputc(' ', dfp[T_HEADER]);
X	(void) time(&now);
X	(void) fputs(ctime(&now), dfp[T_HEADER]);
X
X	/*
X	 * Copy the rest of the header (if any).
X	 */
X
X	for (; !feof(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_HEADER]);
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_HEADER]);
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_HEADER]);
X
X	/*
X	 * Copy the body (if any).
X	 */
X
X	empty_line = FALSE;
X	for (; !feof(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		    && 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 = 0; t < T_MAX; ++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 * 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	if ((fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600)) == -1)
X	{
X		syserr("can't create %s", name);
X		return -1;
X	}
X
X	return fd;
X}
X
END_OF_FILE
if test 5998 -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'\" \(1003 characters\)
sed "s/^X//" >'debug.c' <<'END_OF_FILE'
X/* $Header: debug.c,v 1.1 88/06/06 09:38:23 chip Exp $
X *
X * Debugging output.
X *
X * $Log:	debug.c,v $
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->name);
X		if (d->class == CL_MBOX)
X			message(":%s", d->mailbox);
X
X		message(" (");
X		switch (d->class)
X		{
X		case CL_USER:
X			message("User");
X			break;
X		case CL_MBOX:
X			message("Mailbox");
X			break;
X		case CL_UUCP:
X			message("UUCP");
X			break;
X		}
X		message(", ");
X		switch (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", d->error);
X			break;
X		}
X		message(")\n");
X	}
X}
END_OF_FILE
if test 1003 -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'\" \(2079 characters\)
sed "s/^X//" >'dest.c' <<'END_OF_FILE'
X/* $Header: dest.c,v 1.2 88/08/30 16:12:40 network Exp $
X *
X * Operations on the list of mail destinations.
X *
X * $Log:	dest.c,v $
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->next; d != HEADPTR; d = d->next)
X	{
X		if (d->class != class)
X			continue;
X
X		if (strcmp(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->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->class = class;
X	d->state = ST_WORKING;
X	d->name = copystr(name);
X	if (class == CL_MBOX)
X		d->mailbox = copystr(mailbox);
X
X	if (class != CL_UUCP
X	 && name_context(name) == NULL)
X	{
X		d->state = ST_ERROR;
X		d->error = "No such user";
X	}
X
X	d->prev = HEADPTR->prev;
X	d->next = HEADPTR;
X	d->prev->next = d;
X	d->next->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->next != HEADPTR)
X		return HEADPTR->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->next) != HEADPTR)
X		return d;
X
X	return NULL;
X}
END_OF_FILE
if test 2079 -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'\" \(6731 characters\)
sed "s/^X//" >'dfile.c' <<'END_OF_FILE'
X/* $Header: dfile.c,v 1.2 88/08/25 15:23:56 network Exp $
X *
X * Filter destination(s) through delivery file(s).
X *
X * $Log:	dfile.c,v $
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, goodnames;
X	struct stat st;
X
X	/*
X	 * If there is no global delivery file, then take all the named
X	 * addresses verbatim and return.
X	 */
X
X	if (stat(sys_deliver, &st) == -1)
X	{
X		if (verbose)
X			message("%s: no system delivery file\n",
X				progname);
X
X		for (a = 0; a < dac; ++a)
X			(void) dest(dav[a], (char *) NULL);
X		return;
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	goodnames = 0;
X	for (a = 0; a < dac; ++a)
X	{
X		char    *p;
X
X		for (p = dav[a]; *p; ++p)
X		{
X			if (!isalpha(*p)
X			 && !isdigit(*p)
X			 && !strchr("#%-+._", *p))
X				break;
X		}
X
X		if (*p)
X		{
X			/* Invalid name -- note it and go on. */
X
X			(void) dest(dav[a], (char *) NULL);
X		}
X		else
X		{
X			/* Valid name -- let the delivery file handle it. */
X
X			fav[fac++] = dav[a];
X			++goodnames;
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 (goodnames)
X		(void) do_dfile(eff_ct, fav, (DEST *)NULL);
X
X	free((char *) fav);
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	 * 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->class == CL_USER
X			 && d->state == ST_WORKING
X			 && !d->dfdone)
X			{
X				one_dfile(d);
X				d->dfdone = TRUE;
X			}
X		}
X	} while (nfound > 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->name)) == NULL)
X	{
X		d->state = ST_ERROR;
X		d->error = "Missing context in user_dfile_one()";
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->home, &st) == -1
X	 || (st.st_mode & S_IFMT) != S_IFDIR)
X	{
X		if (verbose)
X			message("%s: home directory %s is missing!\n",
X				ct->name, ct->home);
X		return;
X	}
X
X	if (st.st_mode & 02)
X	{
X		if (verbose)
X			message("%s: home directory is writable to the world!\n",
X				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->home, user_deliver);
X	if (stat(udel_path, &st) == -1)
X	{
X		if (verbose)
X			message("%s has no delivery file\n", 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->state = ST_HOLD;
X
X	fav[0] = "sh";
X	fav[1] = udel_path;
X	fav[2] = 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		{
X			d->state = ST_ERROR;
X			d->error = "No permissions for that context";
X		}
X		else
X			message("No permissions to run as %s\n", ct);
X
X		return -1;
X	}
X
X	/* Make sure that the temp files are readable to the new process. */
X
X	give_temps(ct);
X
X	/* Here we go! */
X
X	if (verbose)
X		message("Processing delivery file as %s\n", ct->name);
X
X	if ((fp = ct_popenv(ct, "/bin/sh", av, "r")) == NULL)
X	{
X		error("can't execute delivery file as %s\n", ct->name);
X		leave(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->state == ST_HOLD)
X			nd->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->state == ST_WORKING) && (mailbox != NULL))
X		{
X			CONTEXT *nct;
X
X			if ((nct = name_context(name)) == NULL)
X			{
X				nd->state = ST_ERROR;
X				nd->error = "Lost context in do_dfile()";
X			}
X			else if (! ok_context(nct))
X			{
X				nd->state = ST_ERROR;
X				nd->error = "No permissions for that context";
X			}
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		{
X			if ((*q++ = *p++) == '/')
X			{
X				while (*p == '/')
X					++p;
X			}
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
Xgive_temps(ct)
XCONTEXT *ct;
X{
X	int     t;
X
X	if (!ct)
X		return;
X
X	for (t = 0; t < T_MAX; ++t)
X	{
X		if (chown(tfile[t], ct->uid, ct->gid) == -1)
X			syserr("can't chown %s", tfile[t]);
X	}
X}
END_OF_FILE
if test 6731 -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'\" \(6559 characters\)
sed "s/^X//" >'lock.c' <<'END_OF_FILE'
X/* $Header: lock.c,v 1.2 88/08/30 16:13:14 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.2  88/08/30  16:13:14  network
X * Portability fixes from Ronald Karr <tron@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
Xlock_name(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
Xunlock_name(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
Xlock_fd(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
Xunlock_fd(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	int     tries, fd;
X
X	for (tries = 0; tries < 10; ++tries)
X	{
X		if (tries)
X			snooze(3);
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		if (verbose && (tries == 0))
X		{
X			message("Waiting to create %s (try #%d)\n",
X				name, tries + 1);
X		}
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 6559 -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

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.