[comp.sources.bugs] Deliver 1.00

chip@ateng.ateng.com (Chip Salzenberg) (02/24/89)

#! /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:  README deliver.8 Makefile config.h context.h deliver.h
#   dest.h patchlevel.h misc.h uid.c getopt.c
# Wrapped by network@ateng on Wed Feb 15 20:36:10 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(2335 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
X$Header: README,v 1.5 88/09/14 20:09:37 network Exp $
X
X
X                             WHAT IS DELIVER?
X
X
XDeliver is a program which delivers electronic mail once it has arrived
Xat a given machine.
X
X
X
X                              WHO CAN USE IT?
X
X
XDeliver is intended for use in three environments:
X
X
X    1.  Any Xenix system.
X
X        Deliver was invented to be a direct replacement for the Xenix
X        program /usr/lib/mail/mail.local.  In fact, on ateng, mail.local is
X	just another link to /usr/bin/deliver.
X
X
X    2.  Any Unix system with smail 2.x.
X
X        You can use deliver for local mail by changing the local mailer
X        definition in config.h:
X	    #define LMAIL(frm)      "/usr/bin/deliver -r '%s'",frm
X
X
X    3.  Any Unix system with either sendmail or smail 3.x.
X
X	Any user can let deliver handle all his mail by putting the
X	following line in his .forward file:
X	    "|/usr/bin/deliver username"
X	Be sure to specify the correct username, or someone else will
X	get your mail!
X
X
X
X                                PORTABILITY
X
X
XDeliver has been compiled and tested under SCO Xenix System V/286 and 4.3
XBSD.  The configuration provides for System V, but I have not been able to
Xtest it. (Patches for all environments are welcome; see below.)
X
X
X
X			   WARNINGS: BE CAREFUL!
X
X
X    1.  Deliver is intended to be run setuid root; otherwise it would not
X        be possible to execute a user delivery file in the context of its
X        owner.  Any setuid root program is a potential security hole!  Be
X        careful of the modes on the deliver binary and the directory where
X        it lives.
X
X    2.  Deliver's flexibility makes it easy to lose lots of mail through
X	carelessness in configuration.  Delivery files should be written
X	with extreme care.
X
X
X
X                          COMPILING AND INSTALLING
X
X
XEdit the Makefile and config.h to taste.  Type "make".  To install, type
X"make install".  See the Makefile for further hints.
X
X
X
X			    HELP SAVE THE WORLD!
X
X
XIf you run into a bug, you are probably not alone.  Save your fellow human
Xbeings from toil and trouble!  Send mail to <bugs-deliver@ateng.uucp>.  Bug
Xreports accepted; patches greatly appreciated.  I will coordinate patches.
X
XThank you, and good night.
X
X	Chip Salzenberg
X	A T Engineering
X
X	<chip@ateng.uucp> or
X	<chip@ateng.uu.net> or
X	<uunet!ateng!chip>
END_OF_FILE
if test 2335 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'deliver.8' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'deliver.8'\"
else
echo shar: Extracting \"'deliver.8'\" \(12161 characters\)
sed "s/^X//" >'deliver.8' <<'END_OF_FILE'
X.\" $Header: deliver.8,v 1.7 89/02/15 19:31:32 network Exp $
X.\"
X.\" Man page for deliver.
X.\"
X.\" $Log:	deliver.8,v $
X.\" Revision 1.7  89/02/15  19:31:32  network
X.\" Complete documentation of recent features.
X.\" 
X.\" Revision 1.6  89/02/15  19:10:54  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.5  88/10/13  13:44:03  network
X.\" patch1: rework the whole man page.
X.\" 
X.\" Revision 1.4  88/10/13  12:18:43  network
X.\" patch1: add "-n" option, and general bug fixes.
X.\" 
X.\" Revision 1.3  88/09/14  19:41:44  network
X.\" Portability to System V and BSD.
X.\" General fixup.
X.\" 
X.\" Revision 1.2  88/08/25  14:58:31  network
X.\" Document -u and -s.  Also general cleanup.
X.\" Change default system delivery file.
X.\" 
X.\" Revision 1.1  88/06/06  09:33:50  chip
X.\" Initial revision
X.\" 
X.\"
X.TH DELIVER 8
X.SH NAME
Xdeliver \- deliver local mail
X.SH SYNOPSIS
X.B deliver
X[ options ] address ...
X.SH DESCRIPTION
XThe
X.I deliver
Xprogram collects a mail message from the standard input and delivers it.
XIt is intended to take over the delivery of all local mail, a job usually
Xhandled by
X.I /bin/mail
X(System V),
X.I /usr/lib/sendmail
X(BSD), or
X.I /usr/lib/mail/mail.local
X(Xenix).
X.PP
X.I deliver
Xintroduces flexibility in the handling of local mail delivery.  All files
Xused to control
X.I deliver
Xare Bourne shell scripts.  Thus anything that can be called by a shell
Xscript can be used to control mail delivery.
X.SH OPTIONS
X.TP
X.B \-b
XInterpret all arguments as mailbox filenames instead of addresses.  Note
Xthat the user running
X.I deliver
Xmust have write permissions on the given mailbox files.  He may also need
Xwrite permissions on their parent directories, depending on the existence
Xof the mailbox file and the local locking protocol.
X.TP
X.B \-n
XInterpret arguments as addresses, but do not run any delivery files; simply
Xdeliver the message to the mailbox(es) of the given user(s).  This option is
Xmost useful when
X.I deliver
Xis executed recursively, since it prevents further recursion.
X.TP
X.B \-A
XPrint resolved addresses; do not deliver the message.  Note that
X.I deliver
Xstill collects a message from the standard input, since delivery files may
Xdo different things depending on message content.  For simple testing,
Xredirect standard input from /dev/null.
X.TP
X.B \-d
XBe verbose; don't deliver to any mailboxes or catch any signals.
X.TP
X.B \-v
XBe verbose, but still deliver.
X.TP
X.B \-t
XDo not remove temporary files before exiting.
X.TP
X.BI \-r " sender"
XPut
X.I sender
Xon the generated From_ line.  Default is to use the address on the From_
Xline in the input, or else the name corresponding to the real uid, or else
X"unknown".
X.TP
X.BI \-h " hostname"
XSet the host name.  The default is configuration dependent.
X.TP
X.BI \-s " system delivery file"
XSpecify an alternate system delivery file.  The default is
X.I /usr/local/lib/deliver.sys.
XFor security reasons, this option disables setuid privileges.
X.TP
X.BI \-p " post-user delivery file"
XSpecify an alternate post-user delivery file.  The default is
X.I /usr/local/lib/deliver.post.
XFor security reasons, this option disables setuid privileges.
X.TP
X.BI \-u " user delivery file"
XSpecify an alternate user delivery file.  The default is
X.I .deliver
X(in each user's home directory).
XFor security reasons, this option disables setuid privileges.
X.PP
XAll command line options are put into environment variables, examined by
X.I deliver
Xon startup; thus all flags are propagated when
X.I deliver
Xis invoked recursively.
X.SH ENVIRONMENT
XFor mail systems based on
X.I smail
X2.x, the LMAIL (local mailer) macro can be changed to call
X.I deliver.
XFor mail systems based on
X.I smail
X3.x or
X.I sendmail,
Xa similar arrangement may be made; or individual users can invoke
X.I deliver
Xby mentioning it in their
X.I .forward
Xfiles.
X.PP
XFor Xenix systems,
X.I deliver
Xmay be used as a direct replacement for
X.IR /usr/lib/mail/mail.local.
X.PP
XFor stock Unix systems, it may be possible to make
X.I /bin/rmail
Xa link to
X.I deliver;
Xhowever, this configuration has not been tested and is not recommended.
XAny postmaster motivated enough to install
X.I deliver,
Xand who wants something better than the standard
X.I /bin/rmail,
Xshould install
X.I smail.
X.SH OPERATION
XBy default,
X.I deliver
Xdeposits mail in the system mailbox for the named user(s).  Also, as a
Xnecessity for use with
X.I smail
X2.x,
X.I deliver
Xalso understands UUCP-style bang addresses well enough to forward the
Xmessage correctly, using
X.I uux.
X.PP
XHowever, this basic behavior only scratches the surface: the usefulness of
X.I deliver
Xderives from its flexibility; its behavior can be made to depend on the
Xrecipient(s), content or any other aspect of mail messages.
X.PP
XWhen
X.I deliver
Xstarts execution, it interprets its arguments in one of three ways.  If the
X.B \-b
X(mailbox) option was specified, then all arguments are interpreted as
Xmailbox pathnames.  Otherwise, if a system delivery file exists and the
X.B \-n
X(no delivery files) option was not specified,
X.I deliver
Xexecutes the system delivery file with all of deliver's arguments
Xas its arguments.
X.I deliver
Xinterprets the delivery file's output as described below.  This procedure
Xgives the postmaster control over delivery to non-existent hosts and users.
XIf the
X.B \-n
Xoption is specified or if there is no system delivery file,
X.I deliver
Xinterprets all its arguments as mail addresses.
X.PP
XAfter possibly executing the system delivery file,
X.I deliver
Xlooks in its list of destinations for valid user names without explicitly
Xnamed mailboxes.  If any of these users have user delivery files in
Xtheir home directories, and if the
X.B \-n
Xoption was not specified,
X.I deliver
Xexecutes each user delivery file with the name of the given user as its
Xonly argument.
X.PP
XAfter executing any user delivery files,
X.I deliver
Xlooks in its list of destinations for simple user names and UUCP
Xaddresses.  If any are found, if the post-user delivery file exists,
Xand if the
X.B \-n
Xoption was not specified,
X.I deliver
Xexecutes the post-user delivery file with these addresses as its arguments.
X.SH "DELIVERY FILES"
XDelivery files are shell scripts.  They are executed by
X.I deliver
Xto control delivery to users.  Note that delivery files do
X.I not
Xcontrol delivery to explicitly named mailboxes.
X.PP
XOn each system the postmaster may create a
X.I system delivery file
Xto controls delivery of all messages.
XThe system delivery file, if it exists, is executed
Xwith the name(s) specified on the
X.I deliver
Xcommand line as its arguments.
X.PP
XThe postmaster may also create a
X.I post-user delivery file
Xwhich is executed after any user delivery files, but before delivery of the
Xmessage to any mailboxes.  This file is particularly useful for implementing
Xsystem-wide aliases, since it can deal with addresses generated by user
Xdelivery files, whereas the system delivery file cannot.
X.PP
XFinally, each user may create a
X.I user delivery file
Xin his home directory.  User delivery files are always executed with exactly
Xone argument: the name of the user in whose home directory the file is
Xfound.
X.PP
XRecursive execution of
X.I deliver
Xis useful, especially with the
X.B \-b
X(mailbox) and
X.B \-n
X(no delivery files) flags.  For example, a user may wish to transform a
Xmessage body before it is stored in a mailbox.  This may be done with a user
Xdelivery file and recursive execution of
X.I deliver.
XFor example, the following user delivery file translates all incoming
Xmessage bodies to lower case, and stores them in the user's default mailbox:
X.TP
X(cat $HEADER; tr '[A-Z]' '[a-z]' <$BODY) | deliver -n "$1"
X.PP
XWhen
X.I deliver
Xexecutes a delivery file, it sets several environment variables, listed
Xbelow.
XNote that these environment variables are both set and used by
X.I deliver;
Xtherefore, all command line options automatically propagate when
X.I deliver
Xis run recursively (within a delivery file).  The environment variable
Xnames set and used by
X.I deliver
Xare:
X.TP
X.B DELFLAGS
XThe command line flags, if any, specified on the
X.I deliver
Xcommand line.
X.TP
X.B SYSDELFILE
XThe system delivery filename.
X.TP
X.B POSTDELFILE
XThe post-user delivery filename.
X.TP
X.B USERDELFILE
XThe user delivery filename, relative to the home directory of each user.
X.TP
X.B HOSTNAME
XThe local host name, either the real hostname or a name specified with the
X.B \-h
Xoption to
X.I deliver.
X.TP
X.B SENDER
XThe sender, either an address specified with
X.B \-r
Xoption to
X.I deliver,
Xor the address given in the From_ line of the message, or the user who
Xinvoked
X.I deliver.
X.TP
X.B HEADER
XThe name of the temporary file containing the message header.
X.TP
X.B BODY
XThe name of the temporary file containing the message body.
X.PP
X.I deliver
Xmonitors the standard output of delivery files for lines of two forms:
Xeither "user" or "user:mailbox".  Those users whose names appear in the
Xoutput of a delivery file will receive the message.  If a mailbox name
Xappears after the user name, then that mailbox receives the message.  If a
Xmailbox name is not specified, the user's default mailbox is used. (The
Xdefault mailbox for a user is configuration-dependent.) If a mailbox is not
Xan absolute pathname, it is interpreted relative to the home directory of
Xthe named user.
X.PP
X.B NOTE 1:
XWhen
X.I deliver
Xexecutes a delivery file, it expects that delivery file to explicitly name
Xall users (and, optionally, mailboxes) where the message should be
Xdelivered.  If a delivery file does not name any users in its output, then
Xthe message will not be delivered to anyone whose mail delivery is
Xcontrolled by that delivery file.
X.PP
XTherefore, a user delivery file containing only "exit" will keep the given
Xuser from receiving any mail.  A system delivery file containing only "exit"
Xwill cause
X.B all
Xmail to disappear.  So be careful!
X.PP
X.B NOTE 2:
XIf
X.I deliver
Xis setuid root -- which it should be for normal operation -- then the system
Xdelivery file is executed as root.  Be
X.I "very careful"
Xabout its permissions and its contents!  Carelessness here can easily
Xcreate a security problem.
X.PP
X.B NOTE 3:
XAll user delivery files are executed in the context of the user in whose
Xhome directory they reside.  A user's "context" includes the uid, gid, and
Xhome directory as specified in /etc/passwd.
X.PP
X.B NOTE 4:
XFor security reasons, if a user's home directory is writable to the world,
X.I deliver
Xwill ignore any delivery file that might be found there.
X.PP
X.B NOTE 5:
XFor security reasons,
X.I deliver
Xrejects lines of the form "user:mailbox" when generated by a user delivery
Xfile unless they are output by the given user's delivery file.  In other
Xwords, no user can request writing a mailbox as another user.
X.SH LOCKING
XSeveral preprocessor labels may be defined during compilation to control
Xthe method(s) used by
X.I deliver
Xto lock mailboxes.  These labels are:
X.RS
X.PP
X.B ML_DOTLOCK
XLock on exclusive creation of the mailbox name with ".lock"
Xappended.  (Version 7 and early BSD mailers use this method.)
X.PP
X.B ML_DOTMLK
XLock on exclusive creation of
X.I /tmp/basename.mlk,
Xwhere
X.I basename
Xis the last component of the mailbox pathname.  (Xenix mailers use this
Xmethod.)
X.PP
X.B ML_LOCKF
XExclusively lock mailbox with lockf().
X.PP
X.B ML_FCNTL
XExclusively lock mailbox with fcntl().
X.PP
X.B ML_LOCKING
XExclusively lock mailbox with locking().
X.PP
X.RE
XOne or both of ML_DOTLOCK and ML_DOTMLK may be specified.  At most one of
XML_LOCKF, ML_FCNTL or ML_LOCKING may be specified.
X.SH FILES
X/usr/local/lib/deliver.sys      system delivery file
X.br
X/usr/local/lib/deliver.post     post-user delivery file
X.br
X~user/.deliver                  user delivery file(s)
X.br
X/etc/systemid                   system name (Xenix only)
X.SH SUPPORT
XEnhancements, enhancement requests, trouble reports, etc., should be mailed
Xto <bugs-deliver@ateng.com>; or for UUCP-only sites,
X<uunet!ateng!bugs-deliver>.
X.SH "SEE ALSO"
X.IR mail (1),
X.IR uux (1),
X.IR smail (8),
X.IR sendmail (8)
END_OF_FILE
if test 12161 -ne `wc -c <'deliver.8'`; then
    echo shar: \"'deliver.8'\" unpacked with wrong size!
fi
# end of 'deliver.8'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(3736 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X# $Header: Makefile,v 1.12 88/11/30 16:24:30 network Exp $
X#
X# Makefile for deliver
X#
X#  +----------------+
X#  | Things to make |
X#  +----------------+
X#
X#       deliver         Compile and link the deliver program.
X#       install         Install deliver.  (You must be root to do this.)
X#       lint            Run lint on all sources, creating lint.out.
X#       shar            Create distribution sharchives.
X#       clean           Clean up.
X#       clobber         Remove everything that can be regenerated.
X#
X#  +---------------+
X#  | Configuration |
X#  +---------------+
X#
X# SHELL
X#       I don't have to tell you...
X#
X# COPY
X#       Your local copy program.  SCO Xenix users may want to change this
X#       to "copy -m" which preserves file modification time.
X#
X# SHAR
X#       Your local sharchive generator.
X#
X# CFLAGS
X#       Compile-time flags to cc.
X#       For BSD systems, include "-DBSD".
X#       For USG (System III and System V) systems, include "-DUSG".
X#
X# LDFLAGS
X#       Link-time flags to cc.  The -i flag creates pure (sharable) code.
X#
X# LIBS
X#       Depending on your environment, you may or may not need to link
X#       with "-lx".  SCO Xenix System V needs it; Altos Xenix doesn't.
X#
X# BIN
X#       Target directory for installation; /usr/bin is recommended.
X#       You may use /usr/local/bin (or whatever), but you must be sure
X#       that the directory you choose is in your PATH during mail
X#       transmission and delivery.
X#
X# DELSHAR
X#       Basename of sharchives created by "make shar".
X#
X
XSHELL = /bin/sh
XCOPY =  cp
XSHAR =  shar
XCFLAGS = -O
XLDFLAGS = -i
XLIBS = -lx
XBIN =   /usr/bin
XDELSHAR =  deliver.sh
X
X#
X# The files that make up the deliver distribution.
X#
X
XDOCS =  README deliver.8
XMF   =  Makefile
X
XHDRS =  config.h context.h deliver.h dest.h patchlevel.h misc.h
X
XDELSRC1 = context.c copymsg.c debug.c dest.c dfile.c lock.c
XDELSRC2 = main.c mbox.c procs.c subs.c sysdep.c uucp.c
XDELSRCS = $(DELSRC1) $(DELSRC2)
XUIDSRCS = uid.c
XCOMSRCS = getopt.c
X
XDELOBJS = context.o copymsg.o debug.o dest.o dfile.o lock.o \
X	  main.o mbox.o procs.o subs.o sysdep.o uucp.o
XUIDOBJS = uid.o
XCOMOBJS = getopt.o
X
X#
X# For GNU Make.  Sorry about the ugliness.
X#
X
X.PHONY: all install lint shar clean clobber
X
X#
X# The default target.
X#
X
Xall: deliver uid
X
X#
X# "make clobber" implies "make clean".
X#
X
Xclobber:: clean
X
X#
X# How to compile and link the program.
X#
X
Xdeliver: $(DELOBJS) $(COMOBJS)
X	$(CC) $(LDFLAGS) -o $@ $(DELOBJS) $(COMOBJS) $(LIBS)
X$(DELOBJS): $(HDRS)
X
Xinstall: deliver uid
X	@if [ `./uid -uU | fgrep '(root)' | wc -l` -ne 2 ]; \
X	then \
X		echo "Sorry!  You must be root to install deliver."; \
X		exit 1; \
X	fi
X	$(COPY) deliver $(BIN)/deliver
X	chgrp root $(BIN)/deliver
X	chown root $(BIN)/deliver
X	chmod 4711 $(BIN)/deliver
X
Xclean::
X	rm -f $(DELOBJS)
Xclobber::
X	rm -f deliver
X
X#
X# A little program to check on user and group id's.
X# (I wish that the System V "id" program were available everywhere.)
X#
X
Xuid: $(UIDSRCS) $(COMOBJS) config.h
X	$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(UIDSRCS) $(COMOBJS) $(LIBS)
X	@rm -f $(UIDOBJS)
X
Xclobber::
X	rm -f uid
X
X#
X# Common subroutines
X#
X
X$(COMOBJS): config.h
X
Xclean::
X	rm -f $(COMOBJS)
X
X#
X# Look for fuzz.
X#
X
Xlint: deliver.lint uid.lint
X
Xdeliver.lint: $(HDRS) $(DELSRCS) $(COMSRCS)
X	lint $(DELSRCS) $(COMSRCS) -lc $(LIBS) >$@
X
Xuid.lint: config.h $(UIDSRCS) $(COMSRCS)
X	lint $(UIDSRCS) $(COMSRCS) -lc $(LIBS) >$@
X
Xclean::
X	rm -f *.lint
X
X#
X# Make distribution sharchives.
X#
X
Xshar:   $(DELSHAR).01 $(DELSHAR).02 $(DELSHAR).03
X$(DELSHAR).01: $(DOCS) $(MF) $(HDRS) $(UIDSRCS) $(COMSRCS)
X	$(SHAR) >$@ $(DOCS) $(MF) $(HDRS) $(UIDSRCS) $(COMSRCS)
X$(DELSHAR).02: $(DELSRC1)
X	$(SHAR) >$@ $(DELSRC1)
X$(DELSHAR).03: $(DELSRC2)
X	$(SHAR) >$@ $(DELSRC2)
X
Xclobber::
X	rm -f $(DELSHAR).??
END_OF_FILE
if test 3736 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'config.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'config.h'\"
else
echo shar: Extracting \"'config.h'\" \(7659 characters\)
sed "s/^X//" >'config.h' <<'END_OF_FILE'
X/* $Header: config.h,v 1.9 89/02/15 19:09:37 network Exp $
X *
X * Deliver configuration.
X *
X * $Log:	config.h,v $
X * Revision 1.9  89/02/15  19:09:37  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.8  89/02/10  18:08:24  network
X * Fix location of mailboxes.
X * 
X * Revision 1.7  89/02/10  15:45:51  network
X * V7 support.
X * 
X * Revision 1.6  88/11/26  13:19:37  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.5  88/11/21  13:12:11  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.4  88/11/18  12:16:39  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.3  88/09/14  19:41:35  network
X * Portability to System V and BSD.
X * General fixup.
X * 
X * Revision 1.2  88/08/25  15:21:15  network
X * Change default system delivery file to "/usr/local/lib/deliver.sys".
X * Add ENV_SYSDEL and ENV_USERDEL for system and user delivery filenames.
X * 
X * Revision 1.1  88/06/06  09:37:27  chip
X * Initial revision
X * 
X */
X
X/*----------------------------------------------------------------------
X * SCO Xenix System V compilers define M_SYSV, which implies USG.
X */
X
X#ifdef M_SYSV
X#ifndef USG
X#define USG
X#endif
X#endif
X
X/*----------------------------------------------------------------------
X * Trusted users.
X * Deliver permits "trusted" users to specify delivery filenames
X * without renouncing setuid privileges.  Essentially, these users
X * are given the root password.  Beware!
X */
X
X#define TRUSTED_USERS   "root", "uucp"
X
X/*----------------------------------------------------------------------
X * Signal function type.
X * Signal catching routines have this return value.
X * (For System V Release 3.0 or later, use "void".)
X */
X
X#define SIGTYPE int
X
X/*----------------------------------------------------------------------
X * Signal flag type.
X * Variables of this type may be set by signal catching routines.
X */
X
X#ifdef __STDC__
X#define SIGFLAG sig_atomic_t
X#else
X#define SIGFLAG short   /* or "volatile short" for aggressive optimizers */
X#endif
X
X/*----------------------------------------------------------------------
X * Various kinds of mailbox locking.
X * You may define one or both of ML_DOTLOCK and ML_DOTMLK.
X * You may define no more than one of ML_FCNTL, ML_LOCKF and ML_LOCKING.
X *
X * File creation locking:
X *     ML_DOTLOCK   create <mailbox>.lock       (most systems except BSD4.3)
X *     ML_DOTMLK    create /tmp/<basename>.mlk  (Xenix)
X *
X * Kernel record locking:
X *     ML_FCNTL     lock with fcntl(F_SETLKW)   (SVID systems only)
X *     ML_LOCKF     lock with lockf(F_LOCK)     (SVID systems only)
X *     ML_LOCKING   lock with locking(LK_LOCK)  (Xenix systems only)
X */
X
X#ifdef M_XENIX
X#define ML_DOTMLK
X#define ML_LOCKING
X#else
X#define ML_DOTLOCK
X#endif
X
X/*----------------------------------------------------------------------
X * Maximum filename length.
X * Note that this is for _filenames_, not _pathnames_.
X * For AT&T file systems, the usual value is 14.
X * For Berzerkley file systems, use something big like 255.
X */
X
X#ifdef BSD
X#define MAX_NAMESIZE    255
X#else
X#define MAX_NAMESIZE    14
X#endif
X
X/*----------------------------------------------------------------------
X * How to get the host name.
X * Define one.
X *
X * HOSTFILE             file containing name    (Xenix)
X * UNAME                uname()                 (System V)
X * GETHOSTNAME          gethostname()           (BSD)
X * HOSTNAME             host name string        (V7)
X */
X
X#ifdef M_XENIX
X#define HOSTFILE   "/etc/systemid"
X#else
X#ifdef USG
X#define UNAME
X#else
X#ifdef BSD
X#define GETHOSTNAME
X#else
X#define HOSTNAME   "cleese"
X#endif
X#endif
X#endif
X
X/*----------------------------------------------------------------------
X * Is <varargs.h> or <stdarg.h> available?
X */
X
X#ifdef __STDC__
X#define HAS_STDARG
X#else
X#ifdef USG
X#define HAS_VARARGS
X#else
X#ifdef BSD
X#define HAS_VARARGS
X#endif
X#endif
X#endif
X
X/*----------------------------------------------------------------------
X * Are vprintf() and friends available?
X */
X
X#ifdef USG
X#define HAS_VPRINTF
X#endif
X
X/*----------------------------------------------------------------------
X * Is putenv() available?
X */
X
X#ifdef USG
X#define HAS_PUTENV
X#endif
X
X/*----------------------------------------------------------------------
X * Is getopt() available?
X */
X
X#ifdef USG
X#define HAS_GETOPT
X#endif
X
X/*----------------------------------------------------------------------
X * Is setvbuf() backwards?
X */
X
X#ifdef M_XENIX
X#define XENIX_SETVBUF
X#endif
X
X/*----------------------------------------------------------------------
X * Name of shell used to execute delivery files.
X */
X
X#define SHELL   "/bin/sh"
X
X/*----------------------------------------------------------------------
X * Characters that may not appear in addresses.
X * (This string should include all metacharacters for your chosen shell.)
X */
X
X#define SANITIZE   "$*?=\\`'\"|^&;{}()<> \t\n"
X
X/*----------------------------------------------------------------------
X * Standard mailbox location.
X *
X * Define either MBX_NAME or MBOX_DIR.
X * If MBX_NAME is defined, then the default mailbox is a file with
X * that name in the user's home directory.
X * If MBX_DIR is defined, then the default mailbox is a file in that
X * directory with the same name as the user.
X *
X * Define MBX_GROUP if all mailboxes must be owned by a specific group.
X * (System V requires this feature.)  If MBX_GROUP is not defined,
X * mailboxes will have their groups set to the recipients' default group.
X *
X * Define MBX_MODE to the file access modes for new mailboxes.
X * (System V requires group write permissions, i.e. 0020.)
X */
X
X#if defined(USG) && !defined(M_XENIX)
X/* #define MBX_NAME   "mbox" */
X#define MBX_DIR     "/usr/mail"
X#define MBX_MODE    0660
X#define MBX_GROUP   "mail"
X#else
X/* #define MBX_NAME   "mbox" */
X#define MBX_DIR     "/usr/spool/mail"
X#define MBX_MODE    0600
X#endif
X
X/*----------------------------------------------------------------------
X * Names of delivery files.
X *
X * SYS_DELIVER          system-wide delivery file
X * POST_DELIVER         post-user delivery file
X * USER_DELIVER         user delivery file (in user's home directory)
X */
X
X#define SYS_DELIVER     "/usr/local/lib/deliver.sys"
X#define POST_DELIVER    "/usr/local/lib/deliver.post"
X#define USER_DELIVER    ".deliver"
X
X/*----------------------------------------------------------------------
X * Environment variables passed to child processes.
X */
X
X#define ENV_DFLAGS      "DELFLAGS"      /* Flags: [-[Avdt]]             */
X#define ENV_SYSDEL      "SYSDELFILE"    /* System delivery file         */
X#define ENV_POSTDEL     "POSTDELFILE"   /* Post-user delivery file      */
X#define ENV_USERDEL     "USERDELFILE"   /* User delivery file           */
X
X#define ENV_HOSTNAME    "HOSTNAME"      /* Name of this host            */
X#define ENV_SENDER      "SENDER"        /* Message sender               */
X#define ENV_HEADER      "HEADER"        /* Message header file          */
X#define ENV_BODY        "BODY"          /* Message body file            */
END_OF_FILE
if test 7659 -ne `wc -c <'config.h'`; then
    echo shar: \"'config.h'\" unpacked with wrong size!
fi
# end of 'config.h'
fi
if test -f 'context.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'context.h'\"
else
echo shar: Extracting \"'context.h'\" \(505 characters\)
sed "s/^X//" >'context.h' <<'END_OF_FILE'
X/* $Header: context.h,v 1.2 89/02/10 15:46:13 network Exp $
X *
X * User context, as found in /etc/passwd.
X *
X * $Log:	context.h,v $
X * Revision 1.2  89/02/10  15:46:13  network
X * V7 support.
X * 
X * Revision 1.1  88/06/06  09:37:40  chip
X * Initial revision
X * 
X */
X
X/*----------------------------------------------------------------------
X * The context structure.
X */
X
X#define CONTEXT struct context
XCONTEXT {
X	CONTEXT *ct_next;
X	int     ct_uid;
X	int     ct_gid;
X	char    *ct_name;
X	char    *ct_home;
X};
END_OF_FILE
if test 505 -ne `wc -c <'context.h'`; then
    echo shar: \"'context.h'\" unpacked with wrong size!
fi
# end of 'context.h'
fi
if test -f 'deliver.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'deliver.h'\"
else
echo shar: Extracting \"'deliver.h'\" \(4153 characters\)
sed "s/^X//" >'deliver.h' <<'END_OF_FILE'
X/* $Header: deliver.h,v 1.8 89/02/15 19:11:07 network Exp $
X *
X * General pull-it-together include file.
X *
X * $Log:	deliver.h,v $
X * Revision 1.8  89/02/15  19:11:07  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:27  network
X * V7 support.
X * 
X * Revision 1.6  88/11/28  18:07:53  network
X * patch5: Copy temp files before handing them to delivery files.
X * patch5: Introduce "uid" program.
X * 
X * Revision 1.5  88/11/18  12:16:55  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.4  88/10/13  12:19:18  network
X * patch1: add "-n" option, and general bug fixes.
X * 
X * Revision 1.3  88/09/14  19:41:50  network
X * Portability to System V and BSD.
X * General fixup.
X * 
X * Revision 1.2  88/08/30  16:13:03  network
X * Add copystr().
X * 
X * Revision 1.1  88/06/06  09:36:49  chip
X * Initial revision
X * 
X */
X
X#include <stdio.h>
X#include <ctype.h>
X
X#include "config.h"
X#include "misc.h"
X#include "context.h"
X#include "dest.h"
X
X/*----------------------------------------------------------------------
X * Global data
X */
X
Xextern  int     verbose;        /* Print debugging messages?            */
Xextern  int     dryrun;         /* Are we making a dry run?             */
Xextern  int     rundfiles;      /* Run delivery files at all?           */
Xextern  int     printaddrs;     /* Address resolution only?             */
Xextern  int     leavetemps;     /* Leave temp files for later perusal   */
Xextern  int     boxdelivery;    /* Args are mailboxes, not addresses    */
Xextern  char    *sender;        /* Who is sending this message?         */
X
Xextern  char    *progname;      /* Name this program was invoked under  */
Xextern  char    *hostname;      /* Name of this host                    */
X
Xextern  char    *sys_deliver;   /* Systemwide delivery file             */
Xextern  char    *post_deliver;  /* Post-user delivery file              */
Xextern  char    *user_deliver;  /* User delivery file                   */
Xextern  char    *shell;         /* Shell used to run delivery files     */
X
Xextern  int     eff_uid;        /* Returned by geteuid()                */
Xextern  int     eff_gid;        /* Returned by getegid()                */
Xextern  int     real_uid;       /* Returned by getuid()                 */
Xextern  int     real_gid;       /* Returned by getgid()                 */
X
Xextern  CONTEXT *eff_ct;        /* Context of effective uid             */
Xextern  CONTEXT *real_ct;       /* Context of real uid                  */
X
Xextern  int     trust_user;     /* Do we trust the user that called us? */
Xextern  int     trust_delfiles; /* Do we trust the delivery files?      */
X
X/* Temp file indices: */
X#define T_HDR      0    /* Message header                       */
X#define T_BODY     1    /* Message body                         */
X#define T_HDRCOPY  2    /* Copy of message header               */
X#define T_BODYCOPY 3    /* Copy of message body                 */
X#define T_MAX      4    /* Number of temp files                 */
X
Xextern  char    *ttype[T_MAX];  /* Temp file types (for messages)       */
Xextern  char    *tfile[T_MAX];  /* Temp file names                      */
Xextern  char    *tenv[T_MAX];   /* Temp file environment names          */
Xextern  int     tfd[T_MAX];     /* Temp file fd's                       */
X
Xextern  SIGFLAG got_sig;        /* We caught a signal and should exit   */
X
X/*----------------------------------------------------------------------
X * Global functions
X */
X
Xchar    *basename();
Xchar    *gethost();
Xchar    *copystr();
Xchar    *derrmsg();
Xchar    *zalloc();
Xchar    *srealloc();
X
XCONTEXT *name_context();
XCONTEXT *uid_context();
X
XFILE    *ct_popenv();
Xint     ct_pclose();
X
XDEST    *dest();
XDEST    *first_dest();
XDEST    *next_dest();
END_OF_FILE
if test 4153 -ne `wc -c <'deliver.h'`; then
    echo shar: \"'deliver.h'\" unpacked with wrong size!
fi
# end of 'deliver.h'
fi
if test -f 'dest.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'dest.h'\"
else
echo shar: Extracting \"'dest.h'\" \(2481 characters\)
sed "s/^X//" >'dest.h' <<'END_OF_FILE'
X/* $Header: dest.h,v 1.2 89/02/10 15:46:38 network Exp $
X *
X * Description of a mail destination and its state.
X *
X * $Log:	dest.h,v $
X * Revision 1.2  89/02/10  15:46:38  network
X * V7 support.
X * 
X * Revision 1.1  88/06/06  09:37:48  chip
X * Initial revision
X * 
X */
X
X/*----------------------------------------------------------------------
X * Destination class.
X */
X
Xtypedef enum {
X	CL_USER,                /* User name, no mailbox                */
X	CL_MBOX,                /* User name, with mailbox name         */
X	CL_UUCP                 /* UUCP address (bang path)             */
X} DCLASS;
X
X/*----------------------------------------------------------------------
X * Destination state.
X */
X
Xtypedef enum {
X	ST_WORKING,             /* the "normal" state                   */
X	ST_HOLD,                /* on hold during expansion             */
X	ST_DONE,                /* all processing complete              */
X	ST_ERROR                /* "something is horribly wrong"        */
X} DSTATE;
X
X/*----------------------------------------------------------------------
X * Types of destination errors.
X */
X
Xtypedef enum {
X	E_IVADDR,               /* invalid address string               */
X	E_NSUSER,               /* no such user                         */
X	E_NSHOST,               /* no such host (UUCP addresses)        */
X	E_CTPERM,               /* no permissions for that context      */
X	E_CTLOST,               /* context lost (should never happen)   */
X	E_MBOX,                 /* can't write to mailbox               */
X	E_UUX                   /* can't pipe to uux                    */
X} DERROR;
X
X/*----------------------------------------------------------------------
X * Structure describing a mail destination.
X */
X
X#define DEST    struct dest
XDEST {
X	DEST    *d_next;        /* next destination in the chain        */
X	DEST    *d_prev;        /* previous destination in the chain    */
X	DCLASS  d_class;        /* destination class                    */
X	DSTATE  d_state;        /* destination state                    */
X	DERROR  d_error;        /* error message (if state is ERROR)    */
X	int     d_dfdone;       /* boolean -- delivery file was run     */
X	char    *d_name;        /* context for delivery                 */
X	char    *d_mailbox;     /* mailbox name or NULL for default     */
X};
X
X/*----------------------------------------------------------------------
X * Action macros.
X */
X
X#define dest_err(d,m)   ((d)->d_state = ST_ERROR, (d)->d_error = (m))
END_OF_FILE
if test 2481 -ne `wc -c <'dest.h'`; then
    echo shar: \"'dest.h'\" unpacked with wrong size!
fi
# end of 'dest.h'
fi
if test -f 'patchlevel.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'patchlevel.h'\"
else
echo shar: Extracting \"'patchlevel.h'\" \(21 characters\)
sed "s/^X//" >'patchlevel.h' <<'END_OF_FILE'
X#define PATCHLEVEL 8
END_OF_FILE
if test 21 -ne `wc -c <'patchlevel.h'`; then
    echo shar: \"'patchlevel.h'\" unpacked with wrong size!
fi
# end of 'patchlevel.h'
fi
if test -f 'misc.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'misc.h'\"
else
echo shar: Extracting \"'misc.h'\" \(2761 characters\)
sed "s/^X//" >'misc.h' <<'END_OF_FILE'
X/* $Header: misc.h,v 1.5 89/02/10 15:47:15 network Exp $
X *
X * Miscellaneous definitions.
X *
X * $Log:	misc.h,v $
X * Revision 1.5  89/02/10  15:47:15  network
X * V7 support.
X * 
X * Revision 1.4  88/11/26  13:21:01  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.3  88/11/21  13:12:29  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.2  88/09/14  19:42:20  network
X * Portability to System V and BSD.
X * General fixup.
X * 
X * Revision 1.1  88/06/06  09:37:58  chip
X * Initial revision
X * 
X */
X
X/*
X * Non-portable include files
X */
X
X#ifdef USG
X#include <fcntl.h>
X#include <string.h>
X#include <memory.h>
X#endif
X
X#ifdef BSD
X#include <strings.h>
X#include <sys/file.h>
X#endif
X
X/*
X * Constants
X */
X
X#ifdef NULL
X#undef NULL
X#endif
X#define NULL    0               /* The One True NULL */
X
X#define FALSE   0
X#define TRUE    1
X
X#ifndef O_RDONLY
X#define O_RDONLY   0
X#define O_WRONLY   1
X#define O_RDWR     2
X#endif
X
X/*
X * Macros.
X */
X
X/* Length parameter for fgets() on given buffer. */
X
X#define GETSIZE(buf)    (int) (sizeof(buf) - 1)
X
X/*
X * Public data.
X */
X
Xextern  char    **environ;
X
X/*
X * Common library functions.
X */
X
Xextern  char    *ctime();
Xextern  char    *getenv();
Xextern  char    *malloc();
Xextern  char    *realloc();
Xextern  char    *mktemp();
Xextern  int     putenv();
Xextern  long    lseek();
Xextern  long    time();
Xextern  void    free();
Xextern  SIGTYPE (*signal())();
X
X/*
X * String search functions.
X */
X
X#ifndef USG
X
X#ifndef BSD
Xextern  char    *index();
Xextern  char    *rindex();
X#endif /* not BSD */
X
X#define strchr          index
X#define strrchr         rindex
X
X#endif
X
X/*
X * Memory copy and zero.
X */
X
X#ifdef USG
X#define Copy(d,s,n)     (void) memcpy(d,s,n)
X#define Zero(d,n)       (void) memset(d,0,(int)(n))
X#else /* not USG */
X#ifdef BSD
X#define Copy(d,s,n)     bcopy(s,d,n)
X#define Zero(d,n)       bzero(d,n)
X#else /* not BSD */
X#define MEMFUNCS        /* define Copy() and Zero() in sysdep.c */
X#endif /* not BSD */
X#endif /* not USG */
X
X/*
X * Line-buffering on stdio files.
X */
X
X#ifdef USG
X
Xextern  int     setvbuf();
X
X#ifdef XENIX_SETVBUF
X#define Linebuf(f)      (void) setvbuf(f, _IOLBF, (char *)NULL, BUFSIZ)
X#else
X#define Linebuf(f)      (void) setvbuf(f, (char *)NULL, _IOLBF, BUFSIZ)
X#endif
X
X#else /* not USG */
X#ifdef BSD
X
Xextern  int     setlinebuf();
X
X#define Linebuf(f)      (void) setlinebuf(f)
X
X#else /* not BSD */
X
X#define Linebuf(f)      /* can't do it */
X
X#endif /* not BSD */
X#endif /* not USG */
END_OF_FILE
if test 2761 -ne `wc -c <'misc.h'`; then
    echo shar: \"'misc.h'\" unpacked with wrong size!
fi
# end of 'misc.h'
fi
if test -f 'uid.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'uid.c'\"
else
echo shar: Extracting \"'uid.c'\" \(2771 characters\)
sed "s/^X//" >'uid.c' <<'END_OF_FILE'
X/* $Header: uid.c,v 1.2 88/11/28 18:08:20 network Exp $
X *
X * I wish the System V "id" program were universally available; but it
X * isn't, so I've written this replacement.
X *
X * usage: uid [-options]
X *
X * Default action is to print one line in the manner of "id":
X *      uid=201(chip) gid=50(group) euid=0(root) egid=0(root)
X * (Note that the "euid" and "egid" entries are not output if they are
X * the same as the "uid" and "gid" values.)
X *
X * If an option string is specified, it disables the normal behavior in
X * favor of displaying id information, one per line, in the order that
X * the options appear in the option string.  Legal options are:
X *      u       real uid
X *      g       real gid
X *      U       effective uid
X *      G       effective gid
X *
X * NOTE: This program is not a paragon of good style.
X *       It's just something I needed for a Makefile.
X *
X * $Log:	uid.c,v $
X * Revision 1.2  88/11/28  18:08:20  network
X * patch5: Copy temp files before handing them to delivery files.
X * patch5: Introduce "uid" program.
X * 
X */
X
X#include <stdio.h>
X#include <pwd.h>
X#include <grp.h>
X#include "config.h"
X
X#ifdef NULL
X#undef NULL
X#endif
X#define NULL 0
X
Xextern  struct passwd   *getpwuid();
Xextern  struct group    *getgrgid();
X
Xchar    *progname = "uid";
X
Xchar    *uid_desc();
Xchar    *gid_desc();
X
Xmain(argc, argv)
Xint     argc;
Xchar    **argv;
X{
X	int     uid, gid, euid, egid;
X	int     c, lines, errcount;
X
X	uid = getuid();
X	gid = getgid();
X	euid = geteuid();
X	egid = getegid();
X
X	errcount = 0;
X	lines = 0;
X
X	while ((c = getopt(argc, argv, "ugUG")) != EOF)
X	{
X		switch (c)
X		{
X		case 'u':
X			(void) printf("%s\n", uid_desc(uid));
X			++lines;
X			break;
X
X		case 'g':
X			(void) printf("%s\n", gid_desc(gid));
X			++lines;
X			break;
X
X		case 'U':
X			(void) printf("%s\n", uid_desc(euid));
X			++lines;
X			break;
X
X		case 'G':
X			(void) printf("%s\n", gid_desc(egid));
X			++lines;
X			break;
X
X		case '?':
X			++errcount;
X			break;
X		}
X	}
X
X	if (errcount)
X	{
X		(void) fprintf(stderr, "usage: uid [-ugUG]\n");
X		exit(1);
X	}
X
X	if (lines == 0)
X	{
X		(void) printf("uid=%s", uid_desc(uid));
X		(void) printf(" gid=%s", gid_desc(gid));
X
X		if (euid != uid)
X			(void) printf(" euid=%s", uid_desc(euid));
X		if (egid != gid)
X			(void) printf(" egid=%s", gid_desc(egid));
X
X		(void) printf("\n");
X	}
X
X	exit(0);
X	/* NOTREACHED */
X}
X
Xchar *
Xuid_desc(uid)
Xint     uid;
X{
X	struct passwd *pw;
X	static char buf[80];
X
X	(void) sprintf(buf, "%d", uid);
X	if ((pw = getpwuid(uid)) != NULL)
X		(void) sprintf(buf + strlen(buf), "(%s)", pw->pw_name);
X
X	return buf;
X}
X
Xchar *
Xgid_desc(gid)
Xint     gid;
X{
X	struct group *gr;
X	static char buf[80];
X
X	(void) sprintf(buf, "%d", gid);
X	if ((gr = getgrgid(gid)) != NULL)
X		(void) sprintf(buf + strlen(buf), "(%s)", gr->gr_name);
X
X	return buf;
X}
END_OF_FILE
if test 2771 -ne `wc -c <'uid.c'`; then
    echo shar: \"'uid.c'\" unpacked with wrong size!
fi
# end of 'uid.c'
fi
if test -f 'getopt.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'getopt.c'\"
else
echo shar: Extracting \"'getopt.c'\" \(1731 characters\)
sed "s/^X//" >'getopt.c' <<'END_OF_FILE'
X/* $Header: getopt.c,v 1.3 89/02/10 15:46:49 network Exp $
X *
X * A version of the public-domain getopt() function, as found
X * in the SVID and fine Unix manuals everywhere.
X *
X * $Log:	getopt.c,v $
X * Revision 1.3  89/02/10  15:46:49  network
X * V7 support.
X * 
X * Revision 1.2  88/11/30  16:24:50  network
X * patch6:  Separate getopt() into its own module.
X * 
X */
X
X#include <stdio.h>
X#include "config.h"
X#include "misc.h"
X
X/*----------------------------------------------------------------------
X * Get command line options.
X * This is essentially the public domain version, just reformatted to
X * match the rest of the deliver program.
X */
X
X#ifndef HAS_GETOPT
X
Xint     opterr = 1;
Xint     optind = 1;
Xint     optopt = 0;
Xchar    *optarg = NULL;
X
X#define ERR(what,c) \
X    if (!opterr) {} else fprintf(stderr,"%s: %s -- %c\n", argv[0], what, c);
X
Xint
Xgetopt(argc, argv, opts)
Xint argc;
Xchar **argv;
Xchar *opts;
X{
X	static int sp = 1;
X	int c;
X	char *cp;
X
X	if (sp == 1)
X	{
X		if (optind >= argc
X		 || argv[optind][0] != '-' || argv[optind][1] == '\0')
X			return EOF;
X
X		if (strcmp(argv[optind], "--") == NULL)
X		{
X			optind++;
X			return EOF;
X		}
X	}
X
X	optopt = c = argv[optind][sp];
X
X	if (c == ':' || (cp = strchr(opts, c)) == NULL)
X	{
X		ERR("illegal option", c);
X		if (argv[optind][++sp] == '\0')
X		{
X			optind++;
X			sp = 1;
X		}
X		return '?';
X	}
X
X	if (*++cp == ':')
X	{
X		if (argv[optind][sp + 1] != '\0')
X			optarg = &argv[optind++][sp + 1];
X		else if (++optind >= argc)
X		{
X			ERR("option requires an argument", c);
X			sp = 1;
X			return '?';
X		}
X		else
X			optarg = argv[optind++];
X
X		sp = 1;
X	}
X	else
X	{
X		if (argv[optind][++sp] == '\0')
X		{
X			sp = 1;
X			optind++;
X		}
X
X		optarg = NULL;
X	}
X
X	return c;
X}
X
X#endif  /* !HAS_GETOPT */
END_OF_FILE
if test 1731 -ne `wc -c <'getopt.c'`; then
    echo shar: \"'getopt.c'\" unpacked with wrong size!
fi
# end of 'getopt.c'
fi
echo shar: End of shell archive.
exit 0
-- 
Chip Salzenberg             <chip@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."

chip@ateng.ateng.com (Chip Salzenberg) (02/24/89)

#! /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@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@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@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."

chip@ateng.ateng.com (Chip Salzenberg) (02/24/89)

#! /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:  main.c mbox.c procs.c subs.c sysdep.c uucp.c
# Wrapped by network@ateng on Wed Feb 15 20:36:33 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'main.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'main.c'\"
else
echo shar: Extracting \"'main.c'\" \(13643 characters\)
sed "s/^X//" >'main.c' <<'END_OF_FILE'
X/* $Header: main.c,v 1.11 89/02/15 19:11:25 network Exp $
X *
X * A program to deliver local mail with some flexibility.
X *
X * $Log:	main.c,v $
X * Revision 1.11  89/02/15  19:11:25  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.10  89/02/10  15:46:59  network
X * V7 support.
X * 
X * Revision 1.9  88/11/28  18:08:03  network
X * patch5: Copy temp files before handing them to delivery files.
X * patch5: Introduce "uid" program.
X * 
X * Revision 1.8  88/11/26  13:20:51  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.7  88/11/18  12:17:17  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.6  88/10/13  12:19:27  network
X * patch1: add "-n" option, and general bug fixes.
X * 
X * Revision 1.5  88/09/14  20:00:03  network
X * Add version string, including patchlevel.
X * 
X * Revision 1.4  88/09/14  19:41:54  network
X * Portability to System V and BSD.
X * General fixup.
X * 
X * Revision 1.3  88/08/30  16:13:54  network
X * Remove general subroutines to new module, subs.c.
X * 
X * Revision 1.2  88/08/25  15:29:59  network
X * Implement -s and -u options and ENV_SYSDEL and ENV_USERDEL environment
X * variables.  Tighten up control over effective and real uid/gid.
X * In particular, renounce setuid privileges if the system or user delivery
X * file is specified.
X * 
X * Revision 1.1  88/06/06  09:38:54  chip
X * Initial revision
X * 
X */
X
X#include "deliver.h"
X#include "patchlevel.h"
X#include <signal.h>
X
X/*
X * External data.
X */
X
X/* Variables set by getopt() [blech] */
X
Xextern  int     optind, opterr;
Xextern  char    *optarg;
X
X/*
X * Local data
X */
X
Xstatic  char    sys_dfl[] = SYS_DELIVER;
Xstatic  char    post_dfl[] = POST_DELIVER;
Xstatic  char    user_dfl[] = USER_DELIVER;
X
X/*
X * Global data
X */
X
Xint     verbose         = FALSE;
Xint     dryrun          = FALSE;
Xint     rundfiles       = TRUE;
Xint     printaddrs      = FALSE;
Xint     leavetemps      = FALSE;
Xint     boxdelivery     = FALSE;
X
Xchar    *progname       = "deliver";
Xchar    version[32]     = "1.0";
Xchar    *shell          = SHELL;
X
Xchar    *sys_deliver    = sys_dfl;
Xchar    *post_deliver   = post_dfl;
Xchar    *user_deliver   = user_dfl;
Xchar    *sender         = NULL;
Xchar    *hostname       = NULL;
X
Xint     eff_uid         = -1;
Xint     eff_gid         = -1;
Xint     real_uid        = -1;
Xint     real_gid        = -1;
X
XCONTEXT *eff_ct         = NULL;
XCONTEXT *real_ct        = NULL;
X
Xint     tty_input       = FALSE;
XSIGFLAG got_sig         = FALSE;
X
Xint     trust_user      = FALSE;
Xint     trust_delfiles  = FALSE;
X
Xchar    *ttype[T_MAX]   = { "header", "body", "header copy", "body copy" };
Xchar    *tfile[T_MAX]   = { NULL, NULL, NULL, NULL };
Xchar    *tenv[T_MAX]    = { NULL, NULL, ENV_HEADER, ENV_BODY };
Xint     tfd[T_MAX]      = { -1, -1, -1, -1 };
X
X/*
X * Local functions.
X */
X
Xstatic  SIGTYPE sighup(), sigint(), sigquit();
X
X/*----------------------------------------------------------------------
X * The Program.
X */
X
Xmain(argc, argv)
Xint     argc;
Xchar    **argv;
X{
X	char    *p;
X	int     u, c, errcount, copy;
X
X	/* Make sure that stdout and stderr are interleaved correctly */
X
X	Linebuf(stdout);
X	Linebuf(stderr);
X
X	/* Figure out the name used to invoke this program. */
X
X	progname = basename(argv[0]);
X
X	/* What version of the program is this? */
X
X	(void) sprintf(version + strlen(version), ".%02d", PATCHLEVEL);
X
X	/* Figure out the name of this host */
X
X	if ((hostname = gethost()) == NULL)
X	{
X		hostname = "unknown";
X		error("unable to determine host name; using \"%s\"\n",
X		      hostname);
X	}
X
X	/* Find effective and real uids and gids. */
X
X	eff_uid = geteuid();
X	eff_gid = getegid();
X	real_uid = getuid();
X	real_gid = getgid();
X
X	if (eff_uid != real_uid && eff_uid != 0)
X	{
X		error("if setuid, must be setuid root\n");
X		leave(1);
X	}
X
X	/* Process environment: handle recursive invocation */
X
X	if ((p = getenv(ENV_DFLAGS)) != NULL)
X	{
X		while (*p)
X		{
X			switch (*p++)
X			{
X			case 'v':
X				verbose = TRUE;
X				break;
X			case 'd':
X				verbose = TRUE;
X				dryrun = TRUE;
X				break;
X			case 'A':
X				printaddrs = TRUE;
X				dryrun = TRUE;
X				break;
X			case 'n':
X				rundfiles = FALSE;
X				break;
X			case 't':
X				leavetemps = TRUE;
X				break;
X			}
X		}
X	}
X
X	if ((p = getenv(ENV_SYSDEL)) != NULL && *p)
X		sys_deliver = p;
X	if ((p = getenv(ENV_POSTDEL)) != NULL && *p)
X		post_deliver = p;
X	if ((p = getenv(ENV_USERDEL)) != NULL && *p)
X		user_deliver = p;
X	if ((p = getenv(ENV_SENDER)) != NULL && *p)
X		sender = p;
X	if ((p = getenv(ENV_HOSTNAME)) != NULL && *p)
X		hostname = p;
X
X	/* Parse command line arguments */
X
X	while ((c = getopt(argc, argv, "vdAntbs:p:u:r:h:")) != EOF)
X	{
X		switch (c)
X		{
X		case 'v':
X			verbose = TRUE;
X			break;
X		case 'd':
X			verbose = TRUE;
X			dryrun = TRUE;
X			break;
X		case 'A':
X			printaddrs = TRUE;
X			dryrun = TRUE;
X			break;
X		case 'n':
X			rundfiles = FALSE;
X			break;
X		case 't':
X			leavetemps = TRUE;
X			break;
X		case 'b':
X			boxdelivery = TRUE;
X			break;
X		case 's':
X			if (*optarg)
X				sys_deliver = optarg;
X			break;
X		case 'p':
X			if (*optarg)
X				post_deliver = optarg;
X			break;
X		case 'u':
X			if (*optarg)
X				user_deliver = optarg;
X			break;
X		case 'r':
X			if (*optarg)
X				sender = optarg;
X			break;
X		case 'h':
X			if (*optarg)
X				hostname = optarg;
X			break;
X		case '?':
X			usage();
X		}
X	}
X
X	/* If no destinations were given, forget it. */
X
X	if (optind >= argc)
X	{
X		error("no recipients specified\n");
X		usage();
X	}
X
X	/* Print a debugging message */
X
X	if (verbose)
X	{
X		message("%s %s running on host %s\n",
X			progname, version, hostname);
X	}
X
X	/* Do we trust our caller? */
X
X	if (trusted_uid(real_uid))
X		trust_user = TRUE;
X
X	/* Do we trust our delivery files? */
X
X	if (strcmp(sys_dfl, sys_deliver) == 0
X	 && strcmp(post_dfl, post_deliver) == 0
X	 && strcmp(user_dfl, user_deliver) == 0)
X		trust_delfiles = TRUE;
X
X	/* Renounce special privileges if something insecure was requested. */
X
X	if (!trust_user && !trust_delfiles)
X	{
X		if (setgid(eff_gid = real_gid) == -1
X		 || setuid(eff_uid = real_uid) == -1)
X		{
X			syserr("can't renounce setuid privileges");
X			leave(1);
X		}
X	}
X
X	/* Get the contexts of our effective and real uids. */
X
X	if ((eff_ct = uid_context(eff_uid)) == NULL)
X		error("invalid effective uid %d!?\n", eff_uid);
X
X	if ((real_ct = uid_context(real_uid)) == NULL)
X		error("invalid real uid %d!?\n", real_uid);
X
X	if (!eff_ct || !real_ct)
X		leave(1);
X
X	if (verbose)
X	{
X		message("effective uid = %s (%d/%d); real uid = %s (%d/%d)\n",
X			eff_ct->ct_name, eff_ct->ct_uid, eff_ct->ct_gid,
X			real_ct->ct_name, real_ct->ct_uid, real_ct->ct_gid);
X	}
X
X	/* Let's be sane about the file creation mask. */
X
X	u = umask(0);
X	u &= ~0700;     /* Let's not deprive ourselves of permissions.  */
X	u |= 022;       /* Let's be reasonably paranoid about writing.  */
X	(void) umask(u);
X
X	/*
X	 * Where is the message coming from?
X	 */
X
X	if (isatty(0))
X		tty_input = TRUE;
X
X	/*
X	 * If we are not going to deliver, or if we are receiving the
X	 * message from a tty, catch signals so we can remove temp files.
X	 * Otherwise, ignore signals.
X	 */
X
X	if (dryrun || tty_input)
X		catch_sigs();
X	else
X		ignore_sigs();
X
X	/*
X	 * Create the temporary files and write the message to them.
X	 */
X
X	copy = copy_message();
X
X	/*
X	 * No more signals...
X	 */
X
X	ignore_sigs();
X
X	/*
X	 * ... but if we had already caught a signal,
X	 *     or if copy_msg() had a problem, leave.
X	 */
X
X	if ((copy < 0) || got_sig)
X	{
X		if (got_sig)
X			error("caught signal - exiting\n");
X		leave(1);
X	}
X
X	/*
X	 * Set up useful environment variables.
X	 * Note that this must be done _after_ copy_message(),
X	 * since that's where the temp files are created.
X	 */
X
X	setup_environ();
X
X	/*
X	 * Perhaps we should consider all arguments as mailbox names...
X	 */
X
X	if (boxdelivery)
X	{
X		int     a;
X
X		if (verbose)
X			message("mailbox delivery as %s\n", real_ct->ct_name);
X
X		/*
X		 * Consider all arguments as mailbox filenames.
X		 */
X
X		for (a = optind; a < argc; ++a)
X			(void) dest(real_ct->ct_name, argv[a]);
X
X		if (verbose)
X			dumpdests("(should all be mailboxes)");
X	}
X
X	/*
X	 * They're not mailbox names, so they should be mail addresses.
X	 */
X
X	else
X	{
X		/* Run all destinations though the system delivery file. */
X
X		if (sys_dfile(argc - optind, argv + optind) >= 0)
X		{
X			if (verbose)
X				dumpdests("after running system delivery file");
X		}
X		else
X		{
X			int     a;
X
X			/*
X			 * System delivery file is missing or ignored.
X			 * Use the argument list verbatim.
X			 */
X
X			for (a = optind; a < argc; ++a)
X				(void) dest(argv[a], (char *) NULL);
X
X			if (verbose)
X				dumpdests("as taken from argument list");
X		}
X
X		/*
X		 * Run each user destination through his delivery file.
X		 */
X
X		if (user_dfiles() >= 0)
X		{
X			if (verbose)
X				dumpdests("after running user delivery files");
X		}
X
X		/*
X		 * Run each remaining destination though the post-user
X		 * delivery file.
X		 */
X
X		if (post_dfile() >= 0)
X		{
X			if (verbose)
X				dumpdests("after running post-user delivery file");
X		}
X	}
X
X	/*
X	 * Drop mail in mailbox(es).
X	 */
X
X	mbox_deliver();
X
X	if (verbose)
X		dumpdests("after delivery to all mailboxes");
X
X	/*
X	 * Send mail to UUCP address(es).
X	 */
X
X	uucp_deliver();
X
X	if (verbose)
X		dumpdests("after delivery to UUCP addresses");
X
X	/*
X	 * Report any errors, and leave.
X	 */
X
X	errcount = report_errors();
X
X	/*
X	 * All done.
X	 */
X
X	leave(errcount ? 1 : 0);
X	/* NOTREACHED */
X}
X
X/*----------------------------------------------------------------------
X * Print a usage message and exit.
X */
X
Xusage()
X{
X	message("Usage: %s [-b][-A][-d][-v][-n][-t][-r from][-h host] args\n", progname);
X	message("-b       All arguments are mailbox filenames.\n");
X	message("         (Default: arguments are user names.)\n");
X	message("-A       Resolve addresses but do not deliver.\n");
X	message("-d       Be verbose but do not deliver.\n");
X	message("-v       Be verbose and deliver.\n");
X	message("-n       Do not run any delivery files.\n");
X	message("-t       Do not remote temp files before exiting.\n");
X	message("-s file  Specify the system delivery filename.\n");
X	message("-p file  Specify the post-user delivery filename.\n");
X	message("-u file  Specify the user delivery filename.\n");
X	message("-r from  Specify the address to appear in the \"From \" line.\n");
X	message("-h host  Specify the host name.\n");
X	message("args     Either user addresses or mailboxes (-b).\n");
X	leave(1);
X}
X
X/*----------------------------------------------------------------------
X * Clean up and exit.
X */
X
Xleave(code)
Xint     code;
X{
X	if (! leavetemps)
X	{
X		int     t;
X
X		for (t = 0; t < T_MAX; ++t)
X		{
X			if (tfd[t] != -1)
X				(void) close(tfd[t]);
X			if (tfile[t] && unlink(tfile[t]) == -1)
X				syserr("can't unlink %s", tfile[t]);
X		}
X	}
X
X	exit(code);
X}
X
X/*----------------------------------------------------------------------
X * Catch signals.
X */
X
Xcatch_sigs()
X{
X	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGHUP, sighup);
X	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGINT, sigint);
X	if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGQUIT, sigquit);
X}
X
X/*----------------------------------------------------------------------
X * Ignore signals.
X */
X
Xignore_sigs()
X{
X	(void) signal(SIGHUP, SIG_IGN);
X	(void) signal(SIGINT, SIG_IGN);
X	(void) signal(SIGQUIT, SIG_IGN);
X}
X
Xstatic SIGTYPE
Xsighup()
X{
X	(void) signal(SIGHUP, sighup);
X	got_sig = TRUE;
X}
X
Xstatic SIGTYPE
Xsigint()
X{
X	(void) signal(SIGINT, sigint);
X	got_sig = TRUE;
X}
X
Xstatic SIGTYPE
Xsigquit()
X{
X	(void) signal(SIGQUIT, sigquit);
X	got_sig = TRUE;
X}
X
X/*----------------------------------------------------------------------
X * Report any errors to stderr.
X * Return an error count.
X */
X
Xint
Xreport_errors()
X{
X	DEST    *d;
X	int     count = 0;
X
X	for (d = first_dest(); d; d = next_dest(d))
X	{
X		if (d->d_state != ST_ERROR)
X			continue;
X
X		if (++count == 1)
X		{
X			error(
X		    "delivery to the following address(es) failed on host %s\n",
X				hostname);
X		}
X
X		message("\t\"%s\"", d->d_name);
X		if (d->d_class == CL_MBOX)
X			message(", mailbox \"%s\"", d->d_mailbox);
X		message(": %s\n", derrmsg(d->d_error));
X	}
X
X	return count;
X}
X
X/*----------------------------------------------------------------------
X * Is the given uid trusted?
X */
X
Xint
Xtrusted_uid(uid)
Xint     uid;
X{
X	CONTEXT *ct;
X	char    **n;
X	static char *t[] = { TRUSTED_USERS, 0 };
X
X	for (n = t; *n; ++n)
X	{
X		if ((ct = name_context(*n)) != NULL && uid == ct->ct_uid)
X			return TRUE;
X	}
X
X	return FALSE;
X}
X
X/*----------------------------------------------------------------------
X * Set up useful environment variables.
X */
X
Xsetup_environ()
X{
X	char    flags[8];
X	int     f = 0;
X
X	flags[f++] = '-';
X	if (verbose)
X		flags[f++] = (dryrun ? 'd' : 'v');
X	if (printaddrs)
X		flags[f++] = 'A';
X	if (leavetemps)
X		flags[f++] = 't';
X	flags[f] = 0;
X
X	alloc_env(ENV_DFLAGS, (f > 1) ? flags : "");
X	if (sys_deliver && *sys_deliver)
X		alloc_env(ENV_SYSDEL, sys_deliver);
X	if (user_deliver && *user_deliver)
X		alloc_env(ENV_USERDEL, user_deliver);
X	if (hostname && *hostname)
X		alloc_env(ENV_HOSTNAME, hostname);
X	if (sender && *sender)
X		alloc_env(ENV_SENDER, sender);
X
X	alloc_env("IFS", " \t\n");
X}
END_OF_FILE
if test 13643 -ne `wc -c <'main.c'`; then
    echo shar: \"'main.c'\" unpacked with wrong size!
fi
# end of 'main.c'
fi
if test -f 'mbox.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'mbox.c'\"
else
echo shar: Extracting \"'mbox.c'\" \(4621 characters\)
sed "s/^X//" >'mbox.c' <<'END_OF_FILE'
X/* $Header: mbox.c,v 1.4 89/02/10 15:47:10 network Exp $
X *
X * Finally!  Put the message in the specified mailbox(es).
X *
X * $Log:	mbox.c,v $
X * Revision 1.4  89/02/10  15:47:10  network
X * V7 support.
X * 
X * Revision 1.3  88/11/28  18:08:13  network
X * patch5: Copy temp files before handing them to delivery files.
X * patch5: Introduce "uid" program.
X * 
X * Revision 1.2  88/09/14  19:42:06  network
X * Portability to System V and BSD.
X * General fixup.
X * 
X * Revision 1.1  88/06/06  09:39:06  chip
X * Initial revision
X * 
X */
X
X#include "deliver.h"
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <errno.h>
X
X/*
X * External data.
X */
X
Xextern  int     errno;
X
X/*
X * Local functions.
X */
X
Xstatic          mbox_one();
Xstatic  int     mbox_write();
X
X/*----------------------------------------------------------------------
X * Deliver mail to all valid destinations.
X */
X
Xmbox_deliver()
X{
X	DEST    *d;
X
X	for (d = first_dest(); d; d = next_dest(d))
X	{
X		switch (d->d_class)
X		{
X		case CL_USER:
X		case CL_MBOX:
X			if (d->d_state == ST_WORKING)
X				mbox_one(d);
X			break;
X		}
X	}
X}
X
X/*----------------------------------------------------------------------
X * Deliver mail to one destination.
X */
X
Xstatic
Xmbox_one(d)
XDEST    *d;
X{
X	CONTEXT *ct;
X	int     ret = 0;
X
X	if (printaddrs)
X	{
X		(void) printf("%s", d->d_name);
X		if (d->d_class == CL_MBOX)
X			(void) printf(":%s", d->d_mailbox);
X		(void) printf("\n");
X	}
X
X	if (dryrun)
X	{
X		d->d_state = ST_DONE;
X		return;
X	}
X
X	if ((ct = name_context(d->d_name)) == NULL)
X	{
X		dest_err(d, E_CTLOST);
X		return;
X	}
X
X	if (! ok_context(ct))
X	{
X		dest_err(d, E_CTPERM);
X		return;
X	}
X
X	if (d->d_class == CL_MBOX)
X	{
X		if (sfork() == 0)
X		{
X			if (become(ct, !boxdelivery) < 0)
X				exit(1);
X			if (mbox_write(d->d_mailbox, ct, FALSE) < 0)
X				exit(1);
X			exit(0);
X		}
X
X		if (await_child() != 0)
X			ret = -1;
X	}
X	else
X	{
X		char    mailbox[100];
X
X		(void) sprintf(mailbox, "%s/%s",
X#ifdef MBX_DIR
X			MBX_DIR, d->d_name
X#else
X			d->d_home, MBX_NAME
X#endif
X			);
X
X		if (mbox_write(mailbox, ct, TRUE) < 0)
X			ret = -1;
X	}
X
X	if (ret >= 0)
X		d->d_state = ST_DONE;
X	else
X		dest_err(d, E_MBOX);
X}
X
X/*----------------------------------------------------------------------
X * Write mail to the named mailbox.
X * If we have to create the mailbox, give it to the specified user.
X * If "is_sys" is true, then we're writing to a system mailbox.
X */
X
Xstatic int
Xmbox_write(mailbox, ct, is_sys)
Xchar    *mailbox;
XCONTEXT *ct;
Xint     is_sys;
X{
X	struct stat st;
X	int     fd, t, mbox_uid, mbox_gid;
X	int     ret = 0;
X
X	if (verbose)
X	{
X		message("As %s, delivering to %s mailbox %s\n",
X			ct->ct_name, (is_sys ? "system" : "user"), mailbox);
X	}
X
X	if (name_lock(mailbox) < 0)
X		return -1;
X
X	while ((fd = open(mailbox, O_WRONLY)) == -1)
X	{
X		if (errno != ENOENT)
X		{
X			syserr("can't open %s", mailbox);
X			break;
X		}
X
X#ifdef O_CREAT
X		fd = open(mailbox, O_WRONLY|O_CREAT|O_EXCL, MBX_MODE);
X
X		/* If it exists now, try open() again. */
X		if (fd == -1 && errno == EEXIST)
X			continue;
X#else
X		fd = creat(mailbox, MBX_MODE);
X#endif
X		if (fd == -1)
X		{
X			syserr("can't create %s", mailbox);
X			break;
X		}
X
X		/* Make sure the mailbox receives the correct modes */
X
X		mbox_uid = ct->ct_uid;
X		mbox_gid = ct->ct_gid;
X
X#ifdef MBX_GROUP
X		if (is_sys)
X		{
X			static int mbox_sv_gid = -2;
X
X			if (mbox_sv_gid == -2)
X				mbox_sv_gid = group_id(MBX_GROUP);
X
X			if (mbox_sv_gid < 0)
X				message("%s: no such group\n", MBX_GROUP);
X			else
X				mbox_gid = mbox_sv_gid;
X		}
X#endif /* MBX_GROUP */
X
X		if (fstat(fd, &st) == -1)
X		{
X			syserr("can't fstat open mailbox?!");
X			(void) close(fd);
X			fd = -1;
X			break;
X		}
X
X		/* Change mailbox ownership if it's not already correct. */
X
X		if ((st.st_uid != mbox_uid || st.st_gid != mbox_gid)
X		 && chown(mailbox, mbox_uid, mbox_gid) == -1)
X		{
X			/* print a message, but that's all. (???) */
X			syserr("can't chown %s to %d,%d",
X				mailbox, mbox_uid, mbox_gid);
X		}
X
X		/* It's open now, so we can stop looping now. */
X
X		break;
X	}
X
X	if (fd == -1)
X	{
X		(void) name_unlock(mailbox);
X		return -1;
X	}
X
X	if (fd_lock(fd) < 0)
X	{
X		(void) close(fd);
X		(void) name_unlock(mailbox);
X		return -1;
X	}
X
X	(void) lseek(fd, 0L, 2); /* No error check: may be a special file */
X
X	for (t = T_HDR; t <= T_BODY; ++t)
X	{
X		if (lseek(tfd[t], 0L, 0) == -1)
X		{
X			syserr("lseek in %s file %s", ttype[t], tfile[t]);
X			ret = -1;
X			break;
X		}
X
X		if (copyfd(tfd[t], fd) < 0)
X		{
X			ret = -1;
X			break;
X		}
X	}
X
X	if (verbose)
X	{
X		if (ret >= 0)
X			message("wrote message to %s\n", mailbox);
X	}
X
X	if (fd_unlock(fd) < 0)
X		ret = -1;
X	(void) close(fd);
X	if (name_unlock(mailbox) < 0)
X		ret = -1;
X
X	return ret;
X}
END_OF_FILE
if test 4621 -ne `wc -c <'mbox.c'`; then
    echo shar: \"'mbox.c'\" unpacked with wrong size!
fi
# end of 'mbox.c'
fi
if test -f 'procs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'procs.c'\"
else
echo shar: Extracting \"'procs.c'\" \(5411 characters\)
sed "s/^X//" >'procs.c' <<'END_OF_FILE'
X/* $Header: procs.c,v 1.4 89/02/10 15:47:31 network Exp $
X *
X * Process management and misc support.
X *
X * $Log:	procs.c,v $
X * Revision 1.4  89/02/10  15:47:31  network
X * V7 support.
X * 
X * Revision 1.3  88/11/26  13:21:07  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/09/14  19:42:28  network
X * Portability to System V and BSD.
X * General fixup.
X * 
X * Revision 1.1  88/06/06  09:39:15  chip
X * Initial revision
X * 
X */
X
X#include "deliver.h"
X#include <errno.h>
X#include <signal.h>
X
X/*
X * External data.
X */
X
Xextern  int     errno;
X
X/*
X * Local data.
X */
X
Xstatic  int     child_pid = -1;
Xstatic  SIGTYPE (*saved_sigpipe)() = SIG_DFL;
X
X/*----------------------------------------------------------------------
X * Like popen(), but execute the child in a specific context.
X * Also, the argument list is already a vector.
X */
X
XFILE *
Xct_popenv(ct, prog, av, mode)
XCONTEXT *ct;
Xchar    *prog;
Xchar    **av;
Xchar    *mode;
X{
X	char    ch;
X	int     child, parent;
X	int     pfd[2];
X
X	if (!ct || !prog || !av || !mode)
X		return NULL;
X
X	if (mode[0] == 'r' && mode[1] == 0)
X		child = 1, parent = 0;
X	else if (mode[0] == 'w' && mode[1] == 0)
X		child = 0, parent = 1;
X	else
X		return NULL;
X
X	/* We can't have more than one child at a time. */
X
X	if (child_pid >= 0)
X	{
X		error("in ct_popen: a process is already open\n");
X		return NULL;
X	}
X
X	/* Make a stab at predicting uid-related failure. */
X
X	if (! ok_context(ct))
X	{
X		error("in ct_popen: no permissions to become %s\n",
X		      ct->ct_name);
X		return NULL;
X	}
X
X	/* Pipes?  Like, tubular, fer shur! */
X
X	if (pipe(pfd) == -1)
X	{
X		syserr("can't create a pipe");
X		return NULL;
X	}
X
X	/* Generate a debugging message. */
X
X	if (verbose)
X	{
X		int a;
X
X		message("Spawning");
X		for (a = 0; av[a]; ++a)
X			message(" %s", av[a]);
X		message("\n");
X	}
X
X	/* Handle the child case */
X
X	if (sfork() == 0)
X	{
X		if (child == 0)
X		{
X			(void) close(0);
X			(void) dup(pfd[0]);     /* ass_u_me 0 */
X		}
X		else
X		{
X			(void) close(0);
X			if (open("/dev/null", O_RDONLY) != 0)
X			{
X				/* This should _never_ happen, but... */
X				syserr("can't open /dev/null");
X				(void) dup(1);  /* ass_u_me 0 */
X			}
X
X			(void) close(1);
X			(void) dup(pfd[1]);     /* ass_u_me 1 */
X		}
X
X		if (become(ct, TRUE) < 0)
X			(void) write(pfd[1], "n", 1);
X		else
X		{
X			int     t;
X
X			(void) write(pfd[1], "y", 1);
X
X			(void) close(pfd[child]);
X			(void) close(pfd[parent]);
X			for (t = 0; t < T_MAX; ++t)
X				(void) close(tfd[t]);
X
X			(void) execv(prog, av);
X			syserr("can't execute %s", prog);
X		}
X
X		exit(127);
X	}
X
X	/* Make sure that a broken pipe won't kill us */
X
X	saved_sigpipe = signal(SIGPIPE, SIG_IGN);
X
X	/* The child must report "OK" before we continue. */
X
X	if ((read(pfd[0], &ch, 1) < 1) || (ch != 'y'))
X	{
X		(void) close(pfd[0]);
X		(void) close(pfd[1]);
X		(void) await_child();
X		return NULL;
X	}
X
X	(void) close(pfd[child]);
X	return fdopen(pfd[parent], mode);
X}
X
X/*----------------------------------------------------------------------
X * Close the stream opened by ct_popen().
X */
X
Xct_pclose(fp)
XFILE    *fp;
X{
X	if (fp)
X		(void) fclose(fp);
X	return await_child();
X}
X
X/*----------------------------------------------------------------------
X * Assume the identity of the given user.
X */
X
Xint
Xbecome(ct, chd)
XCONTEXT *ct;
Xint     chd;
X{
X	char    env_path[32];
X
X	/*
X	 * Assume a new identity.
X	 * Note the importance of doing the setgid() before the setuid().
X	 */
X
X	if (setgid(ct->ct_gid) == -1)
X	{
X		syserr("can't setgid to %d", ct->ct_gid);
X		return -1;
X	}
X	if (setuid(ct->ct_uid) == -1)
X	{
X		syserr("can't setgid to %u", ct->ct_uid);
X		return -1;
X	}
X	if (chd && chdir(ct->ct_home) == -1)
X	{
X		syserr("can't chdir to %s", ct->ct_home);
X		return -1;
X	}
X
X	/* Set up the environment */
X
X	(void) sprintf(env_path, "%s:/bin:/usr/bin",
X			((ct->ct_uid == 0) ? "/etc" : "."));
X	alloc_env("HOME", ct->ct_home);
X	alloc_env("PATH", env_path);
X
X	/* I guess it worked. */
X
X	return 0;
X}
X
X/*----------------------------------------------------------------------
X * Safe fork.  If it doesn't work, it exits.
X */
X
Xint
Xsfork()
X{
X	int     tries;
X
X	/*
X	 * A few safety measures.
X	 */
X
X	(void) await_child();
X	(void) fflush(stdout);
X	(void) fflush(stderr);
X
X	/*
X	 * Be patient in waiting for a fork().
X	 */
X
X	for (tries = 0; tries < 10; ++tries)
X	{
X		if (tries)
X			snooze(3);
X		if ((child_pid = fork()) >= 0)
X			return child_pid;
X		if (errno != EAGAIN)
X			break;
X	}
X
X	syserr("can't fork");
X	leave(1);
X	/* NOTREACHED */
X}
X
X/*----------------------------------------------------------------------
X * Wait for our child (if any) to exit.
X * Returns child's exit status or -1 if there is a problem.
X */
X
Xint
Xawait_child()
X{
X	int     wpid, st;
X
X	if (child_pid < 0)
X		return -1;
X
X	while ((wpid = wait(&st)) >= 0)
X	{
X		if (wpid == child_pid)
X			break;
X	}
X
X	child_pid = -1;
X	if (wpid == -1)
X		syserr("waiting for child");
X
X	(void) signal(SIGPIPE, saved_sigpipe);
X	saved_sigpipe = SIG_DFL;
X
X	if (wpid == -1)
X		return -1;
X
X	if (st & 0xFF)
X	{
X		error("child process died%s due to signal %d.\n",
X			((st & 0x80) ? " and dumped core" : ""),
X			(st & 0x7F));
X
X		return -1;
X	}
X
X	if (verbose)
X	{
X		message("child process exited with status %d.\n",
X			(st >> 8) & 0xFF);
X	}
X
X	return ((st >> 8) & 0xFF);
X}
END_OF_FILE
if test 5411 -ne `wc -c <'procs.c'`; then
    echo shar: \"'procs.c'\" unpacked with wrong size!
fi
# end of 'procs.c'
fi
if test -f 'subs.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'subs.c'\"
else
echo shar: Extracting \"'subs.c'\" \(2775 characters\)
sed "s/^X//" >'subs.c' <<'END_OF_FILE'
X/* $Header: subs.c,v 1.6 89/02/10 15:47:40 network Exp $
X *
X * Miscellaneous subroutines.
X *
X * $Log:	subs.c,v $
X * Revision 1.6  89/02/10  15:47:40  network
X * V7 support.
X * 
X * Revision 1.5  88/11/26  13:21:11  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/10/13  12:20:34  network
X * patch1: add "-n" option, and general bug fixes.
X * 
X * Revision 1.3  88/09/14  19:42:33  network
X * Portability to System V and BSD.
X * General fixup.
X * 
X * Revision 1.2  88/08/30  16:14:53  network
X * New module.  Includes routines from main.c.
X * Also, new routine savestr().
X * 
X */
X
X#include "deliver.h"
X
X/*----------------------------------------------------------------------
X * Allocate memory for an environment variable, and putenv() it.
X */
X
Xalloc_env(name, value)
Xchar    *name;
Xchar    *value;
X{
X	char    *s;
X
X	if (!name || !value)
X		return;
X
X	s = zalloc((unsigned) (strlen(name) + strlen(value) + 2));
X	(void) sprintf(s, "%s=%s", name, value);
X	if (putenv(s))
X		nomem();
X}
X
X/*----------------------------------------------------------------------
X * Allocate and clear.  If it fails, it takes the emergency exit.
X */
X
Xchar *
Xzalloc(size)
Xunsigned size;
X{
X	char    *p;
X
X	if ((p = malloc(size)) == NULL)
X		nomem();
X
X	Zero(p, size);
X	return p;
X}
X
X/*----------------------------------------------------------------------
X * Reallocate to new size.  If it fails, it takes the emergency exit.
X */
X
Xchar *
Xsrealloc(ptr, size)
Xchar    *ptr;
Xunsigned size;
X{
X	char    *p;
X
X	if ((p = realloc(ptr, size)) == NULL)
X		nomem();
X
X	return p;
X}
X
X/*----------------------------------------------------------------------
X * Make an allocated copy of a string.
X */
X
Xchar *
Xcopystr(s)
Xchar    *s;
X{
X	char    *p;
X
X	if (s == NULL)
X		return NULL;
X
X	if ((p = malloc((unsigned) strlen(s) + 1)) == NULL)
X		nomem();
X
X	(void) strcpy(p, s);
X	return p;
X}
X
X/*----------------------------------------------------------------------
X * Emergency exit for memory loss.
X */
X
Xnomem()
X{
X	error("out of memory\n");
X	leave(1);
X}
X
X/*----------------------------------------------------------------------
X * Return the last component of the given pathname.
X */
X
Xchar *
Xbasename(name)
Xchar    *name;
X{
X	char    *b;
X
X	if ((b = strrchr(name, '/')) != NULL)
X		++b;
X	else
X		b = name;
X
X	return (b);
X}
X
X/*----------------------------------------------------------------------
X * Check an address for validity.
X */
X
Xvalid_address(addr)
Xchar    *addr;
X{
X	char    *p;
X	static char sanitize[] = SANITIZE;
X
X	for (p = addr; *p; ++p)
X	{
X		if (strchr(sanitize, *p))
X			return FALSE;
X	}
X
X	return TRUE;
X}
END_OF_FILE
if test 2775 -ne `wc -c <'subs.c'`; then
    echo shar: \"'subs.c'\" unpacked with wrong size!
fi
# end of 'subs.c'
fi
if test -f 'sysdep.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'sysdep.c'\"
else
echo shar: Extracting \"'sysdep.c'\" \(7306 characters\)
sed "s/^X//" >'sysdep.c' <<'END_OF_FILE'
X/* $Header: sysdep.c,v 1.7 89/02/10 15:47:44 network Exp $
X *
X * Routines which are (or might well be) system-dependant.
X * I've put the message routines here since you may need to use
X * the ANSI <stdarg.h> instead of <varargs.h>.
X *
X * $Log:	sysdep.c,v $
X * Revision 1.7  89/02/10  15:47:44  network
X * V7 support.
X * 
X * Revision 1.6  88/11/30  16:24:56  network
X * patch6:  Separate getopt() into its own module.
X * 
X * Revision 1.5  88/11/26  13:21:15  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/10/13  12:20:39  network
X * patch1: add "-n" option, and general bug fixes.
X * 
X * Revision 1.3  88/09/14  20:00:24  network
X * Fix type of gethostname() for BSD.
X * 
X * Revision 1.2  88/09/14  19:42:37  network
X * Portability to System V and BSD.
X * General fixup.
X * 
X * Revision 1.1  88/06/06  09:39:29  chip
X * Initial revision
X * 
X */
X
X#include "deliver.h"
X#include <errno.h>
X#ifdef HAS_STDARG
X#include <stdarg.h>
X#else
X#ifdef HAS_VARARGS
X#include <varargs.h>
X#else
X/*
X * Non-portable home-grown varargs.  Use at your own risk.
X * Especially note that if sizeof(int) > sizeof(short), then
X * "va_arg(..,short)" is broken.
X */
Xtypedef char *va_list;
X#define va_dcl          int va_alist;
X#define va_start(ap)    ap = (char *) &va_alist
X#define va_arg(ap,type) *(type *)(ap += sizeof(type), ap - sizeof(type))
X#define va_end(ap)      /* nothing */
X#endif
X#endif
X
X#ifdef UNAME
X#include <sys/utsname.h>
X#endif
X
X/*
X * External functions.
X */
X
X#ifdef M_XENIX
Xextern  long    nap();
X#else
Xextern  unsigned sleep();
X#endif
X
X/*
X * External data.
X */
X
Xextern  int     errno;
Xextern  int     sys_nerr;
Xextern  char    *sys_errlist[];
X
X/*----------------------------------------------------------------------
X * Print a message.
X */
X
X/* VARARGS */
X#ifdef HAS_STDARG
Xmessage(char *fmt, ...)
X#else
Xmessage(va_alist) va_dcl
X#endif
X{
X	va_list ap;
X
X#ifdef HAS_STDARG
X	va_start(ap, fmt);
X#else
X	char    *fmt;
X	va_start(ap);
X	fmt = va_arg(ap, char *);
X#endif
X
X	(void) vfprintf(stderr, fmt, ap);
X
X	va_end(ap);
X}
X
X/*----------------------------------------------------------------------
X * Print an error message.
X */
X
X/* VARARGS */
X#ifdef HAS_STDARG
Xerror(char *fmt, ...)
X#else
Xerror(va_alist) va_dcl
X#endif
X{
X	va_list ap;
X
X#ifdef HAS_STDARG
X	va_start(ap, fmt);
X#else
X	char    *fmt;
X	va_start(ap);
X	fmt = va_arg(ap, char *);
X#endif
X
X	(void) fprintf(stderr, "%s: ", progname);
X	(void) vfprintf(stderr, fmt, ap);
X
X	va_end(ap);
X}
X
X/*----------------------------------------------------------------------
X * Report an error returned from a system call.
X */
X
X/* VARARGS */
X#ifdef HAS_STDARG
Xsyserr(char *fmt, ...)
X#else
Xsyserr(va_alist) va_dcl
X#endif
X{
X	int     e = errno;
X	va_list ap;
X
X#ifdef HAS_STDARG
X	va_start(ap, fmt);
X#else
X	char    *fmt;
X	va_start(ap);
X	fmt = va_arg(ap, char *);
X#endif
X
X	(void) fprintf(stderr, "%s: ", progname);
X	(void) vfprintf(stderr, fmt, ap);
X	if (e <= sys_nerr)
X		(void) fprintf(stderr, ": %s\n", sys_errlist[e]);
X	else
X		(void) fprintf(stderr, ": unknown system error %d\n", e);
X
X	va_end(ap);
X}
X
X/*----------------------------------------------------------------------
X * Sleep for the given number of seconds.
X */
X
Xsnooze(n)
Xint     n;
X{
X#ifdef M_XENIX
X	(void) nap(n * 1000L);
X#else
X	(void) sleep(n);
X#endif
X}
X
X/*----------------------------------------------------------------------
X * Get the host name from HOSTFILE.
X */
X
X#ifdef HOSTFILE
X
Xchar *
Xgethost()
X{
X	int     fd, rd;
X	char    *p;
X	static char name[32];
X
X	if ((fd = open(HOSTFILE, O_RDONLY)) == -1)
X		return NULL;
X	rd = read(fd, name, sizeof(name) - 1);
X	(void) close(fd);
X
X	if (rd < 1)
X		return NULL;
X	name[rd] = 0;
X	if ((p = strchr(name, '\n')) != NULL)
X		*p = 0;
X
X	return (name[0] ? name : NULL);
X}
X
X#endif /* HOSTFILE */
X
X/*----------------------------------------------------------------------
X * Get the host name via the uname() system call.
X */
X
X#ifdef UNAME
X
Xchar *
Xgethost()
X{
X	static struct utsname u;
X
X	uname(&u);
X	return (u.nodename[0] ? u.nodename : NULL);
X}
X
X#endif /* UNAME */
X
X/*----------------------------------------------------------------------
X * Get the host name via the gethostname() system call.
X */
X
X#ifdef GETHOSTNAME
X
Xchar *
Xgethost()
X{
X	static char hostname[64];
X
X	if (gethostname(hostname, sizeof(hostname)) == -1)
X		return NULL;
X
X	return hostname;
X}
X
X#endif /* GETHOSTNAME */
X
X/*----------------------------------------------------------------------
X * Return a pre-defined HOSTNAME.
X */
X
X#ifdef HOSTNAME
X
Xchar *
Xgethost()
X{
X	return HOSTNAME;
X}
X
X#endif /* HOSTNAME */
X
X/*----------------------------------------------------------------------
X * Variable-argument-list output, System V style.
X */
X
X#ifndef HAS_VPRINTF
X
Xvprintf(fmt, ap)
Xchar    *fmt;
Xva_list ap;
X{
X	int     a,b,c,d,e,f,g,h;
X
X	a = va_arg(ap, int);
X	b = va_arg(ap, int);
X	c = va_arg(ap, int);
X	d = va_arg(ap, int);
X	e = va_arg(ap, int);
X	f = va_arg(ap, int);
X	g = va_arg(ap, int);
X	h = va_arg(ap, int);
X
X	(void) printf(fmt, a,b,c,d,e,f,g,h);
X}
X
Xvfprintf(fp, fmt, ap)
XFILE    *fp;
Xchar    *fmt;
Xva_list ap;
X{
X	int     a,b,c,d,e,f,g,h;
X
X	a = va_arg(ap, int);
X	b = va_arg(ap, int);
X	c = va_arg(ap, int);
X	d = va_arg(ap, int);
X	e = va_arg(ap, int);
X	f = va_arg(ap, int);
X	g = va_arg(ap, int);
X	h = va_arg(ap, int);
X
X	(void) fprintf(fp, fmt, a,b,c,d,e,f,g,h);
X}
X
Xvsprintf(s, fmt, ap)
Xchar    *s;
Xchar    *fmt;
Xva_list ap;
X{
X	int     a,b,c,d,e,f,g,h;
X
X	a = va_arg(ap, int);
X	b = va_arg(ap, int);
X	c = va_arg(ap, int);
X	d = va_arg(ap, int);
X	e = va_arg(ap, int);
X	f = va_arg(ap, int);
X	g = va_arg(ap, int);
X	h = va_arg(ap, int);
X
X	(void) sprintf(s, fmt, a,b,c,d,e,f,g,h);
X}
X
X#endif  /* !HAS_VPRINTF */
X
X/*----------------------------------------------------------------------
X * Add a new environment variable.
X */
X
X#ifndef HAS_PUTENV
X
Xint
Xputenv(s)
Xchar *s;
X{
X	static  char    **env_array;
X	static  int     env_size;
X	char    *e;
X	int     i, j;
X
X	if (env_array == NULL)
X	{
X		for (i = 0; environ[i]; ++i)
X			{}
X		env_size = i + 10;      /* arbitrary */
X		env_array = (char **) zalloc(env_size * sizeof(char *));
X		Copy((char *)env_array, (char *)environ,
X		     (int) ((i + 1) * sizeof(char *)));
X		environ = env_array;
X	}
X	else if (environ != env_array)
X		message("putenv: warning: someone moved environ!\n");
X
X	if ((e = strchr(s, '=')) != NULL)
X		++e;
X	else
X		e = s + strlen(s);
X
X	j = 0;
X	for (i = 0; env_array[i]; ++i)
X	{
X		if (strncmp(env_array[i], s, e - s) != 0)
X			env_array[j++] = env_array[i];
X	}
X
X	if ((j + 1) >= env_size)
X	{
X		env_size += 10;                 /* arbitrary */
X		env_array = (char **) srealloc((char *)env_array,
X					env_size * sizeof(char **));
X	}
X
X	env_array[j++] = s;
X	env_array[j] = NULL;
X
X	environ = env_array;
X	return 0;
X}
X
X#endif  /* !HAS_PUTENV */
X
X/*----------------------------------------------------------------------
X * Memory copy.
X */
X
X#ifdef MEMFUNCS
X
XCopy(dest, src, len)
Xchar    *dest;
Xchar    *src;
Xint     len;
X{
X	while (len-- > 0)
X		*dest++ = *src++;
X}
X
X#endif
X
X/*----------------------------------------------------------------------
X * Memory clear.
X */
X
X#ifdef MEMFUNCS
X
XZero(dest, len)
Xchar    *dest;
Xint     len;
X{
X	while (len-- > 0)
X		*dest++ = 0;
X}
X
X#endif
END_OF_FILE
if test 7306 -ne `wc -c <'sysdep.c'`; then
    echo shar: \"'sysdep.c'\" unpacked with wrong size!
fi
# end of 'sysdep.c'
fi
if test -f 'uucp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'uucp.c'\"
else
echo shar: Extracting \"'uucp.c'\" \(3269 characters\)
sed "s/^X//" >'uucp.c' <<'END_OF_FILE'
X/* $Header: uucp.c,v 1.3 89/02/10 15:47:51 network Exp $
X *
X * Handle mail destined for other hosts via UUCP.
X * Deliver is intended as a very low-level program, so we don't
X * do anything fancy here.  We just hand the message to uux.
X *
X * $Log:	uucp.c,v $
X * Revision 1.3  89/02/10  15:47:51  network
X * V7 support.
X * 
X * Revision 1.2  88/11/28  18:08:23  network
X * patch5: Copy temp files before handing them to delivery files.
X * patch5: Introduce "uid" program.
X * 
X * Revision 1.1  88/06/06  09:39:42  chip
X * Initial revision
X * 
X */
X
X#include "deliver.h"
X#include <sys/types.h>
X#include <sys/stat.h>
X
X/*
X * Local functions.
X */
X
Xstatic  int     uucp_copy();
X
X/*----------------------------------------------------------------------
X * Send mail to UUCP addresses (if any).
X * This is a simple implementation: invoke uux once per address.
X */
X
Xuucp_deliver()
X{
X	struct stat st;
X	DEST    *d;
X	char    *uux;
X	static char uux1[] = "/bin/uux";
X	static char uux2[] = "/usr/bin/uux";
X
X	if (stat(uux1, &st) == 0)
X		uux = uux1;
X	else if (stat(uux2, &st) == 0)
X		uux = uux2;
X	else
X	{
X		error("can't find uux!?\n");
X		return;
X	}
X
X	for (d = first_dest(); d; d = next_dest(d))
X	{
X		FILE    *uux_fp;
X		char    *bang;
X		char    *av[5];
X		char    rmail[40];
X		char    who[BUFSIZ];
X
X		if (d->d_class != CL_UUCP || d->d_state != ST_WORKING)
X			continue;
X
X		if (printaddrs)
X			(void) printf("%s\n", d->d_name);
X
X		if (dryrun)
X		{
X			d->d_state = ST_DONE;
X			continue;
X		}
X
X		bang = strchr(d->d_name, '!');
X		*bang = 0;
X		(void) sprintf(rmail, "%s!rmail", d->d_name);
X		*bang++ = '!';
X		(void) sprintf(who, "(%s)", bang);
X
X		av[0] = "uux";
X		av[1] = "-";
X		av[2] = rmail;
X		av[3] = who;
X		av[4] = NULL;
X		if ((uux_fp = ct_popenv(eff_ct, uux, av, "w")) == NULL)
X			continue;
X
X		if (uucp_copy(uux_fp) < 0)
X			dest_err(d, E_UUX);
X
X		if (ct_pclose(uux_fp))
X		{
X			/* "No such host" overrides piping problems. */
X			dest_err(d, E_NSHOST);
X		}
X		else
X			d->d_state = ST_DONE;
X	}
X}
X
X/*----------------------------------------------------------------------
X * Write the message for UUCP transmission to the given file.
X */
X
Xstatic int
Xuucp_copy(ofp)
XFILE    *ofp;
X{
X	FILE    *ifp;
X	char    *p;
X	register int c;
X	int     fd;
X	char    buf[BUFSIZ];
X
X	if ((fd = dup(tfd[T_HDR])) == -1)
X	{
X		syserr("can't dup header fd");
X		return -1;
X	}
X	(void) lseek(fd, 0L, 0);
X	if ((ifp = fdopen(fd, "r")) == NULL)
X	{
X		error("can't fdopen header fd");
X		return -1;
X	}
X
X	/*
X	 * Copy the header, but tack "remote from" onto the end of the
X	 * From_ line.  (If it weren't for dealing with the From_ line,
X	 * I'd skip stream I/O altogether and use read/write.  Maybe
X	 * I should save the length of the From_ line when I copy it...)
X	 */
X
X	(void) fgets(buf, GETSIZE(buf), ifp);
X	if ((p = strchr(buf, '\n')) != NULL)
X		*p = 0;
X	(void) fprintf(ofp, "%s remote from %s\n", buf, hostname);
X
X	while ((c = getc(ifp)) != EOF)
X		(void) putc(c, ofp);
X
X	(void) fclose(ifp);
X
X	/*
X	 * Copy the body
X	 */
X
X	if ((fd = dup(tfd[T_BODY])) == -1)
X	{
X		syserr("can't dup body fd");
X		return -1;
X	}
X	(void) lseek(fd, 0L, 0);
X	if ((ifp = fdopen(fd, "r")) == NULL)
X	{
X		error("can't fdopen body fd");
X		(void) close(fd);
X		return -1;
X	}
X
X	while ((c = getc(ifp)) != EOF)
X		(void) putc(c, ofp);
X
X	(void) fclose(ifp);
X	return 0;
X}
END_OF_FILE
if test 3269 -ne `wc -c <'uucp.c'`; then
    echo shar: \"'uucp.c'\" unpacked with wrong size!
fi
# end of 'uucp.c'
fi
echo shar: End of shell archive.
exit 0
-- 
Chip Salzenberg             <chip@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."

dhesi@bsu-cs.UUCP (Rahul Dhesi) (02/24/89)

In article <1989Feb23.114554.13798@ateng.ateng.com> chip@ateng.ateng.com
(Chip Salzenberg) writes:
>#! /bin/sh
># This is a shell archive.

Shouldn't the first few lines of every 1500-line posting tell us what
it contains, other than just its name and patch level?
-- 
Rahul Dhesi         UUCP:  <backbones>!{iuvax,pur-ee}!bsu-cs!dhesi
                    ARPA:  bsu-cs!dhesi@iuvax.cs.indiana.edu

jim@tiamat.fsc.com (Jim O'Connor) (02/26/89)

In article <5884@bsu-cs.UUCP>, dhesi@bsu-cs.UUCP (Rahul Dhesi) writes:
> In article <1989Feb23.114554.13798@ateng.ateng.com> chip@ateng.ateng.com
> (Chip Salzenberg) writes:
> >#! /bin/sh
> ># This is a shell archive.
> 
> Shouldn't the first few lines of every 1500-line posting tell us what
> it contains, other than just its name and patch level?

There was a previuos article which announced the presence of the other three
source articles that followed.  The article not only made the announcement,
but also explained why entire sources were being posted rather than just
patches (as is the norm here).

------------- 
James B. O'Connor			jim@tiamat.fsc.com
Filtration Sciences Corporation		615/821-4022 x. 651

*** Altos users unite! mail to "info-altos-request@tiamat.fsc.com" ***