[unix-pc.sources] Sendmail source for the 3B1/7300

dave@galaxia.Newport.RI.US (David H. Brierley) (02/25/89)

Here is the source for the sendmail program.  See the related article
in unix-pc.general for background information.

----- cut here and feed to /bin/sh -----
#! /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 archive 1 (of 8)."
# Contents:  MANIFEST Makefile README READ_ME include
#   include/sysexits.h include/useful.h include/userdbm.h lib
#   lib/Makefile lib/aliases lib/sendmail.hf md md/c.nvmunix.m4
#   md/c.v6.m4 md/c.v7.m4 md/c.vmunix.m4 md/config.dst md/config.m4
#   md/config.m4.mone src src/Makefile src/README src/Version.c
#   src/arpadate.c src/bcopy.c src/clock.c src/conf.h src/convtime.c
#   src/mailstats.h src/rename.c src/stab.c src/stats.c src/sysexits.c
#   src/trace.c src/trace.h src/version.c
# Wrapped by dave@galaxia on Fri Feb 24 20:23:32 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'MANIFEST' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'MANIFEST'\"
else
echo shar: Extracting \"'MANIFEST'\" \(1881 characters\)
sed "s/^X//" >'MANIFEST' <<'END_OF_FILE'
X   File Name		Archive #	Description
X-----------------------------------------------------------
X MANIFEST                   1	This shipping list
X Makefile                   1	
X README                     1	
X READ_ME                    1	
X include                    1	
X include/sysexits.h         1	
X include/useful.h           1	
X include/userdbm.h          1	
X lib                        1	
X lib/Makefile               1	
X lib/aliases                1	
X lib/sendmail.hf            1	
X md                         1	
X md/c.nvmunix.m4            1	
X md/c.v6.m4                 1	
X md/c.v7.m4                 1	
X md/c.vmunix.m4             1	
X md/config.dst              1	
X md/config.m4               1	
X md/config.m4.mone          1	
X src                        1	
X src/Makefile               1	
X src/README                 1	
X src/Version.c              1	
X src/alias.c                2	
X src/arpadate.c             1	
X src/bcopy.c                1	
X src/clock.c                1	
X src/collect.c              2	
X src/conf.c                 3	
X src/conf.h                 1	
X src/convtime.c             1	
X src/daemon.c               4	
X src/deliver.c              8	
X src/domain.c               2	
X src/envelope.c             3	
X src/err.c                  2	
X src/headers.c              5	
X src/macro.c                2	
X src/mailstats.h            1	
X src/main.c                 7	
X src/parseaddr.c            7	
X src/queue.c                6	
X src/readcf.c               5	
X src/recipient.c            3	
X src/rename.c               1	
X src/savemail.c             3	
X src/sendmail.h             6	
X src/srvrsmtp.c             4	
X src/stab.c                 1	
X src/stats.c                1	
X src/sysexits.c             1	
X src/trace.c                1	
X src/trace.h                1	
X src/usersmtp.c             2	
X src/util.c                 4	
X src/version.c              1	
END_OF_FILE
if test 1881 -ne `wc -c <'MANIFEST'`; then
    echo shar: \"'MANIFEST'\" unpacked with wrong size!
fi
# end of 'MANIFEST'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(964 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#
X# Copyright (c) 1988 Regents of the University of California.
X# All rights reserved.
X#
X# Redistribution and use in source and binary forms are permitted
X# provided that this notice is preserved and that due credit is given
X# to the University of California at Berkeley. The name of the University
X# may not be used to endorse or promote products derived from this
X# software without specific prior written permission. This software
X# is provided ``as is'' without express or implied warranty.
X#
X#	@(#)Makefile	4.13 (Berkeley) 5/1/88
X#
XSUBDIR=	lib src aux
X
Xall: ${SUBDIR}
X
X${SUBDIR}: FRC
X	cd $@; make ${MFLAGS}
X
Xclean: FRC
X	for i in ${SUBDIR}; do (cd $$i; make ${MFLAGS} clean); done
X
Xdepend: FRC
X	for i in ${SUBDIR}; do (cd $$i; make ${MFLAGS} depend); done
X
Xinstall: FRC
X	-mkdir ${DESTDIR}/usr/spool/mqueue
X	chown root.wheel ${DESTDIR}/usr/spool/mqueue
X	chmod 775 ${DESTDIR}/usr/spool/mqueue
X	for i in ${SUBDIR}; do (cd $$i; make ${MFLAGS} install); done
X
XFRC:
END_OF_FILE
if test 964 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
fi
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(968 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XThis is the sendmail 5.58 distribution for System V Release 3.1.
XIt may run on earlier releases of System V.  (I haven't tried it).
XOnly the src and include directories are included.  The other
Xdirectories should be available in the normal Berkeley sendmail
Xdistribution.
X
XAs configured, this software has the following software requirements:
X
X	WIN/3B TCP/IP (or equivalent Berkeley networking emulation)
X	dbm library
X
XI wish I could distribute the dbm library (it is not part of System V),
Xbut it came originally from AT&T and thus contains AT&T licensed code.  
XThings may work without the dbm library (undefine DBM and NDBM in src/conf.h), 
Xbut I have not tried it.
X
XOther configurations may work, but the networking configuration is
Xthe only one I have tried.
X
XThere may be minor changes to include files needed to make all of this work.
XI can't quite remember exactly what I changed.
X
XThis software is unsupported.
X
XAllyn Fratkin
XU.C. San Diego
Xallyn@cs.ucsd.edu
END_OF_FILE
if test 968 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
# end of 'README'
fi
if test -f 'READ_ME' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'READ_ME'\"
else
echo shar: Extracting \"'READ_ME'\" \(2609 characters\)
sed "s/^X//" >'READ_ME' <<'END_OF_FILE'
X
X			S E N D M A I L
X
XPresumably by the time you get to this READ_ME file, you will have
Xalready read the tape in, or will have received this distribution as
Xpart of 4.2bsd.  If you do not yet have the distribution online, read
Xthe tape in now -- you will need about one megabyte to hold the tape.
X
XThe first thing you should look at is the documentation.  The two
Xcritical documents are "doc/intro.me" and "doc/op.me".  Read both
Xof these before proceeding with your installation.  If you have read
Xthese before, you should still read through the second one again
Xanyhow, since the installation procedures change occasionally.
X
XA brief tour:
X	FILES	  A list of the files on the tape.
X	Makefile  A Makefile that will do a vanilla install for you.
X	adb	  A set of adb(1) scripts for printing out certain
X		  sendmail data structures.
X	adm	  Administrativia.
X	aux	  Files that represent programs that are not really
X		  "part" of sendmail, or old versions of some files
X		  in src for use by people on other system types.
X	cf	  The configuration files for Berkeley, plus a few
X		  prototype configurations for basic installation
X		  types.
X	doc	  The documentation.
X	include	  The include files used by more than just the sendmail
X		  program itself.
X	lib	  Library routines used by sendmail, but not really
X		  "part" of sendmail itself; these might be candidates
X		  for libc.a.
X	md	  Some m4 files used to isolate machine dependencies.
X	src	  The source for the sendmail program itself.
X	test	  Some files used for testing -- mostly these are
X		  shell files intended as the targets for aliasing.
X
XThe following are the known major problems and their Problem Report
Xnumber:
X
X012, 043	Names of files and programs cannot have upper case letters
X		in them -- these get mapped to lower case due to the
X		case mapping feature.
X178		Because of the different syntaxes used in envelopes versus
X		headers, we will occasionally allow syntactically incorrect
X		headers go out.
X226		Quote interpretation may be incorrect -- this is not well
X		defined in the protocols.
X
XDue to my present circumstances, I am unable to provide a formal
Xlevel of support for this system.  However, I will make whatever
Xattempt I can to answer a reasonable number of questions.  Since
Xthe company I am working for is not working in this area, and has
Xindulged me tremendously in letting me work on this at all, I regret
Xthat I am unable to accept phone calls.  I can be contacted as:
X
X		OLD			NEW
X    Arpanet:	eric@Berkeley		eric@Berkeley.ARPA
X    UUCP:	ucbvax!eric		eric@ucbvax.UUCP
X
XEric Allman
XBritton-Lee, Inc.
X
X@(#)READ_ME	4.1		7/25/83
END_OF_FILE
if test 2609 -ne `wc -c <'READ_ME'`; then
    echo shar: \"'READ_ME'\" unpacked with wrong size!
fi
# end of 'READ_ME'
fi
if test ! -d 'include' ; then
    echo shar: Creating directory \"'include'\"
    mkdir 'include'
fi
if test -f 'include/sysexits.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'include/sysexits.h'\"
else
echo shar: Extracting \"'include/sysexits.h'\" \(3656 characters\)
sed "s/^X//" >'include/sysexits.h' <<'END_OF_FILE'
X/*
X**  SYSEXITS.H -- Exit status codes for system programs.
X**
X**	This include file attempts to categorize possible error
X**	exit statuses for system programs, notably delivermail
X**	and the Berkeley network.
X**
X**	Error numbers begin at EX__BASE to reduce the possibility of
X**	clashing with other exit statuses that random programs may
X**	already return.  The meaning of the codes is approximately
X**	as follows:
X**
X**	EX_USAGE -- The command was used incorrectly, e.g., with
X**		the wrong number of arguments, a bad flag, a bad
X**		syntax in a parameter, or whatever.
X**	EX_DATAERR -- The input data was incorrect in some way.
X**		This should only be used for user's data & not
X**		system files.
X**	EX_NOINPUT -- An input file (not a system file) did not
X**		exist or was not readable.  This could also include
X**		errors like "No message" to a mailer (if it cared
X**		to catch it).
X**	EX_NOUSER -- The user specified did not exist.  This might
X**		be used for mail addresses or remote logins.
X**	EX_NOHOST -- The host specified did not exist.  This is used
X**		in mail addresses or network requests.
X**	EX_UNAVAILABLE -- A service is unavailable.  This can occur
X**		if a support program or file does not exist.  This
X**		can also be used as a catchall message when something
X**		you wanted to do doesn't work, but you don't know
X**		why.
X**	EX_SOFTWARE -- An internal software error has been detected.
X**		This should be limited to non-operating system related
X**		errors as possible.
X**	EX_OSERR -- An operating system error has been detected.
X**		This is intended to be used for such things as "cannot
X**		fork", "cannot create pipe", or the like.  It includes
X**		things like getuid returning a user that does not
X**		exist in the passwd file.
X**	EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp,
X**		etc.) does not exist, cannot be opened, or has some
X**		sort of error (e.g., syntax error).
X**	EX_CANTCREAT -- A (user specified) output file cannot be
X**		created.
X**	EX_IOERR -- An error occurred while doing I/O on some file.
X**	EX_TEMPFAIL -- temporary failure, indicating something that
X**		is not really an error.  In sendmail, this means
X**		that a mailer (e.g.) could not create a connection,
X**		and the request should be reattempted later.
X**	EX_PROTOCOL -- the remote system returned something that
X**		was "not possible" during a protocol exchange.
X**	EX_NOPERM -- You did not have sufficient permission to
X**		perform the operation.  This is not intended for
X**		file system problems, which should use NOINPUT or
X**		CANTCREAT, but rather for higher level permissions.
X**		For example, kre uses this to restrict who students
X**		can send mail to.
X**
X**	Maintained by Eric Allman (eric@berkeley, ucbvax!eric) --
X**		please mail changes to me.
X**
X**			@(#)sysexits.h	4.2		7/31/83
X*/
X
X# define EX_OK		0	/* successful termination */
X
X# define EX__BASE	64	/* base value for error messages */
X
X# define EX_USAGE	64	/* command line usage error */
X# define EX_DATAERR	65	/* data format error */
X# define EX_NOINPUT	66	/* cannot open input */
X# define EX_NOUSER	67	/* addressee unknown */
X# define EX_NOHOST	68	/* host name unknown */
X# define EX_UNAVAILABLE	69	/* service unavailable */
X# define EX_SOFTWARE	70	/* internal software error */
X# define EX_OSERR	71	/* system error (e.g., can't fork) */
X# define EX_OSFILE	72	/* critical OS file missing */
X# define EX_CANTCREAT	73	/* can't create (user) output file */
X# define EX_IOERR	74	/* input/output error */
X# define EX_TEMPFAIL	75	/* temp failure; user is invited to retry */
X# define EX_PROTOCOL	76	/* remote error in protocol */
X# define EX_NOPERM	77	/* permission denied */
END_OF_FILE
if test 3656 -ne `wc -c <'include/sysexits.h'`; then
    echo shar: \"'include/sysexits.h'\" unpacked with wrong size!
fi
# end of 'include/sysexits.h'
fi
if test -f 'include/useful.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'include/useful.h'\"
else
echo shar: Extracting \"'include/useful.h'\" \(1548 characters\)
sed "s/^X//" >'include/useful.h' <<'END_OF_FILE'
X/*
X**  USEFUL.H -- Some useful stuff.
X**
X**	@(#)useful.h	4.2		10/22/87
X*/
X
X# ifndef makedev
X# include <sys/types.h>
X# endif
X
X/* support for bool type */
Xtypedef char	bool;
X# define TRUE	1
X# define FALSE	0
X
X# ifndef NULL
X# define NULL	0
X# endif NULL
X
X/* bit hacking */
X# define bitset(bit, word)	(((word) & (bit)) != 0)
X
X/* some simple functions */
X# ifndef max
X# define max(a, b)	((a) > (b) ? (a) : (b))
X# define min(a, b)	((a) < (b) ? (a) : (b))
X# endif max
X
X/* assertions */
X# ifndef NASSERT
X# define ASSERT(expr, msg, parm)\
X	if (!(expr))\
X	{\
X		fprintf(stderr, "assertion botch: %s:%d: ", __FILE__, __LINE__);\
X		fprintf(stderr, msg, parm);\
X	}
X# else NASSERT
X# define ASSERT(expr, msg, parm)
X# endif NASSERT
X
X/* sccs id's */
X# ifndef lint
X# define SCCSID(arg)	static char SccsId[] = "arg";
X# else lint
X# define SCCSID(arg)
X# endif lint
X
X#ifdef	UNIXPC
Xtypedef unsigned short	u_short;
Xtypedef unsigned long	u_long;
Xtypedef unsigned char	u_char;
X#define rindex		strrchr
X#define index		strchr
X#define bcopy(s,d,l)	memcpy(d,s,l)
X#define bzero(p,l)	memset(p,'\0',l)
X#define sleep		zsleep
X#define message		zmessage
X#endif
X
X/* define the types of some common functions */
Xextern char	*strcpy(), *strncpy();
Xextern char	*strcat(), *strncat();
Xextern char	*malloc();
Xextern char	*index(), *rindex();
Xextern int	errno;
Xextern time_t	time();
Xextern char	*ctime();
X# ifndef V6
Xextern char	*getenv();
X# endif V6
X# ifndef VMUNIX
X#ifndef USG
Xtypedef unsigned short	u_short;
Xtypedef long		u_long;
Xtypedef char		u_char;
Xtypedef int		void;
X#endif
X# endif VMUNIX
END_OF_FILE
if test 1548 -ne `wc -c <'include/useful.h'`; then
    echo shar: \"'include/useful.h'\" unpacked with wrong size!
fi
# end of 'include/useful.h'
fi
if test -f 'include/userdbm.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'include/userdbm.h'\"
else
echo shar: Extracting \"'include/userdbm.h'\" \(187 characters\)
sed "s/^X//" >'include/userdbm.h' <<'END_OF_FILE'
X/*
X**  USERDBM.H -- user-level definitions for the DBM library
X**
X**	Version:
X**		@(#)userdbm.h	4.1		7/25/83
X*/
X
Xtypedef struct
X{
X	char	*dptr;
X	int	dsize;
X} DATUM;
X
Xextern DATUM fetch();
END_OF_FILE
if test 187 -ne `wc -c <'include/userdbm.h'`; then
    echo shar: \"'include/userdbm.h'\" unpacked with wrong size!
fi
# end of 'include/userdbm.h'
fi
if test ! -d 'lib' ; then
    echo shar: Creating directory \"'lib'\"
    mkdir 'lib'
fi
if test -f 'lib/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lib/Makefile'\"
else
echo shar: Extracting \"'lib/Makefile'\" \(701 characters\)
sed "s/^X//" >'lib/Makefile' <<'END_OF_FILE'
X#
X# Copyright (c) 1988 Regents of the University of California.
X# All rights reserved.
X#
X# Redistribution and use in source and binary forms are permitted
X# provided that this notice is preserved and that due credit is given
X# to the University of California at Berkeley. The name of the University
X# may not be used to endorse or promote products derived from this
X# software without specific prior written permission. This software
X# is provided ``as is'' without express or implied warranty.
X#
X#	@(#)Makefile	5.1 (Berkeley) 5/8/88
X#
XALL=	sendmail.hf
XSRCS=	sendmail.hf
X
Xall: FRC
Xclean: FRC
Xdepend: FRC
X
Xinstall: FRC
X	install -c -o bin -g bin -m 444 sendmail.hf ${DESTDIR}/usr/lib/sendmail.hf
X
XFRC:
X
END_OF_FILE
if test 701 -ne `wc -c <'lib/Makefile'`; then
    echo shar: \"'lib/Makefile'\" unpacked with wrong size!
fi
# end of 'lib/Makefile'
fi
if test -f 'lib/aliases' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lib/aliases'\"
else
echo shar: Extracting \"'lib/aliases'\" \(434 characters\)
sed "s/^X//" >'lib/aliases' <<'END_OF_FILE'
X#
X#  Aliases in this file will NOT be expanded in the header from
X#  Mail, but WILL be visible over networks or from /bin/mail.
X#
X#	>>>>>>>>>>	The program "newaliases" must be run after
X#	>> NOTE >>	this file is updated for any changes to
X#	>>>>>>>>>>	show through to sendmail.
X#
X
X# Basic system aliases -- these MUST be present
XMAILER-DAEMON: postmaster
Xpostmaster: root
X
X# Misc aliases for various things...
Xfortune: fortune@ucbvax
END_OF_FILE
if test 434 -ne `wc -c <'lib/aliases'`; then
    echo shar: \"'lib/aliases'\" unpacked with wrong size!
fi
# end of 'lib/aliases'
fi
if test -f 'lib/sendmail.hf' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lib/sendmail.hf'\"
else
echo shar: Extracting \"'lib/sendmail.hf'\" \(1858 characters\)
sed "s/^X//" >'lib/sendmail.hf' <<'END_OF_FILE'
X@(#)	sendmail.hf	4.2	6/7/85
Xcpyr	
Xcpyr	Sendmail
Xcpyr	Copyright (c) 1983  Eric P. Allman
Xcpyr	Berkeley, California
Xcpyr	
Xcpyr	Copyright (c) 1983 Regents of the University of California.
Xcpyr	All rights reserved.  The Berkeley software License Agreement
Xcpyr	specifies the terms and conditions for redistribution.
Xcpyr	
Xcpyr	@(#)sendmail.hf	4.2 (Berkeley) 6/7/85
Xcpyr	
Xsmtp	Commands:
Xsmtp		HELO	MAIL	RCPT	DATA	RSET
Xsmtp		NOOP	QUIT	HELP	VRFY	EXPN
Xsmtp	For more info use "HELP <topic>".
Xsmtp	To report bugs in the implementation contact eric@Berkeley.ARPA
Xsmtp	or eric@UCB-ARPA.ARPA.
Xsmtp	For local information contact postmaster at this site.
Xhelp	HELP [ <topic> ]
Xhelp		The HELP command gives help info.
Xhelo	HELO <hostname>
Xhelo		Introduce yourself.  I am a boor, so I really don't
Xhelo		care if you do.
Xmail	MAIL FROM: <sender>
Xmail		Specifies the sender.
Xrcpt	RCPT TO: <recipient>
Xrcpt		Specifies the recipient.  Can be used any number of times.
Xdata	DATA
Xdata		Following text is collected as the message.
Xdata		End with a single dot.
Xrset	RSET
Xrset		Resets the system.
Xquit	QUIT
Xquit		Exit sendmail (SMTP).
Xvrfy	VRFY <recipient>
Xvrfy		Not implemented to protocol.  Gives some sexy
Xvrfy		information.
Xexpn	EXPN <recipient>
Xexpn		Same as VRFY in this implementation.
Xnoop	NOOP
Xnoop		Do nothing.
Xsend	SEND FROM: <sender>
Xsend		replaces the MAIL command, and can be used to send
Xsend		directly to a users terminal.  Not supported in this
Xsend		implementation.
Xsoml	SOML FROM: <sender>
Xsoml		Send or mail.  If the user is logged in, send directly,
Xsoml		otherwise mail.  Not supported in this implementation.
Xsaml	SAML FROM: <sender>
Xsaml		Send and mail.  Send directly to the user's terminal,
Xsaml		and also mail a letter.  Not supported in this
Xsaml		implementation.
Xturn	TURN
Xturn		Reverses the direction of the connection.  Not currently
Xturn		implemented.
END_OF_FILE
if test 1858 -ne `wc -c <'lib/sendmail.hf'`; then
    echo shar: \"'lib/sendmail.hf'\" unpacked with wrong size!
fi
# end of 'lib/sendmail.hf'
fi
if test ! -d 'md' ; then
    echo shar: Creating directory \"'md'\"
    mkdir 'md'
fi
if test -f 'md/c.nvmunix.m4' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'md/c.nvmunix.m4'\"
else
echo shar: Extracting \"'md/c.nvmunix.m4'\" \(106 characters\)
sed "s/^X//" >'md/c.nvmunix.m4' <<'END_OF_FILE'
Xdnl	@(#)c.nvmunix.m4	4.2		7/27/83
Xdefine(m4CONFIG, -DVMUNIX -DNVMUNIX)dnl
Xdefine(m4LIBS, -ldbm -ljobs)dnl
END_OF_FILE
if test 106 -ne `wc -c <'md/c.nvmunix.m4'`; then
    echo shar: \"'md/c.nvmunix.m4'\" unpacked with wrong size!
fi
# end of 'md/c.nvmunix.m4'
fi
if test -f 'md/c.v6.m4' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'md/c.v6.m4'\"
else
echo shar: Extracting \"'md/c.v6.m4'\" \(73 characters\)
sed "s/^X//" >'md/c.v6.m4' <<'END_OF_FILE'
Xdnl	@(#)c.v6.m4	4.1		7/25/83
Xdefine(m4CONFIG, -DV6)dnl
Xdefine(m4LIBS)dnl
END_OF_FILE
if test 73 -ne `wc -c <'md/c.v6.m4'`; then
    echo shar: \"'md/c.v6.m4'\" unpacked with wrong size!
fi
# end of 'md/c.v6.m4'
fi
if test -f 'md/c.v7.m4' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'md/c.v7.m4'\"
else
echo shar: Extracting \"'md/c.v7.m4'\" \(80 characters\)
sed "s/^X//" >'md/c.v7.m4' <<'END_OF_FILE'
Xdnl	@(#)c.v7.m4	4.1		7/25/83
Xdefine(m4CONFIG, -DV7)dnl
Xdefine(m4LIBS, -ldbm)dnl
END_OF_FILE
if test 80 -ne `wc -c <'md/c.v7.m4'`; then
    echo shar: \"'md/c.v7.m4'\" unpacked with wrong size!
fi
# end of 'md/c.v7.m4'
fi
if test -f 'md/c.vmunix.m4' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'md/c.vmunix.m4'\"
else
echo shar: Extracting \"'md/c.vmunix.m4'\" \(117 characters\)
sed "s/^X//" >'md/c.vmunix.m4' <<'END_OF_FILE'
Xdnl	@(#)c.vmunix.m4	4.4		7/21/86
Xdefine(m4CONFIG, -DVMUNIX -DMXDOMAIN)dnl
Xdefine(m4LIBS, -ldbm)dnl
Xdefine(m4SCCS)dnl
END_OF_FILE
if test 117 -ne `wc -c <'md/c.vmunix.m4'`; then
    echo shar: \"'md/c.vmunix.m4'\" unpacked with wrong size!
fi
# end of 'md/c.vmunix.m4'
fi
if test -f 'md/config.dst' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'md/config.dst'\"
else
echo shar: Extracting \"'md/config.dst'\" \(88 characters\)
sed "s/^X//" >'md/config.dst' <<'END_OF_FILE'
Xdnl	@(#)c.vmunix.m4	4.2		7/27/83
Xdefine(m4CONFIG, -DVMUNIX)dnl
Xdefine(m4LIBS, -ldbm)dnl
END_OF_FILE
if test 88 -ne `wc -c <'md/config.dst'`; then
    echo shar: \"'md/config.dst'\" unpacked with wrong size!
fi
# end of 'md/config.dst'
fi
if test -f 'md/config.m4' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'md/config.m4'\"
else
echo shar: Extracting \"'md/config.m4'\" \(117 characters\)
sed "s/^X//" >'md/config.m4' <<'END_OF_FILE'
Xdnl	@(#)c.vmunix.m4	4.4		7/21/86
Xdefine(m4CONFIG, -DVMUNIX -DMXDOMAIN)dnl
Xdefine(m4LIBS, -ldbm)dnl
Xdefine(m4SCCS)dnl
END_OF_FILE
if test 117 -ne `wc -c <'md/config.m4'`; then
    echo shar: \"'md/config.m4'\" unpacked with wrong size!
fi
# end of 'md/config.m4'
fi
if test -f 'md/config.m4.mone' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'md/config.m4.mone'\"
else
echo shar: Extracting \"'md/config.m4.mone'\" \(89 characters\)
sed "s/^X//" >'md/config.m4.mone' <<'END_OF_FILE'
Xdnl	%W%	%Y%	%G%
Xdefine(m4CONFIG, -DVMUNIX)dnl
Xdefine(m4LIBS, -ldbm)dnl
Xdefine(m4SCCS)dnl
END_OF_FILE
if test 89 -ne `wc -c <'md/config.m4.mone'`; then
    echo shar: \"'md/config.m4.mone'\" unpacked with wrong size!
fi
# end of 'md/config.m4.mone'
fi
if test ! -d 'src' ; then
    echo shar: Creating directory \"'src'\"
    mkdir 'src'
fi
if test -f 'src/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/Makefile'\"
else
echo shar: Extracting \"'src/Makefile'\" \(2952 characters\)
sed "s/^X//" >'src/Makefile' <<'END_OF_FILE'
X#
X#  Sendmail
X#  Copyright (c) 1983  Eric P. Allman
X#  Berkeley, California
X#
X#  Copyright (c) 1983 Regents of the University of California.
X#  All rights reserved.  The Berkeley software License Agreement
X#  specifies the terms and conditions for redistribution.
X#
X#	@(#)Makefile.m4	5.11 (Berkeley) 7/21/86
X#
X#
X#  SENDMAIL Makefile.
X#
X#
X
XLIBS=	-ldir
XDESTDIR=
X
XOBJS1=	conf.o main.o collect.o parseaddr.o alias.o deliver.o \
X	savemail.o err.o readcf.o stab.o headers.o recipient.o \
X	stats.o daemon.o usersmtp.o srvrsmtp.o queue.o \
X	macro.o util.o clock.o trace.o envelope.o domain.o
XOBJS2=	sysexits.o arpadate.o convtime.o rename.o
XOBJS=	$(OBJS1) $(OBJS2)
XSRCS1=	conf.h sendmail.h \
X	conf.c deliver.c main.c parseaddr.c err.c alias.c savemail.c \
X	sysexits.c util.c arpadate.c version.c collect.c \
X	macro.c headers.c readcf.c stab.c recipient.c stats.c daemon.c \
X	usersmtp.c srvrsmtp.c queue.c clock.c trace.c envelope.c domain.c
XSRCS2=	TODO convtime.c
XSRCS=	Version.c $(SRCS1) $(SRCS2)
XALL=	sendmail
X
XCHOWN=	-echo chown
XCHMOD=	chmod
XO=	-O
XCOPTS=	
XCCONFIG=-I../include -I/usr/netinclude
XCFLAGS=	$O $(COPTS) $(CCONFIG)
XASMSED=	../include/asm.sed
XAR=	-ar
XARFLAGS=rvu
XLINT=	lint
XXREF=	ctags -x
XCP=	cp
XMV=	mv
XINSTALL=install -c -s -o root
XM4=	m4
XTOUCH=	touch
XABORT=	false
X
XGET=	sccs get
XDELTA=	sccs delta
XWHAT=	what
XPRT=	sccs prt
XREL=
X
XROOT=	root
XGROUP=	kmem
XOBJMODE=755
X
X# .c.o:
X# 	cc -S ${CFLAGS} $*.c
X# 	sed -f $(ASMSED) $*.s | as -o $*.o
X# 	rm -f $*.s
X
Xsendmail: $(OBJS1) $(OBJS2) Version.o
X	$(CC) $(COPTS) -o sendmail Version.o $(OBJS1) $(OBJS2) $(LIBS)
X	$(CHMOD) $(OBJMODE) sendmail
X	size sendmail; ls -l sendmail; $(WHAT) < Version.o
X
Xinstall: all
X	$(INSTALL) -m 6755 -g $(GROUP) sendmail $(DESTDIR)/usr/lib
X	$(CP) /dev/null $(DESTDIR)/usr/lib/sendmail.fc
X
Xversion: newversion $(OBJS) Version.c
X
Xnewversion:
X	@rm -f SCCS/p.version.c version.c
X	@$(GET) $(REL) -e SCCS/s.version.c
X	@$(DELTA) -s SCCS/s.version.c
X	@$(GET) -t -s SCCS/s.version.c
X
Xfullversion: $(OBJS) dumpVersion Version.o
X
XdumpVersion:
X	rm -f Version.c
X
XVersion.c: version.c
X	@echo generating Version.c from version.c
X	@cp version.c Version.c
X	@chmod 644 Version.c
X	@echo "" >> Version.c
X	@echo "# ifdef COMMENT" >> Version.c
X	@$(PRT) SCCS/s.version.c >> Version.c
X	@echo "" >> Version.c
X	@echo "code versions:" >> Version.c
X	@echo "" >> Version.c
X	@$(WHAT) $(OBJS) >> Version.c
X	@echo "" >> Version.c
X	@echo "# endif COMMENT" >> Version.c
X
X$(OBJS1): sendmail.h
X$(OBJS): conf.h
Xstats.o: mailstats.h
X
Xsendmail.h util.o: ../include/useful.h
X
Xall: $(ALL)
X
X#
X#  Auxiliary support entries
X#
X
Xclean:
X	rm -f core sendmail rmail usersmtp uucp a.out XREF sendmail.cf
X	rm -f *.o
X
Xsources: $(SRCS)
X
X$(SRCS1) $(SRCS2):
X	if test -d SCCS; then $(GET) $(REL) SCCS/s.$@; else $(TOUCH) $@; fi
X
Xprint: $(SRCS)
X	@ls -l | pr -h "sendmail directory"
X	@$(XREF) *.c | pr -h "cross reference listing"
X	@size *.o | pr -h "object code sizes"
X	@pr Makefile *.m4 *.h *.[cs]
X
Xlint:
X	$(LINT) $(CCONFIG) $(SRCS1)
END_OF_FILE
if test 2952 -ne `wc -c <'src/Makefile'`; then
    echo shar: \"'src/Makefile'\" unpacked with wrong size!
fi
# end of 'src/Makefile'
fi
if test -f 'src/README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/README'\"
else
echo shar: Extracting \"'src/README'\" \(968 characters\)
sed "s/^X//" >'src/README' <<'END_OF_FILE'
XThis is the sendmail 5.58 distribution for System V Release 3.1.
XIt may run on earlier releases of System V.  (I haven't tried it).
XOnly the src and include directories are included.  The other
Xdirectories should be available in the normal Berkeley sendmail
Xdistribution.
X
XAs configured, this software has the following software requirements:
X
X	WIN/3B TCP/IP (or equivalent Berkeley networking emulation)
X	dbm library
X
XI wish I could distribute the dbm library (it is not part of System V),
Xbut it came originally from AT&T and thus contains AT&T licensed code.  
XThings may work without the dbm library (undefine DBM and NDBM in src/conf.h), 
Xbut I have not tried it.
X
XOther configurations may work, but the networking configuration is
Xthe only one I have tried.
X
XThere may be minor changes to include files needed to make all of this work.
XI can't quite remember exactly what I changed.
X
XThis software is unsupported.
X
XAllyn Fratkin
XU.C. San Diego
Xallyn@cs.ucsd.edu
END_OF_FILE
if test 968 -ne `wc -c <'src/README'`; then
    echo shar: \"'src/README'\" unpacked with wrong size!
fi
# end of 'src/README'
fi
if test -f 'src/Version.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/Version.c'\"
else
echo shar: Extracting \"'src/Version.c'\" \(411 characters\)
sed "s/^X//" >'src/Version.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)version.c	5.58 (Berkeley) 2/3/87";
X#endif not lint
X
Xchar	Version[] = "5.58";
X
X# ifdef COMMENT
END_OF_FILE
if test 411 -ne `wc -c <'src/Version.c'`; then
    echo shar: \"'src/Version.c'\" unpacked with wrong size!
fi
# end of 'src/Version.c'
fi
if test -f 'src/arpadate.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/arpadate.c'\"
else
echo shar: Extracting \"'src/arpadate.c'\" \(4198 characters\)
sed "s/^X//" >'src/arpadate.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)arpadate.c	5.5 (Berkeley) 3/18/87";
X#endif not lint
X
X# include "conf.h"
X# ifdef USG
X# include <time.h>
X# else
X# include <sys/time.h>
X# ifndef V6
X# include <sys/types.h>
X# include <sys/timeb.h>
X# endif V6
X# endif USG
X# include "useful.h"
X
X# ifdef V6
X# define OLDTIME
X# endif V6
X# ifdef USG
X# define OLDTIME
X# endif USG
X
X/*
X**  ARPADATE -- Create date in ARPANET format
X**
X**	Parameters:
X**		ud -- unix style date string.  if NULL, one is created.
X**
X**	Returns:
X**		pointer to an ARPANET date field
X**
X**	Side Effects:
X**		none
X**
X**	WARNING:
X**		date is stored in a local buffer -- subsequent
X**		calls will overwrite.
X**
X**	Bugs:
X**		Timezone is computed from local time, rather than
X**		from whereever (and whenever) the message was sent.
X**		To do better is very hard.
X**
X**		Some sites are now inserting the timezone into the
X**		local date.  This routine should figure out what
X**		the format is and work appropriately.
X*/
X
Xchar *
Xarpadate(ud)
X	register char *ud;
X{
X	register char *p;
X	register char *q;
X	static char b[40];
X	extern char *ctime();
X	register int i;
X	extern struct tm *localtime();
X	extern bool fconvert();
X# ifdef OLDTIME
X	long t;
X	extern long time();
X# else OLDTIME
X	struct timeb t;
X	extern struct timeb *ftime();
X# endif OLDTIME
X# ifdef V6
X	extern char *StdTimezone, *DstTimezone;
X# endif V6
X# ifdef USG
X	extern char *tzname[2];
X# endif USG
X
X	/*
X	**  Get current time.
X	**	This will be used if a null argument is passed and
X	**	to resolve the timezone.
X	*/
X
X# ifdef OLDTIME
X	(void) time(&t);
X	if (ud == NULL)
X		ud = ctime(&t);
X# else
X	ftime(&t);
X	if (ud == NULL)
X		ud = ctime(&t.time);
X# endif OLDTIME
X
X	/*
X	**  Crack the UNIX date line in a singularly unoriginal way.
X	*/
X
X	q = b;
X
X	p = &ud[0];		/* Mon */
X	*q++ = *p++;
X	*q++ = *p++;
X	*q++ = *p++;
X	*q++ = ',';
X	*q++ = ' ';
X
X	p = &ud[8];		/* 16 */
X	if (*p == ' ')
X		p++;
X	else
X		*q++ = *p++;
X	*q++ = *p++;
X	*q++ = ' ';
X
X	p = &ud[4];		/* Sep */
X	*q++ = *p++;
X	*q++ = *p++;
X	*q++ = *p++;
X	*q++ = ' ';
X
X	p = &ud[22];		/* 79 */
X	*q++ = *p++;
X	*q++ = *p++;
X	*q++ = ' ';
X
X	p = &ud[11];		/* 01:03:52 */
X	for (i = 8; i > 0; i--)
X		*q++ = *p++;
X
X				/* -PST or -PDT */
X# ifdef V6
X	if (localtime(&t)->tm_isdst)
X		p = DstTimezone;
X	else
X		p = StdTimezone;
X# else
X# ifdef USG
X	if (localtime(&t)->tm_isdst)
X		p = tzname[1];
X	else
X		p = tzname[0];
X# else
X	p = localtime(&t.time)->tm_zone;
X# endif USG
X# endif V6
X	if ((strncmp(p, "GMT", 3) == 0 || strncmp(p, "gmt", 3) == 0) &&
X	    p[3] != '\0')
X	{
X		/* hours from GMT */
X		p += 3;
X		*q++ = *p++;
X		if (p[1] == ':')
X			*q++ = '0';
X		else
X			*q++ = *p++;
X		*q++ = *p++;
X		p++;		/* skip ``:'' */
X		*q++ = *p++;
X		*q++ = *p++;
X		*q = '\0';
X	}
X	else if (!fconvert(p, q))
X	{
X		*q++ = ' ';
X		*q++ = *p++;
X		*q++ = *p++;
X		*q++ = *p++;
X		*q = '\0';
X	}
X
X	return (b);
X}
X/*
X**  FCONVERT -- convert foreign timezones to ARPA timezones
X**
X**	This routine is essentially from Teus Hagen.
X**
X**	Parameters:
X**		a -- timezone as returned from UNIX.
X**		b -- place to put ARPA-style timezone.
X**
X**	Returns:
X**		TRUE -- if a conversion was made (and b was filled in).
X**		FALSE -- if this is not a recognized local time.
X**
X**	Side Effects:
X**		none.
X*/
X
X/* UNIX to arpa conversion table */
Xstruct foreign
X{
X	char *f_from; 
X	char *f_to; 
X};
X
Xstatic struct foreign	Foreign[] =
X{
X	{ "EET",	"+0200" },	/* eastern europe */
X	{ "MET",	"+0100" },	/* middle europe */
X	{ "WET",	"GMT"   },	/* western europe */
X	{ "EET DST",	"+0300" },	/* daylight saving times */
X	{ "MET DST",	"+0200" },
X	{ "WET DST",	"+0100" },
X	{ NULL,		NULL	 }
X};
X
Xbool
Xfconvert(a, b)
X	register char *a;
X	char *b;
X{
X	register struct foreign *euptr;
X	register char *p;
X
X	for (euptr = Foreign; euptr->f_from != NULL; euptr++)
X	{
X		extern bool sameword();
X
X		if (sameword(euptr->f_from, a))
X		{
X			p = euptr->f_to;
X			*b++ = ' ';
X			while (*p != '\0')
X				*b++ = *p++;
X			*b = '\0';
X			return (TRUE);
X		}
X	}
X	return (FALSE);
X}
END_OF_FILE
if test 4198 -ne `wc -c <'src/arpadate.c'`; then
    echo shar: \"'src/arpadate.c'\" unpacked with wrong size!
fi
# end of 'src/arpadate.c'
fi
if test -f 'src/bcopy.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/bcopy.c'\"
else
echo shar: Extracting \"'src/bcopy.c'\" \(1094 characters\)
sed "s/^X//" >'src/bcopy.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)bcopy.c	5.1 (Berkeley) 6/7/85";
X#endif not lint
X
X# include "useful.h"
X
X/*
X**  BCOPY -- block copy.
X**
X**	Parameters:
X**		s -- source of bytes.
X**		d -- destination of bytes.
X**		l -- length of block to move.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		copies 's' to 'd' for 'l' bytes.
X**
X**	Notes:
X**		This can be easily written in assembly language on
X**		machines like VAXes if performance is needed.
X*/
X
X/*VARARGS0*/
Xbcopy(s, d, l)
X	register char *s, *d;
X	register int l;
X{
X	while (l-- > 0)
X		*d++ = *s++;
X}
X/*
X**  BZERO -- zero a block of memory
X**
X**	Parameters:
X**		p -- location to clear.
X**		l -- number of bytes to clear.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbzero(p, l)
X	register char *p;
X	register int l;
X{
X	while (l-- > 0)
X		*p++ = 0;
X}
END_OF_FILE
if test 1094 -ne `wc -c <'src/bcopy.c'`; then
    echo shar: \"'src/bcopy.c'\" unpacked with wrong size!
fi
# end of 'src/bcopy.c'
fi
if test -f 'src/clock.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/clock.c'\"
else
echo shar: Extracting \"'src/clock.c'\" \(4366 characters\)
sed "s/^X//" >'src/clock.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)clock.c	5.4 (Berkeley) 12/17/85";
X#endif not lint
X
X# include "sendmail.h"
X# include <signal.h>
X
X/*
X**  SETEVENT -- set an event to happen at a specific time.
X**
X**	Events are stored in a sorted list for fast processing.
X**	An event only applies to the process that set it.
X**
X**	Parameters:
X**		intvl -- intvl until next event occurs.
X**		func -- function to call on event.
X**		arg -- argument to func on event.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
XEVENT *
Xsetevent(intvl, func, arg)
X	time_t intvl;
X	int (*func)();
X	int arg;
X{
X	register EVENT **evp;
X	register EVENT *ev;
X	auto time_t now;
X	extern tick();
X
X# ifdef DEBUG
X	if (intvl <= 0)
X	{
X		syserr("setevent: intvl=%ld\n", intvl);
X		return (NULL);
X	}
X# endif DEBUG
X
X	(void) time(&now);
X
X	/* search event queue for correct position */
X	for (evp = &EventQueue; (ev = *evp) != NULL; evp = &ev->ev_link)
X	{
X		if (ev->ev_time >= now + intvl)
X			break;
X	}
X
X	/* insert new event */
X	ev = (EVENT *) xalloc(sizeof *ev);
X	ev->ev_time = now + intvl;
X	ev->ev_func = func;
X	ev->ev_arg = arg;
X	ev->ev_pid = getpid();
X	ev->ev_link = *evp;
X	*evp = ev;
X
X# ifdef DEBUG
X	if (tTd(5, 5))
X		printf("setevent: intvl=%ld, for=%ld, func=%x, arg=%d, ev=%x\n",
X			intvl, now + intvl, func, arg, ev);
X# endif DEBUG
X
X	tick();
X	return (ev);
X}
X/*
X**  CLREVENT -- remove an event from the event queue.
X**
X**	Parameters:
X**		ev -- pointer to event to remove.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		arranges for event ev to not happen.
X*/
X
Xclrevent(ev)
X	register EVENT *ev;
X{
X	register EVENT **evp;
X
X# ifdef DEBUG
X	if (tTd(5, 5))
X		printf("clrevent: ev=%x\n", ev);
X# endif DEBUG
X	if (ev == NULL)
X		return;
X
X	/* find the parent event */
X	(void) signal(SIGALRM, SIG_IGN);
X	for (evp = &EventQueue; *evp != NULL; evp = &(*evp)->ev_link)
X	{
X		if (*evp == ev)
X			break;
X	}
X
X	/* now remove it */
X	if (*evp != NULL)
X	{
X		*evp = ev->ev_link;
X		free((char *) ev);
X	}
X
X	/* restore clocks and pick up anything spare */
X	tick();
X}
X/*
X**  TICK -- take a clock tick
X**
X**	Called by the alarm clock.  This routine runs events as needed.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		calls the next function in EventQueue.
X*/
X
Xtick()
X{
X	register time_t now;
X	register EVENT *ev;
X	int mypid = getpid();
X
X	(void) signal(SIGALRM, SIG_IGN);
X	(void) alarm(0);
X	now = curtime();
X
X# ifdef DEBUG
X	if (tTd(5, 4))
X		printf("tick: now=%ld\n", now);
X# endif DEBUG
X
X	while ((ev = EventQueue) != NULL &&
X	       (ev->ev_time <= now || ev->ev_pid != mypid))
X	{
X		int (*f)();
X		int arg;
X		int pid;
X
X		/* process the event on the top of the queue */
X		ev = EventQueue;
X		EventQueue = EventQueue->ev_link;
X# ifdef DEBUG
X		if (tTd(5, 6))
X			printf("tick: ev=%x, func=%x, arg=%d, pid=%d\n", ev,
X				ev->ev_func, ev->ev_arg, ev->ev_pid);
X# endif DEBUG
X
X		/* we must be careful in here because ev_func may not return */
X		(void) signal(SIGALRM, tick);
X#ifdef SIGVTALRM
X		/* reset 4.2bsd signal mask to allow future alarms */
X		(void) sigsetmask(sigblock(0) & ~sigmask(SIGALRM));
X#endif SIGVTALRM
X
X		f = ev->ev_func;
X		arg = ev->ev_arg;
X		pid = ev->ev_pid;
X		free((char *) ev);
X		if (pid != getpid())
X			continue;
X		if (EventQueue != NULL)
X		{
X			if (EventQueue->ev_time > now)
X				(void) alarm((unsigned) (EventQueue->ev_time - now));
X			else
X				(void) alarm(3);
X		}
X		(*f)(arg);
X		(void) alarm(0);
X		now = curtime();
X	}
X	(void) signal(SIGALRM, tick);
X	if (EventQueue != NULL)
X		(void) alarm((unsigned) (EventQueue->ev_time - now));
X}
X/*
X**  SLEEP -- a version of sleep that works with this stuff
X**
X**	Because sleep uses the alarm facility, I must reimplement
X**	it here.
X**
X**	Parameters:
X**		intvl -- time to sleep.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		waits for intvl time.  However, other events can
X**		be run during that interval.
X*/
X
Xstatic bool	SleepDone;
X
Xsleep(intvl)
X	unsigned int intvl;
X{
X	extern endsleep();
X
X	if (intvl == 0)
X		return;
X	SleepDone = FALSE;
X	(void) setevent((time_t) intvl, endsleep, 0);
X	while (!SleepDone)
X		pause();
X}
X
Xstatic
Xendsleep()
X{
X	SleepDone = TRUE;
X}
END_OF_FILE
if test 4366 -ne `wc -c <'src/clock.c'`; then
    echo shar: \"'src/clock.c'\" unpacked with wrong size!
fi
# end of 'src/clock.c'
fi
if test -f 'src/conf.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/conf.h'\"
else
echo shar: Extracting \"'src/conf.h'\" \(1955 characters\)
sed "s/^X//" >'src/conf.h' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X**
X**	@(#)conf.h	5.8 (Berkeley) 7/21/86
X*/
X
X/*
X**  CONF.H -- All user-configurable parameters for sendmail
X*/
X
X/*
X**  Table sizes, etc....
X**	There shouldn't be much need to change these....
X*/
X
X# define MAXLINE	1024		/* max line length */
X# define MAXNAME	256		/* max length of a name */
X# define MAXFIELD	2500		/* max total length of a hdr field */
X# define MAXPV		40		/* max # of parms to mailers */
X# define MAXHOP		17		/* max value of HopCount */
X# define MAXATOM	100		/* max atoms per address */
X# define MAXMAILERS	25		/* maximum mailers known to system */
X# define MAXRWSETS	30		/* max # of sets of rewriting rules */
X# define MAXPRIORITIES	25		/* max values for Precedence: field */
X# define MAXTRUST	30		/* maximum number of trusted users */
X# define MAXUSERENVIRON	40		/* max # of items in user environ */
X# define QUEUESIZE	600		/* max # of jobs per queue run */
X# define MAXMXHOSTS	10		/* max # of MX records */
X
X/*
X**  Compilation options.
X**
X**	#define these if they are available; comment them out otherwise.
X*/
X
X/*define DBM		1	/* use DBM library (requires -ldbm) */
X/*define NDBM		1	/* new DBM library available (requires DBM) */
X/*define DEBUG		1	/* enable debugging */
X/* # define LOG		1	/* enable logging */
X/*define SMTP		1	/* enable user and server SMTP */
X# define QUEUE		1	/* enable queueing */
X# define UGLYUUCP	1	/* output ugly UUCP From lines */
X/*define DAEMON		1	/* include the daemon (requires IPC & SMTP) */
X/* # define FLOCK		1	/* use flock file locking */
X# define SETPROCTITLE	1	/* munge argv to display current status */
X/* # define WIZ		1	/* allow wizard mode */
X#define USG			1	/* this is a usg system */
X#define UNIXPC		1	/* att unix-pc/3b1 */
END_OF_FILE
if test 1955 -ne `wc -c <'src/conf.h'`; then
    echo shar: \"'src/conf.h'\" unpacked with wrong size!
fi
# end of 'src/conf.h'
fi
if test -f 'src/convtime.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/convtime.c'\"
else
echo shar: Extracting \"'src/convtime.c'\" \(2851 characters\)
sed "s/^X//" >'src/convtime.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)convtime.c	5.1 (Berkeley) 6/7/85";
X#endif not lint
X
X# include <ctype.h>
X# include "conf.h"
X# include "useful.h"
X
X/*
X**  CONVTIME -- convert time
X**
X**	Takes a time as an ascii string with a trailing character
X**	giving units:
X**	  s -- seconds
X**	  m -- minutes
X**	  h -- hours
X**	  d -- days (default)
X**	  w -- weeks
X**	For example, "3d12h" is three and a half days.
X**
X**	Parameters:
X**		p -- pointer to ascii time.
X**
X**	Returns:
X**		time in seconds.
X**
X**	Side Effects:
X**		none.
X*/
X
Xtime_t
Xconvtime(p)
X	char *p;
X{
X	register time_t t, r;
X	register char c;
X
X	r = 0;
X	while (*p != '\0')
X	{
X		t = 0;
X		while (isdigit(c = *p++))
X			t = t * 10 + (c - '0');
X		if (c == '\0')
X			p--;
X		switch (c)
X		{
X		  case 'w':		/* weeks */
X			t *= 7;
X
X		  case 'd':		/* days */
X		  default:
X			t *= 24;
X
X		  case 'h':		/* hours */
X			t *= 60;
X
X		  case 'm':		/* minutes */
X			t *= 60;
X
X		  case 's':		/* seconds */
X			break;
X		}
X		r += t;
X	}
X
X	return (r);
X}
X/*
X**  PINTVL -- produce printable version of a time interval
X**
X**	Parameters:
X**		intvl -- the interval to be converted
X**		brief -- if TRUE, print this in an extremely compact form
X**			(basically used for logging).
X**
X**	Returns:
X**		A pointer to a string version of intvl suitable for
X**			printing or framing.
X**
X**	Side Effects:
X**		none.
X**
X**	Warning:
X**		The string returned is in a static buffer.
X*/
X
X# define PLURAL(n)	((n) == 1 ? "" : "s")
X
Xchar *
Xpintvl(intvl, brief)
X	time_t intvl;
X	bool brief;
X{
X	static char buf[256];
X	register char *p;
X	int wk, dy, hr, mi, se;
X
X	if (intvl == 0 && !brief)
X		return ("zero seconds");
X
X	/* decode the interval into weeks, days, hours, minutes, seconds */
X	se = intvl % 60;
X	intvl /= 60;
X	mi = intvl % 60;
X	intvl /= 60;
X	hr = intvl % 24;
X	intvl /= 24;
X	if (brief)
X		dy = intvl;
X	else
X	{
X		dy = intvl % 7;
X		intvl /= 7;
X		wk = intvl;
X	}
X
X	/* now turn it into a sexy form */
X	p = buf;
X	if (brief)
X	{
X		if (dy > 0)
X		{
X			(void) sprintf(p, "%d+", dy);
X			p += strlen(p);
X		}
X		(void) sprintf(p, "%02d:%02d:%02d", hr, mi, se);
X		return (buf);
X	}
X
X	/* use the verbose form */
X	if (wk > 0)
X	{
X		(void) sprintf(p, ", %d week%s", wk, PLURAL(wk));
X		p += strlen(p);
X	}
X	if (dy > 0)
X	{
X		(void) sprintf(p, ", %d day%s", dy, PLURAL(dy));
X		p += strlen(p);
X	}
X	if (hr > 0)
X	{
X		(void) sprintf(p, ", %d hour%s", hr, PLURAL(hr));
X		p += strlen(p);
X	}
X	if (mi > 0)
X	{
X		(void) sprintf(p, ", %d minute%s", mi, PLURAL(mi));
X		p += strlen(p);
X	}
X	if (se > 0)
X	{
X		(void) sprintf(p, ", %d second%s", se, PLURAL(se));
X		p += strlen(p);
X	}
X
X	return (buf + 2);
X}
END_OF_FILE
if test 2851 -ne `wc -c <'src/convtime.c'`; then
    echo shar: \"'src/convtime.c'\" unpacked with wrong size!
fi
# end of 'src/convtime.c'
fi
if test -f 'src/mailstats.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/mailstats.h'\"
else
echo shar: Extracting \"'src/mailstats.h'\" \(706 characters\)
sed "s/^X//" >'src/mailstats.h' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X**
X**	"@(#)mailstats.h	5.1 (Berkeley) 5/2/86";
X**
X*/
X
X/*
X**  Statistics structure.
X*/
X
Xstruct statistics
X{
X	time_t	stat_itime;		/* file initialization time */
X	short	stat_size;		/* size of this structure */
X	long	stat_nf[MAXMAILERS];	/* # msgs from each mailer */
X	long	stat_bf[MAXMAILERS];	/* kbytes from each mailer */
X	long	stat_nt[MAXMAILERS];	/* # msgs to each mailer */
X	long	stat_bt[MAXMAILERS];	/* kbytes to each mailer */
X};
END_OF_FILE
if test 706 -ne `wc -c <'src/mailstats.h'`; then
    echo shar: \"'src/mailstats.h'\" unpacked with wrong size!
fi
# end of 'src/mailstats.h'
fi
if test -f 'src/rename.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/rename.c'\"
else
echo shar: Extracting \"'src/rename.c'\" \(281 characters\)
sed "s/^X//" >'src/rename.c' <<'END_OF_FILE'
X/*
X * substitute for BSD/SVR3 rename() system call, from
X * Janet Walz, walz@mimsy.umd.edu & Rich Salz, rsalz@pineapple.bbn.com
X */
X
Xint rename(oldname,newname)
Xchar *oldname,*newname;
X{
X	(void)unlink(newname);
X	if(link(oldname,newname))
X		return(-1);
X	return(unlink(oldname));
X}
XEND_OF_FILE
if test 281 -ne `wc -c <'src/rename.c'`; then
    echo shar: \"'src/rename.c'\" unpacked with wrong size!
fi
# end of 'src/rename.c'
fi
if test -f 'src/stab.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/stab.c'\"
else
echo shar: Extracting \"'src/stab.c'\" \(2239 characters\)
sed "s/^X//" >'src/stab.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)stab.c	5.2 (Berkeley) 6/7/85";
X#endif not lint
X
X# include "sendmail.h"
X
X/*
X**  STAB -- manage the symbol table
X**
X**	Parameters:
X**		name -- the name to be looked up or inserted.
X**		type -- the type of symbol.
X**		op -- what to do:
X**			ST_ENTER -- enter the name if not
X**				already present.
X**			ST_FIND -- find it only.
X**
X**	Returns:
X**		pointer to a STAB entry for this name.
X**		NULL if not found and not entered.
X**
X**	Side Effects:
X**		can update the symbol table.
X*/
X
X# define STABSIZE	400
X
X/* SymTab should be not be static on USG systems */
X#ifndef USG
Xstatic 
X#endif
XSTAB	*SymTab[STABSIZE];
X
XSTAB *
Xstab(name, type, op)
X	char *name;
X	int type;
X	int op;
X{
X	register STAB *s;
X	register STAB **ps;
X	extern bool sameword();
X	register int hfunc;
X	register char *p;
X	extern char lower();
X
X# ifdef DEBUG
X	if (tTd(36, 5))
X		printf("STAB: %s %d ", name, type);
X# endif DEBUG
X
X	/*
X	**  Compute the hashing function
X	**
X	**	We could probably do better....
X	*/
X
X	hfunc = type;
X	for (p = name; *p != '\0'; p++)
X		hfunc = (((hfunc << 7) | lower(*p)) & 077777) % STABSIZE;
X
X# ifdef DEBUG
X	if (tTd(36, 9))
X		printf("(hfunc=%d) ", hfunc);
X# endif DEBUG
X
X	ps = &SymTab[hfunc];
X	while ((s = *ps) != NULL && (!sameword(name, s->s_name) || s->s_type != type))
X		ps = &s->s_next;
X
X	/*
X	**  Dispose of the entry.
X	*/
X
X	if (s != NULL || op == ST_FIND)
X	{
X# ifdef DEBUG
X		if (tTd(36, 5))
X		{
X			if (s == NULL)
X				printf("not found\n");
X			else
X			{
X				long *lp = (long *) s->s_class;
X
X				printf("type %d val %lx %lx %lx %lx\n",
X					s->s_type, lp[0], lp[1], lp[2], lp[3]);
X			}
X		}
X# endif DEBUG
X		return (s);
X	}
X
X	/*
X	**  Make a new entry and link it in.
X	*/
X
X# ifdef DEBUG
X	if (tTd(36, 5))
X		printf("entered\n");
X# endif DEBUG
X
X	/* make new entry */
X	s = (STAB *) xalloc(sizeof *s);
X	bzero((char *) s, sizeof *s);
X	s->s_name = newstr(name);
X	makelower(s->s_name);
X	s->s_type = type;
X
X	/* link it in */
X	*ps = s;
X
X	return (s);
X}
END_OF_FILE
if test 2239 -ne `wc -c <'src/stab.c'`; then
    echo shar: \"'src/stab.c'\" unpacked with wrong size!
fi
# end of 'src/stab.c'
fi
if test -f 'src/stats.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/stats.c'\"
else
echo shar: Extracting \"'src/stats.c'\" \(1994 characters\)
sed "s/^X//" >'src/stats.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)stats.c	5.8 (Berkeley) 5/2/86";
X#endif not lint
X
X# include "sendmail.h"
X# include "mailstats.h"
X
Xstruct statistics	Stat;
X
X#define ONE_K		1000		/* one thousand (twenty-four?) */
X#define KBYTES(x)	(((x) + (ONE_K - 1)) / ONE_K)
X/*
X**  MARKSTATS -- mark statistics
X*/
X
Xmarkstats(e, to)
X	register ENVELOPE *e;
X	register ADDRESS *to;
X{
X	if (to == NULL)
X	{
X		if (e->e_from.q_mailer != NULL)
X		{
X			Stat.stat_nf[e->e_from.q_mailer->m_mno]++;
X			Stat.stat_bf[e->e_from.q_mailer->m_mno] +=
X				KBYTES(CurEnv->e_msgsize);
X		}
X	}
X	else
X	{
X		Stat.stat_nt[to->q_mailer->m_mno]++;
X		Stat.stat_bt[to->q_mailer->m_mno] += KBYTES(CurEnv->e_msgsize);
X	}
X}
X/*
X**  POSTSTATS -- post statistics in the statistics file
X**
X**	Parameters:
X**		sfile -- the name of the statistics file.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		merges the Stat structure with the sfile file.
X*/
X
Xpoststats(sfile)
X	char *sfile;
X{
X	register int fd;
X	struct statistics stat;
X	extern off_t lseek();
X
X	if (sfile == NULL)
X		return;
X
X	(void) time(&Stat.stat_itime);
X	Stat.stat_size = sizeof Stat;
X
X	fd = open(sfile, 2);
X	if (fd < 0)
X	{
X		errno = 0;
X		return;
X	}
X	if (read(fd, (char *) &stat, sizeof stat) == sizeof stat &&
X	    stat.stat_size == sizeof stat)
X	{
X		/* merge current statistics into statfile */
X		register int i;
X
X		for (i = 0; i < MAXMAILERS; i++)
X		{
X			stat.stat_nf[i] += Stat.stat_nf[i];
X			stat.stat_bf[i] += Stat.stat_bf[i];
X			stat.stat_nt[i] += Stat.stat_nt[i];
X			stat.stat_bt[i] += Stat.stat_bt[i];
X		}
X	}
X	else
X		bcopy((char *) &Stat, (char *) &stat, sizeof stat);
X
X	/* write out results */
X	(void) lseek(fd, (off_t) 0, 0);
X	(void) write(fd, (char *) &stat, sizeof stat);
X	(void) close(fd);
X}
END_OF_FILE
if test 1994 -ne `wc -c <'src/stats.c'`; then
    echo shar: \"'src/stats.c'\" unpacked with wrong size!
fi
# end of 'src/stats.c'
fi
if test -f 'src/sysexits.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/sysexits.c'\"
else
echo shar: Extracting \"'src/sysexits.c'\" \(1616 characters\)
sed "s/^X//" >'src/sysexits.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)sysexits.c	5.2 (Berkeley) 6/7/85";
X#endif not lint
X
X# include <sysexits.h>
X# include "conf.h"
X# include "useful.h"
X
X/*
X**  SYSEXITS.C -- error messages corresponding to sysexits.h
X*/
X
Xchar	*SysExMsg[] =
X{
X	/* 64 USAGE */		"500 Bad usage",
X	/* 65 DATAERR */	"501 Data format error",
X	/* 66 NOINPUT */	"550 Cannot open input",
X	/* 67 NOUSER */		"550 User unknown",
X	/* 68 NOHOST */		"550 Host unknown",
X	/* 69 UNAVAILABLE */	"554 Service unavailable",
X	/* 70 SOFTWARE */	"554 Internal error",
X	/* 71 OSERR */		"451 Operating system error",
X	/* 72 OSFILE */		"554 System file missing",
X	/* 73 CANTCREAT */	"550 Can't create output",
X	/* 74 IOERR */		"451 I/O error",
X	/* 75 TEMPFAIL */	"250 Deferred",
X	/* 76 PROTOCOL */	"554 Remote protocol error",
X	/* 77 NOPERM */		"550 Insufficient permission",
X};
X
Xint	N_SysEx = sizeof SysExMsg / sizeof SysExMsg[0];
X/*
X**  STATSTRING -- return string corresponding to an error status
X**
X**	Parameters:
X**		stat -- the status to decode.
X**
X**	Returns:
X**		The string corresponding to that status
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar *
Xstatstring(stat)
X	int stat;
X{
X	static char ebuf[100];
X
X	stat -= EX__BASE;
X	if (stat < 0 || stat >= N_SysEx)
X	{
X		(void) sprintf(ebuf, "554 Unknown status %d", stat + EX__BASE);
X		return (ebuf);
X	}
X
X	return (SysExMsg[stat]);
X}
END_OF_FILE
if test 1616 -ne `wc -c <'src/sysexits.c'`; then
    echo shar: \"'src/sysexits.c'\" unpacked with wrong size!
fi
# end of 'src/sysexits.c'
fi
if test -f 'src/trace.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/trace.c'\"
else
echo shar: Extracting \"'src/trace.c'\" \(1758 characters\)
sed "s/^X//" >'src/trace.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)trace.c	5.3 (Berkeley) 12/17/86";
X#endif not lint
X
X# include "sendmail.h"
X
X/*
X**  TtSETUP -- set up for trace package.
X**
X**	Parameters:
X**		vect -- pointer to trace vector.
X**		size -- number of flags in trace vector.
X**		defflags -- flags to set if no value given.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		environment is set up.
X*/
X
Xu_char		*tTvect;
Xint		tTsize;
Xstatic char	*DefFlags;
X
XtTsetup(vect, size, defflags)
X	u_char *vect;
X	int size;
X	char *defflags;
X{
X	tTvect = vect;
X	tTsize = size;
X	DefFlags = defflags;
X}
X/*
X**  TtFLAG -- process an external trace flag description.
X**
X**	Parameters:
X**		s -- the trace flag.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		sets/clears trace flags.
X*/
X
XtTflag(s)
X	register char *s;
X{
X	int first, last;
X	register int i;
X
X	if (*s == '\0')
X		s = DefFlags;
X
X	for (;;)
X	{
X		/* find first flag to set */
X		i = 0;
X		while (isdigit(*s))
X			i = i * 10 + (*s++ - '0');
X		first = i;
X
X		/* find last flag to set */
X		if (*s == '-')
X		{
X			i = 0;
X			while (isdigit(*++s))
X				i = i * 10 + (*s - '0');
X		}
X		last = i;
X
X		/* find the level to set it to */
X		i = 1;
X		if (*s == '.')
X		{
X			i = 0;
X			while (isdigit(*++s))
X				i = i * 10 + (*s - '0');
X		}
X
X		/* clean up args */
X		if (first >= tTsize)
X			first = tTsize - 1;
X		if (last >= tTsize)
X			last = tTsize - 1;
X
X		/* set the flags */
X		while (first <= last)
X			tTvect[first++] = i;
X
X		/* more arguments? */
X		if (*s++ == '\0')
X			return;
X	}
X}
END_OF_FILE
if test 1758 -ne `wc -c <'src/trace.c'`; then
    echo shar: \"'src/trace.c'\" unpacked with wrong size!
fi
# end of 'src/trace.c'
fi
if test -f 'src/trace.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/trace.h'\"
else
echo shar: Extracting \"'src/trace.h'\" \(565 characters\)
sed "s/^X//" >'src/trace.h' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X**
X**	@(#)trace.h	5.1 (Berkeley) 7/14/85
X*/
X
X/*
X**  Trace Package.
X*/
X
Xtypedef u_char	*TRACEV;
X
Xextern TRACEV	tTvect;			/* current trace vector */
X
X# ifndef tTVECT
X# define tTVECT		tTvect
X# endif tTVECT
X
X# define tTf(flag, level)	(tTVECT[flag] >= level)
X# define tTlevel(flag)		(tTVECT[flag])
END_OF_FILE
if test 565 -ne `wc -c <'src/trace.h'`; then
    echo shar: \"'src/trace.h'\" unpacked with wrong size!
fi
# end of 'src/trace.h'
fi
if test -f 'src/version.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/version.c'\"
else
echo shar: Extracting \"'src/version.c'\" \(394 characters\)
sed "s/^X//" >'src/version.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)version.c	5.58 (Berkeley) 2/3/87";
X#endif not lint
X
Xchar	Version[] = "5.58";
END_OF_FILE
if test 394 -ne `wc -c <'src/version.c'`; then
    echo shar: \"'src/version.c'\" unpacked with wrong size!
fi
# end of 'src/version.c'
fi
echo shar: End of archive 1 \(of 8\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
David H. Brierley
Home: dave@galaxia.Newport.RI.US   {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb@rayssd.ray.com           {sun,decuac,gatech,necntc,ukma}!rayssd!dhb

dave@galaxia.Newport.RI.US (David H. Brierley) (02/25/89)

----- cut here and feed to /bin/sh -----
#! /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 archive 2 (of 8)."
# Contents:  src/alias.c src/collect.c src/domain.c src/err.c
#   src/macro.c src/usersmtp.c
# Wrapped by dave@galaxia on Fri Feb 24 20:23:41 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/alias.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/alias.c'\"
else
echo shar: Extracting \"'src/alias.c'\" \(11559 characters\)
sed "s/^X//" >'src/alias.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X# include "sendmail.h"
X# include <pwd.h>
X# include <sys/stat.h>
X# include <signal.h>
X# include <errno.h>
X# ifdef FLOCK
X# include <sys/file.h>
X# endif FLOCK
X
X#ifndef lint
X# ifdef DBM
Xstatic char	SccsId[] = "@(#)alias.c	5.13 (Berkeley) 4/17/86	(with DBM)";
X# else DBM
Xstatic char	SccsId[] = "@(#)alias.c	5.13 (Berkeley) 4/17/86	(without DBM)";
X# endif DBM
X#endif not lint
X
X
X/*
X**  ALIAS -- Compute aliases.
X**
X**	Scans the alias file for an alias for the given address.
X**	If found, it arranges to deliver to the alias list instead.
X**	Uses libdbm database if -DDBM.
X**
X**	Parameters:
X**		a -- address to alias.
X**		sendq -- a pointer to the head of the send queue
X**			to put the aliases in.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		Aliases found are expanded.
X**
X**	Notes:
X**		If NoAlias (the "-n" flag) is set, no aliasing is
X**			done.
X**
X**	Deficiencies:
X**		It should complain about names that are aliased to
X**			nothing.
X*/
X
X
X#ifdef DBM
Xtypedef struct
X{
X	char	*dptr;
X	int	dsize;
X} DATUM;
Xextern DATUM fetch();
X#endif DBM
X
Xalias(a, sendq)
X	register ADDRESS *a;
X	ADDRESS **sendq;
X{
X	register char *p;
X	extern char *aliaslookup();
X
X# ifdef DEBUG
X	if (tTd(27, 1))
X		printf("alias(%s)\n", a->q_paddr);
X# endif
X
X	/* don't realias already aliased names */
X	if (bitset(QDONTSEND, a->q_flags))
X		return;
X
X	CurEnv->e_to = a->q_paddr;
X
X	/*
X	**  Look up this name
X	*/
X
X	if (NoAlias)
X		p = NULL;
X	else
X		p = aliaslookup(a->q_user);
X	if (p == NULL)
X		return;
X
X	/*
X	**  Match on Alias.
X	**	Deliver to the target list.
X	*/
X
X# ifdef DEBUG
X	if (tTd(27, 1))
X		printf("%s (%s, %s) aliased to %s\n",
X		    a->q_paddr, a->q_host, a->q_user, p);
X# endif
X	message(Arpa_Info, "aliased to %s", p);
X	AliasLevel++;
X	sendtolist(p, a, sendq);
X	AliasLevel--;
X}
X/*
X**  ALIASLOOKUP -- look up a name in the alias file.
X**
X**	Parameters:
X**		name -- the name to look up.
X**
X**	Returns:
X**		the value of name.
X**		NULL if unknown.
X**
X**	Side Effects:
X**		none.
X**
X**	Warnings:
X**		The return value will be trashed across calls.
X*/
X
Xchar *
Xaliaslookup(name)
X	char *name;
X{
X# ifdef DBM
X	DATUM rhs, lhs;
X
X	/* create a key for fetch */
X	lhs.dptr = name;
X	lhs.dsize = strlen(name) + 1;
X	rhs = fetch(lhs);
X	return (rhs.dptr);
X# else DBM
X	register STAB *s;
X
X	s = stab(name, ST_ALIAS, ST_FIND);
X	if (s == NULL)
X		return (NULL);
X	return (s->s_alias);
X# endif DBM
X}
X/*
X**  INITALIASES -- initialize for aliasing
X**
X**	Very different depending on whether we are running DBM or not.
X**
X**	Parameters:
X**		aliasfile -- location of aliases.
X**		init -- if set and if DBM, initialize the DBM files.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		initializes aliases:
X**		if DBM:  opens the database.
X**		if ~DBM: reads the aliases into the symbol table.
X*/
X
X# define DBMMODE	0666
X
Xinitaliases(aliasfile, init)
X	char *aliasfile;
X	bool init;
X{
X#ifdef DBM
X	int atcnt;
X	time_t modtime;
X	bool automatic = FALSE;
X	char buf[MAXNAME];
X#endif DBM
X	struct stat stb;
X	static bool initialized = FALSE;
X
X	if (initialized)
X		return;
X	initialized = TRUE;
X
X	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
X	{
X		if (aliasfile != NULL && init)
X			syserr("Cannot open %s", aliasfile);
X		NoAlias = TRUE;
X		errno = 0;
X		return;
X	}
X
X# ifdef DBM
X	/*
X	**  Check to see that the alias file is complete.
X	**	If not, we will assume that someone died, and it is up
X	**	to us to rebuild it.
X	*/
X
X	if (!init)
X		dbminit(aliasfile);
X	atcnt = SafeAlias * 2;
X	if (atcnt > 0)
X	{
X		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
X		{
X			/*
X			**  Reinitialize alias file in case the new
X			**  one is mv'ed in instead of cp'ed in.
X			**
X			**	Only works with new DBM -- old one will
X			**	just consume file descriptors forever.
X			**	If you have a dbmclose() it can be
X			**	added before the sleep(30).
X			*/
X
X			sleep(30);
X# ifdef NDBM
X			dbminit(aliasfile);
X# endif NDBM
X		}
X	}
X	else
X		atcnt = 1;
X
X	/*
X	**  See if the DBM version of the file is out of date with
X	**  the text version.  If so, go into 'init' mode automatically.
X	**	This only happens if our effective userid owns the DBM
X	**	version or if the mode of the database is 666 -- this
X	**	is an attempt to avoid protection problems.  Note the
X	**	unpalatable hack to see if the stat succeeded.
X	*/
X
X	modtime = stb.st_mtime;
X	(void) strcpy(buf, aliasfile);
X	(void) strcat(buf, ".pag");
X	stb.st_ino = 0;
X	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
X	{
X		errno = 0;
X		if (AutoRebuild && stb.st_ino != 0 &&
X		    ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid()))
X		{
X			init = TRUE;
X			automatic = TRUE;
X			message(Arpa_Info, "rebuilding alias database");
X#ifdef LOG
X			if (LogLevel >= 7)
X				syslog(LOG_INFO, "rebuilding alias database");
X#endif LOG
X		}
X		else
X		{
X#ifdef LOG
X			if (LogLevel >= 7)
X				syslog(LOG_INFO, "alias database out of date");
X#endif LOG
X			message(Arpa_Info, "Warning: alias database out of date");
X		}
X	}
X
X
X	/*
X	**  If necessary, load the DBM file.
X	**	If running without DBM, load the symbol table.
X	*/
X
X	if (init)
X	{
X#ifdef LOG
X		if (LogLevel >= 6)
X		{
X			extern char *username();
X
X			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
X				automatic ? "auto" : "", username());
X		}
X#endif LOG
X		readaliases(aliasfile, TRUE);
X	}
X# else DBM
X	readaliases(aliasfile, init);
X# endif DBM
X}
X/*
X**  READALIASES -- read and process the alias file.
X**
X**	This routine implements the part of initaliases that occurs
X**	when we are not going to use the DBM stuff.
X**
X**	Parameters:
X**		aliasfile -- the pathname of the alias file master.
X**		init -- if set, initialize the DBM stuff.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Reads aliasfile into the symbol table.
X**		Optionally, builds the .dir & .pag files.
X*/
X
Xstatic
Xreadaliases(aliasfile, init)
X	char *aliasfile;
X	bool init;
X{
X	register char *p;
X	char *rhs;
X	bool skipping;
X	int naliases, bytes, longest;
X	FILE *af;
X	int (*oldsigint)();
X	ADDRESS al, bl;
X	register STAB *s;
X	char line[BUFSIZ];
X
X	if ((af = fopen(aliasfile, "r")) == NULL)
X	{
X# ifdef DEBUG
X		if (tTd(27, 1))
X			printf("Can't open %s\n", aliasfile);
X# endif
X		errno = 0;
X		NoAlias++;
X		return;
X	}
X
X# ifdef DBM
X# ifdef FLOCK
X	/* see if someone else is rebuilding the alias file already */
X	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
X	{
X		/* yes, they are -- wait until done and then return */
X		message(Arpa_Info, "Alias file is already being rebuilt");
X		if (OpMode != MD_INITALIAS)
X		{
X			/* wait for other rebuild to complete */
X			(void) flock(fileno(af), LOCK_EX);
X		}
X		(void) fclose(af);
X		errno = 0;
X		return;
X	}
X# endif FLOCK
X
X	/*
X	**  If initializing, create the new DBM files.
X	*/
X
X	if (init)
X	{
X		oldsigint = signal(SIGINT, SIG_IGN);
X		(void) strcpy(line, aliasfile);
X		(void) strcat(line, ".dir");
X		if (close(creat(line, DBMMODE)) < 0)
X		{
X			syserr("cannot make %s", line);
X			(void) signal(SIGINT, oldsigint);
X			return;
X		}
X		(void) strcpy(line, aliasfile);
X		(void) strcat(line, ".pag");
X		if (close(creat(line, DBMMODE)) < 0)
X		{
X			syserr("cannot make %s", line);
X			(void) signal(SIGINT, oldsigint);
X			return;
X		}
X		dbminit(aliasfile);
X	}
X# endif DBM
X
X	/*
X	**  Read and interpret lines
X	*/
X
X	FileName = aliasfile;
X	LineNumber = 0;
X	naliases = bytes = longest = 0;
X	skipping = FALSE;
X	while (fgets(line, sizeof (line), af) != NULL)
X	{
X		int lhssize, rhssize;
X
X		LineNumber++;
X		p = index(line, '\n');
X		if (p != NULL)
X			*p = '\0';
X		switch (line[0])
X		{
X		  case '#':
X		  case '\0':
X			skipping = FALSE;
X			continue;
X
X		  case ' ':
X		  case '\t':
X			if (!skipping)
X				syserr("Non-continuation line starts with space");
X			skipping = TRUE;
X			continue;
X		}
X		skipping = FALSE;
X
X		/*
X		**  Process the LHS
X		**	Find the final colon, and parse the address.
X		**	It should resolve to a local name -- this will
X		**	be checked later (we want to optionally do
X		**	parsing of the RHS first to maximize error
X		**	detection).
X		*/
X
X		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
X			continue;
X		if (*p++ != ':')
X		{
X			syserr("missing colon");
X			continue;
X		}
X		if (parseaddr(line, &al, 1, ':') == NULL)
X		{
X			syserr("illegal alias name");
X			continue;
X		}
X		loweraddr(&al);
X
X		/*
X		**  Process the RHS.
X		**	'al' is the internal form of the LHS address.
X		**	'p' points to the text of the RHS.
X		*/
X
X		rhs = p;
X		for (;;)
X		{
X			register char c;
X
X			if (init && CheckAliases)
X			{
X				/* do parsing & compression of addresses */
X				while (*p != '\0')
X				{
X					extern char *DelimChar;
X
X					while (isspace(*p) || *p == ',')
X						p++;
X					if (*p == '\0')
X						break;
X					if (parseaddr(p, &bl, -1, ',') == NULL)
X						usrerr("%s... bad address", p);
X					p = DelimChar;
X				}
X			}
X			else
X			{
X				p = &p[strlen(p)];
X				if (p[-1] == '\n')
X					*--p = '\0';
X			}
X
X			/* see if there should be a continuation line */
X			c = fgetc(af);
X			if (!feof(af))
X				(void) ungetc(c, af);
X			if (c != ' ' && c != '\t')
X				break;
X
X			/* read continuation line */
X			if (fgets(p, sizeof line - (p - line), af) == NULL)
X				break;
X			LineNumber++;
X		}
X		if (al.q_mailer != LocalMailer)
X		{
X			syserr("cannot alias non-local names");
X			continue;
X		}
X
X		/*
X		**  Insert alias into symbol table or DBM file
X		*/
X
X		lhssize = strlen(al.q_user) + 1;
X		rhssize = strlen(rhs) + 1;
X
X# ifdef DBM
X		if (init)
X		{
X			DATUM key, content;
X
X			key.dsize = lhssize;
X			key.dptr = al.q_user;
X			content.dsize = rhssize;
X			content.dptr = rhs;
X			store(key, content);
X		}
X		else
X# endif DBM
X		{
X			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
X			s->s_alias = newstr(rhs);
X		}
X
X		/* statistics */
X		naliases++;
X		bytes += lhssize + rhssize;
X		if (rhssize > longest)
X			longest = rhssize;
X	}
X
X# ifdef DBM
X	if (init)
X	{
X		/* add the distinquished alias "@" */
X		DATUM key;
X
X		key.dsize = 2;
X		key.dptr = "@";
X		store(key, key);
X
X		/* restore the old signal */
X		(void) signal(SIGINT, oldsigint);
X	}
X# endif DBM
X
X	/* closing the alias file drops the lock */
X	(void) fclose(af);
X	CurEnv->e_to = NULL;
X	FileName = NULL;
X	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
X			naliases, longest, bytes);
X# ifdef LOG
X	if (LogLevel >= 8)
X		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
X			naliases, longest, bytes);
X# endif LOG
X}
X/*
X**  FORWARD -- Try to forward mail
X**
X**	This is similar but not identical to aliasing.
X**
X**	Parameters:
X**		user -- the name of the user who's mail we would like
X**			to forward to.  It must have been verified --
X**			i.e., the q_home field must have been filled
X**			in.
X**		sendq -- a pointer to the head of the send queue to
X**			put this user's aliases in.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		New names are added to send queues.
X*/
X
Xforward(user, sendq)
X	ADDRESS *user;
X	ADDRESS **sendq;
X{
X	char buf[60];
X	extern bool safefile();
X
X# ifdef DEBUG
X	if (tTd(27, 1))
X		printf("forward(%s)\n", user->q_paddr);
X# endif DEBUG
X
X	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
X		return;
X# ifdef DEBUG
X	if (user->q_home == NULL)
X		syserr("forward: no home");
X# endif DEBUG
X
X	/* good address -- look for .forward file in home */
X	define('z', user->q_home, CurEnv);
X	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
X	if (!safefile(buf, user->q_uid, S_IREAD))
X		return;
X
X	/* we do have an address to forward to -- do it */
X	include(buf, "forwarding", user, sendq);
X}
END_OF_FILE
if test 11559 -ne `wc -c <'src/alias.c'`; then
    echo shar: \"'src/alias.c'\" unpacked with wrong size!
fi
# end of 'src/alias.c'
fi
if test -f 'src/collect.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/collect.c'\"
else
echo shar: Extracting \"'src/collect.c'\" \(6760 characters\)
sed "s/^X//" >'src/collect.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)collect.c	5.2 (Berkeley) 6/8/85";
X#endif not lint
X
X# include <errno.h>
X# include "sendmail.h"
X
X/*
X**  COLLECT -- read & parse message header & make temp file.
X**
X**	Creates a temporary file name and copies the standard
X**	input to that file.  Leading UNIX-style "From" lines are
X**	stripped off (after important information is extracted).
X**
X**	Parameters:
X**		sayok -- if set, give an ARPANET style message
X**			to say we are ready to collect input.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Temp file is created and filled.
X**		The from person may be set.
X*/
X
Xcollect(sayok)
X	bool sayok;
X{
X	register FILE *tf;
X	char buf[MAXFIELD+2];
X	register char *p;
X	extern char *hvalue();
X
X	/*
X	**  Create the temp file name and create the file.
X	*/
X
X	CurEnv->e_df = newstr(queuename(CurEnv, 'd'));
X	if ((tf = dfopen(CurEnv->e_df, "w")) == NULL)
X	{
X		syserr("Cannot create %s", CurEnv->e_df);
X		NoReturn = TRUE;
X		finis();
X	}
X	(void) chmod(CurEnv->e_df, FileMode);
X
X	/*
X	**  Tell ARPANET to go ahead.
X	*/
X
X	if (sayok)
X		message("354", "Enter mail, end with \".\" on a line by itself");
X
X	/*
X	**  Try to read a UNIX-style From line
X	*/
X
X	(void) sfgets(buf, sizeof buf, InChannel);
X	fixcrlf(buf, FALSE);
X# ifndef NOTUNIX
X	if (!SaveFrom && strncmp(buf, "From ", 5) == 0)
X	{
X		eatfrom(buf);
X		(void) sfgets(buf, sizeof buf, InChannel);
X		fixcrlf(buf, FALSE);
X	}
X# endif NOTUNIX
X
X	/*
X	**  Copy InChannel to temp file & do message editing.
X	**	To keep certain mailers from getting confused,
X	**	and to keep the output clean, lines that look
X	**	like UNIX "From" lines are deleted in the header.
X	*/
X
X	do
X	{
X		int c;
X		extern bool isheader();
X
X		/* drop out on error */
X		if (ferror(InChannel))
X			break;
X
X		/* if the line is too long, throw the rest away */
X		if (index(buf, '\n') == NULL)
X		{
X			while ((c = getc(InChannel)) != '\n' && c != EOF)
X				continue;
X			/* give an error? */
X		}
X
X		fixcrlf(buf, TRUE);
X
X		/* see if the header is over */
X		if (!isheader(buf))
X			break;
X
X		/* get the rest of this field */
X		while ((c = getc(InChannel)) == ' ' || c == '\t')
X		{
X			p = &buf[strlen(buf)];
X			*p++ = '\n';
X			*p++ = c;
X			if (sfgets(p, MAXFIELD - (p - buf), InChannel) == NULL)
X				break;
X			fixcrlf(p, TRUE);
X		}
X		if (!feof(InChannel) && !ferror(InChannel))
X			(void) ungetc(c, InChannel);
X
X		CurEnv->e_msgsize += strlen(buf);
X
X		/*
X		**  Snarf header away.
X		*/
X
X		if (bitset(H_EOH, chompheader(buf, FALSE)))
X			break;
X	} while (sfgets(buf, MAXFIELD, InChannel) != NULL);
X
X# ifdef DEBUG
X	if (tTd(30, 1))
X		printf("EOH\n");
X# endif DEBUG
X
X	/* throw away a blank line */
X	if (buf[0] == '\0')
X		(void) sfgets(buf, MAXFIELD, InChannel);
X
X	/*
X	**  Collect the body of the message.
X	*/
X
X	do
X	{
X		register char *bp = buf;
X
X		fixcrlf(buf, TRUE);
X
X		/* check for end-of-message */
X		if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0'))
X			break;
X
X		/* check for transparent dot */
X		if (OpMode == MD_SMTP && !IgnrDot && bp[0] == '.' && bp[1] == '.')
X			bp++;
X
X		/*
X		**  Figure message length, output the line to the temp
X		**  file, and insert a newline if missing.
X		*/
X
X		CurEnv->e_msgsize += strlen(bp) + 1;
X		fputs(bp, tf);
X		fputs("\n", tf);
X		if (ferror(tf))
X			tferror(tf);
X	} while (sfgets(buf, MAXFIELD, InChannel) != NULL);
X	if (fflush(tf) != 0)
X		tferror(tf);
X	(void) fclose(tf);
X
X	/* An EOF when running SMTP is an error */
X	if ((feof(InChannel) || ferror(InChannel)) && OpMode == MD_SMTP)
X	{
X		syserr("collect: unexpected close, from=%s", CurEnv->e_from.q_paddr);
X
X		/* don't return an error indication */
X		CurEnv->e_to = NULL;
X		CurEnv->e_flags &= ~EF_FATALERRS;
X
X		/* and don't try to deliver the partial message either */
X		finis();
X	}
X
X	/*
X	**  Find out some information from the headers.
X	**	Examples are who is the from person & the date.
X	*/
X
X	eatheader(CurEnv);
X
X	/*
X	**  Add an Apparently-To: line if we have no recipient lines.
X	*/
X
X	if (hvalue("to") == NULL && hvalue("cc") == NULL &&
X	    hvalue("bcc") == NULL && hvalue("apparently-to") == NULL)
X	{
X		register ADDRESS *q;
X
X		/* create an Apparently-To: field */
X		/*    that or reject the message.... */
X		for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
X		{
X			if (q->q_alias != NULL)
X				continue;
X# ifdef DEBUG
X			if (tTd(30, 3))
X				printf("Adding Apparently-To: %s\n", q->q_paddr);
X# endif DEBUG
X			addheader("apparently-to", q->q_paddr, CurEnv);
X		}
X	}
X
X	if ((CurEnv->e_dfp = fopen(CurEnv->e_df, "r")) == NULL)
X		syserr("Cannot reopen %s", CurEnv->e_df);
X}
X/*
X**  TFERROR -- signal error on writing the temporary file.
X**
X**	Parameters:
X**		tf -- the file pointer for the temporary file.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Gives an error message.
X**		Arranges for following output to go elsewhere.
X*/
X
Xtferror(tf)
X	FILE *tf;
X{
X	if (errno == ENOSPC)
X	{
X		(void) freopen(CurEnv->e_df, "w", tf);
X		fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf);
X		usrerr("452 Out of disk space for temp file");
X	}
X	else
X		syserr("collect: Cannot write %s", CurEnv->e_df);
X	(void) freopen("/dev/null", "w", tf);
X}
X/*
X**  EATFROM -- chew up a UNIX style from line and process
X**
X**	This does indeed make some assumptions about the format
X**	of UNIX messages.
X**
X**	Parameters:
X**		fm -- the from line.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		extracts what information it can from the header,
X**		such as the date.
X*/
X
X# ifndef NOTUNIX
X
Xchar	*DowList[] =
X{
X	"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
X};
X
Xchar	*MonthList[] =
X{
X	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
X	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
X	NULL
X};
X
Xeatfrom(fm)
X	char *fm;
X{
X	register char *p;
X	register char **dt;
X
X# ifdef DEBUG
X	if (tTd(30, 2))
X		printf("eatfrom(%s)\n", fm);
X# endif DEBUG
X
X	/* find the date part */
X	p = fm;
X	while (*p != '\0')
X	{
X		/* skip a word */
X		while (*p != '\0' && *p != ' ')
X			p++;
X		while (*p == ' ')
X			p++;
X		if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':')
X			continue;
X
X		/* we have a possible date */
X		for (dt = DowList; *dt != NULL; dt++)
X			if (strncmp(*dt, p, 3) == 0)
X				break;
X		if (*dt == NULL)
X			continue;
X
X		for (dt = MonthList; *dt != NULL; dt++)
X			if (strncmp(*dt, &p[4], 3) == 0)
X				break;
X		if (*dt != NULL)
X			break;
X	}
X
X	if (*p != NULL)
X	{
X		char *q;
X		extern char *arpadate();
X
X		/* we have found a date */
X		q = xalloc(25);
X		(void) strncpy(q, p, 25);
X		q[24] = '\0';
X		define('d', q, CurEnv);
X		q = arpadate(q);
X		define('a', newstr(q), CurEnv);
X	}
X}
X
X# endif NOTUNIX
END_OF_FILE
if test 6760 -ne `wc -c <'src/collect.c'`; then
    echo shar: \"'src/collect.c'\" unpacked with wrong size!
fi
# end of 'src/collect.c'
fi
if test -f 'src/domain.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/domain.c'\"
else
echo shar: Extracting \"'src/domain.c'\" \(6431 characters\)
sed "s/^X//" >'src/domain.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1986  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1986 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#include "sendmail.h"
X
X#ifndef MXDOMAIN
X#ifndef lint
Xstatic char	SccsId[] = "@(#)domain.c	5.7 (Berkeley) 12/12/87 (no MXDOMAIN)";
X#endif not lint
X#else MXDOMAIN
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)domain.c	5.7 (Berkeley) 12/12/87";
X#endif not lint
X
X# include <sys/param.h>
X# include <arpa/nameser.h>
X# include <resolv.h>
X# include <netdb.h>
X
Xtypedef union {
X	HEADER qb1;
X	char qb2[PACKETSZ];
X} querybuf;
X
Xstatic char	hostbuf[BUFSIZ];
Xint		h_errno;
Xextern u_short	getshort();
X
Xgetmxrr(host, mxhosts, maxmx, localhost)
X	char *host, **mxhosts;
X	int maxmx;
X	char *localhost;
X{
X
X	HEADER *hp;
X	char *eom, *bp, *cp;
X	querybuf buf, answer;
X	int n, n1, i, j, nmx, ancount, qdcount, buflen;
X	int seenlocal;
X	u_short prefer[BUFSIZ];
X	u_short pref, localpref, type, class;
X
X	n = res_mkquery(QUERY, host, C_IN, T_MX, (char *)NULL, 0, NULL,
X		(char *)&buf, sizeof(buf));
X	if (n < 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("res_mkquery failed\n");
X#endif
X		h_errno = NO_RECOVERY;
X		return(-2);
X	}
X	n = res_send((char *)&buf, n, (char *)&answer, sizeof(answer));
X	if (n < 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("res_send failed\n");
X#endif
X		h_errno = TRY_AGAIN;
X		return (-1);
X	}
X	eom = (char *)&answer + n;
X	/*
X	 * find first satisfactory answer
X	 */
X	hp = (HEADER *) &answer;
X	ancount = ntohs(hp->ancount);
X	qdcount = ntohs(hp->qdcount);
X	if (hp->rcode != NOERROR || ancount == 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("rcode = %d, ancount=%d\n", hp->rcode, ancount);
X#endif
X		switch (hp->rcode) {
X			case NXDOMAIN:
X				/* Check if it's an authoritive answer */
X				if (hp->aa) {
X					h_errno = HOST_NOT_FOUND;
X					return(-3);
X				} else {
X					h_errno = TRY_AGAIN;
X					return(-1);
X				}
X			case SERVFAIL:
X				h_errno = TRY_AGAIN;
X				return(-1);
X#ifdef OLDJEEVES
X			/*
X			 * Jeeves (TOPS-20 server) still does not
X			 * support MX records.  For the time being,
X			 * we must accept FORMERRs as the same as
X			 * NOERROR.
X			 */
X			case FORMERR:
X#endif OLDJEEVES
X			case NOERROR:
X				(void) strcpy(hostbuf, host);
X				mxhosts[0] = hostbuf;
X				return(1);
X#ifndef OLDJEEVES
X			case FORMERR:
X#endif OLDJEEVES
X			case NOTIMP:
X			case REFUSED:
X				h_errno = NO_RECOVERY;
X				return(-2);
X		}
X		return (-1);
X	}
X	bp = hostbuf;
X	nmx = 0;
X	seenlocal = 0;
X	buflen = sizeof(hostbuf);
X	cp = (char *)&answer + sizeof(HEADER);
X	if (qdcount) {
X		cp += dn_skipname(cp, eom) + QFIXEDSZ;
X		while (--qdcount > 0)
X			cp += dn_skipname(cp, eom) + QFIXEDSZ;
X	}
X	while (--ancount >= 0 && cp < eom && nmx < maxmx) {
X		if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0)
X			break;
X		cp += n;
X		type = getshort(cp);
X 		cp += sizeof(u_short);
X		/*
X		class = getshort(cp);
X		*/
X 		cp += sizeof(u_short) + sizeof(u_long);
X		n = getshort(cp);
X		cp += sizeof(u_short);
X		if (type != T_MX)  {
X#ifdef DEBUG
X			if (tTd(8, 1) || _res.options & RES_DEBUG)
X				printf("unexpected answer type %d, size %d\n",
X					type, n);
X#endif
X			cp += n;
X			continue;
X		}
X		pref = getshort(cp);
X		cp += sizeof(u_short);
X		if ((n = dn_expand((char *)&answer, eom, cp, bp, buflen)) < 0)
X			break;
X		cp += n;
X		if (sameword(bp, localhost))
X		{
X			seenlocal = 1;
X			localpref = pref;
X			continue;
X		}
X		prefer[nmx] = pref;
X		mxhosts[nmx++] = bp;
X		n1 = strlen(bp)+1;
X		bp += n1;
X		buflen -= n1;
X	}
X	if (nmx == 0) {
X		(void) strcpy(hostbuf, host);
X		mxhosts[0] = hostbuf;
X		return(1);
X	}
X	/* sort the records */
X	for (i = 0; i < nmx; i++) {
X		for (j = i + 1; j < nmx; j++) {
X			if (prefer[i] > prefer[j]) {
X				int temp;
X				char *temp1;
X
X				temp = prefer[i];
X				prefer[i] = prefer[j];
X				prefer[j] = temp;
X				temp1 = mxhosts[i];
X				mxhosts[i] = mxhosts[j];
X				mxhosts[j] = temp1;
X			}
X		}
X		if (seenlocal && (prefer[i] >= localpref))
X		{
X			nmx = i;
X			/*
X			 * We are the first MX, might as well try delivering
X			 * since nobody is supposed to have more info.
X			 */
X			if (nmx == 0)
X			{
X				(void) strcpy(hostbuf, host);
X				mxhosts[0] = hostbuf;
X				return(1);
X			}
X			break;
X		}
X	}
X	return(nmx);
X}
X
X
Xgetcanonname(host, hbsize)
X	char *host;
X	int hbsize;
X{
X
X	HEADER *hp;
X	char *eom, *cp;
X	querybuf buf, answer;
X	int n, ancount, qdcount;
X	u_short type;
X	char nbuf[BUFSIZ];
X	int first;
X
X	n = res_mkquery(QUERY, host, C_IN, T_ANY, (char *)NULL, 0, NULL,
X		(char *)&buf, sizeof(buf));
X	if (n < 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("res_mkquery failed\n");
X#endif
X		h_errno = NO_RECOVERY;
X		return;
X	}
X	n = res_send((char *)&buf, n, (char *)&answer, sizeof(answer));
X	if (n < 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("res_send failed\n");
X#endif
X		h_errno = TRY_AGAIN;
X		return;
X	}
X	eom = (char *)&answer + n;
X	/*
X	 * find first satisfactory answer
X	 */
X	hp = (HEADER *) &answer;
X	ancount = ntohs(hp->ancount);
X	qdcount = ntohs(hp->qdcount);
X	/*
X	 * We don't care about errors here, only if we got an answer
X	 */
X	if (ancount == 0) {
X#ifdef DEBUG
X		if (tTd(8, 1) || _res.options & RES_DEBUG)
X			printf("rcode = %d, ancount=%d\n", hp->rcode, ancount);
X#endif
X		return;
X	}
X	cp = (char *)&answer + sizeof(HEADER);
X	if (qdcount) {
X		cp += dn_skipname(cp, eom) + QFIXEDSZ;
X		while (--qdcount > 0)
X			cp += dn_skipname(cp, eom) + QFIXEDSZ;
X	}
X	first = 1;
X	while (--ancount >= 0 && cp < eom) {
X		if ((n = dn_expand((char *)&answer, eom, cp, nbuf,
X		    sizeof(nbuf))) < 0)
X			break;
X		if (first) {
X			(void)strncpy(host, nbuf, hbsize);
X			host[hbsize - 1] = '\0';
X			first = 0;
X		}
X		cp += n;
X		type = getshort(cp);
X 		cp += sizeof(u_short);
X 		cp += sizeof(u_short) + sizeof(u_long);
X		n = getshort(cp);
X		cp += sizeof(u_short);
X		if (type == T_CNAME)  {
X			/*
X			 * Assume that only one cname will be found.  More
X			 * than one is undefined.
X			 */
X			if ((n = dn_expand((char *)&answer, eom, cp, nbuf,
X			    sizeof(nbuf))) < 0)
X				break;
X			(void)strncpy(host, nbuf, hbsize);
X			host[hbsize - 1] = '\0';
X			getcanonname(host, hbsize);
X			break;
X		}
X		cp += n;
X	}
X	return;
X}
X
Xu_short
Xgetshort(msgp)
X	char *msgp;
X{
X	register u_char *p = (u_char *) msgp;
X	register int u;
X
X	u = *p++ << 8;
X	return ((u_short)(u | *p));
X}
X
X#endif MXDOMAIN
END_OF_FILE
if test 6431 -ne `wc -c <'src/domain.c'`; then
    echo shar: \"'src/domain.c'\" unpacked with wrong size!
fi
# end of 'src/domain.c'
fi
if test -f 'src/err.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/err.c'\"
else
echo shar: Extracting \"'src/err.c'\" \(7229 characters\)
sed "s/^X//" >'src/err.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)err.c	5.7 (Berkeley) 11/22/85";
X#endif not lint
X
X# include "sendmail.h"
X# include <errno.h>
X
X#ifdef	SMTP
X# include <netdb.h>
X#endif
X
X/*
X**  SYSERR -- Print error message.
X**
X**	Prints an error message via printf to the diagnostic
X**	output.  If LOG is defined, it logs it also.
X**
X**	Parameters:
X**		f -- the format string
X**		a, b, c, d, e -- parameters
X**
X**	Returns:
X**		none
X**		Through TopFrame if QuickAbort is set.
X**
X**	Side Effects:
X**		increments Errors.
X**		sets ExitStat.
X*/
X
X# ifdef lint
Xint	sys_nerr;
Xchar	*sys_errlist[];
X# endif lint
Xchar	MsgBuf[BUFSIZ*2];	/* text of most recent message */
X
X/*VARARGS1*/
Xsyserr(fmt, a, b, c, d, e)
X	char *fmt;
X{
X	register char *p;
X	int olderrno = errno;
X	extern char Arpa_PSyserr[];
X	extern char Arpa_TSyserr[];
X
X	/* format and output the error message */
X	if (olderrno == 0)
X		p = Arpa_PSyserr;
X	else
X		p = Arpa_TSyserr;
X	fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, a, b, c, d, e);
X	puterrmsg(MsgBuf);
X
X	/* determine exit status if not already set */
X	if (ExitStat == EX_OK)
X	{
X		if (olderrno == 0)
X			ExitStat = EX_SOFTWARE;
X		else
X			ExitStat = EX_OSERR;
X	}
X
X# ifdef LOG
X	if (LogLevel > 0)
X		syslog(LOG_CRIT, "%s: SYSERR: %s",
X			CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
X			&MsgBuf[4]);
X# endif LOG
X	errno = 0;
X	if (QuickAbort)
X		longjmp(TopFrame, 2);
X}
X/*
X**  USRERR -- Signal user error.
X**
X**	This is much like syserr except it is for user errors.
X**
X**	Parameters:
X**		fmt, a, b, c, d -- printf strings
X**
X**	Returns:
X**		none
X**		Through TopFrame if QuickAbort is set.
X**
X**	Side Effects:
X**		increments Errors.
X*/
X
X/*VARARGS1*/
Xusrerr(fmt, a, b, c, d, e)
X	char *fmt;
X{
X	extern char SuprErrs;
X	extern char Arpa_Usrerr[];
X	extern int errno;
X
X	if (SuprErrs)
X		return;
X
X	fmtmsg(MsgBuf, CurEnv->e_to, Arpa_Usrerr, errno, fmt, a, b, c, d, e);
X	puterrmsg(MsgBuf);
X
X	if (QuickAbort)
X		longjmp(TopFrame, 1);
X}
X/*
X**  MESSAGE -- print message (not necessarily an error)
X**
X**	Parameters:
X**		num -- the default ARPANET error number (in ascii)
X**		msg -- the message (printf fmt) -- if it begins
X**			with a digit, this number overrides num.
X**		a, b, c, d, e -- printf arguments
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		none.
X*/
X
X/*VARARGS2*/
Xmessage(num, msg, a, b, c, d, e)
X	register char *num;
X	register char *msg;
X{
X	errno = 0;
X	fmtmsg(MsgBuf, CurEnv->e_to, num, 0, msg, a, b, c, d, e);
X	putmsg(MsgBuf, FALSE);
X}
X/*
X**  NMESSAGE -- print message (not necessarily an error)
X**
X**	Just like "message" except it never puts the to... tag on.
X**
X**	Parameters:
X**		num -- the default ARPANET error number (in ascii)
X**		msg -- the message (printf fmt) -- if it begins
X**			with three digits, this number overrides num.
X**		a, b, c, d, e -- printf arguments
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		none.
X*/
X
X/*VARARGS2*/
Xnmessage(num, msg, a, b, c, d, e)
X	register char *num;
X	register char *msg;
X{
X	errno = 0;
X	fmtmsg(MsgBuf, (char *) NULL, num, 0, msg, a, b, c, d, e);
X	putmsg(MsgBuf, FALSE);
X}
X/*
X**  PUTMSG -- output error message to transcript and channel
X**
X**	Parameters:
X**		msg -- message to output (in SMTP format).
X**		holdmsg -- if TRUE, don't output a copy of the message to
X**			our output channel.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Outputs msg to the transcript.
X**		If appropriate, outputs it to the channel.
X**		Deletes SMTP reply code number as appropriate.
X*/
X
Xputmsg(msg, holdmsg)
X	char *msg;
X	bool holdmsg;
X{
X	/* output to transcript if serious */
X	if (CurEnv->e_xfp != NULL && (msg[0] == '4' || msg[0] == '5'))
X		fprintf(CurEnv->e_xfp, "%s\n", msg);
X
X	/* output to channel if appropriate */
X	if (!holdmsg && (Verbose || msg[0] != '0'))
X	{
X		(void) fflush(stdout);
X		if (OpMode == MD_SMTP || OpMode == MD_ARPAFTP)
X			fprintf(OutChannel, "%s\r\n", msg);
X		else
X			fprintf(OutChannel, "%s\n", &msg[4]);
X		(void) fflush(OutChannel);
X	}
X}
X/*
X**  PUTERRMSG -- like putmsg, but does special processing for error messages
X**
X**	Parameters:
X**		msg -- the message to output.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Sets the fatal error bit in the envelope as appropriate.
X*/
X
Xputerrmsg(msg)
X	char *msg;
X{
X	/* output the message as usual */
X	putmsg(msg, HoldErrs);
X
X	/* signal the error */
X	Errors++;
X	if (msg[0] == '5')
X		CurEnv->e_flags |= EF_FATALERRS;
X}
X/*
X**  FMTMSG -- format a message into buffer.
X**
X**	Parameters:
X**		eb -- error buffer to get result.
X**		to -- the recipient tag for this message.
X**		num -- arpanet error number.
X**		en -- the error number to display.
X**		fmt -- format of string.
X**		a, b, c, d, e -- arguments.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
X/*VARARGS5*/
Xstatic
Xfmtmsg(eb, to, num, eno, fmt, a, b, c, d, e)
X	register char *eb;
X	char *to;
X	char *num;
X	int eno;
X	char *fmt;
X{
X	char del;
X
X	/* output the reply code */
X	if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
X	{
X		num = fmt;
X		fmt += 4;
X	}
X	if (num[3] == '-')
X		del = '-';
X	else
X		del = ' ';
X	(void) sprintf(eb, "%3.3s%c", num, del);
X	eb += 4;
X
X	/* output the file name and line number */
X	if (FileName != NULL)
X	{
X		(void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
X		eb += strlen(eb);
X	}
X
X	/* output the "to" person */
X	if (to != NULL && to[0] != '\0')
X	{
X		(void) sprintf(eb, "%s... ", to);
X		while (*eb != '\0')
X			*eb++ &= 0177;
X	}
X
X	/* output the message */
X	(void) sprintf(eb, fmt, a, b, c, d, e);
X	while (*eb != '\0')
X		*eb++ &= 0177;
X
X	/* output the error code, if any */
X	if (eno != 0)
X	{
X		extern char *errstring();
X
X		(void) sprintf(eb, ": %s", errstring(eno));
X		eb += strlen(eb);
X	}
X}
X/*
X**  ERRSTRING -- return string description of error code
X**
X**	Parameters:
X**		errno -- the error number to translate
X**
X**	Returns:
X**		A string description of errno.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar *
Xerrstring(errno)
X	int errno;
X{
X	extern char *sys_errlist[];
X	extern int sys_nerr;
X	static char buf[100];
X# ifdef SMTP
X	extern char *SmtpPhase;
X# endif SMTP
X
X# ifdef DAEMON
X# ifdef VMUNIX
X	/*
X	**  Handle special network error codes.
X	**
X	**	These are 4.2/4.3bsd specific; they should be in daemon.c.
X	*/
X
X	switch (errno)
X	{
X	  case ETIMEDOUT:
X	  case ECONNRESET:
X		(void) strcpy(buf, sys_errlist[errno]);
X		if (SmtpPhase != NULL)
X		{
X			(void) strcat(buf, " during ");
X			(void) strcat(buf, SmtpPhase);
X		}
X		if (CurHostName != NULL)
X		{
X			(void) strcat(buf, " with ");
X			(void) strcat(buf, CurHostName);
X		}
X		return (buf);
X
X	  case EHOSTDOWN:
X		if (CurHostName == NULL)
X			break;
X		(void) sprintf(buf, "Host %s is down", CurHostName);
X		return (buf);
X
X	  case ECONNREFUSED:
X		if (CurHostName == NULL)
X			break;
X		(void) sprintf(buf, "Connection refused by %s", CurHostName);
X		return (buf);
X
X	  case (TRY_AGAIN+MAX_ERRNO):
X		(void) sprintf(buf, "Host Name Lookup Failure");
X		return (buf);
X	}
X# endif VMUNIX
X# endif DAEMON
X
X	if (errno > 0 && errno < sys_nerr)
X		return (sys_errlist[errno]);
X
X	(void) sprintf(buf, "Error %d", errno);
X	return (buf);
X}
END_OF_FILE
if test 7229 -ne `wc -c <'src/err.c'`; then
    echo shar: \"'src/err.c'\" unpacked with wrong size!
fi
# end of 'src/err.c'
fi
if test -f 'src/macro.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/macro.c'\"
else
echo shar: Extracting \"'src/macro.c'\" \(4832 characters\)
sed "s/^X//" >'src/macro.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)macro.c	5.3 (Berkeley) 9/19/85";
X#endif not lint
X
X# include "sendmail.h"
X
X/*
X**  EXPAND -- macro expand a string using $x escapes.
X**
X**	Parameters:
X**		s -- the string to expand.
X**		buf -- the place to put the expansion.
X**		buflim -- the buffer limit, i.e., the address
X**			of the last usable position in buf.
X**		e -- envelope in which to work.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
Xexpand(s, buf, buflim, e)
X	register char *s;
X	register char *buf;
X	char *buflim;
X	register ENVELOPE *e;
X{
X	register char *xp;
X	register char *q;
X	bool skipping;		/* set if conditionally skipping output */
X	bool recurse = FALSE;	/* set if recursion required */
X	int i;
X	char xbuf[BUFSIZ];
X	extern char *macvalue();
X
X# ifdef DEBUG
X	if (tTd(35, 24))
X	{
X		printf("expand(");
X		xputs(s);
X		printf(")\n");
X	}
X# endif DEBUG
X
X	skipping = FALSE;
X	if (s == NULL)
X		s = "";
X	for (xp = xbuf; *s != '\0'; s++)
X	{
X		char c;
X
X		/*
X		**  Check for non-ordinary (special?) character.
X		**	'q' will be the interpolated quantity.
X		*/
X
X		q = NULL;
X		c = *s;
X		switch (c)
X		{
X		  case CONDIF:		/* see if var set */
X			c = *++s;
X			skipping = macvalue(c, e) == NULL;
X			continue;
X
X		  case CONDELSE:	/* change state of skipping */
X			skipping = !skipping;
X			continue;
X
X		  case CONDFI:		/* stop skipping */
X			skipping = FALSE;
X			continue;
X
X		  case '\001':		/* macro interpolation */
X			c = *++s;
X			q = macvalue(c & 0177, e);
X			if (q == NULL)
X				continue;
X			break;
X		}
X
X		/*
X		**  Interpolate q or output one character
X		*/
X
X		if (skipping || xp >= &xbuf[sizeof xbuf])
X			continue;
X		if (q == NULL)
X			*xp++ = c;
X		else
X		{
X			/* copy to end of q or max space remaining in buf */
X			while ((c = *q++) != '\0' && xp < &xbuf[sizeof xbuf - 1])
X			{
X				if (iscntrl(c) && !isspace(c))
X					recurse = TRUE;
X				*xp++ = c;
X			}
X		}
X	}
X	*xp = '\0';
X
X# ifdef DEBUG
X	if (tTd(35, 24))
X	{
X		printf("expand ==> ");
X		xputs(xbuf);
X		printf("\n");
X	}
X# endif DEBUG
X
X	/* recurse as appropriate */
X	if (recurse)
X	{
X		expand(xbuf, buf, buflim, e);
X		return;
X	}
X
X	/* copy results out */
X	i = buflim - buf - 1;
X	if (i > xp - xbuf)
X		i = xp - xbuf;
X	bcopy(xbuf, buf, i);
X	buf[i] = '\0';
X}
X/*
X**  DEFINE -- define a macro.
X**
X**	this would be better done using a #define macro.
X**
X**	Parameters:
X**		n -- the macro name.
X**		v -- the macro value.
X**		e -- the envelope to store the definition in.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		e->e_macro[n] is defined.
X**
X**	Notes:
X**		There is one macro for each ASCII character,
X**		although they are not all used.  The currently
X**		defined macros are:
X**
X**		$a   date in ARPANET format (preferring the Date: line
X**		     of the message)
X**		$b   the current date (as opposed to the date as found
X**		     the message) in ARPANET format
X**		$c   hop count
X**		$d   (current) date in UNIX (ctime) format
X**		$e   the SMTP entry message+
X**		$f   raw from address
X**		$g   translated from address
X**		$h   to host
X**		$i   queue id
X**		$j   official SMTP hostname, used in messages+
X**		$l   UNIX-style from line+
X**		$n   name of sendmail ("MAILER-DAEMON" on local
X**		     net typically)+
X**		$o   delimiters ("operators") for address tokens+
X**		$p   my process id in decimal
X**		$q   the string that becomes an address -- this is
X**		     normally used to combine $g & $x.
X**		$r   protocol used to talk to sender
X**		$s   sender's host name
X**		$t   the current time in seconds since 1/1/1970
X**		$u   to user
X**		$v   version number of sendmail
X**		$w   our host name (if it can be determined)
X**		$x   signature (full name) of from person
X**		$y   the tty id of our terminal
X**		$z   home directory of to person
X**
X**		Macros marked with + must be defined in the
X**		configuration file and are used internally, but
X**		are not set.
X**
X**		There are also some macros that can be used
X**		arbitrarily to make the configuration file
X**		cleaner.  In general all upper-case letters
X**		are available.
X*/
X
Xdefine(n, v, e)
X	char n;
X	char *v;
X	register ENVELOPE *e;
X{
X# ifdef DEBUG
X	if (tTd(35, 9))
X	{
X		printf("define(%c as ", n);
X		xputs(v);
X		printf(")\n");
X	}
X# endif DEBUG
X	e->e_macro[n & 0177] = v;
X}
X/*
X**  MACVALUE -- return uninterpreted value of a macro.
X**
X**	Parameters:
X**		n -- the name of the macro.
X**
X**	Returns:
X**		The value of n.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar *
Xmacvalue(n, e)
X	char n;
X	register ENVELOPE *e;
X{
X	n &= 0177;
X	while (e != NULL)
X	{
X		register char *p = e->e_macro[n];
X
X		if (p != NULL)
X			return (p);
X		e = e->e_parent;
X	}
X	return (NULL);
X}
END_OF_FILE
if test 4832 -ne `wc -c <'src/macro.c'`; then
    echo shar: \"'src/macro.c'\" unpacked with wrong size!
fi
# end of 'src/macro.c'
fi
if test -f 'src/usersmtp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/usersmtp.c'\"
else
echo shar: Extracting \"'src/usersmtp.c'\" \(10561 characters\)
sed "s/^X//" >'src/usersmtp.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X
X# include <sysexits.h>
X# include <errno.h>
X# include "sendmail.h"
X
X# ifndef SMTP
X# ifndef lint
Xstatic char	SccsId[] = "@(#)usersmtp.c	5.8 (Berkeley) 12/17/86	(no SMTP)";
X# endif not lint
X# else SMTP
X
X# ifndef lint
Xstatic char	SccsId[] = "@(#)usersmtp.c	5.8 (Berkeley) 12/17/86";
X# endif not lint
X
X
X
X/*
X**  USERSMTP -- run SMTP protocol from the user end.
X**
X**	This protocol is described in RFC821.
X*/
X
X#define REPLYTYPE(r)	((r) / 100)		/* first digit of reply code */
X#define REPLYCLASS(r)	(((r) / 10) % 10)	/* second digit of reply code */
X#define SMTPCLOSING	421			/* "Service Shutting Down" */
X
Xchar	SmtpMsgBuffer[MAXLINE];		/* buffer for commands */
Xchar	SmtpReplyBuffer[MAXLINE];	/* buffer for replies */
Xchar	SmtpError[MAXLINE] = "";	/* save failure error messages */
XFILE	*SmtpOut;			/* output file */
XFILE	*SmtpIn;			/* input file */
Xint	SmtpPid;			/* pid of mailer */
X
X/* following represents the state of the SMTP connection */
Xint	SmtpState;			/* connection state, see below */
X
X#define SMTP_CLOSED	0		/* connection is closed */
X#define SMTP_OPEN	1		/* connection is open for business */
X#define SMTP_SSD	2		/* service shutting down */
X/*
X**  SMTPINIT -- initialize SMTP.
X**
X**	Opens the connection and sends the initial protocol.
X**
X**	Parameters:
X**		m -- mailer to create connection to.
X**		pvp -- pointer to parameter vector to pass to
X**			the mailer.
X**
X**	Returns:
X**		appropriate exit status -- EX_OK on success.
X**		If not EX_OK, it should close the connection.
X**
X**	Side Effects:
X**		creates connection and sends initial protocol.
X*/
X
Xjmp_buf	CtxGreeting;
X
Xsmtpinit(m, pvp)
X	struct mailer *m;
X	char **pvp;
X{
X	register int r;
X	EVENT *gte;
X	char buf[MAXNAME];
X	extern greettimeout();
X
X	/*
X	**  Open the connection to the mailer.
X	*/
X
X#ifdef DEBUG
X	if (SmtpState == SMTP_OPEN)
X		syserr("smtpinit: already open");
X#endif DEBUG
X
X	SmtpIn = SmtpOut = NULL;
X	SmtpState = SMTP_CLOSED;
X	SmtpError[0] = '\0';
X	SmtpPhase = "user open";
X	SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
X	if (SmtpPid < 0)
X	{
X# ifdef DEBUG
X		if (tTd(18, 1))
X			printf("smtpinit: cannot open %s: stat %d errno %d\n",
X			   pvp[0], ExitStat, errno);
X# endif DEBUG
X		if (CurEnv->e_xfp != NULL)
X		{
X			register char *p;
X			extern char *errstring();
X			extern char *statstring();
X
X			if (errno == 0)
X			{
X				p = statstring(ExitStat);
X				fprintf(CurEnv->e_xfp,
X					"%.3s %s.%s... %s\n",
X					p, pvp[1], m->m_name, p);
X			}
X			else
X			{
X				fprintf(CurEnv->e_xfp,
X					"421 %s.%s... Deferred: %s\n",
X					pvp[1], m->m_name, errstring(errno));
X			}
X		}
X		return (ExitStat);
X	}
X	SmtpState = SMTP_OPEN;
X
X	/*
X	**  Get the greeting message.
X	**	This should appear spontaneously.  Give it five minutes to
X	**	happen.
X	*/
X
X	if (setjmp(CtxGreeting) != 0)
X		goto tempfail;
X	gte = setevent((time_t) 300, greettimeout, 0);
X	SmtpPhase = "greeting wait";
X	r = reply(m);
X	clrevent(gte);
X	if (r < 0 || REPLYTYPE(r) != 2)
X		goto tempfail;
X
X	/*
X	**  Send the HELO command.
X	**	My mother taught me to always introduce myself.
X	*/
X
X	smtpmessage("HELO %s", m, MyHostName);
X	SmtpPhase = "HELO wait";
X	r = reply(m);
X	if (r < 0)
X		goto tempfail;
X	else if (REPLYTYPE(r) == 5)
X		goto unavailable;
X	else if (REPLYTYPE(r) != 2)
X		goto tempfail;
X
X	/*
X	**  If this is expected to be another sendmail, send some internal
X	**  commands.
X	*/
X
X	if (bitnset(M_INTERNAL, m->m_flags))
X	{
X		/* tell it to be verbose */
X		smtpmessage("VERB", m);
X		r = reply(m);
X		if (r < 0)
X			goto tempfail;
X
X		/* tell it we will be sending one transaction only */
X		smtpmessage("ONEX", m);
X		r = reply(m);
X		if (r < 0)
X			goto tempfail;
X	}
X
X	/*
X	**  Send the MAIL command.
X	**	Designates the sender.
X	*/
X
X	expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
X	if (CurEnv->e_from.q_mailer == LocalMailer ||
X	    !bitnset(M_FROMPATH, m->m_flags))
X	{
X		smtpmessage("MAIL From:<%s>", m, buf);
X	}
X	else
X	{
X		smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
X			buf[0] == '@' ? ',' : ':', buf);
X	}
X	SmtpPhase = "MAIL wait";
X	r = reply(m);
X	if (r < 0 || REPLYTYPE(r) == 4)
X		goto tempfail;
X	else if (r == 250)
X		return (EX_OK);
X	else if (r == 552)
X		goto unavailable;
X
X	/* protocol error -- close up */
X	smtpquit(m);
X	return (EX_PROTOCOL);
X
X	/* signal a temporary failure */
X  tempfail:
X	smtpquit(m);
X	return (EX_TEMPFAIL);
X
X	/* signal service unavailable */
X  unavailable:
X	smtpquit(m);
X	return (EX_UNAVAILABLE);
X}
X
X
Xstatic
Xgreettimeout()
X{
X	/* timeout reading the greeting message */
X	longjmp(CtxGreeting, 1);
X}
X/*
X**  SMTPRCPT -- designate recipient.
X**
X**	Parameters:
X**		to -- address of recipient.
X**		m -- the mailer we are sending to.
X**
X**	Returns:
X**		exit status corresponding to recipient status.
X**
X**	Side Effects:
X**		Sends the mail via SMTP.
X*/
X
Xsmtprcpt(to, m)
X	ADDRESS *to;
X	register MAILER *m;
X{
X	register int r;
X	extern char *remotename();
X
X	smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
X
X	SmtpPhase = "RCPT wait";
X	r = reply(m);
X	if (r < 0 || REPLYTYPE(r) == 4)
X		return (EX_TEMPFAIL);
X	else if (REPLYTYPE(r) == 2)
X		return (EX_OK);
X	else if (r == 550 || r == 551 || r == 553)
X		return (EX_NOUSER);
X	else if (r == 552 || r == 554)
X		return (EX_UNAVAILABLE);
X	return (EX_PROTOCOL);
X}
X/*
X**  SMTPDATA -- send the data and clean up the transaction.
X**
X**	Parameters:
X**		m -- mailer being sent to.
X**		e -- the envelope for this message.
X**
X**	Returns:
X**		exit status corresponding to DATA command.
X**
X**	Side Effects:
X**		none.
X*/
X
Xsmtpdata(m, e)
X	struct mailer *m;
X	register ENVELOPE *e;
X{
X	register int r;
X
X	/*
X	**  Send the data.
X	**	First send the command and check that it is ok.
X	**	Then send the data.
X	**	Follow it up with a dot to terminate.
X	**	Finally get the results of the transaction.
X	*/
X
X	/* send the command and check ok to proceed */
X	smtpmessage("DATA", m);
X	SmtpPhase = "DATA wait";
X	r = reply(m);
X	if (r < 0 || REPLYTYPE(r) == 4)
X		return (EX_TEMPFAIL);
X	else if (r == 554)
X		return (EX_UNAVAILABLE);
X	else if (r != 354)
X		return (EX_PROTOCOL);
X
X	/* now output the actual message */
X	(*e->e_puthdr)(SmtpOut, m, CurEnv);
X	putline("\n", SmtpOut, m);
X	(*e->e_putbody)(SmtpOut, m, CurEnv);
X
X	/* terminate the message */
X	fprintf(SmtpOut, ".%s", m->m_eol);
X	if (Verbose && !HoldErrs)
X		nmessage(Arpa_Info, ">>> .");
X
X	/* check for the results of the transaction */
X	SmtpPhase = "result wait";
X	r = reply(m);
X	if (r < 0 || REPLYTYPE(r) == 4)
X		return (EX_TEMPFAIL);
X	else if (r == 250)
X		return (EX_OK);
X	else if (r == 552 || r == 554)
X		return (EX_UNAVAILABLE);
X	return (EX_PROTOCOL);
X}
X/*
X**  SMTPQUIT -- close the SMTP connection.
X**
X**	Parameters:
X**		m -- a pointer to the mailer.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		sends the final protocol and closes the connection.
X*/
X
Xsmtpquit(m)
X	register MAILER *m;
X{
X	int i;
X
X	/* if the connection is already closed, don't bother */
X	if (SmtpIn == NULL)
X		return;
X
X	/* send the quit message if not a forced quit */
X	if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
X	{
X		smtpmessage("QUIT", m);
X		(void) reply(m);
X		if (SmtpState == SMTP_CLOSED)
X			return;
X	}
X
X	/* now actually close the connection */
X	(void) fclose(SmtpIn);
X	(void) fclose(SmtpOut);
X	SmtpIn = SmtpOut = NULL;
X	SmtpState = SMTP_CLOSED;
X
X	/* and pick up the zombie */
X	i = endmailer(SmtpPid, m->m_argv[0]);
X	if (i != EX_OK)
X		syserr("smtpquit %s: stat %d", m->m_argv[0], i);
X}
X/*
X**  REPLY -- read arpanet reply
X**
X**	Parameters:
X**		m -- the mailer we are reading the reply from.
X**
X**	Returns:
X**		reply code it reads.
X**
X**	Side Effects:
X**		flushes the mail file.
X*/
X
Xreply(m)
X	MAILER *m;
X{
X	(void) fflush(SmtpOut);
X
X	if (tTd(18, 1))
X		printf("reply\n");
X
X	/*
X	**  Read the input line, being careful not to hang.
X	*/
X
X	for (;;)
X	{
X		register int r;
X		register char *p;
X
X		/* actually do the read */
X		if (CurEnv->e_xfp != NULL)
X			(void) fflush(CurEnv->e_xfp);	/* for debugging */
X
X		/* if we are in the process of closing just give the code */
X		if (SmtpState == SMTP_CLOSED)
X			return (SMTPCLOSING);
X
X		/* get the line from the other side */
X		p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
X		if (p == NULL)
X		{
X			extern char MsgBuf[];		/* err.c */
X			extern char Arpa_TSyserr[];	/* conf.c */
X
X			/* if the remote end closed early, fake an error */
X			if (errno == 0)
X# ifdef ECONNRESET
X				errno = ECONNRESET;
X# else ECONNRESET
X				errno = EPIPE;
X# endif ECONNRESET
X
X			message(Arpa_TSyserr, "reply: read error");
X# ifdef DEBUG
X			/* if debugging, pause so we can see state */
X			if (tTd(18, 100))
X				pause();
X# endif DEBUG
X# ifdef LOG
X			syslog(LOG_ERR, "%s", &MsgBuf[4]);
X# endif LOG
X			SmtpState = SMTP_CLOSED;
X			smtpquit(m);
X			return (-1);
X		}
X		fixcrlf(SmtpReplyBuffer, TRUE);
X
X		if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
X		{
X			/* serious error -- log the previous command */
X			if (SmtpMsgBuffer[0] != '\0')
X				fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
X			SmtpMsgBuffer[0] = '\0';
X
X			/* now log the message as from the other side */
X			fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
X		}
X
X		/* display the input for verbose mode */
X		if (Verbose && !HoldErrs)
X			nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
X
X		/* if continuation is required, we can go on */
X		if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
X			continue;
X
X		/* decode the reply code */
X		r = atoi(SmtpReplyBuffer);
X
X		/* extra semantics: 0xx codes are "informational" */
X		if (r < 100)
X			continue;
X
X		/* reply code 421 is "Service Shutting Down" */
X		if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
X		{
X			/* send the quit protocol */
X			SmtpState = SMTP_SSD;
X			smtpquit(m);
X		}
X
X		/* save temporary failure messages for posterity */
X		if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
X			(void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
X
X		return (r);
X	}
X}
X/*
X**  SMTPMESSAGE -- send message to server
X**
X**	Parameters:
X**		f -- format
X**		m -- the mailer to control formatting.
X**		a, b, c -- parameters
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		writes message to SmtpOut.
X*/
X
X/*VARARGS1*/
Xsmtpmessage(f, m, a, b, c)
X	char *f;
X	MAILER *m;
X{
X	(void) sprintf(SmtpMsgBuffer, f, a, b, c);
X	if (tTd(18, 1) || (Verbose && !HoldErrs))
X		nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
X	if (SmtpOut != NULL)
X		fprintf(SmtpOut, "%s%s", SmtpMsgBuffer, m->m_eol);
X}
X
X# endif SMTP
END_OF_FILE
if test 10561 -ne `wc -c <'src/usersmtp.c'`; then
    echo shar: \"'src/usersmtp.c'\" unpacked with wrong size!
fi
# end of 'src/usersmtp.c'
fi
echo shar: End of archive 2 \(of 8\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
David H. Brierley
Home: dave@galaxia.Newport.RI.US   {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb@rayssd.ray.com           {sun,decuac,gatech,necntc,ukma}!rayssd!dhb

dave@galaxia.Newport.RI.US (David H. Brierley) (02/25/89)

----- cut here and feed to /bin/sh -----
#! /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 archive 3 (of 8)."
# Contents:  src/conf.c src/envelope.c src/recipient.c src/savemail.c
# Wrapped by dave@galaxia on Fri Feb 24 20:23:49 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/conf.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/conf.c'\"
else
echo shar: Extracting \"'src/conf.c'\" \(12327 characters\)
sed "s/^X//" >'src/conf.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)conf.c	5.14 (Berkeley) 1/10/86";
X#endif not lint
X
X# include <pwd.h>
X# include <sys/ioctl.h>
X# ifdef sun
X# include <sys/param.h>
X# endif sun
X# include "sendmail.h"
X
X/*
X**  CONF.C -- Sendmail Configuration Tables.
X**
X**	Defines the configuration of this installation.
X**
X**	Compilation Flags:
X**		V6 -- running on a version 6 system.  This determines
X**			whether to define certain routines between
X**			the two systems.  If you are running a funny
X**			system, e.g., V6 with long tty names, this
X**			should be checked carefully.
X**		VMUNIX -- running on a Berkeley UNIX system.
X**
X**	Configuration Variables:
X**		HdrInfo -- a table describing well-known header fields.
X**			Each entry has the field name and some flags,
X**			which are described in sendmail.h.
X**
X**	Notes:
X**		I have tried to put almost all the reasonable
X**		configuration information into the configuration
X**		file read at runtime.  My intent is that anything
X**		here is a function of the version of UNIX you
X**		are running, or is really static -- for example
X**		the headers are a superset of widely used
X**		protocols.  If you find yourself playing with
X**		this file too much, you may be making a mistake!
X*/
X
X
X
X
X/*
X**  Header info table
X**	Final (null) entry contains the flags used for any other field.
X**
X**	Not all of these are actually handled specially by sendmail
X**	at this time.  They are included as placeholders, to let
X**	you know that "someday" I intend to have sendmail do
X**	something with them.
X*/
X
Xstruct hdrinfo	HdrInfo[] =
X{
X		/* originator fields, most to least significant  */
X	"resent-sender",	H_FROM|H_RESENT,
X	"resent-from",		H_FROM|H_RESENT,
X	"resent-reply-to",	H_FROM|H_RESENT,
X	"sender",		H_FROM,
X	"from",			H_FROM,
X	"reply-to",		H_FROM,
X	"full-name",		H_ACHECK,
X	"return-receipt-to",	H_FROM,
X	"errors-to",		H_FROM,
X		/* destination fields */
X	"to",			H_RCPT,
X	"resent-to",		H_RCPT|H_RESENT,
X	"cc",			H_RCPT,
X	"resent-cc",		H_RCPT|H_RESENT,
X	"bcc",			H_RCPT|H_ACHECK,
X	"resent-bcc",		H_RCPT|H_ACHECK|H_RESENT,
X		/* message identification and control */
X	"message-id",		0,
X	"resent-message-id",	H_RESENT,
X	"message",		H_EOH,
X	"text",			H_EOH,
X		/* date fields */
X	"date",			0,
X	"resent-date",		H_RESENT,
X		/* trace fields */
X	"received",		H_TRACE|H_FORCE,
X	"via",			H_TRACE|H_FORCE,
X	"mail-from",		H_TRACE|H_FORCE,
X
X	NULL,			0,
X};
X
X
X/*
X**  ARPANET error message numbers.
X*/
X
Xchar	Arpa_Info[] =		"050";	/* arbitrary info */
Xchar	Arpa_TSyserr[] =	"451";	/* some (transient) system error */
Xchar	Arpa_PSyserr[] =	"554";	/* some (permanent) system error */
Xchar	Arpa_Usrerr[] =		"554";	/* some (fatal) user error */
X
X
X
X/*
X**  Location of system files/databases/etc.
X*/
X
Xchar	*ConfFile =	"/usr/lib/sendmail.cf";	/* runtime configuration */
Xchar	*FreezeFile =	"/usr/lib/sendmail.fc";	/* frozen version of above */
X
X
X
X/*
X**  Miscellaneous stuff.
X*/
X
Xint	DtableSize =	50;		/* max open files; reset in 4.2bsd */
X/*
X**  SETDEFAULTS -- set default values
X**
X**	Because of the way freezing is done, these must be initialized
X**	using direct code.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Initializes a bunch of global variables to their
X**		default values.
X*/
X
Xsetdefaults()
X{
X	QueueLA = 8;
X	QueueFactor = 10000;
X	RefuseLA = 12;
X	SpaceSub = ' ';
X	WkRecipFact = 1000;
X	WkClassFact = 1800;
X	WkTimeFact = 9000;
X	FileMode = 0644;
X	DefUid = 1;
X	DefGid = 1;
X}
X
X# ifdef V6
X/*
X**  TTYNAME -- return name of terminal.
X**
X**	Parameters:
X**		fd -- file descriptor to check.
X**
X**	Returns:
X**		pointer to full path of tty.
X**		NULL if no tty.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar *
Xttyname(fd)
X	int fd;
X{
X	register char tn;
X	static char pathn[] = "/dev/ttyx";
X
X	/* compute the pathname of the controlling tty */
X	if ((tn = ttyn(fd)) == NULL)
X	{
X		errno = 0;
X		return (NULL);
X	}
X	pathn[8] = tn;
X	return (pathn);
X}
X/*
X**  FDOPEN -- Open a stdio file given an open file descriptor.
X**
X**	This is included here because it is standard in v7, but we
X**	need it in v6.
X**
X**	Algorithm:
X**		Open /dev/null to create a descriptor.
X**		Close that descriptor.
X**		Copy the existing fd into the descriptor.
X**
X**	Parameters:
X**		fd -- the open file descriptor.
X**		type -- "r", "w", or whatever.
X**
X**	Returns:
X**		The file descriptor it creates.
X**
X**	Side Effects:
X**		none
X**
X**	Called By:
X**		deliver
X**
X**	Notes:
X**		The mode of fd must match "type".
X*/
X
XFILE *
Xfdopen(fd, type)
X	int fd;
X	char *type;
X{
X	register FILE *f;
X
X	f = fopen("/dev/null", type);
X	(void) close(fileno(f));
X	fileno(f) = fd;
X	return (f);
X}
X/*
X**  INDEX -- Return pointer to character in string
X**
X**	For V7 compatibility.
X**
X**	Parameters:
X**		s -- a string to scan.
X**		c -- a character to look for.
X**
X**	Returns:
X**		If c is in s, returns the address of the first
X**			instance of c in s.
X**		NULL if c is not in s.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar *
Xindex(s, c)
X	register char *s;
X	register char c;
X{
X	while (*s != '\0')
X	{
X		if (*s++ == c)
X			return (--s);
X	}
X	return (NULL);
X}
X/*
X**  UMASK -- fake the umask system call.
X**
X**	Since V6 always acts like the umask is zero, we will just
X**	assume the same thing.
X*/
X
X/*ARGSUSED*/
Xumask(nmask)
X{
X	return (0);
X}
X
X
X/*
X**  GETRUID -- get real user id.
X*/
X
Xgetruid()
X{
X	return (getuid() & 0377);
X}
X
X
X/*
X**  GETRGID -- get real group id.
X*/
X
Xgetrgid()
X{
X	return (getgid() & 0377);
X}
X
X
X/*
X**  GETEUID -- get effective user id.
X*/
X
Xgeteuid()
X{
X	return ((getuid() >> 8) & 0377);
X}
X
X
X/*
X**  GETEGID -- get effective group id.
X*/
X
Xgetegid()
X{
X	return ((getgid() >> 8) & 0377);
X}
X
X# endif V6
X
X# ifndef V6
X
X/*
X**  GETRUID -- get real user id (V7)
X*/
X
Xgetruid()
X{
X	if (OpMode == MD_DAEMON)
X		return (RealUid);
X	else
X		return (getuid());
X}
X
X
X/*
X**  GETRGID -- get real group id (V7).
X*/
X
Xgetrgid()
X{
X	if (OpMode == MD_DAEMON)
X		return (RealGid);
X	else
X		return (getgid());
X}
X
X# endif V6
X/*
X**  USERNAME -- return the user id of the logged in user.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		The login name of the logged in user.
X**
X**	Side Effects:
X**		none.
X**
X**	Notes:
X**		The return value is statically allocated.
X*/
X
Xchar *
Xusername()
X{
X	static char *myname = NULL;
X	extern char *getlogin();
X	register struct passwd *pw;
X	extern struct passwd *getpwuid();
X
X	/* cache the result */
X	if (myname == NULL)
X	{
X		myname = getlogin();
X		if (myname == NULL || myname[0] == '\0')
X		{
X
X			pw = getpwuid(getruid());
X			if (pw != NULL)
X				myname = pw->pw_name;
X		}
X		else
X		{
X
X			pw = getpwnam(myname);
X			if(getuid() != pw->pw_uid)
X			{
X				pw = getpwuid(getuid());
X				if (pw != NULL)
X					myname = pw->pw_name;
X			}
X		}
X		if (myname == NULL || myname[0] == '\0')
X		{
X			syserr("Who are you?");
X			myname = "postmaster";
X		}
X	}
X
X	return (myname);
X}
X/*
X**  TTYPATH -- Get the path of the user's tty
X**
X**	Returns the pathname of the user's tty.  Returns NULL if
X**	the user is not logged in or if s/he has write permission
X**	denied.
X**
X**	Parameters:
X**		none
X**
X**	Returns:
X**		pathname of the user's tty.
X**		NULL if not logged in or write permission denied.
X**
X**	Side Effects:
X**		none.
X**
X**	WARNING:
X**		Return value is in a local buffer.
X**
X**	Called By:
X**		savemail
X*/
X
X# include <sys/stat.h>
X
Xchar *
Xttypath()
X{
X	struct stat stbuf;
X	register char *pathn;
X	extern char *ttyname();
X	extern char *getlogin();
X
X	/* compute the pathname of the controlling tty */
X	if ((pathn = ttyname(2)) == NULL && (pathn = ttyname(1)) == NULL &&
X	    (pathn = ttyname(0)) == NULL)
X	{
X		errno = 0;
X		return (NULL);
X	}
X
X	/* see if we have write permission */
X	if (stat(pathn, &stbuf) < 0 || !bitset(02, stbuf.st_mode))
X	{
X		errno = 0;
X		return (NULL);
X	}
X
X	/* see if the user is logged in */
X	if (getlogin() == NULL)
X		return (NULL);
X
X	/* looks good */
X	return (pathn);
X}
X/*
X**  CHECKCOMPAT -- check for From and To person compatible.
X**
X**	This routine can be supplied on a per-installation basis
X**	to determine whether a person is allowed to send a message.
X**	This allows restriction of certain types of internet
X**	forwarding or registration of users.
X**
X**	If the hosts are found to be incompatible, an error
X**	message should be given using "usrerr" and FALSE should
X**	be returned.
X**
X**	'NoReturn' can be set to suppress the return-to-sender
X**	function; this should be done on huge messages.
X**
X**	Parameters:
X**		to -- the person being sent to.
X**
X**	Returns:
X**		TRUE -- ok to send.
X**		FALSE -- not ok.
X**
X**	Side Effects:
X**		none (unless you include the usrerr stuff)
X*/
X
Xbool
Xcheckcompat(to)
X	register ADDRESS *to;
X{
X# ifdef lint
X	if (to == NULL)
X		to++;
X# endif lint
X# ifdef EXAMPLE_CODE
X	/* this code is intended as an example only */
X	register STAB *s;
X
X	s = stab("arpa", ST_MAILER, ST_FIND);
X	if (s != NULL && CurEnv->e_from.q_mailer != LocalMailer &&
X	    to->q_mailer == s->s_mailer)
X	{
X		usrerr("No ARPA mail through this machine: see your system administration");
X		/* NoReturn = TRUE; to supress return copy */
X		return (FALSE);
X	}
X# endif EXAMPLE_CODE
X	return (TRUE);
X}
X/*
X**  HOLDSIGS -- arrange to hold all signals
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Arranges that signals are held.
X*/
X
Xholdsigs()
X{
X}
X/*
X**  RLSESIGS -- arrange to release all signals
X**
X**	This undoes the effect of holdsigs.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Arranges that signals are released.
X*/
X
Xrlsesigs()
X{
X}
X/*
X**  GETLA -- get the current load average
X**
X**	This code stolen from la.c.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		The current load average as an integer.
X**
X**	Side Effects:
X**		none.
X*/
X
X#ifdef VMUNIX
X
X#include <nlist.h>
X
Xstruct	nlist Nl[] =
X{
X	{ "_avenrun" },
X#define	X_AVENRUN	0
X	{ 0 },
X};
X
Xgetla()
X{
X	static int kmem = -1;
X# ifdef sun
X	long avenrun[3];
X# else
X	double avenrun[3];
X# endif
X	extern off_t lseek();
X
X	if (kmem < 0)
X	{
X		kmem = open("/dev/kmem", 0, 0);
X		if (kmem < 0)
X			return (-1);
X		(void) ioctl(kmem, (int) FIOCLEX, (char *) 0);
X		nlist("/vmunix", Nl);
X		if (Nl[0].n_type == 0)
X			return (-1);
X	}
X	if (lseek(kmem, (off_t) Nl[X_AVENRUN].n_value, 0) == -1 ||
X	    read(kmem, (char *) avenrun, sizeof(avenrun)) < sizeof(avenrun))
X	{
X		/* thank you Ian */
X		return (-1);
X	}
X# ifdef sun
X	return ((int) (avenrun[0] + FSCALE/2) >> FSHIFT);
X# else
X	return ((int) (avenrun[0] + 0.5));
X# endif
X}
X
X#else VMUNIX
X
Xgetla()
X{
X	return (0);
X}
X
X#endif VMUNIX
X/*
X**  SHOULDQUEUE -- should this message be queued or sent?
X**
X**	Compares the message cost to the load average to decide.
X**
X**	Parameters:
X**		pri -- the priority of the message in question.
X**
X**	Returns:
X**		TRUE -- if this message should be queued up for the
X**			time being.
X**		FALSE -- if the load is low enough to send this message.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xshouldqueue(pri)
X	long pri;
X{
X	int la;
X
X	la = getla();
X	if (la < QueueLA)
X		return (FALSE);
X	return (pri > (QueueFactor / (la - QueueLA + 1)));
X}
X/*
X**  SETPROCTITLE -- set process title for ps
X**
X**	Parameters:
X**		fmt -- a printf style format string.
X**		a, b, c -- possible parameters to fmt.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Clobbers argv of our main procedure so ps(1) will
X**		display the title.
X*/
X
X/*VARARGS1*/
Xsetproctitle(fmt, a, b, c)
X	char *fmt;
X{
X# ifdef SETPROCTITLE
X	register char *p;
X	register int i;
X	extern char **Argv;
X	extern char *LastArgv;
X	char buf[MAXLINE];
X
X	(void) sprintf(buf, fmt, a, b, c);
X
X	/* make ps print "(sendmail)" */
X	p = Argv[0];
X	*p++ = '-';
X
X	i = strlen(buf);
X	if (i > LastArgv - p - 2)
X	{
X		i = LastArgv - p - 2;
X		buf[i] = '\0';
X	}
X	(void) strcpy(p, buf);
X	p += i;
X	while (p < LastArgv)
X		*p++ = ' ';
X# endif SETPROCTITLE
X}
X/*
X**  REAPCHILD -- pick up the body of my child, lest it become a zombie
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Picks up extant zombies.
X*/
X
X# ifdef VMUNIX
X# include <sys/wait.h>
X# endif VMUNIX
X
Xreapchild()
X{
X# ifdef WNOHANG
X	union wait status;
X
X	while (wait3(&status, WNOHANG, (struct rusage *) NULL) > 0)
X		continue;
X# else WNOHANG
X	auto int status;
X
X	while (wait(&status) > 0)
X		continue;
X# endif WNOHANG
X}
END_OF_FILE
if test 12327 -ne `wc -c <'src/conf.c'`; then
    echo shar: \"'src/conf.c'\" unpacked with wrong size!
fi
# end of 'src/conf.c'
fi
if test -f 'src/envelope.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/envelope.c'\"
else
echo shar: Extracting \"'src/envelope.c'\" \(12701 characters\)
sed "s/^X//" >'src/envelope.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)envelope.c	5.12 (Berkeley) 12/17/85";
X#endif not lint
X
X#include <pwd.h>
X#include "sendmail.h"
X#ifdef	UNIXPC
X#include <time.h>
X#else
X#include <sys/time.h>
X#endif
X#include <sys/stat.h>
X
X/*
X**  NEWENVELOPE -- allocate a new envelope
X**
X**	Supports inheritance.
X**
X**	Parameters:
X**		e -- the new envelope to fill in.
X**
X**	Returns:
X**		e.
X**
X**	Side Effects:
X**		none.
X*/
X
XENVELOPE *
Xnewenvelope(e)
X	register ENVELOPE *e;
X{
X	register ENVELOPE *parent;
X	extern putheader(), putbody();
X	extern ENVELOPE BlankEnvelope;
X
X	parent = CurEnv;
X	if (e == CurEnv)
X		parent = e->e_parent;
X	clearenvelope(e, TRUE);
X	if (e == CurEnv)
X		bcopy((char *) &NullAddress, (char *) &e->e_from, sizeof e->e_from);
X	else
X		bcopy((char *) &CurEnv->e_from, (char *) &e->e_from, sizeof e->e_from);
X	e->e_parent = parent;
X	e->e_ctime = curtime();
X	e->e_msgpriority = parent->e_msgsize;
X	e->e_puthdr = putheader;
X	e->e_putbody = putbody;
X	if (CurEnv->e_xfp != NULL)
X		(void) fflush(CurEnv->e_xfp);
X
X	return (e);
X}
X/*
X**  DROPENVELOPE -- deallocate an envelope.
X**
X**	Parameters:
X**		e -- the envelope to deallocate.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		housekeeping necessary to dispose of an envelope.
X**		Unlocks this queue file.
X*/
X
Xdropenvelope(e)
X	register ENVELOPE *e;
X{
X	bool queueit = FALSE;
X	register ADDRESS *q;
X
X#ifdef DEBUG
X	if (tTd(50, 1))
X	{
X		printf("dropenvelope %x id=", e);
X		xputs(e->e_id);
X		printf(" flags=%o\n", e->e_flags);
X	}
X#endif DEBUG
X#ifdef LOG
X	if (LogLevel > 10)
X		syslog(LOG_DEBUG, "dropenvelope, id=%s, flags=%o, pid=%d",
X				  e->e_id == NULL ? "(none)" : e->e_id,
X				  e->e_flags, getpid());
X#endif LOG
X
X	/* we must have an id to remove disk files */
X	if (e->e_id == NULL)
X		return;
X
X	/*
X	**  Extract state information from dregs of send list.
X	*/
X
X	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
X	{
X		if (bitset(QQUEUEUP, q->q_flags))
X			queueit = TRUE;
X	}
X
X	/*
X	**  Send back return receipts as requested.
X	*/
X
X	if (e->e_receiptto != NULL && bitset(EF_SENDRECEIPT, e->e_flags))
X	{
X		auto ADDRESS *rlist = NULL;
X
X		sendtolist(CurEnv->e_receiptto, (ADDRESS *) NULL, &rlist);
X		(void) returntosender("Return receipt", rlist, FALSE);
X	}
X
X	/*
X	**  Arrange to send error messages if there are fatal errors.
X	*/
X
X	if (bitset(EF_FATALERRS|EF_TIMEOUT, e->e_flags) && ErrorMode != EM_QUIET)
X		savemail(e);
X
X	/*
X	**  Instantiate or deinstantiate the queue.
X	*/
X
X	if ((!queueit && !bitset(EF_KEEPQUEUE, e->e_flags)) ||
X	    bitset(EF_CLRQUEUE, e->e_flags))
X	{
X		if (e->e_df != NULL)
X			xunlink(e->e_df);
X		xunlink(queuename(e, 'q'));
X	}
X	else if (queueit || !bitset(EF_INQUEUE, e->e_flags))
X	{
X#ifdef QUEUE
X		queueup(e, FALSE, FALSE);
X#else QUEUE
X		syserr("dropenvelope: queueup");
X#endif QUEUE
X	}
X
X	/* now unlock the job */
X	closexscript(e);
X	unlockqueue(e);
X
X	/* make sure that this envelope is marked unused */
X	e->e_id = e->e_df = NULL;
X	if (e->e_dfp != NULL)
X		(void) fclose(e->e_dfp);
X	e->e_dfp = NULL;
X}
X/*
X**  CLEARENVELOPE -- clear an envelope without unlocking
X**
X**	This is normally used by a child process to get a clean
X**	envelope without disturbing the parent.
X**
X**	Parameters:
X**		e -- the envelope to clear.
X**		fullclear - if set, the current envelope is total
X**			garbage and should be ignored; otherwise,
X**			release any resources it may indicate.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Closes files associated with the envelope.
X**		Marks the envelope as unallocated.
X*/
X
Xclearenvelope(e, fullclear)
X	register ENVELOPE *e;
X	bool fullclear;
X{
X	register HDR *bh;
X	register HDR **nhp;
X	extern ENVELOPE BlankEnvelope;
X
X	if (!fullclear)
X	{
X		/* clear out any file information */
X		if (e->e_xfp != NULL)
X			(void) fclose(e->e_xfp);
X		if (e->e_dfp != NULL)
X			(void) fclose(e->e_dfp);
X	}
X
X	/* now clear out the data */
X	STRUCTCOPY(BlankEnvelope, *e);
X	bh = BlankEnvelope.e_header;
X	nhp = &e->e_header;
X	while (bh != NULL)
X	{
X		*nhp = (HDR *) xalloc(sizeof *bh);
X		bcopy((char *) bh, (char *) *nhp, sizeof *bh);
X		bh = bh->h_link;
X		nhp = &(*nhp)->h_link;
X	}
X}
X/*
X**  INITSYS -- initialize instantiation of system
X**
X**	In Daemon mode, this is done in the child.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Initializes the system macros, some global variables,
X**		etc.  In particular, the current time in various
X**		forms is set.
X*/
X
Xinitsys()
X{
X	static char cbuf[5];			/* holds hop count */
X	static char pbuf[10];			/* holds pid */
X#ifdef TTYNAME
X	static char ybuf[10];			/* holds tty id */
X	register char *p;
X#endif TTYNAME
X	extern char *ttyname();
X	extern char *macvalue();
X	extern char Version[];
X
X	/*
X	**  Give this envelope a reality.
X	**	I.e., an id, a transcript, and a creation time.
X	*/
X
X	openxscript(CurEnv);
X	CurEnv->e_ctime = curtime();
X
X	/*
X	**  Set OutChannel to something useful if stdout isn't it.
X	**	This arranges that any extra stuff the mailer produces
X	**	gets sent back to the user on error (because it is
X	**	tucked away in the transcript).
X	*/
X
X	if (OpMode == MD_DAEMON && QueueRun)
X		OutChannel = CurEnv->e_xfp;
X
X	/*
X	**  Set up some basic system macros.
X	*/
X
X	/* process id */
X	(void) sprintf(pbuf, "%d", getpid());
X	define('p', pbuf, CurEnv);
X
X	/* hop count */
X	(void) sprintf(cbuf, "%d", CurEnv->e_hopcount);
X	define('c', cbuf, CurEnv);
X
X	/* time as integer, unix time, arpa time */
X	settime();
X
X#ifdef TTYNAME
X	/* tty name */
X	if (macvalue('y', CurEnv) == NULL)
X	{
X		p = ttyname(2);
X		if (p != NULL)
X		{
X			if (rindex(p, '/') != NULL)
X				p = rindex(p, '/') + 1;
X			(void) strcpy(ybuf, p);
X			define('y', ybuf, CurEnv);
X		}
X	}
X#endif TTYNAME
X}
X/*
X**  SETTIME -- set the current time.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Sets the various time macros -- $a, $b, $d, $t.
X*/
X
Xsettime()
X{
X	register char *p;
X	auto time_t now;
X	static char tbuf[20];			/* holds "current" time */
X	static char dbuf[30];			/* holds ctime(tbuf) */
X	register struct tm *tm;
X	extern char *arpadate();
X	extern struct tm *gmtime();
X	extern char *macvalue();
X
X	now = curtime();
X	tm = gmtime(&now);
X	(void) sprintf(tbuf, "%02d%02d%02d%02d%02d", tm->tm_year, tm->tm_mon+1,
X			tm->tm_mday, tm->tm_hour, tm->tm_min);
X	define('t', tbuf, CurEnv);
X	(void) strcpy(dbuf, ctime(&now));
X	*index(dbuf, '\n') = '\0';
X	if (macvalue('d', CurEnv) == NULL)
X		define('d', dbuf, CurEnv);
X	p = newstr(arpadate(dbuf));
X	if (macvalue('a', CurEnv) == NULL)
X		define('a', p, CurEnv);
X	define('b', p, CurEnv);
X}
X/*
X**  OPENXSCRIPT -- Open transcript file
X**
X**	Creates a transcript file for possible eventual mailing or
X**	sending back.
X**
X**	Parameters:
X**		e -- the envelope to create the transcript in/for.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		Creates the transcript file.
X*/
X
Xopenxscript(e)
X	register ENVELOPE *e;
X{
X	register char *p;
X
X# ifdef LOG
X	if (LogLevel > 19)
X		syslog(LOG_DEBUG, "%s: openx%s", e->e_id, e->e_xfp == NULL ? "" : " (no)");
X# endif LOG
X	if (e->e_xfp != NULL)
X		return;
X	p = queuename(e, 'x');
X	e->e_xfp = fopen(p, "w");
X	if (e->e_xfp == NULL)
X		syserr("Can't create %s", p);
X	else
X		(void) chmod(p, 0644);
X}
X/*
X**  CLOSEXSCRIPT -- close the transcript file.
X**
X**	Parameters:
X**		e -- the envelope containing the transcript to close.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
Xclosexscript(e)
X	register ENVELOPE *e;
X{
X	if (e->e_xfp == NULL)
X		return;
X	(void) fclose(e->e_xfp);
X	e->e_xfp = NULL;
X}
X/*
X**  SETSENDER -- set the person who this message is from
X**
X**	Under certain circumstances allow the user to say who
X**	s/he is (using -f or -r).  These are:
X**	1.  The user's uid is zero (root).
X**	2.  The user's login name is in an approved list (typically
X**	    from a network server).
X**	3.  The address the user is trying to claim has a
X**	    "!" character in it (since #2 doesn't do it for
X**	    us if we are dialing out for UUCP).
X**	A better check to replace #3 would be if the
X**	effective uid is "UUCP" -- this would require me
X**	to rewrite getpwent to "grab" uucp as it went by,
X**	make getname more nasty, do another passwd file
X**	scan, or compile the UID of "UUCP" into the code,
X**	all of which are reprehensible.
X**
X**	Assuming all of these fail, we figure out something
X**	ourselves.
X**
X**	Parameters:
X**		from -- the person we would like to believe this message
X**			is from, as specified on the command line.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		sets sendmail's notion of who the from person is.
X*/
X
Xsetsender(from)
X	char *from;
X{
X	register char **pvp;
X	char *realname = NULL;
X	register struct passwd *pw;
X	char buf[MAXNAME];
X	char pvpbuf[PSBUFSIZE];
X	extern struct passwd *getpwnam();
X	extern char *macvalue();
X	extern char **prescan();
X	extern bool safefile();
X	extern char *FullName;
X
X# ifdef DEBUG
X	if (tTd(45, 1))
X		printf("setsender(%s)\n", from == NULL ? "" : from);
X# endif DEBUG
X
X	/*
X	**  Figure out the real user executing us.
X	**	Username can return errno != 0 on non-errors.
X	*/
X
X	if (QueueRun || OpMode == MD_SMTP || OpMode == MD_ARPAFTP)
X		realname = from;
X	if (realname == NULL || realname[0] == '\0')
X	{
X		extern char *username();
X
X		realname = username();
X	}
X
X	/*
X	**  Determine if this real person is allowed to alias themselves.
X	*/
X
X	if (from != NULL)
X	{
X		extern bool trusteduser();
X
X		if (!trusteduser(realname) &&
X# ifdef DEBUG
X		    (!tTd(1, 9) || getuid() != geteuid()) &&
X# endif DEBUG
X		    index(from, '!') == NULL && getuid() != 0)
X		{
X			/* network sends -r regardless (why why why?) */
X			/* syserr("%s, you cannot use the -f flag", realname); */
X			from = NULL;
X		}
X	}
X
X	SuprErrs = TRUE;
X	if (from == NULL || parseaddr(from, &CurEnv->e_from, 1, '\0') == NULL)
X	{
X		/* log garbage addresses for traceback */
X		if (from != NULL)
X		{
X# ifdef LOG
X			if (LogLevel >= 1)
X				syslog(LOG_ERR, "Unparseable user %s wants to be %s",
X						realname, from);
X# endif LOG
X		}
X		from = newstr(realname);
X		if (parseaddr(from, &CurEnv->e_from, 1, '\0') == NULL &&
X		    parseaddr("postmaster", &CurEnv->e_from, 1, '\0') == NULL)
X		{
X			syserr("setsender: can't even parse postmaster!");
X		}
X	}
X	else
X		FromFlag = TRUE;
X	CurEnv->e_from.q_flags |= QDONTSEND;
X	loweraddr(&CurEnv->e_from);
X	SuprErrs = FALSE;
X
X	if (CurEnv->e_from.q_mailer == LocalMailer &&
X	    (pw = getpwnam(CurEnv->e_from.q_user)) != NULL)
X	{
X		/*
X		**  Process passwd file entry.
X		*/
X
X
X		/* extract home directory */
X		CurEnv->e_from.q_home = newstr(pw->pw_dir);
X		define('z', CurEnv->e_from.q_home, CurEnv);
X
X		/* extract user and group id */
X		CurEnv->e_from.q_uid = pw->pw_uid;
X		CurEnv->e_from.q_gid = pw->pw_gid;
X
X		/* if the user has given fullname already, don't redefine */
X		if (FullName == NULL)
X			FullName = macvalue('x', CurEnv);
X		if (FullName != NULL && FullName[0] == '\0')
X			FullName = NULL;
X
X		/* extract full name from passwd file */
X		if (FullName == NULL && pw->pw_gecos != NULL &&
X		    strcmp(pw->pw_name, CurEnv->e_from.q_user) == 0)
X		{
X			buildfname(pw->pw_gecos, CurEnv->e_from.q_user, buf);
X			if (buf[0] != '\0')
X				FullName = newstr(buf);
X		}
X		if (FullName != NULL)
X			define('x', FullName, CurEnv);
X	}
X	else
X	{
X#ifndef V6
X		if (CurEnv->e_from.q_home == NULL)
X			CurEnv->e_from.q_home = getenv("HOME");
X#endif V6
X		CurEnv->e_from.q_uid = getuid();
X		CurEnv->e_from.q_gid = getgid();
X	}
X
X	if (CurEnv->e_from.q_uid != 0)
X	{
X		DefUid = CurEnv->e_from.q_uid;
X		DefGid = CurEnv->e_from.q_gid;
X	}
X
X	/*
X	**  Rewrite the from person to dispose of possible implicit
X	**	links in the net.
X	*/
X
X	pvp = prescan(from, '\0', pvpbuf);
X	if (pvp == NULL)
X	{
X		syserr("cannot prescan from (%s)", from);
X		finis();
X	}
X	rewrite(pvp, 3);
X	rewrite(pvp, 1);
X	rewrite(pvp, 4);
X	cataddr(pvp, buf, sizeof buf);
X	define('f', newstr(buf), CurEnv);
X
X	/* save the domain spec if this mailer wants it */
X	if (CurEnv->e_from.q_mailer != NULL &&
X	    bitnset(M_CANONICAL, CurEnv->e_from.q_mailer->m_flags))
X	{
X		extern char **copyplist();
X
X		while (*pvp != NULL && strcmp(*pvp, "@") != 0)
X			pvp++;
X		if (*pvp != NULL)
X			CurEnv->e_fromdomain = copyplist(pvp, TRUE);
X	}
X}
X/*
X**  TRUSTEDUSER -- tell us if this user is to be trusted.
X**
X**	Parameters:
X**		user -- the user to be checked.
X**
X**	Returns:
X**		TRUE if the user is in an approved list.
X**		FALSE otherwise.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xtrusteduser(user)
X	char *user;
X{
X	register char **ulist;
X	extern char *TrustedUsers[];
X
X	for (ulist = TrustedUsers; *ulist != NULL; ulist++)
X		if (strcmp(*ulist, user) == 0)
X			return (TRUE);
X	return (FALSE);
X}
END_OF_FILE
if test 12701 -ne `wc -c <'src/envelope.c'`; then
    echo shar: \"'src/envelope.c'\" unpacked with wrong size!
fi
# end of 'src/envelope.c'
fi
if test -f 'src/recipient.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/recipient.c'\"
else
echo shar: Extracting \"'src/recipient.c'\" \(12741 characters\)
sed "s/^X//" >'src/recipient.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)recipient.c	5.9 (Berkeley) 10/23/86";
X#endif not lint
X
X# include <pwd.h>
X# include "sendmail.h"
X# include <sys/stat.h>
X
X/*
X**  SENDTOLIST -- Designate a send list.
X**
X**	The parameter is a comma-separated list of people to send to.
X**	This routine arranges to send to all of them.
X**
X**	Parameters:
X**		list -- the send list.
X**		ctladdr -- the address template for the person to
X**			send to -- effective uid/gid are important.
X**			This is typically the alias that caused this
X**			expansion.
X**		sendq -- a pointer to the head of a queue to put
X**			these people into.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		none.
X*/
X
X# define MAXRCRSN	10
X
Xsendtolist(list, ctladdr, sendq)
X	char *list;
X	ADDRESS *ctladdr;
X	ADDRESS **sendq;
X{
X	register char *p;
X	register ADDRESS *al;	/* list of addresses to send to */
X	bool firstone;		/* set on first address sent */
X	bool selfref;		/* set if this list includes ctladdr */
X	char delimiter;		/* the address delimiter */
X
X# ifdef DEBUG
X	if (tTd(25, 1))
X	{
X		printf("sendto: %s\n   ctladdr=", list);
X		printaddr(ctladdr, FALSE);
X	}
X# endif DEBUG
X
X	/* heuristic to determine old versus new style addresses */
X	if (ctladdr == NULL &&
X	    (index(list, ',') != NULL || index(list, ';') != NULL ||
X	     index(list, '<') != NULL || index(list, '(') != NULL))
X		CurEnv->e_flags &= ~EF_OLDSTYLE;
X	delimiter = ' ';
X	if (!bitset(EF_OLDSTYLE, CurEnv->e_flags) || ctladdr != NULL)
X		delimiter = ',';
X
X	firstone = TRUE;
X	selfref = FALSE;
X	al = NULL;
X
X	for (p = list; *p != '\0'; )
X	{
X		register ADDRESS *a;
X		extern char *DelimChar;		/* defined in prescan */
X
X		/* parse the address */
X		while (isspace(*p) || *p == ',')
X			p++;
X		a = parseaddr(p, (ADDRESS *) NULL, 1, delimiter);
X		p = DelimChar;
X		if (a == NULL)
X			continue;
X		a->q_next = al;
X		a->q_alias = ctladdr;
X
X		/* see if this should be marked as a primary address */
X		if (ctladdr == NULL ||
X		    (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags)))
X			a->q_flags |= QPRIMARY;
X
X		/* put on send queue or suppress self-reference */
X		if (ctladdr != NULL && sameaddr(ctladdr, a))
X			selfref = TRUE;
X		else
X			al = a;
X		firstone = FALSE;
X	}
X
X	/* if this alias doesn't include itself, delete ctladdr */
X	if (!selfref && ctladdr != NULL)
X		ctladdr->q_flags |= QDONTSEND;
X
X	/* arrange to send to everyone on the local send list */
X	while (al != NULL)
X	{
X		register ADDRESS *a = al;
X		extern ADDRESS *recipient();
X
X		al = a->q_next;
X		a = recipient(a, sendq);
X
X		/* arrange to inherit full name */
X		if (a->q_fullname == NULL && ctladdr != NULL)
X			a->q_fullname = ctladdr->q_fullname;
X	}
X
X	CurEnv->e_to = NULL;
X}
X/*
X**  RECIPIENT -- Designate a message recipient
X**
X**	Saves the named person for future mailing.
X**
X**	Parameters:
X**		a -- the (preparsed) address header for the recipient.
X**		sendq -- a pointer to the head of a queue to put the
X**			recipient in.  Duplicate supression is done
X**			in this queue.
X**
X**	Returns:
X**		The actual address in the queue.  This will be "a" if
X**		the address is not a duplicate, else the original address.
X**
X**	Side Effects:
X**		none.
X*/
X
XADDRESS *
Xrecipient(a, sendq)
X	register ADDRESS *a;
X	register ADDRESS **sendq;
X{
X	register ADDRESS *q;
X	ADDRESS **pq;
X	register struct mailer *m;
X	register char *p;
X	bool quoted = FALSE;		/* set if the addr has a quote bit */
X	char buf[MAXNAME];		/* unquoted image of the user name */
X	extern ADDRESS *getctladdr();
X	extern bool safefile();
X
X	CurEnv->e_to = a->q_paddr;
X	m = a->q_mailer;
X	errno = 0;
X# ifdef DEBUG
X	if (tTd(26, 1))
X	{
X		printf("\nrecipient: ");
X		printaddr(a, FALSE);
X	}
X# endif DEBUG
X
X	/* break aliasing loops */
X	if (AliasLevel > MAXRCRSN)
X	{
X		usrerr("aliasing/forwarding loop broken");
X		return (a);
X	}
X
X	/*
X	**  Finish setting up address structure.
X	*/
X
X	/* set the queue timeout */
X	a->q_timeout = TimeOut;
X
X	/* map user & host to lower case if requested on non-aliases */
X	if (a->q_alias == NULL)
X		loweraddr(a);
X
X	/* get unquoted user for file, program or user.name check */
X	(void) strcpy(buf, a->q_user);
X	for (p = buf; *p != '\0' && !quoted; p++)
X	{
X		if (!isascii(*p) && (*p & 0377) != (SpaceSub & 0377))
X			quoted = TRUE;
X	}
X	stripquotes(buf, TRUE);
X
X	/* do sickly crude mapping for program mailing, etc. */
X	if (m == LocalMailer && buf[0] == '|')
X	{
X		a->q_mailer = m = ProgMailer;
X		a->q_user++;
X		if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail)
X		{
X			a->q_flags |= QDONTSEND|QBADADDR;
X			usrerr("Cannot mail directly to programs");
X		}
X	}
X
X	/*
X	**  Look up this person in the recipient list.
X	**	If they are there already, return, otherwise continue.
X	**	If the list is empty, just add it.  Notice the cute
X	**	hack to make from addresses suppress things correctly:
X	**	the QDONTSEND bit will be set in the send list.
X	**	[Please note: the emphasis is on "hack."]
X	*/
X
X	for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
X	{
X		if (!ForceMail && sameaddr(q, a))
X		{
X# ifdef DEBUG
X			if (tTd(26, 1))
X			{
X				printf("%s in sendq: ", a->q_paddr);
X				printaddr(q, FALSE);
X			}
X# endif DEBUG
X			if (!bitset(QDONTSEND, a->q_flags))
X				message(Arpa_Info, "duplicate suppressed");
X			if (!bitset(QPRIMARY, q->q_flags))
X				q->q_flags |= a->q_flags;
X			return (q);
X		}
X	}
X
X	/* add address on list */
X	*pq = a;
X	a->q_next = NULL;
X	CurEnv->e_nrcpts++;
X
X	/*
X	**  Alias the name and handle :include: specs.
X	*/
X
X	if (m == LocalMailer && !bitset(QDONTSEND, a->q_flags))
X	{
X		if (strncmp(a->q_user, ":include:", 9) == 0)
X		{
X			a->q_flags |= QDONTSEND;
X			if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail)
X			{
X				a->q_flags |= QBADADDR;
X				usrerr("Cannot mail directly to :include:s");
X			}
X			else
X			{
X				message(Arpa_Info, "including file %s", &a->q_user[9]);
X				include(&a->q_user[9], " sending", a, sendq);
X			}
X		}
X		else
X			alias(a, sendq);
X	}
X
X	/*
X	**  If the user is local and still being sent, verify that
X	**  the address is good.  If it is, try to forward.
X	**  If the address is already good, we have a forwarding
X	**  loop.  This can be broken by just sending directly to
X	**  the user (which is probably correct anyway).
X	*/
X
X	if (!bitset(QDONTSEND, a->q_flags) && m == LocalMailer)
X	{
X		struct stat stb;
X		extern bool writable();
X
X		/* see if this is to a file */
X		if (buf[0] == '/')
X		{
X			p = rindex(buf, '/');
X			/* check if writable or creatable */
X			if (a->q_alias == NULL && !tTd(0, 1) && !QueueRun && !ForceMail)
X			{
X				a->q_flags |= QDONTSEND|QBADADDR;
X				usrerr("Cannot mail directly to files");
X			}
X			else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) :
X			    (*p = '\0', !safefile(buf, getruid(), S_IWRITE|S_IEXEC)))
X			{
X				a->q_flags |= QBADADDR;
X				giveresponse(EX_CANTCREAT, m, CurEnv);
X			}
X		}
X		else
X		{
X			register struct passwd *pw;
X			extern struct passwd *finduser();
X
X			/* warning -- finduser may trash buf */
X			pw = finduser(buf);
X			if (pw == NULL)
X			{
X				a->q_flags |= QBADADDR;
X				giveresponse(EX_NOUSER, m, CurEnv);
X			}
X			else
X			{
X				char nbuf[MAXNAME];
X
X				if (strcmp(a->q_user, pw->pw_name) != 0)
X				{
X					a->q_user = newstr(pw->pw_name);
X					(void) strcpy(buf, pw->pw_name);
X				}
X				a->q_home = newstr(pw->pw_dir);
X				a->q_uid = pw->pw_uid;
X				a->q_gid = pw->pw_gid;
X				a->q_flags |= QGOODUID;
X				buildfname(pw->pw_gecos, pw->pw_name, nbuf);
X				if (nbuf[0] != '\0')
X					a->q_fullname = newstr(nbuf);
X				if (!quoted)
X					forward(a, sendq);
X			}
X		}
X	}
X	return (a);
X}
X/*
X**  FINDUSER -- find the password entry for a user.
X**
X**	This looks a lot like getpwnam, except that it may want to
X**	do some fancier pattern matching in /etc/passwd.
X**
X**	This routine contains most of the time of many sendmail runs.
X**	It deserves to be optimized.
X**
X**	Parameters:
X**		name -- the name to match against.
X**
X**	Returns:
X**		A pointer to a pw struct.
X**		NULL if name is unknown or ambiguous.
X**
X**	Side Effects:
X**		may modify name.
X*/
X
Xstruct passwd *
Xfinduser(name)
X	char *name;
X{
X	register struct passwd *pw;
X	register char *p;
X	extern struct passwd *getpwent();
X	extern struct passwd *getpwnam();
X
X	/* map upper => lower case */
X	for (p = name; *p != '\0'; p++)
X	{
X		if (isascii(*p) && isupper(*p))
X			*p = tolower(*p);
X	}
X
X	/* look up this login name using fast path */
X	if ((pw = getpwnam(name)) != NULL)
X		return (pw);
X
X	/* search for a matching full name instead */
X	for (p = name; *p != '\0'; p++)
X	{
X		if (*p == (SpaceSub & 0177) || *p == '_')
X			*p = ' ';
X	}
X	(void) setpwent();
X	while ((pw = getpwent()) != NULL)
X	{
X		char buf[MAXNAME];
X		extern bool sameword();
X
X		buildfname(pw->pw_gecos, pw->pw_name, buf);
X		if (index(buf, ' ') != NULL && sameword(buf, name))
X		{
X			message(Arpa_Info, "sending to login name %s", pw->pw_name);
X			return (pw);
X		}
X	}
X	return (NULL);
X}
X/*
X**  WRITABLE -- predicate returning if the file is writable.
X**
X**	This routine must duplicate the algorithm in sys/fio.c.
X**	Unfortunately, we cannot use the access call since we
X**	won't necessarily be the real uid when we try to
X**	actually open the file.
X**
X**	Notice that ANY file with ANY execute bit is automatically
X**	not writable.  This is also enforced by mailfile.
X**
X**	Parameters:
X**		s -- pointer to a stat struct for the file.
X**
X**	Returns:
X**		TRUE -- if we will be able to write this file.
X**		FALSE -- if we cannot write this file.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xwritable(s)
X	register struct stat *s;
X{
X	int euid, egid;
X	int bits;
X
X	if (bitset(0111, s->st_mode))
X		return (FALSE);
X	euid = getruid();
X	egid = getrgid();
X	if (geteuid() == 0)
X	{
X		if (bitset(S_ISUID, s->st_mode))
X			euid = s->st_uid;
X		if (bitset(S_ISGID, s->st_mode))
X			egid = s->st_gid;
X	}
X
X	if (euid == 0)
X		return (TRUE);
X	bits = S_IWRITE;
X	if (euid != s->st_uid)
X	{
X		bits >>= 3;
X		if (egid != s->st_gid)
X			bits >>= 3;
X	}
X	return ((s->st_mode & bits) != 0);
X}
X/*
X**  INCLUDE -- handle :include: specification.
X**
X**	Parameters:
X**		fname -- filename to include.
X**		msg -- message to print in verbose mode.
X**		ctladdr -- address template to use to fill in these
X**			addresses -- effective user/group id are
X**			the important things.
X**		sendq -- a pointer to the head of the send queue
X**			to put these addresses in.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		reads the :include: file and sends to everyone
X**		listed in that file.
X*/
X
Xinclude(fname, msg, ctladdr, sendq)
X	char *fname;
X	char *msg;
X	ADDRESS *ctladdr;
X	ADDRESS **sendq;
X{
X	char buf[MAXLINE];
X	register FILE *fp;
X	char *oldto = CurEnv->e_to;
X	char *oldfilename = FileName;
X	int oldlinenumber = LineNumber;
X
X	fp = fopen(fname, "r");
X	if (fp == NULL)
X	{
X		usrerr("Cannot open %s", fname);
X		return;
X	}
X	if (getctladdr(ctladdr) == NULL)
X	{
X		struct stat st;
X
X		if (fstat(fileno(fp), &st) < 0)
X			syserr("Cannot fstat %s!", fname);
X		ctladdr->q_uid = st.st_uid;
X		ctladdr->q_gid = st.st_gid;
X		ctladdr->q_flags |= QGOODUID;
X	}
X
X	/* read the file -- each line is a comma-separated list. */
X	FileName = fname;
X	LineNumber = 0;
X	while (fgets(buf, sizeof buf, fp) != NULL)
X	{
X		register char *p = index(buf, '\n');
X
X		if (p != NULL)
X			*p = '\0';
X		if (buf[0] == '\0')
X			continue;
X		CurEnv->e_to = oldto;
X		message(Arpa_Info, "%s to %s", msg, buf);
X		AliasLevel++;
X		sendtolist(buf, ctladdr, sendq);
X		AliasLevel--;
X	}
X
X	(void) fclose(fp);
X	FileName = oldfilename;
X	LineNumber = oldlinenumber;
X}
X/*
X**  SENDTOARGV -- send to an argument vector.
X**
X**	Parameters:
X**		argv -- argument vector to send to.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		puts all addresses on the argument vector onto the
X**			send queue.
X*/
X
Xsendtoargv(argv)
X	register char **argv;
X{
X	register char *p;
X	extern bool sameword();
X
X	while ((p = *argv++) != NULL)
X	{
X		if (argv[0] != NULL && argv[1] != NULL && sameword(argv[0], "at"))
X		{
X			char nbuf[MAXNAME];
X
X			if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf)
X				usrerr("address overflow");
X			else
X			{
X				(void) strcpy(nbuf, p);
X				(void) strcat(nbuf, "@");
X				(void) strcat(nbuf, argv[1]);
X				p = newstr(nbuf);
X				argv += 2;
X			}
X		}
X		sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
X	}
X}
X/*
X**  GETCTLADDR -- get controlling address from an address header.
X**
X**	If none, get one corresponding to the effective userid.
X**
X**	Parameters:
X**		a -- the address to find the controller of.
X**
X**	Returns:
X**		the controlling address.
X**
X**	Side Effects:
X**		none.
X*/
X
XADDRESS *
Xgetctladdr(a)
X	register ADDRESS *a;
X{
X	while (a != NULL && !bitset(QGOODUID, a->q_flags))
X		a = a->q_alias;
X	return (a);
X}
END_OF_FILE
if test 12741 -ne `wc -c <'src/recipient.c'`; then
    echo shar: \"'src/recipient.c'\" unpacked with wrong size!
fi
# end of 'src/recipient.c'
fi
if test -f 'src/savemail.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/savemail.c'\"
else
echo shar: Extracting \"'src/savemail.c'\" \(11670 characters\)
sed "s/^X//" >'src/savemail.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)savemail.c	5.7 (Berkeley) 12/7/85";
X#endif not lint
X
X# include <pwd.h>
X# include "sendmail.h"
X
X/*
X**  SAVEMAIL -- Save mail on error
X**
X**	If mailing back errors, mail it back to the originator
X**	together with an error message; otherwise, just put it in
X**	dead.letter in the user's home directory (if he exists on
X**	this machine).
X**
X**	Parameters:
X**		e -- the envelope containing the message in error.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		Saves the letter, by writing or mailing it back to the
X**		sender, or by putting it in dead.letter in her home
X**		directory.
X*/
X
X/* defines for state machine */
X# define ESM_REPORT	0	/* report to sender's terminal */
X# define ESM_MAIL	1	/* mail back to sender */
X# define ESM_QUIET	2	/* messages have already been returned */
X# define ESM_DEADLETTER	3	/* save in ~/dead.letter */
X# define ESM_POSTMASTER	4	/* return to postmaster */
X# define ESM_USRTMP	5	/* save in /usr/tmp/dead.letter */
X# define ESM_PANIC	6	/* leave the locked queue/transcript files */
X# define ESM_DONE	7	/* the message is successfully delivered */
X
X
Xsavemail(e)
X	register ENVELOPE *e;
X{
X	register struct passwd *pw;
X	register FILE *fp;
X	int state;
X	auto ADDRESS *q;
X	char buf[MAXLINE+1];
X	extern struct passwd *getpwnam();
X	register char *p;
X	extern char *ttypath();
X	typedef int (*fnptr)();
X
X# ifdef DEBUG
X	if (tTd(6, 1))
X		printf("\nsavemail, ErrorMode = %c\n", ErrorMode);
X# endif DEBUG
X
X	if (bitset(EF_RESPONSE, e->e_flags))
X		return;
X	if (e->e_class < 0)
X	{
X		message(Arpa_Info, "Dumping junk mail");
X		return;
X	}
X	ForceMail = TRUE;
X	e->e_flags &= ~EF_FATALERRS;
X
X	/*
X	**  In the unhappy event we don't know who to return the mail
X	**  to, make someone up.
X	*/
X
X	if (e->e_from.q_paddr == NULL)
X	{
X		if (parseaddr("root", &e->e_from, 0, '\0') == NULL)
X		{
X			syserr("Cannot parse root!");
X			ExitStat = EX_SOFTWARE;
X			finis();
X		}
X	}
X	e->e_to = NULL;
X
X	/*
X	**  Basic state machine.
X	**
X	**	This machine runs through the following states:
X	**
X	**	ESM_QUIET	Errors have already been printed iff the
X	**			sender is local.
X	**	ESM_REPORT	Report directly to the sender's terminal.
X	**	ESM_MAIL	Mail response to the sender.
X	**	ESM_DEADLETTER	Save response in ~/dead.letter.
X	**	ESM_POSTMASTER	Mail response to the postmaster.
X	**	ESM_PANIC	Save response anywhere possible.
X	*/
X
X	/* determine starting state */
X	switch (ErrorMode)
X	{
X	  case EM_WRITE:
X		state = ESM_REPORT;
X		break;
X
X	  case EM_BERKNET:
X		/* mail back, but return o.k. exit status */
X		ExitStat = EX_OK;
X
X		/* fall through.... */
X
X	  case EM_MAIL:
X		state = ESM_MAIL;
X		break;
X
X	  case EM_PRINT:
X	  case '\0':
X		state = ESM_QUIET;
X		break;
X
X	  case EM_QUIET:
X		/* no need to return anything at all */
X		return;
X
X	  default:
X		syserr("savemail: ErrorMode x%x\n");
X		state = ESM_MAIL;
X		break;
X	}
X
X	while (state != ESM_DONE)
X	{
X# ifdef DEBUG
X		if (tTd(6, 5))
X			printf("  state %d\n", state);
X# endif DEBUG
X
X		switch (state)
X		{
X		  case ESM_QUIET:
X			if (e->e_from.q_mailer == LocalMailer)
X				state = ESM_DEADLETTER;
X			else
X				state = ESM_MAIL;
X			break;
X
X		  case ESM_REPORT:
X
X			/*
X			**  If the user is still logged in on the same terminal,
X			**  then write the error messages back to hir (sic).
X			*/
X
X			p = ttypath();
X			if (p == NULL || freopen(p, "w", stdout) == NULL)
X			{
X				state = ESM_MAIL;
X				break;
X			}
X
X			expand("\001n", buf, &buf[sizeof buf - 1], e);
X			printf("\r\nMessage from %s...\r\n", buf);
X			printf("Errors occurred while sending mail.\r\n");
X			if (e->e_xfp != NULL)
X			{
X				(void) fflush(e->e_xfp);
X				fp = fopen(queuename(e, 'x'), "r");
X			}
X			else
X				fp = NULL;
X			if (fp == NULL)
X			{
X				syserr("Cannot open %s", queuename(e, 'x'));
X				printf("Transcript of session is unavailable.\r\n");
X			}
X			else
X			{
X				printf("Transcript follows:\r\n");
X				while (fgets(buf, sizeof buf, fp) != NULL &&
X				       !ferror(stdout))
X					fputs(buf, stdout);
X				(void) fclose(fp);
X			}
X			printf("Original message will be saved in dead.letter.\r\n");
X			if (ferror(stdout))
X				(void) syserr("savemail: stdout: write err");
X			state = ESM_DEADLETTER;
X			break;
X
X		  case ESM_MAIL:
X		  case ESM_POSTMASTER:
X			/*
X			**  If mailing back, do it.
X			**	Throw away all further output.  Don't alias,
X			**	since this could cause loops, e.g., if joe
X			**	mails to joe@x, and for some reason the network
X			**	for @x is down, then the response gets sent to
X			**	joe@x, which gives a response, etc.  Also force
X			**	the mail to be delivered even if a version of
X			**	it has already been sent to the sender.
X			*/
X
X			if (state == ESM_MAIL)
X			{
X				if (e->e_errorqueue == NULL)
X					sendtolist(e->e_from.q_paddr,
X						(ADDRESS *) NULL,
X						&e->e_errorqueue);
X
X				/* deliver a cc: to the postmaster if desired */
X				if (PostMasterCopy != NULL)
X					sendtolist(PostMasterCopy,
X						(ADDRESS *) NULL,
X						&e->e_errorqueue);
X				q = e->e_errorqueue;
X			}
X			else
X			{
X				if (parseaddr("postmaster", q, 0, '\0') == NULL)
X				{
X					syserr("cannot parse postmaster!");
X					ExitStat = EX_SOFTWARE;
X					state = ESM_USRTMP;
X					break;
X				}
X			}
X			if (returntosender(e->e_message != NULL ? e->e_message :
X					   "Unable to deliver mail",
X					   q, TRUE) == 0)
X			{
X				state = ESM_DONE;
X				break;
X			}
X
X			state = state == ESM_MAIL ? ESM_POSTMASTER : ESM_USRTMP;
X			break;
X
X		  case ESM_DEADLETTER:
X			/*
X			**  Save the message in dead.letter.
X			**	If we weren't mailing back, and the user is
X			**	local, we should save the message in
X			**	~/dead.letter so that the poor person doesn't
X			**	have to type it over again -- and we all know
X			**	what poor typists UNIX users are.
X			*/
X
X			p = NULL;
X			if (e->e_from.q_mailer == LocalMailer)
X			{
X				if (e->e_from.q_home != NULL)
X					p = e->e_from.q_home;
X				else if ((pw = getpwnam(e->e_from.q_user)) != NULL)
X					p = pw->pw_dir;
X			}
X			if (p == NULL)
X			{
X				syserr("Can't return mail to %s", e->e_from.q_paddr);
X				state = ESM_MAIL;
X				break;
X			}
X			if (e->e_dfp != NULL)
X			{
X				auto ADDRESS *q;
X				bool oldverb = Verbose;
X
X				/* we have a home directory; open dead.letter */
X				define('z', p, e);
X				expand("\001z/dead.letter", buf, &buf[sizeof buf - 1], e);
X				Verbose = TRUE;
X				message(Arpa_Info, "Saving message in %s", buf);
X				Verbose = oldverb;
X				e->e_to = buf;
X				q = NULL;
X				sendtolist(buf, (ADDRESS *) NULL, &q);
X				if (deliver(e, q) == 0)
X					state = ESM_DONE;
X				else
X					state = ESM_MAIL;
X			}
X			else
X			{
X				/* no data file -- try mailing back */
X				state = ESM_MAIL;
X			}
X			break;
X
X		  case ESM_USRTMP:
X			/*
X			**  Log the mail in /usr/tmp/dead.letter.
X			*/
X
X			fp = dfopen("/usr/tmp/dead.letter", "a");
X			if (fp == NULL)
X			{
X				state = ESM_PANIC;
X				break;
X			}
X
X			putfromline(fp, ProgMailer);
X			(*e->e_puthdr)(fp, ProgMailer, e);
X			putline("\n", fp, ProgMailer);
X			(*e->e_putbody)(fp, ProgMailer, e);
X			putline("\n", fp, ProgMailer);
X			(void) fflush(fp);
X			state = ferror(fp) ? ESM_PANIC : ESM_DONE;
X			(void) fclose(fp);
X			break;
X
X		  default:
X			syserr("savemail: unknown state %d", state);
X
X			/* fall through ... */
X
X		  case ESM_PANIC:
X			syserr("savemail: HELP!!!!");
X# ifdef LOG
X			if (LogLevel >= 1)
X				syslog(LOG_ALERT, "savemail: HELP!!!!");
X# endif LOG
X
X			/* leave the locked queue & transcript files around */
X			exit(EX_SOFTWARE);
X		}
X	}
X}
X/*
X**  RETURNTOSENDER -- return a message to the sender with an error.
X**
X**	Parameters:
X**		msg -- the explanatory message.
X**		returnq -- the queue of people to send the message to.
X**		sendbody -- if TRUE, also send back the body of the
X**			message; otherwise just send the header.
X**
X**	Returns:
X**		zero -- if everything went ok.
X**		else -- some error.
X**
X**	Side Effects:
X**		Returns the current message to the sender via
X**		mail.
X*/
X
Xstatic bool	SendBody;
X
X#define MAXRETURNS	6	/* max depth of returning messages */
X
Xreturntosender(msg, returnq, sendbody)
X	char *msg;
X	ADDRESS *returnq;
X	bool sendbody;
X{
X	char buf[MAXNAME];
X	extern putheader(), errbody();
X	register ENVELOPE *ee;
X	extern ENVELOPE *newenvelope();
X	ENVELOPE errenvelope;
X	static int returndepth;
X	register ADDRESS *q;
X
X# ifdef DEBUG
X	if (tTd(6, 1))
X	{
X		printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n",
X		       msg, returndepth, CurEnv);
X		printf("\treturnq=");
X		printaddr(returnq, TRUE);
X	}
X# endif DEBUG
X
X	if (++returndepth >= MAXRETURNS)
X	{
X		if (returndepth != MAXRETURNS)
X			syserr("returntosender: infinite recursion on %s", returnq->q_paddr);
X		/* don't "unrecurse" and fake a clean exit */
X		/* returndepth--; */
X		return (0);
X	}
X
X	SendBody = sendbody;
X	define('g', "\001f", CurEnv);
X	ee = newenvelope(&errenvelope);
X	define('a', "\001b", ee);
X	ee->e_puthdr = putheader;
X	ee->e_putbody = errbody;
X	ee->e_flags |= EF_RESPONSE;
X	ee->e_sendqueue = returnq;
X	openxscript(ee);
X	for (q = returnq; q != NULL; q = q->q_next)
X	{
X		if (q->q_alias == NULL)
X			addheader("to", q->q_paddr, ee);
X	}
X
X	(void) sprintf(buf, "Returned mail: %s", msg);
X	addheader("subject", buf, ee);
X
X	/* fake up an address header for the from person */
X	expand("\001n", buf, &buf[sizeof buf - 1], CurEnv);
X	if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL)
X	{
X		syserr("Can't parse myself!");
X		ExitStat = EX_SOFTWARE;
X		returndepth--;
X		return (-1);
X	}
X	loweraddr(&ee->e_from);
X
X	/* push state into submessage */
X	CurEnv = ee;
X	define('f', "\001n", ee);
X	define('x', "Mail Delivery Subsystem", ee);
X	eatheader(ee);
X
X	/* actually deliver the error message */
X	sendall(ee, SM_DEFAULT);
X
X	/* restore state */
X	dropenvelope(ee);
X	CurEnv = CurEnv->e_parent;
X	returndepth--;
X
X	/* should check for delivery errors here */
X	return (0);
X}
X/*
X**  ERRBODY -- output the body of an error message.
X**
X**	Typically this is a copy of the transcript plus a copy of the
X**	original offending message.
X**
X**	Parameters:
X**		fp -- the output file.
X**		m -- the mailer to output to.
X**		e -- the envelope we are working in.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		Outputs the body of an error message.
X*/
X
Xerrbody(fp, m, e)
X	register FILE *fp;
X	register struct mailer *m;
X	register ENVELOPE *e;
X{
X	register FILE *xfile;
X	char buf[MAXLINE];
X	char *p;
X
X	/*
X	**  Output transcript of errors
X	*/
X
X	(void) fflush(stdout);
X	p = queuename(e->e_parent, 'x');
X	if ((xfile = fopen(p, "r")) == NULL)
X	{
X		syserr("Cannot open %s", p);
X		fprintf(fp, "  ----- Transcript of session is unavailable -----\n");
X	}
X	else
X	{
X		fprintf(fp, "   ----- Transcript of session follows -----\n");
X		if (e->e_xfp != NULL)
X			(void) fflush(e->e_xfp);
X		while (fgets(buf, sizeof buf, xfile) != NULL)
X			putline(buf, fp, m);
X		(void) fclose(xfile);
X	}
X	errno = 0;
X
X	/*
X	**  Output text of original message
X	*/
X
X	if (NoReturn)
X		fprintf(fp, "\n   ----- Return message suppressed -----\n\n");
X	else if (e->e_parent->e_dfp != NULL)
X	{
X		if (SendBody)
X		{
X			putline("\n", fp, m);
X			putline("   ----- Unsent message follows -----\n", fp, m);
X			(void) fflush(fp);
X			putheader(fp, m, e->e_parent);
X			putline("\n", fp, m);
X			putbody(fp, m, e->e_parent);
X		}
X		else
X		{
X			putline("\n", fp, m);
X			putline("  ----- Message header follows -----\n", fp, m);
X			(void) fflush(fp);
X			putheader(fp, m, e->e_parent);
X		}
X	}
X	else
X	{
X		putline("\n", fp, m);
X		putline("  ----- No message was collected -----\n", fp, m);
X		putline("\n", fp, m);
X	}
X
X	/*
X	**  Cleanup and exit
X	*/
X
X	if (errno != 0)
X		syserr("errbody: I/O error");
X}
END_OF_FILE
if test 11670 -ne `wc -c <'src/savemail.c'`; then
    echo shar: \"'src/savemail.c'\" unpacked with wrong size!
fi
# end of 'src/savemail.c'
fi
echo shar: End of archive 3 \(of 8\).
cp /dev/null ark3isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
David H. Brierley
Home: dave@galaxia.Newport.RI.US   {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb@rayssd.ray.com           {sun,decuac,gatech,necntc,ukma}!rayssd!dhb

dave@galaxia.Newport.RI.US (David H. Brierley) (02/25/89)

----- cut here and feed to /bin/sh -----
#! /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 archive 4 (of 8)."
# Contents:  src/daemon.c src/srvrsmtp.c src/util.c
# Wrapped by dave@galaxia on Fri Feb 24 20:23:57 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/daemon.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/daemon.c'\"
else
echo shar: Extracting \"'src/daemon.c'\" \(13317 characters\)
sed "s/^X//" >'src/daemon.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X
X# include <errno.h>
X# include "sendmail.h"
X
X# ifndef DAEMON
X# ifndef lint
Xstatic char	SccsId[] = "@(#)daemon.c	5.22 (Berkeley) 7/27/87	(w/o daemon mode)";
X# endif not lint
X# else
X
X# include <netdb.h>
X# include "sys/signal.h"
X# include <sys/wait.h>
X# include <sys/time.h>
X# ifdef VMUNIX
X# include <sys/resource.h>
X# endif /* VMUNIX */
X
X#ifdef USG
X#define SIGCHLD	SIGCLD
X#endif
X
X# ifndef lint
Xstatic char	SccsId[] = "@(#)daemon.c	5.22 (Berkeley) 7/27/87 (with daemon mode)";
X# endif not lint
X
X/*
X**  DAEMON.C -- routines to use when running as a daemon.
X**
X**	This entire file is highly dependent on the 4.2 BSD
X**	interprocess communication primitives.  No attempt has
X**	been made to make this file portable to Version 7,
X**	Version 6, MPX files, etc.  If you should try such a
X**	thing yourself, I recommend chucking the entire file
X**	and starting from scratch.  Basic semantics are:
X**
X**	getrequests()
X**		Opens a port and initiates a connection.
X**		Returns in a child.  Must set InChannel and
X**		OutChannel appropriately.
X**	clrdaemon()
X**		Close any open files associated with getting
X**		the connection; this is used when running the queue,
X**		etc., to avoid having extra file descriptors during
X**		the queue run and to avoid confusing the network
X**		code (if it cares).
X**	makeconnection(host, port, outfile, infile)
X**		Make a connection to the named host on the given
X**		port.  Set *outfile and *infile to the files
X**		appropriate for communication.  Returns zero on
X**		success, else an exit status describing the
X**		error.
X**	maphostname(hbuf, hbufsize)
X**		Convert the entry in hbuf into a canonical form.  It
X**		may not be larger than hbufsize.
X*/
X/*
X**  GETREQUESTS -- open mail IPC port and get requests.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Waits until some interesting activity occurs.  When
X**		it does, a child is created to process it, and the
X**		parent waits for completion.  Return from this
X**		routine is always in the child.  The file pointers
X**		"InChannel" and "OutChannel" should be set to point
X**		to the communication channel.
X*/
X
Xstruct sockaddr_in	SendmailAddress;/* internet address of sendmail */
X
Xint	DaemonSocket	= -1;		/* fd describing socket */
Xchar	*NetName;			/* name of home (local?) network */
X
Xgetrequests()
X{
X	int t;
X	register struct servent *sp;
X	int on = 1;
X	extern reapchild();
X
X	/*
X	**  Set up the address for the mailer.
X	*/
X
X	sp = getservbyname("smtp", "tcp");
X	if (sp == NULL)
X	{
X		syserr("server \"smtp\" unknown");
X		goto severe;
X	}
X	SendmailAddress.sin_family = AF_INET;
X	SendmailAddress.sin_addr.s_addr = INADDR_ANY;
X	SendmailAddress.sin_port = sp->s_port;
X
X	/*
X	**  Try to actually open the connection.
X	*/
X
X# ifdef DEBUG
X	if (tTd(15, 1))
X		printf("getrequests: port 0x%x\n", SendmailAddress.sin_port);
X# endif DEBUG
X
X	/* get a socket for the SMTP connection */
X	DaemonSocket = socket(AF_INET, SOCK_STREAM, 0);
X	if (DaemonSocket < 0)
X	{
X		/* probably another daemon already */
X		syserr("getrequests: can't create socket");
X	  severe:
X# ifdef LOG
X		if (LogLevel > 0)
X			syslog(LOG_ALERT, "cannot get connection");
X# endif LOG
X		finis();
X	}
X
X#ifdef DEBUG
X	/* turn on network debugging? */
X	if (tTd(15, 15))
X		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
X#endif DEBUG
X
X	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);
X	(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on);
X
X	if (bind(DaemonSocket, &SendmailAddress, sizeof SendmailAddress) < 0)
X	{
X		syserr("getrequests: cannot bind");
X		(void) close(DaemonSocket);
X		goto severe;
X	}
X	if (listen(DaemonSocket, 10) < 0)
X	{
X		syserr("getrequests: cannot listen");
X		(void) close(DaemonSocket);
X		goto severe;
X	}
X
X	(void) signal(SIGCHLD, reapchild);
X
X# ifdef DEBUG
X	if (tTd(15, 1))
X		printf("getrequests: %d\n", DaemonSocket);
X# endif DEBUG
X
X	for (;;)
X	{
X		register int pid;
X		auto int lotherend;
X		struct sockaddr_in otherend;
X		extern int RefuseLA;
X
X		/* see if we are rejecting connections */
X		while (getla() > RefuseLA)
X			sleep(5);
X
X		/* wait for a connection */
X		do
X		{
X			errno = 0;
X			lotherend = sizeof otherend;
X			t = accept(DaemonSocket, &otherend, &lotherend);
X		} while (t < 0 && errno == EINTR);
X		if (t < 0)
X		{
X			syserr("getrequests: accept");
X			sleep(5);
X			continue;
X		}
X
X		/*
X		**  Create a subprocess to process the mail.
X		*/
X
X# ifdef DEBUG
X		if (tTd(15, 2))
X			printf("getrequests: forking (fd = %d)\n", t);
X# endif DEBUG
X
X		pid = fork();
X		if (pid < 0)
X		{
X			syserr("daemon: cannot fork");
X			sleep(10);
X			(void) close(t);
X			continue;
X		}
X
X		if (pid == 0)
X		{
X			extern struct hostent *gethostbyaddr();
X			register struct hostent *hp;
X			char buf[MAXNAME];
X
X			/*
X			**  CHILD -- return to caller.
X			**	Collect verified idea of sending host.
X			**	Verify calling user id if possible here.
X			*/
X
X			(void) signal(SIGCHLD, SIG_DFL);
X
X			/* determine host name */
X			hp = gethostbyaddr((char *) &otherend.sin_addr, sizeof otherend.sin_addr, AF_INET);
X			if (hp != NULL)
X			{
X				(void) strcpy(buf, hp->h_name);
X				if (NetName != NULL && NetName[0] != '\0' &&
X				    index(hp->h_name, '.') == NULL)
X				{
X					(void) strcat(buf, ".");
X					(void) strcat(buf, NetName);
X				}
X			}
X			else
X			{
X				extern char *inet_ntoa();
X
X				/* produce a dotted quad */
X				(void) sprintf(buf, "[%s]",
X					inet_ntoa(otherend.sin_addr));
X			}
X
X			/* should we check for illegal connection here? XXX */
X
X			RealHostName = newstr(buf);
X
X			(void) close(DaemonSocket);
X			InChannel = fdopen(t, "r");
X			OutChannel = fdopen(dup(t), "w");
X# ifdef DEBUG
X			if (tTd(15, 2))
X				printf("getreq: returning\n");
X# endif DEBUG
X# ifdef LOG
X			if (LogLevel > 11)
X				syslog(LOG_DEBUG, "connected, pid=%d", getpid());
X# endif LOG
X			return;
X		}
X
X		/* close the port so that others will hang (for a while) */
X		(void) close(t);
X	}
X	/*NOTREACHED*/
X}
X/*
X**  CLRDAEMON -- reset the daemon connection
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		releases any resources used by the passive daemon.
X*/
X
Xclrdaemon()
X{
X	if (DaemonSocket >= 0)
X		(void) close(DaemonSocket);
X	DaemonSocket = -1;
X}
X/*
X**  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
X**
X**	Parameters:
X**		host -- the name of the host.
X**		port -- the port number to connect to.
X**		outfile -- a pointer to a place to put the outfile
X**			descriptor.
X**		infile -- ditto for infile.
X**
X**	Returns:
X**		An exit code telling whether the connection could be
X**			made and if not why not.
X**
X**	Side Effects:
X**		none.
X*/
X
Xint	h_errno;	/*this will go away when code implemented*/
X
Xmakeconnection(host, port, outfile, infile)
X	char *host;
X	u_short port;
X	FILE **outfile;
X	FILE **infile;
X{
X	register int i, s;
X	register struct hostent *hp = (struct hostent *)NULL;
X	extern char *inet_ntoa();
X	int sav_errno;
X
X	/*
X	**  Set up the address for the mailer.
X	**	Accept "[a.b.c.d]" syntax for host name.
X	*/
X
X	h_errno = 0;
X	errno = 0;
X
X	if (host[0] == '[')
X	{
X		long hid;
X		register char *p = index(host, ']');
X
X		if (p != NULL)
X		{
X			*p = '\0';
X			hid = inet_addr(&host[1]);
X			*p = ']';
X		}
X		if (p == NULL || hid == -1)
X		{
X			usrerr("Invalid numeric domain spec \"%s\"", host);
X			return (EX_NOHOST);
X		}
X		SendmailAddress.sin_addr.s_addr = hid;
X	}
X	else
X	{
X		hp = gethostbyname(host);
X		if (hp == NULL)
X		{
X#ifdef TRY_AGAIN
X			if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
X#else
X			if (errno == ETIMEDOUT)
X#endif
X				return (EX_TEMPFAIL);
X
X			/*
X			**  XXX Should look for mail forwarder record here
X			**  XXX if (h_errno == NO_ADDRESS).
X			*/
X
X			return (EX_NOHOST);
X		}
X		bcopy(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
X		i = 1;
X	}
X
X	/*
X	**  Determine the port number.
X	*/
X
X	if (port != 0)
X		SendmailAddress.sin_port = htons(port);
X	else
X	{
X		register struct servent *sp = getservbyname("smtp", "tcp");
X
X		if (sp == NULL)
X		{
X			syserr("makeconnection: server \"smtp\" unknown");
X			return (EX_OSFILE);
X		}
X		SendmailAddress.sin_port = sp->s_port;
X	}
X
X	/*
X	**  Try to actually open the connection.
X	*/
X
Xagain:
X# ifdef DEBUG
X	if (tTd(16, 1))
X		printf("makeconnection (%s [%s])\n", host,
X		    inet_ntoa(SendmailAddress.sin_addr.s_addr));
X# endif DEBUG
X
X	s = socket(AF_INET, SOCK_STREAM, 0);
X	if (s < 0)
X	{
X		syserr("makeconnection: no socket");
X		sav_errno = errno;
X		goto failure;
X	}
X
X# ifdef DEBUG
X	if (tTd(16, 1))
X		printf("makeconnection: %d\n", s);
X
X	/* turn on network debugging? */
X	if (tTd(16, 14))
X	{
X		int on = 1;
X		(void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
X	}
X# endif DEBUG
X	(void) fflush(CurEnv->e_xfp);			/* for debugging */
X	errno = 0;					/* for debugging */
X	SendmailAddress.sin_family = AF_INET;
X	if (connect(s, &SendmailAddress, sizeof SendmailAddress) < 0)
X	{
X		sav_errno = errno;
X		(void) close(s);
X#ifdef VMUNIX
X		/*
X		 * the name server returns a list of addresses, but we don't get it
X		 */
X		if (hp && hp->h_addr_list[i])
X		{
X			bcopy(hp->h_addr_list[i++],
X			    (char *)&SendmailAddress.sin_addr, hp->h_length);
X			goto again;
X		}
X#endif
X
X		/* failure, decide if temporary or not */
X	failure:
X		switch (sav_errno)
X		{
X		  case EISCONN:
X		  case ETIMEDOUT:
X		  case EINPROGRESS:
X		  case EALREADY:
X		  case EADDRINUSE:
X		  case EHOSTDOWN:
X		  case ENETDOWN:
X		  case ENETRESET:
X		  case ENOBUFS:
X		  case ECONNREFUSED:
X		  case ECONNRESET:
X		  case EHOSTUNREACH:
X		  case ENETUNREACH:
X			/* there are others, I'm sure..... */
X			return (EX_TEMPFAIL);
X
X		  case EPERM:
X			/* why is this happening? */
X			syserr("makeconnection: funny failure, addr=%lx, port=%x",
X				SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port);
X			return (EX_TEMPFAIL);
X
X		  default:
X			message(Arpa_Info, "%s", errstring(sav_errno));
X			return (EX_UNAVAILABLE);
X		}
X	}
X
X	/* connection ok, put it into canonical form */
X	*outfile = fdopen(s, "w");
X#if defined(u3b2) || defined(u3b20)
X	(void) setvbuf(*outfile, (char *) NULL, _IOLBF, 0);
X#endif
X	*infile = fdopen(s, "r");
X
X	return (EX_OK);
X}
X/*
X**  MYHOSTNAME -- return the name of this host.
X**
X**	Parameters:
X**		hostbuf -- a place to return the name of this host.
X**		size -- the size of hostbuf.
X**
X**	Returns:
X**		A list of aliases for this host.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar **
Xmyhostname(hostbuf, size)
X	char hostbuf[];
X	int size;
X{
X	extern struct hostent *gethostbyname();
X	struct hostent *hp;
X
X	if (gethostname(hostbuf, size) < 0)
X	{
X		(void) strcpy(hostbuf, "localhost");
X	}
X	hp = gethostbyname(hostbuf);
X	if (hp != NULL)
X	{
X		(void) strcpy(hostbuf, hp->h_name);
X		return (hp->h_aliases);
X	}
X	else
X		return (NULL);
X}
X/*
X**  MAPHOSTNAME -- turn a hostname into canonical form
X**
X**	Parameters:
X**		hbuf -- a buffer containing a hostname.
X**		hbsize -- the size of hbuf.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Looks up the host specified in hbuf.  If it is not
X**		the canonical name for that host, replace it with
X**		the canonical name.  If the name is unknown, or it
X**		is already the canonical name, leave it unchanged.
X*/
X
Xmaphostname(hbuf, hbsize)
X	char *hbuf;
X	int hbsize;
X{
X	register struct hostent *hp;
X	extern struct hostent *gethostbyname();
X
X	/*
X	**  If first character is a bracket, then it is an address
X	**  lookup.  Address is copied into a temporary buffer to
X	**  strip the brackets and to preserve hbuf if address is
X	**  unknown.
X	*/
X
X	if (*hbuf == '[')
X	{
X		extern struct hostent *gethostbyaddr();
X		u_long in_addr;
X		char ptr[256];
X		char *bptr;
X
X		(void) strcpy(ptr, hbuf);
X		bptr = index(ptr,']');
X		*bptr = '\0';
X		in_addr = inet_addr(&ptr[1]);
X		hp = gethostbyaddr((char *) &in_addr, sizeof(struct in_addr), AF_INET);
X		if (hp == NULL)
X			return;
X	}
X	else
X	{
X		makelower(hbuf);
X#ifdef MXDOMAIN
X		getcanonname(hbuf, hbsize);
X		return;
X#else MXDOMAIN
X		hp = gethostbyname(hbuf);
X#endif
X	}
X	if (hp != NULL)
X	{
X		int i = strlen(hp->h_name);
X
X		if (i >= hbsize)
X			hp->h_name[hbsize - 1] = '\0';
X		(void) strcpy(hbuf, hp->h_name);
X	}
X}
X
X#endif /* DAEMON */
X#ifndef DAEMON
X/* code for systems without sophisticated networking */
X
X/*
X**  MYHOSTNAME -- stub version for case of no daemon code.
X**
X**	Can't convert to upper case here because might be a UUCP name.
X**
X**	Mark, you can change this to be anything you want......
X*/
X
Xchar **
Xmyhostname(hostbuf, size)
X	char hostbuf[];
X	int size;
X{
X	register FILE *f;
X
X	hostbuf[0] = '\0';
X	f = fopen("/usr/include/whoami", "r");
X	if (f != NULL)
X	{
X		(void) fgets(hostbuf, size, f);
X		fixcrlf(hostbuf, TRUE);
X		(void) fclose(f);
X	}
X	return (NULL);
X}
X/*
X**  MAPHOSTNAME -- turn a hostname into canonical form
X**
X**	Parameters:
X**		hbuf -- a buffer containing a hostname.
X**		hbsize -- the size of hbuf.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Looks up the host specified in hbuf.  If it is not
X**		the canonical name for that host, replace it with
X**		the canonical name.  If the name is unknown, or it
X**		is already the canonical name, leave it unchanged.
X*/
X
X/*ARGSUSED*/
Xmaphostname(hbuf, hbsize)
X	char *hbuf;
X	int hbsize;
X{
X	return;
X}
X
X#endif DAEMON
END_OF_FILE
if test 13317 -ne `wc -c <'src/daemon.c'`; then
    echo shar: \"'src/daemon.c'\" unpacked with wrong size!
fi
# end of 'src/daemon.c'
fi
if test -f 'src/srvrsmtp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/srvrsmtp.c'\"
else
echo shar: Extracting \"'src/srvrsmtp.c'\" \(14241 characters\)
sed "s/^X//" >'src/srvrsmtp.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X
X# include <errno.h>
X# include "sendmail.h"
X# include <signal.h>
X
X# ifndef SMTP
X# ifndef lint
Xstatic char	SccsId[] = "@(#)srvrsmtp.c	5.19 (Berkeley) 2/3/87	(no SMTP)";
X# endif not lint
X# else SMTP
X
X# ifndef lint
Xstatic char	SccsId[] = "@(#)srvrsmtp.c	5.19 (Berkeley) 2/3/87";
X# endif not lint
X
X/*
X**  SMTP -- run the SMTP protocol.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		never.
X**
X**	Side Effects:
X**		Reads commands from the input channel and processes
X**			them.
X*/
X
Xstruct cmd
X{
X	char	*cmdname;	/* command name */
X	int	cmdcode;	/* internal code, see below */
X};
X
X/* values for cmdcode */
X# define CMDERROR	0	/* bad command */
X# define CMDMAIL	1	/* mail -- designate sender */
X# define CMDRCPT	2	/* rcpt -- designate recipient */
X# define CMDDATA	3	/* data -- send message text */
X# define CMDRSET	4	/* rset -- reset state */
X# define CMDVRFY	5	/* vrfy -- verify address */
X# define CMDHELP	6	/* help -- give usage info */
X# define CMDNOOP	7	/* noop -- do nothing */
X# define CMDQUIT	8	/* quit -- close connection and die */
X# define CMDHELO	9	/* helo -- be polite */
X# define CMDDBGQSHOW	10	/* showq -- show send queue (DEBUG) */
X# define CMDDBGDEBUG	11	/* debug -- set debug mode */
X# define CMDVERB	12	/* verb -- go into verbose mode */
X# define CMDDBGKILL	13	/* kill -- kill sendmail */
X# define CMDDBGWIZ	14	/* wiz -- become a wizard */
X# define CMDONEX	15	/* onex -- sending one transaction only */
X
Xstatic struct cmd	CmdTab[] =
X{
X	"mail",		CMDMAIL,
X	"rcpt",		CMDRCPT,
X	"data",		CMDDATA,
X	"rset",		CMDRSET,
X	"vrfy",		CMDVRFY,
X	"expn",		CMDVRFY,
X	"help",		CMDHELP,
X	"noop",		CMDNOOP,
X	"quit",		CMDQUIT,
X	"helo",		CMDHELO,
X	"verb",		CMDVERB,
X	"onex",		CMDONEX,
X# ifdef DEBUG
X	"showq",	CMDDBGQSHOW,
X	"debug",	CMDDBGDEBUG,
X# endif DEBUG
X# ifdef WIZ
X	"kill",		CMDDBGKILL,
X# endif WIZ
X	"wiz",		CMDDBGWIZ,
X	NULL,		CMDERROR,
X};
X
X# ifdef WIZ
Xbool	IsWiz = FALSE;			/* set if we are a wizard */
X# endif WIZ
Xchar	*WizWord;			/* the wizard word to compare against */
Xbool	InChild = FALSE;		/* true if running in a subprocess */
Xbool	OneXact = FALSE;		/* one xaction only this run */
X
X#define EX_QUIT		22		/* special code for QUIT command */
X
Xsmtp()
X{
X	register char *p;
X	register struct cmd *c;
X	char *cmd;
X	extern char *skipword();
X	extern bool sameword();
X	bool hasmail;			/* mail command received */
X	auto ADDRESS *vrfyqueue;
X	ADDRESS *a;
X	char *sendinghost;
X	char inp[MAXLINE];
X	char cmdbuf[100];
X	extern char Version[];
X	extern tick();
X	extern bool iswiz();
X	extern char *arpadate();
X	extern char *macvalue();
X	extern ADDRESS *recipient();
X	extern ENVELOPE BlankEnvelope;
X	extern ENVELOPE *newenvelope();
X
X	hasmail = FALSE;
X	if (OutChannel != stdout)
X	{
X		/* arrange for debugging output to go to remote host */
X		(void) close(1);
X		(void) dup(fileno(OutChannel));
X	}
X	settime();
X	if (RealHostName != NULL)
X	{
X		CurHostName = RealHostName;
X		setproctitle("srvrsmtp %s", CurHostName);
X	}
X	else
X	{
X		/* this must be us!! */
X		CurHostName = MyHostName;
X	}
X	expand("\001e", inp, &inp[sizeof inp], CurEnv);
X	message("220", inp);
X	SmtpPhase = "startup";
X	sendinghost = NULL;
X	for (;;)
X	{
X		/* arrange for backout */
X		if (setjmp(TopFrame) > 0 && InChild)
X			finis();
X		QuickAbort = FALSE;
X		HoldErrs = FALSE;
X
X		/* setup for the read */
X		CurEnv->e_to = NULL;
X		Errors = 0;
X		(void) fflush(stdout);
X
X		/* read the input line */
X		p = sfgets(inp, sizeof inp, InChannel);
X
X		/* handle errors */
X		if (p == NULL)
X		{
X			/* end of file, just die */
X			message("421", "%s Lost input channel to %s",
X				MyHostName, CurHostName);
X			finis();
X		}
X
X		/* clean up end of line */
X		fixcrlf(inp, TRUE);
X
X		/* echo command to transcript */
X		if (CurEnv->e_xfp != NULL)
X			fprintf(CurEnv->e_xfp, "<<< %s\n", inp);
X
X		/* break off command */
X		for (p = inp; isspace(*p); p++)
X			continue;
X		cmd = p;
X		for (cmd = cmdbuf; *p != '\0' && !isspace(*p); )
X			*cmd++ = *p++;
X		*cmd = '\0';
X
X		/* throw away leading whitespace */
X		while (isspace(*p))
X			p++;
X
X		/* decode command */
X		for (c = CmdTab; c->cmdname != NULL; c++)
X		{
X			if (sameword(c->cmdname, cmdbuf))
X				break;
X		}
X
X		/* process command */
X		switch (c->cmdcode)
X		{
X		  case CMDHELO:		/* hello -- introduce yourself */
X			SmtpPhase = "HELO";
X			setproctitle("%s: %s", CurHostName, inp);
X			if (sameword(p, MyHostName))
X			{
X				/* connected to an echo server */
X				message("553", "%s I refuse to talk to myself",
X					MyHostName);
X				break;
X			}
X			if (RealHostName != NULL && !sameword(p, RealHostName))
X			{
X				char hostbuf[MAXNAME];
X
X				(void) sprintf(hostbuf, "%s (%s)", p, RealHostName);
X				sendinghost = newstr(hostbuf);
X			}
X			else
X				sendinghost = newstr(p);
X			message("250", "%s Hello %s, pleased to meet you",
X				MyHostName, p);
X			break;
X
X		  case CMDMAIL:		/* mail -- designate sender */
X			SmtpPhase = "MAIL";
X
X			/* force a sending host even if no HELO given */
X			if (RealHostName != NULL && macvalue('s', CurEnv) == NULL)
X				sendinghost = RealHostName;
X
X			/* check for validity of this command */
X			if (hasmail)
X			{
X				message("503", "Sender already specified");
X				break;
X			}
X			if (InChild)
X			{
X				syserr("Nested MAIL command");
X				exit(0);
X			}
X
X			/* fork a subprocess to process this command */
X			if (runinchild("SMTP-MAIL") > 0)
X				break;
X			define('s', sendinghost, CurEnv);
X			initsys();
X			setproctitle("%s %s: %s", CurEnv->e_id,
X				CurHostName, inp);
X
X			/* child -- go do the processing */
X			p = skipword(p, "from");
X			if (p == NULL)
X				break;
X			setsender(p);
X			if (Errors == 0)
X			{
X				message("250", "Sender ok");
X				hasmail = TRUE;
X			}
X			else if (InChild)
X				finis();
X			break;
X
X		  case CMDRCPT:		/* rcpt -- designate recipient */
X			SmtpPhase = "RCPT";
X			setproctitle("%s %s: %s", CurEnv->e_id,
X				CurHostName, inp);
X			if (setjmp(TopFrame) > 0)
X			{
X				CurEnv->e_flags &= ~EF_FATALERRS;
X				break;
X			}
X			QuickAbort = TRUE;
X			p = skipword(p, "to");
X			if (p == NULL)
X				break;
X			a = parseaddr(p, (ADDRESS *) NULL, 1, '\0');
X			if (a == NULL)
X				break;
X			a->q_flags |= QPRIMARY;
X			a = recipient(a, &CurEnv->e_sendqueue);
X			if (Errors != 0)
X				break;
X
X			/* no errors during parsing, but might be a duplicate */
X			CurEnv->e_to = p;
X			if (!bitset(QBADADDR, a->q_flags))
X				message("250", "Recipient ok");
X			else
X			{
X				/* punt -- should keep message in ADDRESS.... */
X				message("550", "Addressee unknown");
X			}
X			CurEnv->e_to = NULL;
X			break;
X
X		  case CMDDATA:		/* data -- text of mail */
X			SmtpPhase = "DATA";
X			if (!hasmail)
X			{
X				message("503", "Need MAIL command");
X				break;
X			}
X			else if (CurEnv->e_nrcpts <= 0)
X			{
X				message("503", "Need RCPT (recipient)");
X				break;
X			}
X
X			/* collect the text of the message */
X			SmtpPhase = "collect";
X			setproctitle("%s %s: %s", CurEnv->e_id,
X				CurHostName, inp);
X			collect(TRUE);
X			if (Errors != 0)
X				break;
X
X			/*
X			**  Arrange to send to everyone.
X			**	If sending to multiple people, mail back
X			**		errors rather than reporting directly.
X			**	In any case, don't mail back errors for
X			**		anything that has happened up to
X			**		now (the other end will do this).
X			**	Truncate our transcript -- the mail has gotten
X			**		to us successfully, and if we have
X			**		to mail this back, it will be easier
X			**		on the reader.
X			**	Then send to everyone.
X			**	Finally give a reply code.  If an error has
X			**		already been given, don't mail a
X			**		message back.
X			**	We goose error returns by clearing error bit.
X			*/
X
X			SmtpPhase = "delivery";
X			if (CurEnv->e_nrcpts != 1)
X			{
X				HoldErrs = TRUE;
X				ErrorMode = EM_MAIL;
X			}
X			CurEnv->e_flags &= ~EF_FATALERRS;
X			CurEnv->e_xfp = freopen(queuename(CurEnv, 'x'), "w", CurEnv->e_xfp);
X
X			/* send to all recipients */
X			sendall(CurEnv, SM_DEFAULT);
X			CurEnv->e_to = NULL;
X
X			/* save statistics */
X			markstats(CurEnv, (ADDRESS *) NULL);
X
X			/* issue success if appropriate and reset */
X			if (Errors == 0 || HoldErrs)
X				message("250", "Ok");
X			else
X				CurEnv->e_flags &= ~EF_FATALERRS;
X
X			/* if in a child, pop back to our parent */
X			if (InChild)
X				finis();
X
X			/* clean up a bit */
X			hasmail = 0;
X			dropenvelope(CurEnv);
X			CurEnv = newenvelope(CurEnv);
X			CurEnv->e_flags = BlankEnvelope.e_flags;
X			break;
X
X		  case CMDRSET:		/* rset -- reset state */
X			message("250", "Reset state");
X			if (InChild)
X				finis();
X			break;
X
X		  case CMDVRFY:		/* vrfy -- verify address */
X			if (runinchild("SMTP-VRFY") > 0)
X				break;
X			setproctitle("%s: %s", CurHostName, inp);
X			vrfyqueue = NULL;
X			QuickAbort = TRUE;
X			sendtolist(p, (ADDRESS *) NULL, &vrfyqueue);
X			if (Errors != 0)
X			{
X				if (InChild)
X					finis();
X				break;
X			}
X			while (vrfyqueue != NULL)
X			{
X				register ADDRESS *a = vrfyqueue->q_next;
X				char *code;
X
X				while (a != NULL && bitset(QDONTSEND|QBADADDR, a->q_flags))
X					a = a->q_next;
X
X				if (!bitset(QDONTSEND|QBADADDR, vrfyqueue->q_flags))
X				{
X					if (a != NULL)
X						code = "250-";
X					else
X						code = "250";
X					if (vrfyqueue->q_fullname == NULL)
X						message(code, "<%s>", vrfyqueue->q_paddr);
X					else
X						message(code, "%s <%s>",
X						    vrfyqueue->q_fullname, vrfyqueue->q_paddr);
X				}
X				else if (a == NULL)
X					message("554", "Self destructive alias loop");
X				vrfyqueue = a;
X			}
X			if (InChild)
X				finis();
X			break;
X
X		  case CMDHELP:		/* help -- give user info */
X			if (*p == '\0')
X				p = "SMTP";
X			help(p);
X			break;
X
X		  case CMDNOOP:		/* noop -- do nothing */
X			message("200", "OK");
X			break;
X
X		  case CMDQUIT:		/* quit -- leave mail */
X			message("221", "%s closing connection", MyHostName);
X			if (InChild)
X				ExitStat = EX_QUIT;
X			finis();
X
X		  case CMDVERB:		/* set verbose mode */
X			Verbose = TRUE;
X			SendMode = SM_DELIVER;
X			message("200", "Verbose mode");
X			break;
X
X		  case CMDONEX:		/* doing one transaction only */
X			OneXact = TRUE;
X			message("200", "Only one transaction");
X			break;
X
X# ifdef DEBUG
X		  case CMDDBGQSHOW:	/* show queues */
X			printf("Send Queue=");
X			printaddr(CurEnv->e_sendqueue, TRUE);
X			break;
X
X		  case CMDDBGDEBUG:	/* set debug mode */
X			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
X			tTflag(p);
X			message("200", "Debug set");
X			break;
X# endif DEBUG
X
X# ifdef WIZ
X		  case CMDDBGKILL:	/* kill the parent */
X			if (!iswiz())
X				break;
X			if (kill(MotherPid, SIGTERM) >= 0)
X				message("200", "Mother is dead");
X			else
X				message("500", "Can't kill Mom");
X			break;
X
X		  case CMDDBGWIZ:	/* become a wizard */
X			if (WizWord != NULL)
X			{
X				char seed[3];
X				extern char *crypt();
X
X				(void) strncpy(seed, WizWord, 2);
X				if (strcmp(WizWord, crypt(p, seed)) == 0)
X				{
X					IsWiz = TRUE;
X					message("200", "Please pass, oh mighty wizard");
X					break;
X				}
X			}
X			message("500", "You are no wizard!");
X			break;
X
X# else WIZ
X		  case CMDDBGWIZ:	/* try to become a wizard */
X			message("500", "You wascal wabbit!  Wandering wizards won't win!");
X			break;
X# endif WIZ
X
X		  case CMDERROR:	/* unknown command */
X			message("500", "Command unrecognized");
X			break;
X
X		  default:
X			syserr("smtp: unknown code %d", c->cmdcode);
X			break;
X		}
X	}
X}
X/*
X**  SKIPWORD -- skip a fixed word.
X**
X**	Parameters:
X**		p -- place to start looking.
X**		w -- word to skip.
X**
X**	Returns:
X**		p following w.
X**		NULL on error.
X**
X**	Side Effects:
X**		clobbers the p data area.
X*/
X
Xstatic char *
Xskipword(p, w)
X	register char *p;
X	char *w;
X{
X	register char *q;
X	extern bool sameword();
X
X	/* find beginning of word */
X	while (isspace(*p))
X		p++;
X	q = p;
X
X	/* find end of word */
X	while (*p != '\0' && *p != ':' && !isspace(*p))
X		p++;
X	while (isspace(*p))
X		*p++ = '\0';
X	if (*p != ':')
X	{
X	  syntax:
X		message("501", "Syntax error");
X		Errors++;
X		return (NULL);
X	}
X	*p++ = '\0';
X	while (isspace(*p))
X		p++;
X
X	/* see if the input word matches desired word */
X	if (!sameword(q, w))
X		goto syntax;
X
X	return (p);
X}
X/*
X**  HELP -- implement the HELP command.
X**
X**	Parameters:
X**		topic -- the topic we want help for.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		outputs the help file to message output.
X*/
X
Xhelp(topic)
X	char *topic;
X{
X	register FILE *hf;
X	int len;
X	char buf[MAXLINE];
X	bool noinfo;
X
X	if (HelpFile == NULL || (hf = fopen(HelpFile, "r")) == NULL)
X	{
X		/* no help */
X		errno = 0;
X		message("502", "HELP not implemented");
X		return;
X	}
X
X	len = strlen(topic);
X	makelower(topic);
X	noinfo = TRUE;
X
X	while (fgets(buf, sizeof buf, hf) != NULL)
X	{
X		if (strncmp(buf, topic, len) == 0)
X		{
X			register char *p;
X
X			p = index(buf, '\t');
X			if (p == NULL)
X				p = buf;
X			else
X				p++;
X			fixcrlf(p, TRUE);
X			message("214-", p);
X			noinfo = FALSE;
X		}
X	}
X
X	if (noinfo)
X		message("504", "HELP topic unknown");
X	else
X		message("214", "End of HELP info");
X	(void) fclose(hf);
X}
X/*
X**  ISWIZ -- tell us if we are a wizard
X**
X**	If not, print a nasty message.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		TRUE if we are a wizard.
X**		FALSE if we are not a wizard.
X**
X**	Side Effects:
X**		Prints a 500 exit stat if we are not a wizard.
X*/
X
X#ifdef WIZ
X
Xbool
Xiswiz()
X{
X	if (!IsWiz)
X		message("500", "Mere mortals musn't mutter that mantra");
X	return (IsWiz);
X}
X
X#endif WIZ
X/*
X**  RUNINCHILD -- return twice -- once in the child, then in the parent again
X**
X**	Parameters:
X**		label -- a string used in error messages
X**
X**	Returns:
X**		zero in the child
X**		one in the parent
X**
X**	Side Effects:
X**		none.
X*/
X
Xruninchild(label)
X	char *label;
X{
X	int childpid;
X
X	if (!OneXact)
X	{
X		childpid = dofork();
X		if (childpid < 0)
X		{
X			syserr("%s: cannot fork", label);
X			return (1);
X		}
X		if (childpid > 0)
X		{
X			auto int st;
X
X			/* parent -- wait for child to complete */
X			st = waitfor(childpid);
X			if (st == -1)
X				syserr("%s: lost child", label);
X
X			/* if we exited on a QUIT command, complete the process */
X			if (st == (EX_QUIT << 8))
X				finis();
X
X			return (1);
X		}
X		else
X		{
X			/* child */
X			InChild = TRUE;
X			QuickAbort = FALSE;
X			clearenvelope(CurEnv, FALSE);
X		}
X	}
X
X	/* open alias database */
X	initaliases(AliasFile, FALSE);
X
X	return (0);
X}
X
X# endif SMTP
END_OF_FILE
if test 14241 -ne `wc -c <'src/srvrsmtp.c'`; then
    echo shar: \"'src/srvrsmtp.c'\" unpacked with wrong size!
fi
# end of 'src/srvrsmtp.c'
fi
if test -f 'src/util.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/util.c'\"
else
echo shar: Extracting \"'src/util.c'\" \(13955 characters\)
sed "s/^X//" >'src/util.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)util.c	5.9 (Berkeley) 12/17/86";
X#endif not lint
X
X# include "sendmail.h"
X# include <stdio.h>
X# include <sys/stat.h>
X# include <sysexits.h>
X# include <errno.h>
X
X/*
X**  STRIPQUOTES -- Strip quotes & quote bits from a string.
X**
X**	Runs through a string and strips off unquoted quote
X**	characters and quote bits.  This is done in place.
X**
X**	Parameters:
X**		s -- the string to strip.
X**		qf -- if set, remove actual `` " '' characters
X**			as well as the quote bits.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X**
X**	Called By:
X**		deliver
X*/
X
Xstripquotes(s, qf)
X	char *s;
X	bool qf;
X{
X	register char *p;
X	register char *q;
X	register char c;
X
X	if (s == NULL)
X		return;
X
X	for (p = q = s; (c = *p++) != '\0'; )
X	{
X		if (c != '"' || !qf)
X			*q++ = c & 0177;
X	}
X	*q = '\0';
X}
X/*
X**  QSTRLEN -- give me the string length assuming 0200 bits add a char
X**
X**	Parameters:
X**		s -- the string to measure.
X**
X**	Reurns:
X**		The length of s, including space for backslash escapes.
X**
X**	Side Effects:
X**		none.
X*/
X
Xqstrlen(s)
X	register char *s;
X{
X	register int l = 0;
X	register char c;
X
X	while ((c = *s++) != '\0')
X	{
X		if (bitset(0200, c))
X			l++;
X		l++;
X	}
X	return (l);
X}
X/*
X**  CAPITALIZE -- return a copy of a string, properly capitalized.
X**
X**	Parameters:
X**		s -- the string to capitalize.
X**
X**	Returns:
X**		a pointer to a properly capitalized string.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar *
Xcapitalize(s)
X	register char *s;
X{
X	static char buf[50];
X	register char *p;
X
X	p = buf;
X
X	for (;;)
X	{
X		while (!isalpha(*s) && *s != '\0')
X			*p++ = *s++;
X		if (*s == '\0')
X			break;
X		*p++ = toupper(*s++);
X		while (isalpha(*s))
X			*p++ = *s++;
X	}
X
X	*p = '\0';
X	return (buf);
X}
X/*
X**  XALLOC -- Allocate memory and bitch wildly on failure.
X**
X**	THIS IS A CLUDGE.  This should be made to give a proper
X**	error -- but after all, what can we do?
X**
X**	Parameters:
X**		sz -- size of area to allocate.
X**
X**	Returns:
X**		pointer to data region.
X**
X**	Side Effects:
X**		Memory is allocated.
X*/
X
Xchar *
Xxalloc(sz)
X	register int sz;
X{
X	register char *p;
X	extern char *malloc();
X
X	p = malloc((unsigned) sz);
X	if (p == NULL)
X	{
X		syserr("Out of memory!!");
X		abort();
X		/* exit(EX_UNAVAILABLE); */
X	}
X	return (p);
X}
X/*
X**  COPYPLIST -- copy list of pointers.
X**
X**	This routine is the equivalent of newstr for lists of
X**	pointers.
X**
X**	Parameters:
X**		list -- list of pointers to copy.
X**			Must be NULL terminated.
X**		copycont -- if TRUE, copy the contents of the vector
X**			(which must be a string) also.
X**
X**	Returns:
X**		a copy of 'list'.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar **
Xcopyplist(list, copycont)
X	char **list;
X	bool copycont;
X{
X	register char **vp;
X	register char **newvp;
X
X	for (vp = list; *vp != NULL; vp++)
X		continue;
X
X	vp++;
X
X	newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
X	bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
X
X	if (copycont)
X	{
X		for (vp = newvp; *vp != NULL; vp++)
X			*vp = newstr(*vp);
X	}
X
X	return (newvp);
X}
X/*
X**  PRINTAV -- print argument vector.
X**
X**	Parameters:
X**		av -- argument vector.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		prints av.
X*/
X
Xprintav(av)
X	register char **av;
X{
X	while (*av != NULL)
X	{
X		if (tTd(0, 44))
X			printf("\n\t%08x=", *av);
X		else
X			(void) putchar(' ');
X		xputs(*av++);
X	}
X	(void) putchar('\n');
X}
X/*
X**  LOWER -- turn letter into lower case.
X**
X**	Parameters:
X**		c -- character to turn into lower case.
X**
X**	Returns:
X**		c, in lower case.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar
Xlower(c)
X	register char c;
X{
X	if (isascii(c) && isupper(c))
X		c = c - 'A' + 'a';
X	return (c);
X}
X/*
X**  XPUTS -- put string doing control escapes.
X**
X**	Parameters:
X**		s -- string to put.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		output to stdout
X*/
X
Xxputs(s)
X	register char *s;
X{
X	register char c;
X
X	if (s == NULL)
X	{
X		printf("<null>");
X		return;
X	}
X	(void) putchar('"');
X	while ((c = *s++) != '\0')
X	{
X		if (!isascii(c))
X		{
X			(void) putchar('\\');
X			c &= 0177;
X		}
X		if (c < 040 || c >= 0177)
X		{
X			(void) putchar('^');
X			c ^= 0100;
X		}
X		(void) putchar(c);
X	}
X	(void) putchar('"');
X	(void) fflush(stdout);
X}
X/*
X**  MAKELOWER -- Translate a line into lower case
X**
X**	Parameters:
X**		p -- the string to translate.  If NULL, return is
X**			immediate.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		String pointed to by p is translated to lower case.
X**
X**	Called By:
X**		parse
X*/
X
Xmakelower(p)
X	register char *p;
X{
X	register char c;
X
X	if (p == NULL)
X		return;
X	for (; (c = *p) != '\0'; p++)
X		if (isascii(c) && isupper(c))
X			*p = c - 'A' + 'a';
X}
X/*
X**  SAMEWORD -- return TRUE if the words are the same
X**
X**	Ignores case.
X**
X**	Parameters:
X**		a, b -- the words to compare.
X**
X**	Returns:
X**		TRUE if a & b match exactly (modulo case)
X**		FALSE otherwise.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xsameword(a, b)
X	register char *a, *b;
X{
X	char ca, cb;
X
X	do
X	{
X		ca = *a++;
X		cb = *b++;
X		if (isascii(ca) && isupper(ca))
X			ca = ca - 'A' + 'a';
X		if (isascii(cb) && isupper(cb))
X			cb = cb - 'A' + 'a';
X	} while (ca != '\0' && ca == cb);
X	return (ca == cb);
X}
X/*
X**  BUILDFNAME -- build full name from gecos style entry.
X**
X**	This routine interprets the strange entry that would appear
X**	in the GECOS field of the password file.
X**
X**	Parameters:
X**		p -- name to build.
X**		login -- the login name of this user (for &).
X**		buf -- place to put the result.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbuildfname(p, login, buf)
X	register char *p;
X	char *login;
X	char *buf;
X{
X	register char *bp = buf;
X
X	if (*p == '*')
X		p++;
X	while (*p != '\0' && *p != ',' && *p != ';' && *p != '%')
X	{
X		if (*p == '&')
X		{
X			(void) strcpy(bp, login);
X			*bp = toupper(*bp);
X			while (*bp != '\0')
X				bp++;
X			p++;
X		}
X		else
X			*bp++ = *p++;
X	}
X	*bp = '\0';
X}
X/*
X**  SAFEFILE -- return true if a file exists and is safe for a user.
X**
X**	Parameters:
X**		fn -- filename to check.
X**		uid -- uid to compare against.
X**		mode -- mode bits that must match.
X**
X**	Returns:
X**		TRUE if fn exists, is owned by uid, and matches mode.
X**		FALSE otherwise.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xsafefile(fn, uid, mode)
X	char *fn;
X	int uid;
X	int mode;
X{
X	struct stat stbuf;
X
X	if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid &&
X	    (stbuf.st_mode & mode) == mode)
X		return (TRUE);
X	errno = 0;
X	return (FALSE);
X}
X/*
X**  FIXCRLF -- fix <CR><LF> in line.
X**
X**	Looks for the <CR><LF> combination and turns it into the
X**	UNIX canonical <NL> character.  It only takes one line,
X**	i.e., it is assumed that the first <NL> found is the end
X**	of the line.
X**
X**	Parameters:
X**		line -- the line to fix.
X**		stripnl -- if true, strip the newline also.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		line is changed in place.
X*/
X
Xfixcrlf(line, stripnl)
X	char *line;
X	bool stripnl;
X{
X	register char *p;
X
X	p = index(line, '\n');
X	if (p == NULL)
X		return;
X	if (p[-1] == '\r')
X		p--;
X	if (!stripnl)
X		*p++ = '\n';
X	*p = '\0';
X}
X/*
X**  DFOPEN -- determined file open
X**
X**	This routine has the semantics of fopen, except that it will
X**	keep trying a few times to make this happen.  The idea is that
X**	on very loaded systems, we may run out of resources (inodes,
X**	whatever), so this tries to get around it.
X*/
X
XFILE *
Xdfopen(filename, mode)
X	char *filename;
X	char *mode;
X{
X	register int tries;
X	register FILE *fp;
X
X	for (tries = 0; tries < 10; tries++)
X	{
X		sleep((unsigned) (10 * tries));
X		errno = 0;
X		fp = fopen(filename, mode);
X		if (fp != NULL)
X			break;
X		if (errno != ENFILE && errno != EINTR)
X			break;
X	}
X	errno = 0;
X	return (fp);
X}
X/*
X**  PUTLINE -- put a line like fputs obeying SMTP conventions
X**
X**	This routine always guarantees outputing a newline (or CRLF,
X**	as appropriate) at the end of the string.
X**
X**	Parameters:
X**		l -- line to put.
X**		fp -- file to put it onto.
X**		m -- the mailer used to control output.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		output of l to fp.
X*/
X
X# define SMTPLINELIM	990	/* maximum line length */
X
Xputline(l, fp, m)
X	register char *l;
X	FILE *fp;
X	MAILER *m;
X{
X	register char *p;
X	char svchar;
X
X	/* strip out 0200 bits -- these can look like TELNET protocol */
X	if (bitnset(M_LIMITS, m->m_flags))
X	{
X		p = l;
X		while ((*p++ &= ~0200) != 0)
X			continue;
X	}
X
X	do
X	{
X		/* find the end of the line */
X		p = index(l, '\n');
X		if (p == NULL)
X			p = &l[strlen(l)];
X
X		/* check for line overflow */
X		while ((p - l) > SMTPLINELIM && bitnset(M_LIMITS, m->m_flags))
X		{
X			register char *q = &l[SMTPLINELIM - 1];
X
X			svchar = *q;
X			*q = '\0';
X			if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
X				(void) putc('.', fp);
X			fputs(l, fp);
X			(void) putc('!', fp);
X			fputs(m->m_eol, fp);
X			*q = svchar;
X			l = q;
X		}
X
X		/* output last part */
X		svchar = *p;
X		*p = '\0';
X		if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
X			(void) putc('.', fp);
X		fputs(l, fp);
X		fputs(m->m_eol, fp);
X		*p = svchar;
X		l = p;
X		if (*l == '\n')
X			l++;
X	} while (l[0] != '\0');
X}
X/*
X**  XUNLINK -- unlink a file, doing logging as appropriate.
X**
X**	Parameters:
X**		f -- name of file to unlink.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		f is unlinked.
X*/
X
Xxunlink(f)
X	char *f;
X{
X	register int i;
X
X# ifdef LOG
X	if (LogLevel > 20)
X		syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f);
X# endif LOG
X
X	i = unlink(f);
X# ifdef LOG
X	if (i < 0 && LogLevel > 21)
X		syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
X# endif LOG
X}
X/*
X**  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
X**
X**	Parameters:
X**		buf -- place to put the input line.
X**		siz -- size of buf.
X**		fp -- file to read from.
X**
X**	Returns:
X**		NULL on error (including timeout).  This will also leave
X**			buf containing a null string.
X**		buf otherwise.
X**
X**	Side Effects:
X**		none.
X*/
X
Xstatic jmp_buf	CtxReadTimeout;
X
X#ifndef ETIMEDOUT
X#define ETIMEDOUT	EINTR
X#endif
X
Xchar *
Xsfgets(buf, siz, fp)
X	char *buf;
X	int siz;
X	FILE *fp;
X{
X	register EVENT *ev = NULL;
X	register char *p;
X	extern readtimeout();
X
X	/* set the timeout */
X	if (ReadTimeout != 0)
X	{
X		if (setjmp(CtxReadTimeout) != 0)
X		{
X			errno = ETIMEDOUT;
X			syserr("net timeout");
X			buf[0] = '\0';
X			return (NULL);
X		}
X		ev = setevent((time_t) ReadTimeout, readtimeout, 0);
X	}
X
X	/* try to read */
X	p = NULL;
X	while (p == NULL && !feof(fp) && !ferror(fp))
X	{
X		errno = 0;
X		p = fgets(buf, siz, fp);
X		if (errno == EINTR)
X			clearerr(fp);
X	}
X
X	/* clear the event if it has not sprung */
X	clrevent(ev);
X
X	/* clean up the books and exit */
X	LineNumber++;
X	if (p == NULL)
X	{
X		buf[0] = '\0';
X		return (NULL);
X	}
X	for (p = buf; *p != '\0'; p++)
X		*p &= ~0200;
X	return (buf);
X}
X
Xstatic
Xreadtimeout()
X{
X	longjmp(CtxReadTimeout, 1);
X}
X/*
X**  FGETFOLDED -- like fgets, but know about folded lines.
X**
X**	Parameters:
X**		buf -- place to put result.
X**		n -- bytes available.
X**		f -- file to read from.
X**
X**	Returns:
X**		buf on success, NULL on error or EOF.
X**
X**	Side Effects:
X**		buf gets lines from f, with continuation lines (lines
X**		with leading white space) appended.  CRLF's are mapped
X**		into single newlines.  Any trailing NL is stripped.
X*/
X
Xchar *
Xfgetfolded(buf, n, f)
X	char *buf;
X	register int n;
X	FILE *f;
X{
X	register char *p = buf;
X	register int i;
X
X	n--;
X	while ((i = getc(f)) != EOF)
X	{
X		if (i == '\r')
X		{
X			i = getc(f);
X			if (i != '\n')
X			{
X				if (i != EOF)
X					(void) ungetc(i, f);
X				i = '\r';
X			}
X		}
X		if (--n > 0)
X			*p++ = i;
X		if (i == '\n')
X		{
X			LineNumber++;
X			i = getc(f);
X			if (i != EOF)
X				(void) ungetc(i, f);
X			if (i != ' ' && i != '\t')
X			{
X				*--p = '\0';
X				return (buf);
X			}
X		}
X	}
X	return (NULL);
X}
X/*
X**  CURTIME -- return current time.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		the current time.
X**
X**	Side Effects:
X**		none.
X*/
X
Xtime_t
Xcurtime()
X{
X	auto time_t t;
X
X	(void) time(&t);
X	return (t);
X}
X/*
X**  ATOBOOL -- convert a string representation to boolean.
X**
X**	Defaults to "TRUE"
X**
X**	Parameters:
X**		s -- string to convert.  Takes "tTyY" as true,
X**			others as false.
X**
X**	Returns:
X**		A boolean representation of the string.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xatobool(s)
X	register char *s;
X{
X	if (*s == '\0' || index("tTyY", *s) != NULL)
X		return (TRUE);
X	return (FALSE);
X}
X/*
X**  ATOOCT -- convert a string representation to octal.
X**
X**	Parameters:
X**		s -- string to convert.
X**
X**	Returns:
X**		An integer representing the string interpreted as an
X**		octal number.
X**
X**	Side Effects:
X**		none.
X*/
X
Xatooct(s)
X	register char *s;
X{
X	register int i = 0;
X
X	while (*s >= '0' && *s <= '7')
X		i = (i << 3) | (*s++ - '0');
X	return (i);
X}
X/*
X**  WAITFOR -- wait for a particular process id.
X**
X**	Parameters:
X**		pid -- process id to wait for.
X**
X**	Returns:
X**		status of pid.
X**		-1 if pid never shows up.
X**
X**	Side Effects:
X**		none.
X*/
X
Xwaitfor(pid)
X	int pid;
X{
X	auto int st;
X	int i;
X
X	do
X	{
X		errno = 0;
X		i = wait(&st);
X	} while ((i >= 0 || errno == EINTR) && i != pid);
X	if (i < 0)
X		st = -1;
X	return (st);
X}
X/*
X**  BITINTERSECT -- tell if two bitmaps intersect
X**
X**	Parameters:
X**		a, b -- the bitmaps in question
X**
X**	Returns:
X**		TRUE if they have a non-null intersection
X**		FALSE otherwise
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xbitintersect(a, b)
X	BITMAP a;
X	BITMAP b;
X{
X	int i;
X
X	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
X		if ((a[i] & b[i]) != 0)
X			return (TRUE);
X	return (FALSE);
X}
X/*
X**  BITZEROP -- tell if a bitmap is all zero
X**
X**	Parameters:
X**		map -- the bit map to check
X**
X**	Returns:
X**		TRUE if map is all zero.
X**		FALSE if there are any bits set in map.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xbitzerop(map)
X	BITMAP map;
X{
X	int i;
X
X	for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
X		if (map[i] != 0)
X			return (FALSE);
X	return (TRUE);
X}
END_OF_FILE
if test 13955 -ne `wc -c <'src/util.c'`; then
    echo shar: \"'src/util.c'\" unpacked with wrong size!
fi
# end of 'src/util.c'
fi
echo shar: End of archive 4 \(of 8\).
cp /dev/null ark4isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
David H. Brierley
Home: dave@galaxia.Newport.RI.US   {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb@rayssd.ray.com           {sun,decuac,gatech,necntc,ukma}!rayssd!dhb

dave@galaxia.Newport.RI.US (David H. Brierley) (02/25/89)

----- cut here and feed to /bin/sh -----
#! /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 archive 5 (of 8)."
# Contents:  src/headers.c src/readcf.c
# Wrapped by dave@galaxia on Fri Feb 24 20:24:05 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/headers.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/headers.c'\"
else
echo shar: Extracting \"'src/headers.c'\" \(16858 characters\)
sed "s/^X//" >'src/headers.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)headers.c	5.7 (Berkeley) 9/21/85";
X#endif not lint
X
X# include <errno.h>
X# include "sendmail.h"
X
X/*
X**  CHOMPHEADER -- process and save a header line.
X**
X**	Called by collect and by readcf to deal with header lines.
X**
X**	Parameters:
X**		line -- header as a text line.
X**		def -- if set, this is a default value.
X**
X**	Returns:
X**		flags for this header.
X**
X**	Side Effects:
X**		The header is saved on the header list.
X**		Contents of 'line' are destroyed.
X*/
X
Xchompheader(line, def)
X	char *line;
X	bool def;
X{
X	register char *p;
X	register HDR *h;
X	HDR **hp;
X	char *fname;
X	char *fvalue;
X	struct hdrinfo *hi;
X	bool cond = FALSE;
X	BITMAP mopts;
X	extern char *crackaddr();
X
X# ifdef DEBUG
X	if (tTd(31, 6))
X		printf("chompheader: %s\n", line);
X# endif DEBUG
X
X	/* strip off options */
X	clrbitmap(mopts);
X	p = line;
X	if (*p == '?')
X	{
X		/* have some */
X		register char *q = index(p + 1, *p);
X		
X		if (q != NULL)
X		{
X			*q++ = '\0';
X			while (*++p != '\0')
X				setbitn(*p, mopts);
X			p = q;
X		}
X		else
X			syserr("chompheader: syntax error, line \"%s\"", line);
X		cond = TRUE;
X	}
X
X	/* find canonical name */
X	fname = p;
X	p = index(p, ':');
X	if (p == NULL)
X	{
X		syserr("chompheader: syntax error, line \"%s\"", line);
X		return (0);
X	}
X	fvalue = &p[1];
X	while (isspace(*--p))
X		continue;
X	*++p = '\0';
X	makelower(fname);
X
X	/* strip field value on front */
X	if (*fvalue == ' ')
X		fvalue++;
X
X	/* see if it is a known type */
X	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
X	{
X		if (strcmp(hi->hi_field, fname) == 0)
X			break;
X	}
X
X	/* see if this is a resent message */
X	if (!def && bitset(H_RESENT, hi->hi_flags))
X		CurEnv->e_flags |= EF_RESENT;
X
X	/* if this means "end of header" quit now */
X	if (bitset(H_EOH, hi->hi_flags))
X		return (hi->hi_flags);
X
X	/* drop explicit From: if same as what we would generate -- for MH */
X	p = "resent-from";
X	if (!bitset(EF_RESENT, CurEnv->e_flags))
X		p += 7;
X	if (!def && !QueueRun && strcmp(fname, p) == 0)
X	{
X		if (CurEnv->e_from.q_paddr != NULL &&
X		    strcmp(fvalue, CurEnv->e_from.q_paddr) == 0)
X			return (hi->hi_flags);
X	}
X
X	/* delete default value for this header */
X	for (hp = &CurEnv->e_header; (h = *hp) != NULL; hp = &h->h_link)
X	{
X		if (strcmp(fname, h->h_field) == 0 &&
X		    bitset(H_DEFAULT, h->h_flags) &&
X		    !bitset(H_FORCE, h->h_flags))
X			h->h_value = NULL;
X	}
X
X	/* create a new node */
X	h = (HDR *) xalloc(sizeof *h);
X	h->h_field = newstr(fname);
X	h->h_value = NULL;
X	h->h_link = NULL;
X	bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts);
X	*hp = h;
X	h->h_flags = hi->hi_flags;
X	if (def)
X		h->h_flags |= H_DEFAULT;
X	if (cond)
X		h->h_flags |= H_CHECK;
X	if (h->h_value != NULL)
X		free((char *) h->h_value);
X	h->h_value = newstr(fvalue);
X
X	/* hack to see if this is a new format message */
X	if (!def && bitset(H_RCPT|H_FROM, h->h_flags) &&
X	    (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL ||
X	     index(fvalue, '<') != NULL || index(fvalue, ';') != NULL))
X	{
X		CurEnv->e_flags &= ~EF_OLDSTYLE;
X	}
X
X	return (h->h_flags);
X}
X/*
X**  ADDHEADER -- add a header entry to the end of the queue.
X**
X**	This bypasses the special checking of chompheader.
X**
X**	Parameters:
X**		field -- the name of the header field.
X**		value -- the value of the field.  It must be lower-cased.
X**		e -- the envelope to add them to.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		adds the field on the list of headers for this envelope.
X*/
X
Xaddheader(field, value, e)
X	char *field;
X	char *value;
X	ENVELOPE *e;
X{
X	register HDR *h;
X	register struct hdrinfo *hi;
X	HDR **hp;
X
X	/* find info struct */
X	for (hi = HdrInfo; hi->hi_field != NULL; hi++)
X	{
X		if (strcmp(field, hi->hi_field) == 0)
X			break;
X	}
X
X	/* find current place in list -- keep back pointer? */
X	for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
X	{
X		if (strcmp(field, h->h_field) == 0)
X			break;
X	}
X
X	/* allocate space for new header */
X	h = (HDR *) xalloc(sizeof *h);
X	h->h_field = field;
X	h->h_value = newstr(value);
X	h->h_link = *hp;
X	h->h_flags = hi->hi_flags | H_DEFAULT;
X	clrbitmap(h->h_mflags);
X	*hp = h;
X}
X/*
X**  HVALUE -- return value of a header.
X**
X**	Only "real" fields (i.e., ones that have not been supplied
X**	as a default) are used.
X**
X**	Parameters:
X**		field -- the field name.
X**
X**	Returns:
X**		pointer to the value part.
X**		NULL if not found.
X**
X**	Side Effects:
X**		none.
X*/
X
Xchar *
Xhvalue(field)
X	char *field;
X{
X	register HDR *h;
X
X	for (h = CurEnv->e_header; h != NULL; h = h->h_link)
X	{
X		if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0)
X			return (h->h_value);
X	}
X	return (NULL);
X}
X/*
X**  ISHEADER -- predicate telling if argument is a header.
X**
X**	A line is a header if it has a single word followed by
X**	optional white space followed by a colon.
X**
X**	Parameters:
X**		s -- string to check for possible headerness.
X**
X**	Returns:
X**		TRUE if s is a header.
X**		FALSE otherwise.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xisheader(s)
X	register char *s;
X{
X	while (*s > ' ' && *s != ':' && *s != '\0')
X		s++;
X
X	/* following technically violates RFC822 */
X	while (isspace(*s))
X		s++;
X
X	return (*s == ':');
X}
X/*
X**  EATHEADER -- run through the stored header and extract info.
X**
X**	Parameters:
X**		e -- the envelope to process.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Sets a bunch of global variables from information
X**			in the collected header.
X**		Aborts the message if the hop count is exceeded.
X*/
X
Xeatheader(e)
X	register ENVELOPE *e;
X{
X	register HDR *h;
X	register char *p;
X	int hopcnt = 0;
X
X#ifdef DEBUG
X	if (tTd(32, 1))
X		printf("----- collected header -----\n");
X#endif DEBUG
X	for (h = e->e_header; h != NULL; h = h->h_link)
X	{
X#ifdef DEBUG
X		extern char *capitalize();
X
X		if (tTd(32, 1))
X			printf("%s: %s\n", capitalize(h->h_field), h->h_value);
X#endif DEBUG
X		/* count the number of times it has been processed */
X		if (bitset(H_TRACE, h->h_flags))
X			hopcnt++;
X
X		/* send to this person if we so desire */
X		if (GrabTo && bitset(H_RCPT, h->h_flags) &&
X		    !bitset(H_DEFAULT, h->h_flags) &&
X		    (!bitset(EF_RESENT, CurEnv->e_flags) || bitset(H_RESENT, h->h_flags)))
X		{
X			sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
X		}
X
X		/* log the message-id */
X#ifdef LOG
X		if (!QueueRun && LogLevel > 8 && h->h_value != NULL &&
X		    strcmp(h->h_field, "message-id") == 0)
X		{
X			char buf[MAXNAME];
X
X			p = h->h_value;
X			if (bitset(H_DEFAULT, h->h_flags))
X			{
X				expand(p, buf, &buf[sizeof buf], e);
X				p = buf;
X			}
X			syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p);
X		}
X#endif LOG
X	}
X#ifdef DEBUG
X	if (tTd(32, 1))
X		printf("----------------------------\n");
X#endif DEBUG
X
X	/* store hop count */
X	if (hopcnt > e->e_hopcount)
X		e->e_hopcount = hopcnt;
X
X	/* message priority */
X	p = hvalue("precedence");
X	if (p != NULL)
X		e->e_class = priencode(p);
X	if (!QueueRun)
X		e->e_msgpriority = e->e_msgsize
X				 - e->e_class * WkClassFact
X				 + e->e_nrcpts * WkRecipFact;
X
X	/* return receipt to */
X	p = hvalue("return-receipt-to");
X	if (p != NULL)
X		e->e_receiptto = p;
X
X	/* errors to */
X	p = hvalue("errors-to");
X	if (p != NULL)
X		sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue);
X
X	/* from person */
X	if (OpMode == MD_ARPAFTP)
X	{
X		register struct hdrinfo *hi = HdrInfo;
X
X		for (p = NULL; p == NULL && hi->hi_field != NULL; hi++)
X		{
X			if (bitset(H_FROM, hi->hi_flags))
X				p = hvalue(hi->hi_field);
X		}
X		if (p != NULL)
X			setsender(p);
X	}
X
X	/* full name of from person */
X	p = hvalue("full-name");
X	if (p != NULL)
X		define('x', p, e);
X
X	/* date message originated */
X	p = hvalue("posted-date");
X	if (p == NULL)
X		p = hvalue("date");
X	if (p != NULL)
X	{
X		define('a', p, e);
X		/* we don't have a good way to do canonical conversion ....
X		define('d', newstr(arpatounix(p)), e);
X		.... so we will ignore the problem for the time being */
X	}
X
X	/*
X	**  Log collection information.
X	*/
X
X# ifdef LOG
X	if (!QueueRun && LogLevel > 1)
X	{
X		syslog(LOG_INFO, "%s: from=%s, size=%ld, class=%d\n",
X		       CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize,
X		       CurEnv->e_class);
X	}
X# endif LOG
X}
X/*
X**  PRIENCODE -- encode external priority names into internal values.
X**
X**	Parameters:
X**		p -- priority in ascii.
X**
X**	Returns:
X**		priority as a numeric level.
X**
X**	Side Effects:
X**		none.
X*/
X
Xpriencode(p)
X	char *p;
X{
X	register int i;
X	extern bool sameword();
X
X	for (i = 0; i < NumPriorities; i++)
X	{
X		if (sameword(p, Priorities[i].pri_name))
X			return (Priorities[i].pri_val);
X	}
X
X	/* unknown priority */
X	return (0);
X}
X/*
X**  CRACKADDR -- parse an address and turn it into a macro
X**
X**	This doesn't actually parse the address -- it just extracts
X**	it and replaces it with "$g".  The parse is totally ad hoc
X**	and isn't even guaranteed to leave something syntactically
X**	identical to what it started with.  However, it does leave
X**	something semantically identical.
X**
X**	The process is kind of strange.  There are a number of
X**	interesting cases:
X**		1.  comment <address> comment	==> comment <$g> comment
X**		2.  address			==> address
X**		3.  address (comment)		==> $g (comment)
X**		4.  (comment) address		==> (comment) $g
X**	And then there are the hard cases....
X**		5.  add (comment) ress		==> $g (comment)
X**		6.  comment <address (comment)>	==> comment <$g (comment)>
X**		7.    .... etc ....
X**
X**	Parameters:
X**		addr -- the address to be cracked.
X**
X**	Returns:
X**		a pointer to the new version.
X**
X**	Side Effects:
X**		none.
X**
X**	Warning:
X**		The return value is saved in local storage and should
X**		be copied if it is to be reused.
X*/
X
Xchar *
Xcrackaddr(addr)
X	register char *addr;
X{
X	register char *p;
X	register int i;
X	static char buf[MAXNAME];
X	char *rhs;
X	bool gotaddr;
X	register char *bp;
X
X# ifdef DEBUG
X	if (tTd(33, 1))
X		printf("crackaddr(%s)\n", addr);
X# endif DEBUG
X
X	(void) strcpy(buf, "");
X	rhs = NULL;
X
X	/* strip leading spaces */
X	while (*addr != '\0' && isspace(*addr))
X		addr++;
X
X	/*
X	**  See if we have anything in angle brackets.  If so, that is
X	**  the address part, and the rest is the comment.
X	*/
X
X	p = index(addr, '<');
X	if (p != NULL)
X	{
X		/* copy the beginning of the addr field to the buffer */
X		*p = '\0';
X		(void) strcpy(buf, addr);
X		(void) strcat(buf, "<");
X		*p++ = '<';
X
X		/* skip spaces */
X		while (isspace(*p))
X			p++;
X
X		/* find the matching right angle bracket */
X		addr = p;
X		for (i = 0; *p != '\0'; p++)
X		{
X			switch (*p)
X			{
X			  case '<':
X				i++;
X				break;
X
X			  case '>':
X				i--;
X				break;
X			}
X			if (i < 0)
X				break;
X		}
X
X		/* p now points to the closing quote (or a null byte) */
X		if (*p != '\0')
X		{
X			/* make rhs point to the extra stuff at the end */
X			rhs = p;
X			*p++ = '\0';
X		}
X	}
X
X	/*
X	**  Now parse the real address part.  "addr" points to the (null
X	**  terminated) version of what we are inerested in; rhs points
X	**  to the extra stuff at the end of the line, if any.
X	*/
X
X	p = addr;
X
X	/* now strip out comments */
X	bp = &buf[strlen(buf)];
X	gotaddr = FALSE;
X	for (; *p != '\0'; p++)
X	{
X		if (*p == '(')
X		{
X			/* copy to matching close paren */
X			*bp++ = *p++;
X			for (i = 0; *p != '\0'; p++)
X			{
X				*bp++ = *p;
X				switch (*p)
X				{
X				  case '(':
X					i++;
X					break;
X
X				  case ')':
X					i--;
X					break;
X				}
X				if (i < 0)
X					break;
X			}
X			continue;
X		}
X
X		/*
X		**  If this is the first "real" character we have seen,
X		**  then we put the "$g" in the buffer now.
X		*/
X
X		if (isspace(*p))
X			*bp++ = *p;
X		else if (!gotaddr)
X		{
X			(void) strcpy(bp, "\001g");
X			bp += 2;
X			gotaddr = TRUE;
X		}
X	}
X
X	/* hack, hack.... strip trailing blanks */
X	do
X	{
X		*bp-- = '\0';
X	} while (isspace(*bp));
X	bp++;
X
X	/* put any right hand side back on */
X	if (rhs != NULL)
X	{
X		*rhs = '>';
X		(void) strcpy(bp, rhs);
X	}
X
X# ifdef DEBUG
X	if (tTd(33, 1))
X		printf("crackaddr=>`%s'\n", buf);
X# endif DEBUG
X
X	return (buf);
X}
X/*
X**  PUTHEADER -- put the header part of a message from the in-core copy
X**
X**	Parameters:
X**		fp -- file to put it on.
X**		m -- mailer to use.
X**		e -- envelope to use.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
Xputheader(fp, m, e)
X	register FILE *fp;
X	register MAILER *m;
X	register ENVELOPE *e;
X{
X	char buf[BUFSIZ];
X	register HDR *h;
X	extern char *arpadate();
X	extern char *capitalize();
X	char obuf[MAXLINE];
X
X	for (h = e->e_header; h != NULL; h = h->h_link)
X	{
X		register char *p;
X		extern bool bitintersect();
X
X		if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
X		    !bitintersect(h->h_mflags, m->m_flags))
X			continue;
X
X		/* handle Resent-... headers specially */
X		if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
X			continue;
X
X		p = h->h_value;
X		if (bitset(H_DEFAULT, h->h_flags))
X		{
X			/* macro expand value if generated internally */
X			expand(p, buf, &buf[sizeof buf], e);
X			p = buf;
X			if (p == NULL || *p == '\0')
X				continue;
X		}
X
X		if (bitset(H_FROM|H_RCPT, h->h_flags))
X		{
X			/* address field */
X			bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
X
X			if (bitset(H_FROM, h->h_flags))
X				oldstyle = FALSE;
X			commaize(h, p, fp, oldstyle, m);
X		}
X		else
X		{
X			/* vanilla header line */
X			register char *nlp;
X
X			(void) sprintf(obuf, "%s: ", capitalize(h->h_field));
X			while ((nlp = index(p, '\n')) != NULL)
X			{
X				*nlp = '\0';
X				(void) strcat(obuf, p);
X				*nlp = '\n';
X				putline(obuf, fp, m);
X				p = ++nlp;
X				obuf[0] = '\0';
X			}
X			(void) strcat(obuf, p);
X			putline(obuf, fp, m);
X		}
X	}
X}
X/*
X**  COMMAIZE -- output a header field, making a comma-translated list.
X**
X**	Parameters:
X**		h -- the header field to output.
X**		p -- the value to put in it.
X**		fp -- file to put it to.
X**		oldstyle -- TRUE if this is an old style header.
X**		m -- a pointer to the mailer descriptor.  If NULL,
X**			don't transform the name at all.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		outputs "p" to file "fp".
X*/
X
Xcommaize(h, p, fp, oldstyle, m)
X	register HDR *h;
X	register char *p;
X	FILE *fp;
X	bool oldstyle;
X	register MAILER *m;
X{
X	register char *obp;
X	int opos;
X	bool firstone = TRUE;
X	char obuf[MAXLINE + 3];
X
X	/*
X	**  Output the address list translated by the
X	**  mailer and with commas.
X	*/
X
X# ifdef DEBUG
X	if (tTd(14, 2))
X		printf("commaize(%s: %s)\n", h->h_field, p);
X# endif DEBUG
X
X	obp = obuf;
X	(void) sprintf(obp, "%s: ", capitalize(h->h_field));
X	opos = strlen(h->h_field) + 2;
X	obp += opos;
X
X	/*
X	**  Run through the list of values.
X	*/
X
X	while (*p != '\0')
X	{
X		register char *name;
X		char savechar;
X		extern char *remotename();
X		extern char *DelimChar;		/* defined in prescan */
X
X		/*
X		**  Find the end of the name.  New style names
X		**  end with a comma, old style names end with
X		**  a space character.  However, spaces do not
X		**  necessarily delimit an old-style name -- at
X		**  signs mean keep going.
X		*/
X
X		/* find end of name */
X		while (isspace(*p) || *p == ',')
X			p++;
X		name = p;
X		for (;;)
X		{
X			char *oldp;
X			char pvpbuf[PSBUFSIZE];
X			extern bool isatword();
X			extern char **prescan();
X
X			(void) prescan(p, oldstyle ? ' ' : ',', pvpbuf);
X			p = DelimChar;
X
X			/* look to see if we have an at sign */
X			oldp = p;
X			while (*p != '\0' && isspace(*p))
X				p++;
X
X			if (*p != '@' && !isatword(p))
X			{
X				p = oldp;
X				break;
X			}
X			p += *p == '@' ? 1 : 2;
X			while (*p != '\0' && isspace(*p))
X				p++;
X		}
X		/* at the end of one complete name */
X
X		/* strip off trailing white space */
X		while (p >= name && (isspace(*p) || *p == ',' || *p == '\0'))
X			p--;
X		if (++p == name)
X			continue;
X		savechar = *p;
X		*p = '\0';
X
X		/* translate the name to be relative */
X		name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE);
X		if (*name == '\0')
X		{
X			*p = savechar;
X			continue;
X		}
X
X		/* output the name with nice formatting */
X		opos += qstrlen(name);
X		if (!firstone)
X			opos += 2;
X		if (opos > 78 && !firstone)
X		{
X			(void) strcpy(obp, ",\n");
X			putline(obuf, fp, m);
X			obp = obuf;
X			(void) sprintf(obp, "        ");
X			opos = strlen(obp);
X			obp += opos;
X			opos += qstrlen(name);
X		}
X		else if (!firstone)
X		{
X			(void) sprintf(obp, ", ");
X			obp += 2;
X		}
X
X		/* strip off quote bits as we output */
X		while (*name != '\0' && obp < &obuf[MAXLINE])
X		{
X			if (bitset(0200, *name))
X				*obp++ = '\\';
X			*obp++ = *name++ & ~0200;
X		}
X		firstone = FALSE;
X		*p = savechar;
X	}
X	(void) strcpy(obp, "\n");
X	putline(obuf, fp, m);
X}
X/*
X**  ISATWORD -- tell if the word we are pointing to is "at".
X**
X**	Parameters:
X**		p -- word to check.
X**
X**	Returns:
X**		TRUE -- if p is the word at.
X**		FALSE -- otherwise.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xisatword(p)
X	register char *p;
X{
X	extern char lower();
X
X	if (lower(p[0]) == 'a' && lower(p[1]) == 't' &&
X	    p[2] != '\0' && isspace(p[2]))
X		return (TRUE);
X	return (FALSE);
X}
END_OF_FILE
if test 16858 -ne `wc -c <'src/headers.c'`; then
    echo shar: \"'src/headers.c'\" unpacked with wrong size!
fi
# end of 'src/headers.c'
fi
if test -f 'src/readcf.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/readcf.c'\"
else
echo shar: Extracting \"'src/readcf.c'\" \(18332 characters\)
sed "s/^X//" >'src/readcf.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)readcf.c	5.10 (Berkeley) 1/11/86";
X#endif not lint
X
X# include "sendmail.h"
X
X/*
X**  READCF -- read control file.
X**
X**	This routine reads the control file and builds the internal
X**	form.
X**
X**	The file is formatted as a sequence of lines, each taken
X**	atomically.  The first character of each line describes how
X**	the line is to be interpreted.  The lines are:
X**		Dxval		Define macro x to have value val.
X**		Cxword		Put word into class x.
X**		Fxfile [fmt]	Read file for lines to put into
X**				class x.  Use scanf string 'fmt'
X**				or "%s" if not present.  Fmt should
X**				only produce one string-valued result.
X**		Hname: value	Define header with field-name 'name'
X**				and value as specified; this will be
X**				macro expanded immediately before
X**				use.
X**		Sn		Use rewriting set n.
X**		Rlhs rhs	Rewrite addresses that match lhs to
X**				be rhs.
X**		Mn arg=val...	Define mailer.  n is the internal name.
X**				Args specify mailer parameters.
X**		Oxvalue		Set option x to value.
X**		Pname=value	Set precedence name to value.
X**
X**	Parameters:
X**		cfname -- control file name.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Builds several internal tables.
X*/
X
Xreadcf(cfname)
X	char *cfname;
X{
X	FILE *cf;
X	int ruleset = 0;
X	char *q;
X	char **pv;
X	struct rewrite *rwp = NULL;
X	char buf[MAXLINE];
X	register char *p;
X	extern char **prescan();
X	extern char **copyplist();
X	char exbuf[MAXLINE];
X	char pvpbuf[PSBUFSIZE];
X	extern char *fgetfolded();
X	extern char *munchstring();
X
X	cf = fopen(cfname, "r");
X	if (cf == NULL)
X	{
X		syserr("cannot open %s", cfname);
X		exit(EX_OSFILE);
X	}
X
X	FileName = cfname;
X	LineNumber = 0;
X	while (fgetfolded(buf, sizeof buf, cf) != NULL)
X	{
X		/* map $ into \001 (ASCII SOH) for macro expansion */
X		for (p = buf; *p != '\0'; p++)
X		{
X			if (*p != '$')
X				continue;
X
X			if (p[1] == '$')
X			{
X				/* actual dollar sign.... */
X				(void) strcpy(p, p + 1);
X				continue;
X			}
X
X			/* convert to macro expansion character */
X			*p = '\001';
X		}
X
X		/* interpret this line */
X		switch (buf[0])
X		{
X		  case '\0':
X		  case '#':		/* comment */
X			break;
X
X		  case 'R':		/* rewriting rule */
X			for (p = &buf[1]; *p != '\0' && *p != '\t'; p++)
X				continue;
X
X			if (*p == '\0')
X			{
X				syserr("invalid rewrite line \"%s\"", buf);
X				break;
X			}
X
X			/* allocate space for the rule header */
X			if (rwp == NULL)
X			{
X				RewriteRules[ruleset] = rwp =
X					(struct rewrite *) xalloc(sizeof *rwp);
X			}
X			else
X			{
X				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
X				rwp = rwp->r_next;
X			}
X			rwp->r_next = NULL;
X
X			/* expand and save the LHS */
X			*p = '\0';
X			expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
X			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf);
X			if (rwp->r_lhs != NULL)
X				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
X
X			/* expand and save the RHS */
X			while (*++p == '\t')
X				continue;
X			q = p;
X			while (*p != '\0' && *p != '\t')
X				p++;
X			*p = '\0';
X			expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
X			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf);
X			if (rwp->r_rhs != NULL)
X				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
X			break;
X
X		  case 'S':		/* select rewriting set */
X			ruleset = atoi(&buf[1]);
X			if (ruleset >= MAXRWSETS || ruleset < 0)
X			{
X				syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
X				ruleset = 0;
X			}
X			rwp = NULL;
X			break;
X
X		  case 'D':		/* macro definition */
X			define(buf[1], newstr(munchstring(&buf[2])), CurEnv);
X			break;
X
X		  case 'H':		/* required header line */
X			(void) chompheader(&buf[1], TRUE);
X			break;
X
X		  case 'C':		/* word class */
X		  case 'F':		/* word class from file */
X			/* read list of words from argument or file */
X			if (buf[0] == 'F')
X			{
X				/* read from file */
X				for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
X					continue;
X				if (*p == '\0')
X					p = "%s";
X				else
X				{
X					*p = '\0';
X					while (isspace(*++p))
X						continue;
X				}
X				fileclass(buf[1], &buf[2], p);
X				break;
X			}
X
X			/* scan the list of words and set class for all */
X			for (p = &buf[2]; *p != '\0'; )
X			{
X				register char *wd;
X				char delim;
X
X				while (*p != '\0' && isspace(*p))
X					p++;
X				wd = p;
X				while (*p != '\0' && !isspace(*p))
X					p++;
X				delim = *p;
X				*p = '\0';
X				if (wd[0] != '\0')
X					setclass(buf[1], wd);
X				*p = delim;
X			}
X			break;
X
X		  case 'M':		/* define mailer */
X			makemailer(&buf[1]);
X			break;
X
X		  case 'O':		/* set option */
X			setoption(buf[1], &buf[2], TRUE, FALSE);
X			break;
X
X		  case 'P':		/* set precedence */
X			if (NumPriorities >= MAXPRIORITIES)
X			{
X				toomany('P', MAXPRIORITIES);
X				break;
X			}
X			for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
X				continue;
X			if (*p == '\0')
X				goto badline;
X			*p = '\0';
X			Priorities[NumPriorities].pri_name = newstr(&buf[1]);
X			Priorities[NumPriorities].pri_val = atoi(++p);
X			NumPriorities++;
X			break;
X
X		  case 'T':		/* trusted user(s) */
X			p = &buf[1];
X			while (*p != '\0')
X			{
X				while (isspace(*p))
X					p++;
X				q = p;
X				while (*p != '\0' && !isspace(*p))
X					p++;
X				if (*p != '\0')
X					*p++ = '\0';
X				if (*q == '\0')
X					continue;
X				for (pv = TrustedUsers; *pv != NULL; pv++)
X					continue;
X				if (pv >= &TrustedUsers[MAXTRUST])
X				{
X					toomany('T', MAXTRUST);
X					break;
X				}
X				*pv = newstr(q);
X			}
X			break;
X
X		  default:
X		  badline:
X			syserr("unknown control line \"%s\"", buf);
X		}
X	}
X	FileName = NULL;
X}
X/*
X**  TOOMANY -- signal too many of some option
X**
X**	Parameters:
X**		id -- the id of the error line
X**		maxcnt -- the maximum possible values
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		gives a syserr.
X*/
X
Xtoomany(id, maxcnt)
X	char id;
X	int maxcnt;
X{
X	syserr("too many %c lines, %d max", id, maxcnt);
X}
X/*
X**  FILECLASS -- read members of a class from a file
X**
X**	Parameters:
X**		class -- class to define.
X**		filename -- name of file to read.
X**		fmt -- scanf string to use for match.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**
X**		puts all lines in filename that match a scanf into
X**			the named class.
X*/
X
Xfileclass(class, filename, fmt)
X	int class;
X	char *filename;
X	char *fmt;
X{
X	FILE *f;
X	char buf[MAXLINE];
X
X	f = fopen(filename, "r");
X	if (f == NULL)
X	{
X		syserr("cannot open %s", filename);
X		return;
X	}
X
X	while (fgets(buf, sizeof buf, f) != NULL)
X	{
X		register STAB *s;
X		register char *p;
X# ifdef SCANF
X		char wordbuf[MAXNAME+1];
X
X		if (sscanf(buf, fmt, wordbuf) != 1)
X			continue;
X		p = wordbuf;
X# else SCANF
X		p = buf;
X# endif SCANF
X
X		/*
X		**  Break up the match into words.
X		*/
X
X		while (*p != '\0')
X		{
X			register char *q;
X
X			/* strip leading spaces */
X			while (isspace(*p))
X				p++;
X			if (*p == '\0')
X				break;
X
X			/* find the end of the word */
X			q = p;
X			while (*p != '\0' && !isspace(*p))
X				p++;
X			if (*p != '\0')
X				*p++ = '\0';
X
X			/* enter the word in the symbol table */
X			s = stab(q, ST_CLASS, ST_ENTER);
X			setbitn(class, s->s_class);
X		}
X	}
X
X	(void) fclose(f);
X}
X/*
X**  MAKEMAILER -- define a new mailer.
X**
X**	Parameters:
X**		line -- description of mailer.  This is in labeled
X**			fields.  The fields are:
X**			   P -- the path to the mailer
X**			   F -- the flags associated with the mailer
X**			   A -- the argv for this mailer
X**			   S -- the sender rewriting set
X**			   R -- the recipient rewriting set
X**			   E -- the eol string
X**			The first word is the canonical name of the mailer.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		enters the mailer into the mailer table.
X*/
X
Xmakemailer(line)
X	char *line;
X{
X	register char *p;
X	register struct mailer *m;
X	register STAB *s;
X	int i;
X	char fcode;
X	extern int NextMailer;
X	extern char **makeargv();
X	extern char *munchstring();
X	extern char *DelimChar;
X	extern long atol();
X
X	/* allocate a mailer and set up defaults */
X	m = (struct mailer *) xalloc(sizeof *m);
X	bzero((char *) m, sizeof *m);
X	m->m_mno = NextMailer;
X	m->m_eol = "\n";
X
X	/* collect the mailer name */
X	for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++)
X		continue;
X	if (*p != '\0')
X		*p++ = '\0';
X	m->m_name = newstr(line);
X
X	/* now scan through and assign info from the fields */
X	while (*p != '\0')
X	{
X		while (*p != '\0' && (*p == ',' || isspace(*p)))
X			p++;
X
X		/* p now points to field code */
X		fcode = *p;
X		while (*p != '\0' && *p != '=' && *p != ',')
X			p++;
X		if (*p++ != '=')
X		{
X			syserr("`=' expected");
X			return;
X		}
X		while (isspace(*p))
X			p++;
X
X		/* p now points to the field body */
X		p = munchstring(p);
X
X		/* install the field into the mailer struct */
X		switch (fcode)
X		{
X		  case 'P':		/* pathname */
X			m->m_mailer = newstr(p);
X			break;
X
X		  case 'F':		/* flags */
X			for (; *p != '\0'; p++)
X				setbitn(*p, m->m_flags);
X			break;
X
X		  case 'S':		/* sender rewriting ruleset */
X		  case 'R':		/* recipient rewriting ruleset */
X			i = atoi(p);
X			if (i < 0 || i >= MAXRWSETS)
X			{
X				syserr("invalid rewrite set, %d max", MAXRWSETS);
X				return;
X			}
X			if (fcode == 'S')
X				m->m_s_rwset = i;
X			else
X				m->m_r_rwset = i;
X			break;
X
X		  case 'E':		/* end of line string */
X			m->m_eol = newstr(p);
X			break;
X
X		  case 'A':		/* argument vector */
X			m->m_argv = makeargv(p);
X			break;
X
X		  case 'M':		/* maximum message size */
X			m->m_maxsize = atol(p);
X			break;
X		}
X
X		p = DelimChar;
X	}
X
X	/* now store the mailer away */
X	if (NextMailer >= MAXMAILERS)
X	{
X		syserr("too many mailers defined (%d max)", MAXMAILERS);
X		return;
X	}
X	Mailer[NextMailer++] = m;
X	s = stab(m->m_name, ST_MAILER, ST_ENTER);
X	s->s_mailer = m;
X}
X/*
X**  MUNCHSTRING -- translate a string into internal form.
X**
X**	Parameters:
X**		p -- the string to munch.
X**
X**	Returns:
X**		the munched string.
X**
X**	Side Effects:
X**		Sets "DelimChar" to point to the string that caused us
X**		to stop.
X*/
X
Xchar *
Xmunchstring(p)
X	register char *p;
X{
X	register char *q;
X	bool backslash = FALSE;
X	bool quotemode = FALSE;
X	static char buf[MAXLINE];
X	extern char *DelimChar;
X
X	for (q = buf; *p != '\0'; p++)
X	{
X		if (backslash)
X		{
X			/* everything is roughly literal */
X			backslash = FALSE;
X			switch (*p)
X			{
X			  case 'r':		/* carriage return */
X				*q++ = '\r';
X				continue;
X
X			  case 'n':		/* newline */
X				*q++ = '\n';
X				continue;
X
X			  case 'f':		/* form feed */
X				*q++ = '\f';
X				continue;
X
X			  case 'b':		/* backspace */
X				*q++ = '\b';
X				continue;
X			}
X			*q++ = *p;
X		}
X		else
X		{
X			if (*p == '\\')
X				backslash = TRUE;
X			else if (*p == '"')
X				quotemode = !quotemode;
X			else if (quotemode || *p != ',')
X				*q++ = *p;
X			else
X				break;
X		}
X	}
X
X	DelimChar = p;
X	*q++ = '\0';
X	return (buf);
X}
X/*
X**  MAKEARGV -- break up a string into words
X**
X**	Parameters:
X**		p -- the string to break up.
X**
X**	Returns:
X**		a char **argv (dynamically allocated)
X**
X**	Side Effects:
X**		munges p.
X*/
X
Xchar **
Xmakeargv(p)
X	register char *p;
X{
X	char *q;
X	int i;
X	char **avp;
X	char *argv[MAXPV + 1];
X
X	/* take apart the words */
X	i = 0;
X	while (*p != '\0' && i < MAXPV)
X	{
X		q = p;
X		while (*p != '\0' && !isspace(*p))
X			p++;
X		while (isspace(*p))
X			*p++ = '\0';
X		argv[i++] = newstr(q);
X	}
X	argv[i++] = NULL;
X
X	/* now make a copy of the argv */
X	avp = (char **) xalloc(sizeof *avp * i);
X	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
X
X	return (avp);
X}
X/*
X**  PRINTRULES -- print rewrite rules (for debugging)
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		prints rewrite rules.
X*/
X
X# ifdef DEBUG
X
Xprintrules()
X{
X	register struct rewrite *rwp;
X	register int ruleset;
X
X	for (ruleset = 0; ruleset < 10; ruleset++)
X	{
X		if (RewriteRules[ruleset] == NULL)
X			continue;
X		printf("\n----Rule Set %d:", ruleset);
X
X		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
X		{
X			printf("\nLHS:");
X			printav(rwp->r_lhs);
X			printf("RHS:");
X			printav(rwp->r_rhs);
X		}
X	}
X}
X
X# endif DEBUG
X/*
X**  SETOPTION -- set global processing option
X**
X**	Parameters:
X**		opt -- option name.
X**		val -- option value (as a text string).
X**		safe -- set if this came from a configuration file.
X**			Some options (if set from the command line) will
X**			reset the user id to avoid security problems.
X**		sticky -- if set, don't let other setoptions override
X**			this value.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Sets options as implied by the arguments.
X*/
X
Xstatic BITMAP	StickyOpt;		/* set if option is stuck */
Xextern char	*NetName;		/* name of home (local) network */
X# ifdef SMTP
X# ifdef WIZ
Xextern char	*WizWord;		/* the stored wizard password */
X# endif WIZ
X# endif SMTP
X
Xsetoption(opt, val, safe, sticky)
X	char opt;
X	char *val;
X	bool safe;
X	bool sticky;
X{
X	extern bool atobool();
X	extern time_t convtime();
X	extern int QueueLA;
X	extern int RefuseLA;
X	extern bool trusteduser();
X	extern char *username();
X
X# ifdef DEBUG
X	if (tTd(37, 1))
X		printf("setoption %c=%s", opt, val);
X# endif DEBUG
X
X	/*
X	**  See if this option is preset for us.
X	*/
X
X	if (bitnset(opt, StickyOpt))
X	{
X# ifdef DEBUG
X		if (tTd(37, 1))
X			printf(" (ignored)\n");
X# endif DEBUG
X		return;
X	}
X
X	/*
X	**  Check to see if this option can be specified by this user.
X	*/
X
X	if (!safe && getruid() == 0)
X		safe = TRUE;
X	if (!safe && index("deiLmorsv", opt) == NULL)
X	{
X# ifdef DEBUG
X		if (tTd(37, 1))
X			printf(" (unsafe)");
X# endif DEBUG
X		if (getruid() != geteuid())
X		{
X			printf("(Resetting uid)\n");
X			(void) setgid(getgid());
X			(void) setuid(getuid());
X		}
X	}
X#ifdef DEBUG
X	else if (tTd(37, 1))
X		printf("\n");
X#endif DEBUG
X
X	switch (opt)
X	{
X	  case 'A':		/* set default alias file */
X		if (val[0] == '\0')
X			AliasFile = "aliases";
X		else
X			AliasFile = newstr(val);
X		break;
X
X	  case 'a':		/* look N minutes for "@:@" in alias file */
X		if (val[0] == '\0')
X			SafeAlias = 5;
X		else
X			SafeAlias = atoi(val);
X		break;
X
X	  case 'B':		/* substitution for blank character */
X		SpaceSub = val[0];
X		if (SpaceSub == '\0')
X			SpaceSub = ' ';
X		break;
X
X	  case 'c':		/* don't connect to "expensive" mailers */
X		NoConnect = atobool(val);
X		break;
X
X	  case 'C':		/* checkpoint after N connections */
X		CheckPointLimit = atoi(val);
X		break;
X
X	  case 'd':		/* delivery mode */
X		switch (*val)
X		{
X		  case '\0':
X			SendMode = SM_DELIVER;
X			break;
X
X		  case SM_QUEUE:	/* queue only */
X#ifndef QUEUE
X			syserr("need QUEUE to set -odqueue");
X#endif QUEUE
X			/* fall through..... */
X
X		  case SM_DELIVER:	/* do everything */
X		  case SM_FORK:		/* fork after verification */
X			SendMode = *val;
X			break;
X
X		  default:
X			syserr("Unknown delivery mode %c", *val);
X			exit(EX_USAGE);
X		}
X		break;
X
X	  case 'D':		/* rebuild alias database as needed */
X		AutoRebuild = atobool(val);
X		break;
X
X	  case 'e':		/* set error processing mode */
X		switch (*val)
X		{
X		  case EM_QUIET:	/* be silent about it */
X		  case EM_MAIL:		/* mail back */
X		  case EM_BERKNET:	/* do berknet error processing */
X		  case EM_WRITE:	/* write back (or mail) */
X			HoldErrs = TRUE;
X			/* fall through... */
X
X		  case EM_PRINT:	/* print errors normally (default) */
X			ErrorMode = *val;
X			break;
X		}
X		break;
X
X	  case 'F':		/* file mode */
X		FileMode = atooct(val) & 0777;
X		break;
X
X	  case 'f':		/* save Unix-style From lines on front */
X		SaveFrom = atobool(val);
X		break;
X
X	  case 'g':		/* default gid */
X		DefGid = atoi(val);
X		break;
X
X	  case 'H':		/* help file */
X		if (val[0] == '\0')
X			HelpFile = "sendmail.hf";
X		else
X			HelpFile = newstr(val);
X		break;
X
X	  case 'i':		/* ignore dot lines in message */
X		IgnrDot = atobool(val);
X		break;
X
X	  case 'L':		/* log level */
X		LogLevel = atoi(val);
X		break;
X
X	  case 'M':		/* define macro */
X		define(val[0], newstr(&val[1]), CurEnv);
X		sticky = FALSE;
X		break;
X
X	  case 'm':		/* send to me too */
X		MeToo = atobool(val);
X		break;
X
X	  case 'n':		/* validate RHS in newaliases */
X		CheckAliases = atobool(val);
X		break;
X
X# ifdef DAEMON
X	  case 'N':		/* home (local?) network name */
X		NetName = newstr(val);
X		break;
X# endif DAEMON
X
X	  case 'o':		/* assume old style headers */
X		if (atobool(val))
X			CurEnv->e_flags |= EF_OLDSTYLE;
X		else
X			CurEnv->e_flags &= ~EF_OLDSTYLE;
X		break;
X
X	  case 'P':		/* postmaster copy address for returned mail */
X		PostMasterCopy = newstr(val);
X		break;
X
X	  case 'q':		/* slope of queue only function */
X		QueueFactor = atoi(val);
X		break;
X
X	  case 'Q':		/* queue directory */
X		if (val[0] == '\0')
X			QueueDir = "mqueue";
X		else
X			QueueDir = newstr(val);
X		break;
X
X	  case 'r':		/* read timeout */
X		ReadTimeout = convtime(val);
X		break;
X
X	  case 'S':		/* status file */
X		if (val[0] == '\0')
X			StatFile = "sendmail.st";
X		else
X			StatFile = newstr(val);
X		break;
X
X	  case 's':		/* be super safe, even if expensive */
X		SuperSafe = atobool(val);
X		break;
X
X	  case 'T':		/* queue timeout */
X		TimeOut = convtime(val);
X		break;
X
X	  case 't':		/* time zone name */
X# ifdef V6
X		StdTimezone = newstr(val);
X		DstTimezone = index(StdTimeZone, ',');
X		if (DstTimezone == NULL)
X			syserr("bad time zone spec");
X		else
X			*DstTimezone++ = '\0';
X# endif V6
X		break;
X
X	  case 'u':		/* set default uid */
X		DefUid = atoi(val);
X		break;
X
X	  case 'v':		/* run in verbose mode */
X		Verbose = atobool(val);
X		break;
X
X# ifdef SMTP
X# ifdef WIZ
X	  case 'W':		/* set the wizards password */
X		WizWord = newstr(val);
X		break;
X# endif WIZ
X# endif SMTP
X
X	  case 'x':		/* load avg at which to auto-queue msgs */
X		QueueLA = atoi(val);
X		break;
X
X	  case 'X':		/* load avg at which to auto-reject connections */
X		RefuseLA = atoi(val);
X		break;
X
X	  case 'y':		/* work recipient factor */
X		WkRecipFact = atoi(val);
X		break;
X
X	  case 'Y':		/* fork jobs during queue runs */
X		ForkQueueRuns = atobool(val);
X		break;
X
X	  case 'z':		/* work message class factor */
X		WkClassFact = atoi(val);
X		break;
X
X	  case 'Z':		/* work time factor */
X		WkTimeFact = atoi(val);
X		break;
X
X	  default:
X		break;
X	}
X	if (sticky)
X		setbitn(opt, StickyOpt);
X	return;
X}
X/*
X**  SETCLASS -- set a word into a class
X**
X**	Parameters:
X**		class -- the class to put the word in.
X**		word -- the word to enter
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		puts the word into the symbol table.
X*/
X
Xsetclass(class, word)
X	int class;
X	char *word;
X{
X	register STAB *s;
X
X	s = stab(word, ST_CLASS, ST_ENTER);
X	setbitn(class, s->s_class);
X}
END_OF_FILE
if test 18332 -ne `wc -c <'src/readcf.c'`; then
    echo shar: \"'src/readcf.c'\" unpacked with wrong size!
fi
# end of 'src/readcf.c'
fi
echo shar: End of archive 5 \(of 8\).
cp /dev/null ark5isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
David H. Brierley
Home: dave@galaxia.Newport.RI.US   {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb@rayssd.ray.com           {sun,decuac,gatech,necntc,ukma}!rayssd!dhb

dave@galaxia.Newport.RI.US (David H. Brierley) (02/25/89)

----- cut here and feed to /bin/sh -----
#! /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 archive 6 (of 8)."
# Contents:  src/queue.c src/sendmail.h
# Wrapped by dave@galaxia on Fri Feb 24 20:24:09 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/queue.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/queue.c'\"
else
echo shar: Extracting \"'src/queue.c'\" \(20282 characters\)
sed "s/^X//" >'src/queue.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X
X# include "sendmail.h"
X# include <sys/stat.h>
X# include <dirent.h>
X# include <signal.h>
X# include <errno.h>
X
X# ifndef QUEUE
X# ifndef lint
Xstatic char	SccsId[] = "@(#)queue.c	5.22 (Berkeley) 10/14/86	(no queueing)";
X# endif not lint
X# else QUEUE
X
X# ifndef lint
Xstatic char	SccsId[] = "@(#)queue.c	5.22 (Berkeley) 10/14/86";
X# endif not lint
X
X/*
X**  Work queue.
X*/
X
Xstruct work
X{
X	char		*w_name;	/* name of control file */
X	long		w_pri;		/* priority of message, see below */
X	time_t		w_ctime;	/* creation time of message */
X	struct work	*w_next;	/* next in queue */
X};
X
Xtypedef struct work	WORK;
X
XWORK	*WorkQ;			/* queue of things to be done */
X/*
X**  QUEUEUP -- queue a message up for future transmission.
X**
X**	Parameters:
X**		e -- the envelope to queue up.
X**		queueall -- if TRUE, queue all addresses, rather than
X**			just those with the QQUEUEUP flag set.
X**		announce -- if TRUE, tell when you are queueing up.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		The current request are saved in a control file.
X*/
X
Xqueueup(e, queueall, announce)
X	register ENVELOPE *e;
X	bool queueall;
X	bool announce;
X{
X	char *tf;
X	char *qf;
X	char buf[MAXLINE];
X	register FILE *tfp;
X	register HDR *h;
X	register ADDRESS *q;
X	MAILER nullmailer;
X
X	/*
X	**  Create control file.
X	*/
X
X	tf = newstr(queuename(e, 't'));
X	tfp = fopen(tf, "w");
X	if (tfp == NULL)
X	{
X		syserr("queueup: cannot create temp file %s", tf);
X		return;
X	}
X	(void) chmod(tf, FileMode);
X
X# ifdef DEBUG
X	if (tTd(40, 1))
X		printf("queueing %s\n", e->e_id);
X# endif DEBUG
X
X	/*
X	**  If there is no data file yet, create one.
X	*/
X
X	if (e->e_df == NULL)
X	{
X		register FILE *dfp;
X		extern putbody();
X
X		e->e_df = newstr(queuename(e, 'd'));
X		dfp = fopen(e->e_df, "w");
X		if (dfp == NULL)
X		{
X			syserr("queueup: cannot create %s", e->e_df);
X			(void) fclose(tfp);
X			return;
X		}
X		(void) chmod(e->e_df, FileMode);
X		(*e->e_putbody)(dfp, ProgMailer, e);
X		(void) fclose(dfp);
X		e->e_putbody = putbody;
X	}
X
X	/*
X	**  Output future work requests.
X	**	Priority and creation time should be first, since
X	**	they are required by orderq.
X	*/
X
X	/* output message priority */
X	fprintf(tfp, "P%ld\n", e->e_msgpriority);
X
X	/* output creation time */
X	fprintf(tfp, "T%ld\n", e->e_ctime);
X
X	/* output name of data file */
X	fprintf(tfp, "D%s\n", e->e_df);
X
X	/* message from envelope, if it exists */
X	if (e->e_message != NULL)
X		fprintf(tfp, "M%s\n", e->e_message);
X
X	/* output name of sender */
X	fprintf(tfp, "S%s\n", e->e_from.q_paddr);
X
X	/* output list of recipient addresses */
X	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
X	{
X		if (queueall ? !bitset(QDONTSEND, q->q_flags) :
X			       bitset(QQUEUEUP, q->q_flags))
X		{
X			fprintf(tfp, "R%s\n", q->q_paddr);
X			if (announce)
X			{
X				e->e_to = q->q_paddr;
X				message(Arpa_Info, "queued");
X				if (LogLevel > 4)
X					logdelivery("queued");
X				e->e_to = NULL;
X			}
X#ifdef DEBUG
X			if (tTd(40, 1))
X			{
X				printf("queueing ");
X				printaddr(q, FALSE);
X			}
X#endif DEBUG
X		}
X	}
X
X	/* output list of error recipients */
X	for (q = e->e_errorqueue; q != NULL; q = q->q_next)
X	{
X		if (!bitset(QDONTSEND, q->q_flags))
X			fprintf(tfp, "E%s\n", q->q_paddr);
X	}
X
X	/*
X	**  Output headers for this message.
X	**	Expand macros completely here.  Queue run will deal with
X	**	everything as absolute headers.
X	**		All headers that must be relative to the recipient
X	**		can be cracked later.
X	**	We set up a "null mailer" -- i.e., a mailer that will have
X	**	no effect on the addresses as they are output.
X	*/
X
X	bzero((char *) &nullmailer, sizeof nullmailer);
X	nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1;
X	nullmailer.m_eol = "\n";
X
X	define('g', "\001f", e);
X	for (h = e->e_header; h != NULL; h = h->h_link)
X	{
X		extern bool bitzerop();
X
X		/* don't output null headers */
X		if (h->h_value == NULL || h->h_value[0] == '\0')
X			continue;
X
X		/* don't output resent headers on non-resent messages */
X		if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
X			continue;
X
X		/* output this header */
X		fprintf(tfp, "H");
X
X		/* if conditional, output the set of conditions */
X		if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags))
X		{
X			int j;
X
X			(void) putc('?', tfp);
X			for (j = '\0'; j <= '\177'; j++)
X				if (bitnset(j, h->h_mflags))
X					(void) putc(j, tfp);
X			(void) putc('?', tfp);
X		}
X
X		/* output the header: expand macros, convert addresses */
X		if (bitset(H_DEFAULT, h->h_flags))
X		{
X			(void) expand(h->h_value, buf, &buf[sizeof buf], e);
X			fprintf(tfp, "%s: %s\n", h->h_field, buf);
X		}
X		else if (bitset(H_FROM|H_RCPT, h->h_flags))
X		{
X			commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags),
X				 &nullmailer);
X		}
X		else
X			fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
X	}
X
X	/*
X	**  Clean up.
X	*/
X
X	(void) fclose(tfp);
X	qf = queuename(e, 'q');
X	if (tf != NULL)
X	{
X		(void) unlink(qf);
X		if (rename(tf, qf) < 0)
X			syserr("cannot unlink(%s, %s), df=%s", tf, qf, e->e_df);
X		errno = 0;
X	}
X
X# ifdef LOG
X	/* save log info */
X	if (LogLevel > 15)
X		syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
X# endif LOG
X}
X/*
X**  RUNQUEUE -- run the jobs in the queue.
X**
X**	Gets the stuff out of the queue in some presumably logical
X**	order and processes them.
X**
X**	Parameters:
X**		forkflag -- TRUE if the queue scanning should be done in
X**			a child process.  We double-fork so it is not our
X**			child and we don't have to clean up after it.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		runs things in the mail queue.
X*/
X
Xrunqueue(forkflag)
X	bool forkflag;
X{
X	extern bool shouldqueue();
X
X	/*
X	**  If no work will ever be selected, don't even bother reading
X	**  the queue.
X	*/
X
X	if (shouldqueue(-100000000L))
X	{
X		if (Verbose)
X			printf("Skipping queue run -- load average too high\n");
X
X		if (forkflag)
X			return;
X		finis();
X	}
X
X	/*
X	**  See if we want to go off and do other useful work.
X	*/
X
X	if (forkflag)
X	{
X		int pid;
X
X		pid = dofork();
X		if (pid != 0)
X		{
X			extern reapchild();
X
X			/* parent -- pick up intermediate zombie */
X#ifndef SIGCHLD
X			(void) waitfor(pid);
X#else SIGCHLD
X			(void) signal(SIGCHLD, reapchild);
X#endif SIGCHLD
X			if (QueueIntvl != 0)
X				(void) setevent(QueueIntvl, runqueue, TRUE);
X			return;
X		}
X		/* child -- double fork */
X#ifndef SIGCHLD
X		if (fork() != 0)
X			exit(EX_OK);
X#else SIGCHLD
X		(void) signal(SIGCHLD, SIG_DFL);
X#endif SIGCHLD
X	}
X
X	setproctitle("running queue");
X
X# ifdef LOG
X	if (LogLevel > 11)
X		syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid());
X# endif LOG
X
X	/*
X	**  Release any resources used by the daemon code.
X	*/
X
X# ifdef DAEMON
X	clrdaemon();
X# endif DAEMON
X
X	/*
X	**  Make sure the alias database is open.
X	*/
X
X	initaliases(AliasFile, FALSE);
X
X	/*
X	**  Start making passes through the queue.
X	**	First, read and sort the entire queue.
X	**	Then, process the work in that order.
X	**		But if you take too long, start over.
X	*/
X
X	/* order the existing work requests */
X	(void) orderq(FALSE);
X
X	/* process them once at a time */
X	while (WorkQ != NULL)
X	{
X		WORK *w = WorkQ;
X
X		WorkQ = WorkQ->w_next;
X		dowork(w);
X		free(w->w_name);
X		free((char *) w);
X	}
X
X	/* exit without the usual cleanup */
X	exit(ExitStat);
X}
X/*
X**  ORDERQ -- order the work queue.
X**
X**	Parameters:
X**		doall -- if set, include everything in the queue (even
X**			the jobs that cannot be run because the load
X**			average is too high).  Otherwise, exclude those
X**			jobs.
X**
X**	Returns:
X**		The number of request in the queue (not necessarily
X**		the number of requests in WorkQ however).
X**
X**	Side Effects:
X**		Sets WorkQ to the queue of available work, in order.
X*/
X
X# define NEED_P		001
X# define NEED_T		002
X
Xorderq(doall)
X	bool doall;
X{
X	register struct dirent *d;
X	register WORK *w;
X	DIR *f;
X	register int i;
X	WORK wlist[QUEUESIZE+1];
X	int wn = -1;
X	extern workcmpf();
X
X	/* clear out old WorkQ */
X	for (w = WorkQ; w != NULL; )
X	{
X		register WORK *nw = w->w_next;
X
X		WorkQ = nw;
X		free(w->w_name);
X		free((char *) w);
X		w = nw;
X	}
X
X	/* open the queue directory */
X	f = opendir(".");
X	if (f == NULL)
X	{
X		syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
X		return (0);
X	}
X
X	/*
X	**  Read the work directory.
X	*/
X
X	while ((d = readdir(f)) != NULL)
X	{
X		FILE *cf;
X		char lbuf[MAXNAME];
X
X		/* is this an interesting entry? */
X		if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
X			continue;
X
X		/* yes -- open control file (if not too many files) */
X		if (++wn >= QUEUESIZE)
X			continue;
X		cf = fopen(d->d_name, "r");
X		if (cf == NULL)
X		{
X			/* this may be some random person sending hir msgs */
X			/* syserr("orderq: cannot open %s", cbuf); */
X#ifdef DEBUG
X			if (tTd(41, 2))
X				printf("orderq: cannot open %s (%d)\n",
X					d->d_name, errno);
X#endif DEBUG
X			errno = 0;
X			wn--;
X			continue;
X		}
X		w = &wlist[wn];
X		w->w_name = newstr(d->d_name);
X
X		/* make sure jobs in creation don't clog queue */
X		w->w_pri = 0x7fffffff;
X		w->w_ctime = 0;
X
X		/* extract useful information */
X		i = NEED_P | NEED_T;
X		while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
X		{
X			extern long atol();
X
X			switch (lbuf[0])
X			{
X			  case 'P':
X				w->w_pri = atol(&lbuf[1]);
X				i &= ~NEED_P;
X				break;
X
X			  case 'T':
X				w->w_ctime = atol(&lbuf[1]);
X				i &= ~NEED_T;
X				break;
X			}
X		}
X		(void) fclose(cf);
X
X		if (!doall && shouldqueue(w->w_pri))
X		{
X			/* don't even bother sorting this job in */
X			wn--;
X		}
X	}
X	(void) closedir(f);
X	wn++;
X
X	/*
X	**  Sort the work directory.
X	*/
X
X	qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf);
X
X	/*
X	**  Convert the work list into canonical form.
X	**	Should be turning it into a list of envelopes here perhaps.
X	*/
X
X	WorkQ = NULL;
X	for (i = min(wn, QUEUESIZE); --i >= 0; )
X	{
X		w = (WORK *) xalloc(sizeof *w);
X		w->w_name = wlist[i].w_name;
X		w->w_pri = wlist[i].w_pri;
X		w->w_ctime = wlist[i].w_ctime;
X		w->w_next = WorkQ;
X		WorkQ = w;
X	}
X
X# ifdef DEBUG
X	if (tTd(40, 1))
X	{
X		for (w = WorkQ; w != NULL; w = w->w_next)
X			printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
X	}
X# endif DEBUG
X
X	return (wn);
X}
X/*
X**  WORKCMPF -- compare function for ordering work.
X**
X**	Parameters:
X**		a -- the first argument.
X**		b -- the second argument.
X**
X**	Returns:
X**		-1 if a < b
X**		 0 if a == b
X**		+1 if a > b
X**
X**	Side Effects:
X**		none.
X*/
X
Xworkcmpf(a, b)
X	register WORK *a;
X	register WORK *b;
X{
X	long pa = a->w_pri + a->w_ctime;
X	long pb = b->w_pri + b->w_ctime;
X
X	if (pa == pb)
X		return (0);
X	else if (pa > pb)
X		return (1);
X	else
X		return (-1);
X}
X/*
X**  DOWORK -- do a work request.
X**
X**	Parameters:
X**		w -- the work request to be satisfied.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		The work request is satisfied if possible.
X*/
X
Xdowork(w)
X	register WORK *w;
X{
X	register int i;
X	extern bool shouldqueue();
X
X# ifdef DEBUG
X	if (tTd(40, 1))
X		printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
X# endif DEBUG
X
X	/*
X	**  Ignore jobs that are too expensive for the moment.
X	*/
X
X	if (shouldqueue(w->w_pri))
X	{
X		if (Verbose)
X			printf("\nSkipping %s\n", w->w_name + 2);
X		return;
X	}
X
X	/*
X	**  Fork for work.
X	*/
X
X	if (ForkQueueRuns)
X	{
X		i = fork();
X		if (i < 0)
X		{
X			syserr("dowork: cannot fork");
X			return;
X		}
X	}
X	else
X	{
X		i = 0;
X	}
X
X	if (i == 0)
X	{
X		/*
X		**  CHILD
X		**	Lock the control file to avoid duplicate deliveries.
X		**		Then run the file as though we had just read it.
X		**	We save an idea of the temporary name so we
X		**		can recover on interrupt.
X		*/
X
X		/* set basic modes, etc. */
X		(void) alarm(0);
X		clearenvelope(CurEnv, FALSE);
X		QueueRun = TRUE;
X		ErrorMode = EM_MAIL;
X		CurEnv->e_id = &w->w_name[2];
X# ifdef LOG
X		if (LogLevel > 11)
X			syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id,
X			       getpid());
X# endif LOG
X
X		/* don't use the headers from sendmail.cf... */
X		CurEnv->e_header = NULL;
X
X		/* lock the control file during processing */
X		if (link(w->w_name, queuename(CurEnv, 'l')) < 0)
X		{
X			/* being processed by another queuer */
X# ifdef LOG
X			if (LogLevel > 4)
X				syslog(LOG_DEBUG, "%s: locked", CurEnv->e_id);
X# endif LOG
X			if (ForkQueueRuns)
X				exit(EX_OK);
X			else
X				return;
X		}
X
X		/* do basic system initialization */
X		initsys();
X
X		/* read the queue control file */
X		readqf(CurEnv, TRUE);
X		CurEnv->e_flags |= EF_INQUEUE;
X		eatheader(CurEnv);
X
X		/* do the delivery */
X		if (!bitset(EF_FATALERRS, CurEnv->e_flags))
X			sendall(CurEnv, SM_DELIVER);
X
X		/* finish up and exit */
X		if (ForkQueueRuns)
X			finis();
X		else
X			dropenvelope(CurEnv);
X	}
X	else
X	{
X		/*
X		**  Parent -- pick up results.
X		*/
X
X		errno = 0;
X		(void) waitfor(i);
X	}
X}
X/*
X**  READQF -- read queue file and set up environment.
X**
X**	Parameters:
X**		e -- the envelope of the job to run.
X**		full -- if set, read in all information.  Otherwise just
X**			read in info needed for a queue print.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		cf is read and created as the current job, as though
X**		we had been invoked by argument.
X*/
X
Xreadqf(e, full)
X	register ENVELOPE *e;
X	bool full;
X{
X	char *qf;
X	register FILE *qfp;
X	char buf[MAXFIELD];
X	extern char *fgetfolded();
X	extern long atol();
X
X	/*
X	**  Read and process the file.
X	*/
X
X	qf = queuename(e, 'q');
X	qfp = fopen(qf, "r");
X	if (qfp == NULL)
X	{
X		syserr("readqf: no control file %s", qf);
X		return;
X	}
X	FileName = qf;
X	LineNumber = 0;
X	if (Verbose && full)
X		printf("\nRunning %s\n", e->e_id);
X	while (fgetfolded(buf, sizeof buf, qfp) != NULL)
X	{
X# ifdef DEBUG
X		if (tTd(40, 4))
X			printf("+++++ %s\n", buf);
X# endif DEBUG
X		switch (buf[0])
X		{
X		  case 'R':		/* specify recipient */
X			sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue);
X			break;
X
X		  case 'E':		/* specify error recipient */
X			sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue);
X			break;
X
X		  case 'H':		/* header */
X			if (full)
X				(void) chompheader(&buf[1], FALSE);
X			break;
X
X		  case 'M':		/* message */
X			e->e_message = newstr(&buf[1]);
X			break;
X
X		  case 'S':		/* sender */
X			setsender(newstr(&buf[1]));
X			break;
X
X		  case 'D':		/* data file name */
X			if (!full)
X				break;
X			e->e_df = newstr(&buf[1]);
X			e->e_dfp = fopen(e->e_df, "r");
X			if (e->e_dfp == NULL)
X				syserr("readqf: cannot open %s", e->e_df);
X			break;
X
X		  case 'T':		/* init time */
X			e->e_ctime = atol(&buf[1]);
X			break;
X
X		  case 'P':		/* message priority */
X			e->e_msgpriority = atol(&buf[1]) + WkTimeFact;
X			break;
X
X		  case '\0':		/* blank line; ignore */
X			break;
X
X		  default:
X			syserr("readqf(%s:%d): bad line \"%s\"", e->e_id,
X				LineNumber, buf);
X			break;
X		}
X	}
X
X	(void) fclose(qfp);
X	FileName = NULL;
X
X	/*
X	**  If we haven't read any lines, this queue file is empty.
X	**  Arrange to remove it without referencing any null pointers.
X	*/
X
X	if (LineNumber == 0)
X	{
X		errno = 0;
X		e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
X	}
X}
X/*
X**  PRINTQUEUE -- print out a representation of the mail queue
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Prints a listing of the mail queue on the standard output.
X*/
X
Xprintqueue()
X{
X	register WORK *w;
X	FILE *f;
X	int nrequests;
X	char buf[MAXLINE];
X
X	/*
X	**  Read and order the queue.
X	*/
X
X	nrequests = orderq(TRUE);
X
X	/*
X	**  Print the work list that we have read.
X	*/
X
X	/* first see if there is anything */
X	if (nrequests <= 0)
X	{
X		printf("Mail queue is empty\n");
X		return;
X	}
X
X	printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
X	if (nrequests > QUEUESIZE)
X		printf(", only %d printed", QUEUESIZE);
X	if (Verbose)
X		printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
X	else
X		printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
X	for (w = WorkQ; w != NULL; w = w->w_next)
X	{
X		struct stat st;
X		auto time_t submittime = 0;
X		long dfsize = -1;
X		char lf[20];
X		char message[MAXLINE];
X		extern bool shouldqueue();
X
X		f = fopen(w->w_name, "r");
X		if (f == NULL)
X		{
X			errno = 0;
X			continue;
X		}
X		printf("%7s", w->w_name + 2);
X		(void) strcpy(lf, w->w_name);
X		lf[0] = 'l';
X		if (stat(lf, &st) >= 0)
X			printf("*");
X		else if (shouldqueue(w->w_pri))
X			printf("X");
X		else
X			printf(" ");
X		errno = 0;
X
X		message[0] = '\0';
X		while (fgets(buf, sizeof buf, f) != NULL)
X		{
X			fixcrlf(buf, TRUE);
X			switch (buf[0])
X			{
X			  case 'M':	/* error message */
X				(void) strcpy(message, &buf[1]);
X				break;
X
X			  case 'S':	/* sender name */
X				if (Verbose)
X					printf("%8ld %10ld %.12s %.38s", dfsize,
X					    w->w_pri, ctime(&submittime) + 4,
X					    &buf[1]);
X				else
X					printf("%8ld %.16s %.45s", dfsize,
X					    ctime(&submittime), &buf[1]);
X				if (message[0] != '\0')
X					printf("\n\t\t (%.60s)", message);
X				break;
X
X			  case 'R':	/* recipient name */
X				if (Verbose)
X					printf("\n\t\t\t\t\t %.38s", &buf[1]);
X				else
X					printf("\n\t\t\t\t  %.45s", &buf[1]);
X				break;
X
X			  case 'T':	/* creation time */
X				submittime = atol(&buf[1]);
X				break;
X
X			  case 'D':	/* data file name */
X				if (stat(&buf[1], &st) >= 0)
X					dfsize = st.st_size;
X				break;
X			}
X		}
X		if (submittime == (time_t) 0)
X			printf(" (no control file)");
X		printf("\n");
X		(void) fclose(f);
X	}
X}
X
X# endif QUEUE
X/*
X**  QUEUENAME -- build a file name in the queue directory for this envelope.
X**
X**	Assigns an id code if one does not already exist.
X**	This code is very careful to avoid trashing existing files
X**	under any circumstances.
X**		We first create an nf file that is only used when
X**		assigning an id.  This file is always empty, so that
X**		we can never accidently truncate an lf file.
X**
X**	Parameters:
X**		e -- envelope to build it in/from.
X**		type -- the file type, used as the first character
X**			of the file name.
X**
X**	Returns:
X**		a pointer to the new file name (in a static buffer).
X**
X**	Side Effects:
X**		Will create the lf and qf files if no id code is
X**		already assigned.  This will cause the envelope
X**		to be modified.
X*/
X
Xchar *
Xqueuename(e, type)
X	register ENVELOPE *e;
X	char type;
X{
X	static char buf[MAXNAME];
X	static int pid = -1;
X	char c1 = 'A';
X	char c2 = 'A';
X
X	if (e->e_id == NULL)
X	{
X		char qf[20];
X		char nf[20];
X		char lf[20];
X
X		/* find a unique id */
X		if (pid != getpid())
X		{
X			/* new process -- start back at "AA" */
X			pid = getpid();
X			c1 = 'A';
X			c2 = 'A' - 1;
X		}
X		(void) sprintf(qf, "qfAA%05d", pid);
X		(void) strcpy(lf, qf);
X		lf[0] = 'l';
X		(void) strcpy(nf, qf);
X		nf[0] = 'n';
X
X		while (c1 < '~' || c2 < 'Z')
X		{
X			int i;
X
X			if (c2 >= 'Z')
X			{
X				c1++;
X				c2 = 'A' - 1;
X			}
X			lf[2] = nf[2] = qf[2] = c1;
X			lf[3] = nf[3] = qf[3] = ++c2;
X# ifdef DEBUG
X			if (tTd(7, 20))
X				printf("queuename: trying \"%s\"\n", nf);
X# endif DEBUG
X
X# ifdef QUEUE
X			if (access(lf, 0) >= 0 || access(qf, 0) >= 0)
X				continue;
X			errno = 0;
X			i = creat(nf, FileMode);
X			if (i < 0)
X			{
X				(void) unlink(nf);	/* kernel bug */
X				continue;
X			}
X			(void) close(i);
X			i = link(nf, lf);
X			(void) unlink(nf);
X			if (i < 0)
X				continue;
X			if (link(lf, qf) >= 0)
X				break;
X			(void) unlink(lf);
X# else QUEUE
X			if (close(creat(qf, FileMode)) >= 0)
X				break;
X# endif QUEUE
X		}
X		if (c1 >= '~' && c2 >= 'Z')
X		{
X			syserr("queuename: Cannot create \"%s\" in \"%s\"",
X				qf, QueueDir);
X			exit(EX_OSERR);
X		}
X		e->e_id = newstr(&qf[2]);
X		define('i', e->e_id, e);
X# ifdef DEBUG
X		if (tTd(7, 1))
X			printf("queuename: assigned id %s, env=%x\n", e->e_id, e);
X# ifdef LOG
X		if (LogLevel > 16)
X			syslog(LOG_DEBUG, "%s: assigned id", e->e_id);
X# endif LOG
X# endif DEBUG
X	}
X
X	if (type == '\0')
X		return (NULL);
X	(void) sprintf(buf, "%cf%s", type, e->e_id);
X# ifdef DEBUG
X	if (tTd(7, 2))
X		printf("queuename: %s\n", buf);
X# endif DEBUG
X	return (buf);
X}
X/*
X**  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
X**
X**	Parameters:
X**		e -- the envelope to unlock.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		unlocks the queue for `e'.
X*/
X
Xunlockqueue(e)
X	ENVELOPE *e;
X{
X	/* remove the transcript */
X#ifdef DEBUG
X# ifdef LOG
X	if (LogLevel > 19)
X		syslog(LOG_DEBUG, "%s: unlock", e->e_id);
X# endif LOG
X	if (!tTd(51, 4))
X#endif DEBUG
X		xunlink(queuename(e, 'x'));
X
X# ifdef QUEUE
X	/* last but not least, remove the lock */
X	xunlink(queuename(e, 'l'));
X# endif QUEUE
X}
END_OF_FILE
if test 20282 -ne `wc -c <'src/queue.c'`; then
    echo shar: \"'src/queue.c'\" unpacked with wrong size!
fi
# end of 'src/queue.c'
fi
if test -f 'src/sendmail.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/sendmail.h'\"
else
echo shar: Extracting \"'src/sendmail.h'\" \(19799 characters\)
sed "s/^X//" >'src/sendmail.h' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X**
X**	@(#)sendmail.h	5.10 (Berkeley) 10/14/86
X*/
X
X/*
X**  SENDMAIL.H -- Global definitions for sendmail.
X*/
X
X
X
X# ifdef _DEFINE
X# define EXTERN
X# ifndef lint
Xstatic char SmailSccsId[] =	"@(#)sendmail.h	5.10		10/14/86";
X# endif lint
X# else  _DEFINE
X# define EXTERN extern
X# endif _DEFINE
X
X# include <stdio.h>
X# include <ctype.h>
X# include <setjmp.h>
X# include "conf.h"
X# include "useful.h"
X
X# ifdef LOG
X# include <sys/syslog.h>
X# endif LOG
X
X# ifdef DAEMON
X# include <sys/socket.h>
X# include <netinet/in.h>
X# endif DAEMON
X
X
X# define PSBUFSIZE	(MAXNAME + MAXATOM)	/* size of prescan buffer */
X
X
X/*
X**  Data structure for bit maps.
X**
X**	Each bit in this map can be referenced by an ascii character.
X**	This is 128 possible bits, or 12 8-bit bytes.
X*/
X
X#define BITMAPBYTES	16	/* number of bytes in a bit map */
X#define BYTEBITS	8	/* number of bits in a byte */
X
X/* internal macros */
X#define _BITWORD(bit)	(bit / (BYTEBITS * sizeof (int)))
X#define _BITBIT(bit)	(1 << (bit % (BYTEBITS * sizeof (int))))
X
Xtypedef int	BITMAP[BITMAPBYTES / sizeof (int)];
X
X/* test bit number N */
X#define bitnset(bit, map)	((map)[_BITWORD(bit)] & _BITBIT(bit))
X
X/* set bit number N */
X#define setbitn(bit, map)	(map)[_BITWORD(bit)] |= _BITBIT(bit)
X
X/* clear bit number N */
X#define clrbitn(bit, map)	(map)[_BITWORD(bit)] &= ~_BITBIT(bit)
X
X/* clear an entire bit map */
X#define clrbitmap(map)		bzero((char *) map, BITMAPBYTES)
X/*
X**  Address structure.
X**	Addresses are stored internally in this structure.
X*/
X
Xstruct address
X{
X	char		*q_paddr;	/* the printname for the address */
X	char		*q_user;	/* user name */
X	char		*q_host;	/* host name */
X	struct mailer	*q_mailer;	/* mailer to use */
X	u_short		q_flags;	/* status flags, see below */
X	short		q_uid;		/* user-id of receiver (if known) */
X	short		q_gid;		/* group-id of receiver (if known) */
X	char		*q_home;	/* home dir (local mailer only) */
X	char		*q_fullname;	/* full name if known */
X	struct address	*q_next;	/* chain */
X	struct address	*q_alias;	/* address this results from */
X	struct address	*q_tchain;	/* temporary use chain */
X	time_t		q_timeout;	/* timeout for this address */
X};
X
Xtypedef struct address ADDRESS;
X
X# define QDONTSEND	000001	/* don't send to this address */
X# define QBADADDR	000002	/* this address is verified bad */
X# define QGOODUID	000004	/* the q_uid q_gid fields are good */
X# define QPRIMARY	000010	/* set from argv */
X# define QQUEUEUP	000020	/* queue for later transmission */
X/*
X**  Mailer definition structure.
X**	Every mailer known to the system is declared in this
X**	structure.  It defines the pathname of the mailer, some
X**	flags associated with it, and the argument vector to
X**	pass to it.  The flags are defined in conf.c
X**
X**	The argument vector is expanded before actual use.  All
X**	words except the first are passed through the macro
X**	processor.
X*/
X
Xstruct mailer
X{
X	char	*m_name;	/* symbolic name of this mailer */
X	char	*m_mailer;	/* pathname of the mailer to use */
X	BITMAP	m_flags;	/* status flags, see below */
X	short	m_mno;		/* mailer number internally */
X	char	**m_argv;	/* template argument vector */
X	short	m_s_rwset;	/* rewriting set for sender addresses */
X	short	m_r_rwset;	/* rewriting set for recipient addresses */
X	char	*m_eol;		/* end of line string */
X	long	m_maxsize;	/* size limit on message to this mailer */
X};
X
Xtypedef struct mailer	MAILER;
X
X/* bits for m_flags */
X# define M_CANONICAL	'C'	/* make addresses canonical "u@dom" */
X# define M_EXPENSIVE	'e'	/* it costs to use this mailer.... */
X# define M_ESCFROM	'E'	/* escape From lines to >From */
X# define M_FOPT		'f'	/* mailer takes picky -f flag */
X# define M_HST_UPPER	'h'	/* preserve host case distinction */
X# define M_INTERNAL	'I'	/* SMTP to another sendmail site */
X# define M_LOCAL	'l'	/* delivery is to this host */
X# define M_LIMITS	'L'	/* must enforce SMTP line limits */
X# define M_MUSER	'm'	/* can handle multiple users at once */
X# define M_NHDR		'n'	/* don't insert From line */
X# define M_FROMPATH	'p'	/* use reverse-path in MAIL FROM: */
X# define M_ROPT		'r'	/* mailer takes picky -r flag */
X# define M_SECURE_PORT	'R'	/* try to send on a reserved TCP port */
X# define M_STRIPQ	's'	/* strip quote chars from user/host */
X# define M_RESTR	'S'	/* must be daemon to execute */
X# define M_USR_UPPER	'u'	/* preserve user case distinction */
X# define M_UGLYUUCP	'U'	/* this wants an ugly UUCP from line */
X# define M_XDOT		'X'	/* use hidden-dot algorithm */
X
XEXTERN MAILER	*Mailer[MAXMAILERS+1];
X
XEXTERN MAILER	*LocalMailer;		/* ptr to local mailer */
XEXTERN MAILER	*ProgMailer;		/* ptr to program mailer */
X/*
X**  Header structure.
X**	This structure is used internally to store header items.
X*/
X
Xstruct header
X{
X	char		*h_field;	/* the name of the field */
X	char		*h_value;	/* the value of that field */
X	struct header	*h_link;	/* the next header */
X	u_short		h_flags;	/* status bits, see below */
X	BITMAP		h_mflags;	/* m_flags bits needed */
X};
X
Xtypedef struct header	HDR;
X
X/*
X**  Header information structure.
X**	Defined in conf.c, this struct declares the header fields
X**	that have some magic meaning.
X*/
X
Xstruct hdrinfo
X{
X	char	*hi_field;	/* the name of the field */
X	u_short	hi_flags;	/* status bits, see below */
X};
X
Xextern struct hdrinfo	HdrInfo[];
X
X/* bits for h_flags and hi_flags */
X# define H_EOH		00001	/* this field terminates header */
X# define H_RCPT		00002	/* contains recipient addresses */
X# define H_DEFAULT	00004	/* if another value is found, drop this */
X# define H_RESENT	00010	/* this address is a "Resent-..." address */
X# define H_CHECK	00020	/* check h_mflags against m_flags */
X# define H_ACHECK	00040	/* ditto, but always (not just default) */
X# define H_FORCE	00100	/* force this field, even if default */
X# define H_TRACE	00200	/* this field contains trace information */
X# define H_FROM		00400	/* this is a from-type field */
X# define H_VALID	01000	/* this field has a validated value */
X/*
X**  Envelope structure.
X**	This structure defines the message itself.  There is usually
X**	only one of these -- for the message that we originally read
X**	and which is our primary interest -- but other envelopes can
X**	be generated during processing.  For example, error messages
X**	will have their own envelope.
X*/
X
Xstruct envelope
X{
X	HDR		*e_header;	/* head of header list */
X	long		e_msgpriority;	/* adjusted priority of this message */
X	time_t		e_ctime;	/* time message appeared in the queue */
X	char		*e_to;		/* the target person */
X	char		*e_receiptto;	/* return receipt address */
X	ADDRESS		e_from;		/* the person it is from */
X	char		**e_fromdomain;	/* the domain part of the sender */
X	ADDRESS		*e_sendqueue;	/* list of message recipients */
X	ADDRESS		*e_errorqueue;	/* the queue for error responses */
X	long		e_msgsize;	/* size of the message in bytes */
X	int		e_nrcpts;	/* number of recipients */
X	short		e_class;	/* msg class (priority, junk, etc.) */
X	short		e_flags;	/* flags, see below */
X	short		e_hopcount;	/* number of times processed */
X	int		(*e_puthdr)();	/* function to put header of message */
X	int		(*e_putbody)();	/* function to put body of message */
X	struct envelope	*e_parent;	/* the message this one encloses */
X	struct envelope *e_sibling;	/* the next envelope of interest */
X	char		*e_df;		/* location of temp file */
X	FILE		*e_dfp;		/* temporary file */
X	char		*e_id;		/* code for this entry in queue */
X	FILE		*e_xfp;		/* transcript file */
X	char		*e_message;	/* error message */
X	char		*e_macro[128];	/* macro definitions */
X};
X
Xtypedef struct envelope	ENVELOPE;
X
X/* values for e_flags */
X#define EF_OLDSTYLE	000001		/* use spaces (not commas) in hdrs */
X#define EF_INQUEUE	000002		/* this message is fully queued */
X#define EF_TIMEOUT	000004		/* this message is too old */
X#define EF_CLRQUEUE	000010		/* disk copy is no longer needed */
X#define EF_SENDRECEIPT	000020		/* send a return receipt */
X#define EF_FATALERRS	000040		/* fatal errors occured */
X#define EF_KEEPQUEUE	000100		/* keep queue files always */
X#define EF_RESPONSE	000200		/* this is an error or return receipt */
X#define EF_RESENT	000400		/* this message is being forwarded */
X
XEXTERN ENVELOPE	*CurEnv;	/* envelope currently being processed */
X/*
X**  Message priority classes.
X**
X**	The message class is read directly from the Priority: header
X**	field in the message.
X**
X**	CurEnv->e_msgpriority is the number of bytes in the message plus
X**	the creation time (so that jobs ``tend'' to be ordered correctly),
X**	adjusted by the message class, the number of recipients, and the
X**	amount of time the message has been sitting around.  This number
X**	is used to order the queue.  Higher values mean LOWER priority.
X**
X**	Each priority class point is worth WkClassFact priority points;
X**	each recipient is worth WkRecipFact priority points.  Each time
X**	we reprocess a message the priority is adjusted by WkTimeFact.
X**	WkTimeFact should normally decrease the priority so that jobs
X**	that have historically failed will be run later; thanks go to
X**	Jay Lepreau at Utah for pointing out the error in my thinking.
X**
X**	The "class" is this number, unadjusted by the age or size of
X**	this message.  Classes with negative representations will have
X**	error messages thrown away if they are not local.
X*/
X
Xstruct priority
X{
X	char	*pri_name;	/* external name of priority */
X	int	pri_val;	/* internal value for same */
X};
X
XEXTERN struct priority	Priorities[MAXPRIORITIES];
XEXTERN int		NumPriorities;	/* pointer into Priorities */
X/*
X**  Rewrite rules.
X*/
X
Xstruct rewrite
X{
X	char	**r_lhs;	/* pattern match */
X	char	**r_rhs;	/* substitution value */
X	struct rewrite	*r_next;/* next in chain */
X};
X
XEXTERN struct rewrite	*RewriteRules[MAXRWSETS];
X
X/*
X**  Special characters in rewriting rules.
X**	These are used internally only.
X**	The COND* rules are actually used in macros rather than in
X**		rewriting rules, but are given here because they
X**		cannot conflict.
X*/
X
X/* left hand side items */
X# define MATCHZANY	'\020'	/* match zero or more tokens */
X# define MATCHANY	'\021'	/* match one or more tokens */
X# define MATCHONE	'\022'	/* match exactly one token */
X# define MATCHCLASS	'\023'	/* match one token in a class */
X# define MATCHNCLASS	'\024'	/* match anything not in class */
X# define MATCHREPL	'\025'	/* replacement on RHS for above */
X
X/* right hand side items */
X# define CANONNET	'\026'	/* canonical net, next token */
X# define CANONHOST	'\027'	/* canonical host, next token */
X# define CANONUSER	'\030'	/* canonical user, next N tokens */
X# define CALLSUBR	'\031'	/* call another rewriting set */
X
X/* conditionals in macros */
X# define CONDIF		'\032'	/* conditional if-then */
X# define CONDELSE	'\033'	/* conditional else */
X# define CONDFI		'\034'	/* conditional fi */
X
X/* bracket characters for host name lookup */
X# define HOSTBEGIN	'\035'	/* hostname lookup begin */
X# define HOSTEND	'\036'	/* hostname lookup end */
X
X/* \001 is also reserved as the macro expansion character */
X/*
X**  Information about hosts that we have looked up recently.
X**
X**	This stuff is 4.2/3bsd specific.
X*/
X
X# ifdef DAEMON
X# ifdef VMUNIX
X
X# define HOSTINFO	struct hostinfo
X
XHOSTINFO
X{
X	char		*ho_name;	/* name of this host */
X	struct in_addr	ho_inaddr;	/* internet address */
X	short		ho_flags;	/* flag bits, see below */
X	short		ho_errno;	/* error number on last connection */
X	short		ho_exitstat;	/* exit status from last connection */
X};
X
X
X/* flag bits */
X#define HOF_VALID	00001		/* this entry is valid */
X
X# endif VMUNIX
X# endif DAEMON
X/*
X**  Symbol table definitions
X*/
X
Xstruct symtab
X{
X	char		*s_name;	/* name to be entered */
X	char		s_type;		/* general type (see below) */
X	struct symtab	*s_next;	/* pointer to next in chain */
X	union
X	{
X		BITMAP		sv_class;	/* bit-map of word classes */
X		ADDRESS		*sv_addr;	/* pointer to address header */
X		MAILER		*sv_mailer;	/* pointer to mailer */
X		char		*sv_alias;	/* alias */
X# ifdef HOSTINFO
X		HOSTINFO	sv_host;	/* host information */
X# endif HOSTINFO
X	}	s_value;
X};
X
Xtypedef struct symtab	STAB;
X
X/* symbol types */
X# define ST_UNDEF	0	/* undefined type */
X# define ST_CLASS	1	/* class map */
X# define ST_ADDRESS	2	/* an address in parsed format */
X# define ST_MAILER	3	/* a mailer header */
X# define ST_ALIAS	4	/* an alias */
X# define ST_HOST	5	/* host information */
X
X# define s_class	s_value.sv_class
X# define s_address	s_value.sv_addr
X# define s_mailer	s_value.sv_mailer
X# define s_alias	s_value.sv_alias
X# define s_host		s_value.sv_host
X
Xextern STAB	*stab();
X
X/* opcodes to stab */
X# define ST_FIND	0	/* find entry */
X# define ST_ENTER	1	/* enter if not there */
X/*
X**  STRUCT EVENT -- event queue.
X**
X**	Maintained in sorted order.
X**
X**	We store the pid of the process that set this event to insure
X**	that when we fork we will not take events intended for the parent.
X*/
X
Xstruct event
X{
X	time_t		ev_time;	/* time of the function call */
X	int		(*ev_func)();	/* function to call */
X	int		ev_arg;		/* argument to ev_func */
X	int		ev_pid;		/* pid that set this event */
X	struct event	*ev_link;	/* link to next item */
X};
X
Xtypedef struct event	EVENT;
X
XEXTERN EVENT	*EventQueue;		/* head of event queue */
X/*
X**  Operation, send, and error modes
X**
X**	The operation mode describes the basic operation of sendmail.
X**	This can be set from the command line, and is "send mail" by
X**	default.
X**
X**	The send mode tells how to send mail.  It can be set in the
X**	configuration file.  It's setting determines how quickly the
X**	mail will be delivered versus the load on your system.  If the
X**	-v (verbose) flag is given, it will be forced to SM_DELIVER
X**	mode.
X**
X**	The error mode tells how to return errors.
X*/
X
XEXTERN char	OpMode;		/* operation mode, see below */
X
X#define MD_DELIVER	'm'		/* be a mail sender */
X#define MD_ARPAFTP	'a'		/* old-style arpanet protocols */
X#define MD_SMTP		's'		/* run SMTP on standard input */
X#define MD_DAEMON	'd'		/* run as a daemon */
X#define MD_VERIFY	'v'		/* verify: don't collect or deliver */
X#define MD_TEST		't'		/* test mode: resolve addrs only */
X#define MD_INITALIAS	'i'		/* initialize alias database */
X#define MD_PRINT	'p'		/* print the queue */
X#define MD_FREEZE	'z'		/* freeze the configuration file */
X
X
XEXTERN char	SendMode;	/* send mode, see below */
X
X#define SM_DELIVER	'i'		/* interactive delivery */
X#define SM_QUICKD	'j'		/* deliver w/o queueing */
X#define SM_FORK		'b'		/* deliver in background */
X#define SM_QUEUE	'q'		/* queue, don't deliver */
X#define SM_VERIFY	'v'		/* verify only (used internally) */
X
X/* used only as a parameter to sendall */
X#define SM_DEFAULT	'\0'		/* unspecified, use SendMode */
X
X
XEXTERN char	ErrorMode;	/* error mode, see below */
X
X#define EM_PRINT	'p'		/* print errors */
X#define EM_MAIL		'm'		/* mail back errors */
X#define EM_WRITE	'w'		/* write back errors */
X#define EM_BERKNET	'e'		/* special berknet processing */
X#define EM_QUIET	'q'		/* don't print messages (stat only) */
X
X/* offset used to issure that the error messages for name server error
X * codes are unique.
X */
X#define	MAX_ERRNO	100
X/*
X**  Global variables.
X*/
X
XEXTERN bool	FromFlag;	/* if set, "From" person is explicit */
XEXTERN bool	NoAlias;	/* if set, don't do any aliasing */
XEXTERN bool	ForceMail;	/* if set, mail even if already got a copy */
XEXTERN bool	MeToo;		/* send to the sender also */
XEXTERN bool	IgnrDot;	/* don't let dot end messages */
XEXTERN bool	SaveFrom;	/* save leading "From" lines */
XEXTERN bool	Verbose;	/* set if blow-by-blow desired */
XEXTERN bool	GrabTo;		/* if set, get recipients from msg */
XEXTERN bool	NoReturn;	/* don't return letter to sender */
XEXTERN bool	SuprErrs;	/* set if we are suppressing errors */
XEXTERN bool	QueueRun;	/* currently running message from the queue */
XEXTERN bool	HoldErrs;	/* only output errors to transcript */
XEXTERN bool	NoConnect;	/* don't connect to non-local mailers */
XEXTERN bool	SuperSafe;	/* be extra careful, even if expensive */
XEXTERN bool	ForkQueueRuns;	/* fork for each job when running the queue */
XEXTERN bool	AutoRebuild;	/* auto-rebuild the alias database as needed */
XEXTERN bool	CheckAliases;	/* parse addresses during newaliases */
XEXTERN int	SafeAlias;	/* minutes to wait until @:@ in alias file */
XEXTERN time_t	TimeOut;	/* time until timeout */
XEXTERN FILE	*InChannel;	/* input connection */
XEXTERN FILE	*OutChannel;	/* output connection */
XEXTERN int	RealUid;	/* when Daemon, real uid of caller */
XEXTERN int	RealGid;	/* when Daemon, real gid of caller */
XEXTERN int	DefUid;		/* default uid to run as */
XEXTERN int	DefGid;		/* default gid to run as */
XEXTERN int	OldUmask;	/* umask when sendmail starts up */
XEXTERN int	Errors;		/* set if errors (local to single pass) */
XEXTERN int	ExitStat;	/* exit status code */
XEXTERN int	AliasLevel;	/* depth of aliasing */
XEXTERN int	MotherPid;	/* proc id of parent process */
XEXTERN int	LineNumber;	/* line number in current input */
XEXTERN time_t	ReadTimeout;	/* timeout on reads */
XEXTERN int	LogLevel;	/* level of logging to perform */
XEXTERN int	FileMode;	/* mode on files */
XEXTERN int	QueueLA;	/* load average starting forced queueing */
XEXTERN int	RefuseLA;	/* load average refusing connections are */
XEXTERN int	QueueFactor;	/* slope of queue function */
XEXTERN time_t	QueueIntvl;	/* intervals between running the queue */
XEXTERN char	*AliasFile;	/* location of alias file */
XEXTERN char	*HelpFile;	/* location of SMTP help file */
XEXTERN char	*StatFile;	/* location of statistics summary */
XEXTERN char	*QueueDir;	/* location of queue directory */
XEXTERN char	*FileName;	/* name to print on error messages */
XEXTERN char	*SmtpPhase;	/* current phase in SMTP processing */
XEXTERN char	*MyHostName;	/* name of this host for SMTP messages */
XEXTERN char	*RealHostName;	/* name of host we are talking to */
XEXTERN char	*CurHostName;	/* current host we are dealing with */
XEXTERN jmp_buf	TopFrame;	/* branch-to-top-of-loop-on-error frame */
XEXTERN bool	QuickAbort;	/*  .... but only if we want a quick abort */
Xextern char	*ConfFile;	/* location of configuration file [conf.c] */
Xextern char	*FreezeFile;	/* location of frozen memory image [conf.c] */
Xextern char	Arpa_Info[];	/* the reply code for Arpanet info [conf.c] */
Xextern ADDRESS	NullAddress;	/* a null (template) address [main.c] */
XEXTERN char	SpaceSub;	/* substitution for <lwsp> */
XEXTERN int	WkClassFact;	/* multiplier for message class -> priority */
XEXTERN int	WkRecipFact;	/* multiplier for # of recipients -> priority */
XEXTERN int	WkTimeFact;	/* priority offset each time this job is run */
XEXTERN int	CheckPointLimit;	/* deliveries before checkpointing */
XEXTERN int	Nmx;			/* number of MX RRs */
XEXTERN char	*PostMasterCopy;	/* address to get errs cc's */
XEXTERN char	*MxHosts[MAXMXHOSTS+1];	/* for MX RRs */
XEXTERN char	*TrustedUsers[MAXTRUST+1];	/* list of trusted users */
XEXTERN char	*UserEnviron[MAXUSERENVIRON+1];	/* saved user environment */
X/*
X**  Trace information
X*/
X
X/* trace vector and macros for debugging flags */
XEXTERN u_char	tTdvect[100];
X# define tTd(flag, level)	(tTdvect[flag] >= level)
X# define tTdlevel(flag)		(tTdvect[flag])
X/*
X**  Miscellaneous information.
X*/
X
X# include	<sysexits.h>
X
X
X/*
X**  Some in-line functions
X*/
X
X/* set exit status */
X#define setstat(s)	{ \
X				if (ExitStat == EX_OK || ExitStat == EX_TEMPFAIL) \
X					ExitStat = s; \
X			}
X
X/* make a copy of a string */
X#define newstr(s)	strcpy(xalloc(strlen(s) + 1), s)
X
X#define STRUCTCOPY(s, d)	d = s
X
X
X/*
X**  Declarations of useful functions
X*/
X
Xextern ADDRESS	*parseaddr();
Xextern char	*xalloc();
Xextern bool	sameaddr();
Xextern FILE	*dfopen();
Xextern EVENT	*setevent();
Xextern char	*sfgets();
Xextern char	*queuename();
Xextern time_t	curtime();
END_OF_FILE
if test 19799 -ne `wc -c <'src/sendmail.h'`; then
    echo shar: \"'src/sendmail.h'\" unpacked with wrong size!
fi
# end of 'src/sendmail.h'
fi
echo shar: End of archive 6 \(of 8\).
cp /dev/null ark6isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
David H. Brierley
Home: dave@galaxia.Newport.RI.US   {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb@rayssd.ray.com           {sun,decuac,gatech,necntc,ukma}!rayssd!dhb

dave@galaxia.Newport.RI.US (David H. Brierley) (02/25/89)

----- cut here and feed to /bin/sh -----
#! /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 archive 7 (of 8)."
# Contents:  src/main.c src/parseaddr.c
# Wrapped by dave@galaxia on Fri Feb 24 20:24:15 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/main.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/main.c'\"
else
echo shar: Extracting \"'src/main.c'\" \(21371 characters\)
sed "s/^X//" >'src/main.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xchar copyright[] =
X"@(#) Copyright (c) 1980 Regents of the University of California.\n\
X All rights reserved.\n";
X#endif not lint
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)main.c	5.13 (Berkeley) 3/24/87";
X#endif not lint
X
X# define  _DEFINE
X# include <signal.h>
X# include <sgtty.h>
X# include "sendmail.h"
X
X# ifdef lint
Xchar	edata, end;
X# endif lint
X
X/*
X**  SENDMAIL -- Post mail to a set of destinations.
X**
X**	This is the basic mail router.  All user mail programs should
X**	call this routine to actually deliver mail.  Sendmail in
X**	turn calls a bunch of mail servers that do the real work of
X**	delivering the mail.
X**
X**	Sendmail is driven by tables read in from /usr/lib/sendmail.cf
X**	(read by readcf.c).  Some more static configuration info,
X**	including some code that you may want to tailor for your
X**	installation, is in conf.c.  You may also want to touch
X**	daemon.c (if you have some other IPC mechanism), acct.c
X**	(to change your accounting), names.c (to adjust the name
X**	server mechanism).
X**
X**	Usage:
X**		/usr/lib/sendmail [flags] addr ...
X**
X**		See the associated documentation for details.
X**
X**	Author:
X**		Eric Allman, UCB/INGRES (until 10/81)
X**			     Britton-Lee, Inc., purveyors of fine
X**				database computers (from 11/81)
X**		The support of the INGRES Project and Britton-Lee is
X**			gratefully acknowledged.  Britton-Lee in
X**			particular had absolutely nothing to gain from
X**			my involvement in this project.
X*/
X
X
X
X
X
Xint		NextMailer;	/* "free" index into Mailer struct */
Xchar		*FullName;	/* sender's full name */
XENVELOPE	BlankEnvelope;	/* a "blank" envelope */
XENVELOPE	MainEnvelope;	/* the envelope around the basic letter */
XADDRESS		NullAddress =	/* a null address */
X		{ "", "", "" };
X
X/*
X**  Pointers for setproctitle.
X**	This allows "ps" listings to give more useful information.
X**	These must be kept out of BSS for frozen configuration files
X**		to work.
X*/
X
X# ifdef SETPROCTITLE
Xchar		**Argv = NULL;		/* pointer to argument vector */
Xchar		*LastArgv = NULL;	/* end of argv */
X# endif SETPROCTITLE
X
X#ifdef DAEMON
X#ifndef SMTP
XERROR %%%%   Cannot have daemon mode without SMTP   %%%% ERROR
X#endif SMTP
X#endif DAEMON
X
X
X
X
X
X
Xmain(argc, argv, envp)
X	int argc;
X	char **argv;
X	char **envp;
X{
X	register char *p;
X	char **av;
X	extern int finis();
X	extern char Version[];
X	char *from;
X	typedef int (*fnptr)();
X	STAB *st;
X	register int i;
X	bool readconfig = TRUE;
X	bool queuemode = FALSE;		/* process queue requests */
X	bool nothaw;
X	static bool reenter = FALSE;
X	char jbuf[30];			/* holds MyHostName */
X	extern bool safefile();
X	extern time_t convtime();
X	extern putheader(), putbody();
X	extern ENVELOPE *newenvelope();
X	extern intsig();
X	extern char **myhostname();
X	extern char *arpadate();
X	extern char **environ;
X
X	/*
X	**  Check to see if we reentered.
X	**	This would normally happen if e_putheader or e_putbody
X	**	were NULL when invoked.
X	*/
X
X	if (reenter)
X	{
X		syserr("main: reentered!");
X		abort();
X	}
X	reenter = TRUE;
X
X#ifdef notdef
X	/*
X	 * the following line does not have the effect the author expected --
X	 * it makes the time always use eastern time EST, EDT -- allyn
X	 */
X
X	/* Enforce use of local time */
X	unsetenv("TZ");
X#endif
X
X	/*
X	**  Be sure we have enough file descriptors.
X	**	But also be sure that 0, 1, & 2 are open.
X	*/
X
X	i = open("/dev/null", 2);
X	while (i >= 0 && i < 2)
X		i = dup(i);
X	for (i = 3; i < 50; i++)
X		(void) close(i);
X	errno = 0;
X
X	/*
X	**  Set default values for variables.
X	**	These cannot be in initialized data space.
X	*/
X
X	setdefaults();
X
X	/* set up the blank envelope */
X	BlankEnvelope.e_puthdr = putheader;
X	BlankEnvelope.e_putbody = putbody;
X	BlankEnvelope.e_xfp = NULL;
X	STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
X	CurEnv = &BlankEnvelope;
X	STRUCTCOPY(NullAddress, MainEnvelope.e_from);
X
X	/*
X	**  Do a quick prescan of the argument list.
X	**	We do this to find out if we can potentially thaw the
X	**	configuration file.  If not, we do the thaw now so that
X	**	the argument processing applies to this run rather than
X	**	to the run that froze the configuration.
X	*/
X
X	argv[argc] = NULL;
X	av = argv;
X	nothaw = FALSE;
X	while ((p = *++av) != NULL)
X	{
X		if (strncmp(p, "-C", 2) == 0)
X		{
X			ConfFile = &p[2];
X			if (ConfFile[0] == '\0')
X				ConfFile = "sendmail.cf";
X			(void) setgid(getrgid());
X			(void) setuid(getruid());
X			nothaw = TRUE;
X		}
X		else if (strncmp(p, "-bz", 3) == 0)
X			nothaw = TRUE;
X# ifdef DEBUG
X		else if (strncmp(p, "-d", 2) == 0)
X		{
X			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
X			tTflag(&p[2]);
X			setbuf(stdout, (char *) NULL);
X			printf("Version %s\n", Version);
X		}
X# endif DEBUG
X	}
X	if (!nothaw)
X		readconfig = !thaw(FreezeFile);
X
X	/* reset the environment after the thaw */
X	for (i = 0; i < MAXUSERENVIRON && envp[i] != NULL; i++)
X		UserEnviron[i] = newstr(envp[i]);
X	UserEnviron[i] = NULL;
X	environ = UserEnviron;
X
X# ifdef SETPROCTITLE
X	/*
X	**  Save start and extent of argv for setproctitle.
X	*/
X
X	Argv = argv;
X	if (i > 0)
X		LastArgv = envp[i - 1] + strlen(envp[i - 1]);
X	else
X		LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
X# endif SETPROCTITLE
X
X	/*
X	**  Now do basic initialization
X	*/
X
X	InChannel = stdin;
X	OutChannel = stdout;
X	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGINT, intsig);
X	if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
X		(void) signal(SIGHUP, intsig);
X	(void) signal(SIGTERM, intsig);
X	(void) signal(SIGPIPE, SIG_IGN);
X	OldUmask = umask(0);
X	OpMode = MD_DELIVER;
X	MotherPid = getpid();
X# ifndef V6
X	FullName = getenv("NAME");
X# endif V6
X
X# ifdef LOG
X	openlog("sendmail", LOG_PID, LOG_MAIL);
X# endif LOG
X	errno = 0;
X	from = NULL;
X
X	if (readconfig)
X	{
X		/* initialize some macros, etc. */
X		initmacros();
X
X		/* hostname */
X		av = myhostname(jbuf, sizeof jbuf);
X		if (jbuf[0] != '\0')
X		{
X#ifdef DEBUG
X			if (tTd(0, 4))
X				printf("canonical name: %s\n", jbuf);
X#endif DEBUG
X			p = newstr(jbuf);
X			define('w', p, CurEnv);
X			setclass('w', p);
X		}
X		while (av != NULL && *av != NULL)
X		{
X#ifdef DEBUG
X			if (tTd(0, 4))
X				printf("\ta.k.a.: %s\n", *av);
X#endif DEBUG
X			setclass('w', *av++);
X		}
X
X		/* version */
X		define('v', Version, CurEnv);
X	}
X
X	/* current time */
X	define('b', arpadate((char *) NULL), CurEnv);
X
X	/*
X	** Crack argv.
X	*/
X
X	av = argv;
X	p = rindex(*av, '/');
X	if (p++ == NULL)
X		p = *av;
X	if (strcmp(p, "newaliases") == 0)
X		OpMode = MD_INITALIAS;
X	else if (strcmp(p, "mailq") == 0)
X		OpMode = MD_PRINT;
X	else if (strcmp(p, "smtpd") == 0)
X		OpMode = MD_DAEMON;
X	while ((p = *++av) != NULL && p[0] == '-')
X	{
X		switch (p[1])
X		{
X		  case 'b':	/* operations mode */
X			switch (p[2])
X			{
X			  case MD_DAEMON:
X# ifndef DAEMON
X				syserr("Daemon mode not implemented");
X				break;
X# endif DAEMON
X			  case MD_SMTP:
X# ifndef SMTP
X				syserr("I don't speak SMTP");
X				break;
X# endif SMTP
X			  case MD_ARPAFTP:
X			  case MD_DELIVER:
X			  case MD_VERIFY:
X			  case MD_TEST:
X			  case MD_INITALIAS:
X			  case MD_PRINT:
X			  case MD_FREEZE:
X				OpMode = p[2];
X				break;
X
X			  default:
X				syserr("Invalid operation mode %c", p[2]);
X				break;
X			}
X			break;
X
X		  case 'C':	/* select configuration file (already done) */
X			break;
X
X# ifdef DEBUG
X		  case 'd':	/* debugging -- redo in case frozen */
X			tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
X			tTflag(&p[2]);
X			setbuf(stdout, (char *) NULL);
X			break;
X# endif DEBUG
X
X		  case 'f':	/* from address */
X		  case 'r':	/* obsolete -f flag */
X			p += 2;
X			if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
X			{
X				p = *++av;
X				if (p == NULL || *p == '-')
X				{
X					syserr("No \"from\" person");
X					av--;
X					break;
X				}
X			}
X			if (from != NULL)
X			{
X				syserr("More than one \"from\" person");
X				break;
X			}
X			from = newstr(p);
X			break;
X
X		  case 'F':	/* set full name */
X			p += 2;
X			if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
X			{
X				syserr("Bad -F flag");
X				av--;
X				break;
X			}
X			FullName = newstr(p);
X			break;
X
X		  case 'h':	/* hop count */
X			p += 2;
X			if (*p == '\0' && ((p = *++av) == NULL || !isdigit(*p)))
X			{
X				syserr("Bad hop count (%s)", p);
X				av--;
X				break;
X			}
X			CurEnv->e_hopcount = atoi(p);
X			break;
X		
X		  case 'n':	/* don't alias */
X			NoAlias = TRUE;
X			break;
X
X		  case 'o':	/* set option */
X			setoption(p[2], &p[3], FALSE, TRUE);
X			break;
X
X		  case 'q':	/* run queue files at intervals */
X# ifdef QUEUE
X			queuemode = TRUE;
X			QueueIntvl = convtime(&p[2]);
X# else QUEUE
X			syserr("I don't know about queues");
X# endif QUEUE
X			break;
X
X		  case 't':	/* read recipients from message */
X			GrabTo = TRUE;
X			break;
X
X			/* compatibility flags */
X		  case 'c':	/* connect to non-local mailers */
X		  case 'e':	/* error message disposition */
X		  case 'i':	/* don't let dot stop me */
X		  case 'm':	/* send to me too */
X		  case 'T':	/* set timeout interval */
X		  case 'v':	/* give blow-by-blow description */
X			setoption(p[1], &p[2], FALSE, TRUE);
X			break;
X
X		  case 's':	/* save From lines in headers */
X			setoption('f', &p[2], FALSE, TRUE);
X			break;
X
X# ifdef DBM
X		  case 'I':	/* initialize alias DBM file */
X			OpMode = MD_INITALIAS;
X			break;
X# endif DBM
X		}
X	}
X
X	/*
X	**  Do basic initialization.
X	**	Read system control file.
X	**	Extract special fields for local use.
X	*/
X
X	if (OpMode == MD_FREEZE || readconfig)
X		readcf(ConfFile);
X
X	switch (OpMode)
X	{
X	  case MD_FREEZE:
X		/* this is critical to avoid forgeries of the frozen config */
X		(void) setgid(getgid());
X		(void) setuid(getuid());
X
X		/* freeze the configuration */
X		freeze(FreezeFile);
X		exit(EX_OK);
X
X	  case MD_INITALIAS:
X		Verbose = TRUE;
X		break;
X	}
X
X	/* do heuristic mode adjustment */
X	if (Verbose)
X	{
X		/* turn off noconnect option */
X		setoption('c', "F", TRUE, FALSE);
X
X		/* turn on interactive delivery */
X		setoption('d', "", TRUE, FALSE);
X	}
X
X	/* our name for SMTP codes */
X	expand("\001j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
X	MyHostName = jbuf;
X
X	/* the indices of local and program mailers */
X	st = stab("local", ST_MAILER, ST_FIND);
X	if (st == NULL)
X		syserr("No local mailer defined");
X	else
X		LocalMailer = st->s_mailer;
X	st = stab("prog", ST_MAILER, ST_FIND);
X	if (st == NULL)
X		syserr("No prog mailer defined");
X	else
X		ProgMailer = st->s_mailer;
X
X	/* operate in queue directory */
X	if (chdir(QueueDir) < 0)
X	{
X		syserr("cannot chdir(%s)", QueueDir);
X		exit(EX_SOFTWARE);
X	}
X
X	/*
X	**  Do operation-mode-dependent initialization.
X	*/
X
X	switch (OpMode)
X	{
X	  case MD_PRINT:
X		/* print the queue */
X#ifdef QUEUE
X		dropenvelope(CurEnv);
X		printqueue();
X		exit(EX_OK);
X#else QUEUE
X		usrerr("No queue to print");
X		finis();
X#endif QUEUE
X
X	  case MD_INITALIAS:
X		/* initialize alias database */
X		initaliases(AliasFile, TRUE);
X		exit(EX_OK);
X
X	  case MD_DAEMON:
X		/* don't open alias database -- done in srvrsmtp */
X		break;
X
X	  default:
X		/* open the alias database */
X		initaliases(AliasFile, FALSE);
X		break;
X	}
X
X# ifdef DEBUG
X	if (tTd(0, 15))
X	{
X		/* print configuration table (or at least part of it) */
X		printrules();
X		for (i = 0; i < MAXMAILERS; i++)
X		{
X			register struct mailer *m = Mailer[i];
X			int j;
X
X			if (m == NULL)
X				continue;
X			printf("mailer %d (%s): P=%s S=%d R=%d M=%ld F=", i, m->m_name,
X				m->m_mailer, m->m_s_rwset, m->m_r_rwset,
X				m->m_maxsize);
X			for (j = '\0'; j <= '\177'; j++)
X				if (bitnset(j, m->m_flags))
X					(void) putchar(j);
X			printf(" E=");
X			xputs(m->m_eol);
X			printf("\n");
X		}
X	}
X# endif DEBUG
X
X	/*
X	**  Switch to the main envelope.
X	*/
X
X	CurEnv = newenvelope(&MainEnvelope);
X	MainEnvelope.e_flags = BlankEnvelope.e_flags;
X
X	/*
X	**  If test mode, read addresses from stdin and process.
X	*/
X
X	if (OpMode == MD_TEST)
X	{
X		char buf[MAXLINE];
X
X		printf("ADDRESS TEST MODE\nEnter <ruleset> <address>\n");
X		for (;;)
X		{
X			register char **pvp;
X			char *q;
X			extern char *DelimChar;
X
X			printf("> ");
X			(void) fflush(stdout);
X			if (fgets(buf, sizeof buf, stdin) == NULL)
X				finis();
X			for (p = buf; isspace(*p); *p++)
X				continue;
X			q = p;
X			while (*p != '\0' && !isspace(*p))
X				p++;
X			if (*p == '\0')
X				continue;
X			*p = '\0';
X			do
X			{
X				extern char **prescan();
X				char pvpbuf[PSBUFSIZE];
X
X				pvp = prescan(++p, ',', pvpbuf);
X				if (pvp == NULL)
X					continue;
X				rewrite(pvp, 3);
X				p = q;
X				while (*p != '\0')
X				{
X					rewrite(pvp, atoi(p));
X					while (*p != '\0' && *p++ != ',')
X						continue;
X				}
X			} while (*(p = DelimChar) != '\0');
X		}
X	}
X
X# ifdef QUEUE
X	/*
X	**  If collecting stuff from the queue, go start doing that.
X	*/
X
X	if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
X	{
X		runqueue(FALSE);
X		finis();
X	}
X# endif QUEUE
X
X	/*
X	**  If a daemon, wait for a request.
X	**	getrequests will always return in a child.
X	**	If we should also be processing the queue, start
X	**		doing it in background.
X	**	We check for any errors that might have happened
X	**		during startup.
X	*/
X
X	if (OpMode == MD_DAEMON || QueueIntvl != 0)
X	{
X		if (!tTd(0, 1))
X		{
X			/* put us in background */
X			i = fork();
X			if (i < 0)
X				syserr("daemon: cannot fork");
X			if (i != 0)
X				exit(0);
X
X			/* get our pid right */
X			MotherPid = getpid();
X
X			/* disconnect from our controlling tty */
X			disconnect(TRUE);
X		}
X
X# ifdef QUEUE
X		if (queuemode)
X		{
X			runqueue(TRUE);
X			if (OpMode != MD_DAEMON)
X				for (;;)
X					pause();
X		}
X# endif QUEUE
X		dropenvelope(CurEnv);
X
X#ifdef DAEMON
X		getrequests();
X
X		/* at this point we are in a child: reset state */
X		OpMode = MD_SMTP;
X		(void) newenvelope(CurEnv);
X		openxscript(CurEnv);
X#endif DAEMON
X	}
X	
X# ifdef SMTP
X	/*
X	**  If running SMTP protocol, start collecting and executing
X	**  commands.  This will never return.
X	*/
X
X	if (OpMode == MD_SMTP)
X		smtp();
X# endif SMTP
X
X	/*
X	**  Do basic system initialization and set the sender
X	*/
X
X	initsys();
X	setsender(from);
X
X	if (OpMode != MD_ARPAFTP && *av == NULL && !GrabTo)
X	{
X		usrerr("Recipient names must be specified");
X
X		/* collect body for UUCP return */
X		if (OpMode != MD_VERIFY)
X			collect(FALSE);
X		finis();
X	}
X	if (OpMode == MD_VERIFY)
X		SendMode = SM_VERIFY;
X
X	/*
X	**  Scan argv and deliver the message to everyone.
X	*/
X
X	sendtoargv(av);
X
X	/* if we have had errors sofar, arrange a meaningful exit stat */
X	if (Errors > 0 && ExitStat == EX_OK)
X		ExitStat = EX_USAGE;
X
X	/*
X	**  Read the input mail.
X	*/
X
X	CurEnv->e_to = NULL;
X	if (OpMode != MD_VERIFY || GrabTo)
X		collect(FALSE);
X	errno = 0;
X
X	/* collect statistics */
X	if (OpMode != MD_VERIFY)
X		markstats(CurEnv, (ADDRESS *) NULL);
X
X# ifdef DEBUG
X	if (tTd(1, 1))
X		printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
X# endif DEBUG
X
X	/*
X	**  Actually send everything.
X	**	If verifying, just ack.
X	*/
X
X	CurEnv->e_from.q_flags |= QDONTSEND;
X	CurEnv->e_to = NULL;
X	sendall(CurEnv, SM_DEFAULT);
X
X	/*
X	** All done.
X	*/
X
X	finis();
X}
X/*
X**  FINIS -- Clean up and exit.
X**
X**	Parameters:
X**		none
X**
X**	Returns:
X**		never
X**
X**	Side Effects:
X**		exits sendmail
X*/
X
Xfinis()
X{
X# ifdef DEBUG
X	if (tTd(2, 1))
X		printf("\n====finis: stat %d e_flags %o\n", ExitStat, CurEnv->e_flags);
X# endif DEBUG
X
X	/* clean up temp files */
X	CurEnv->e_to = NULL;
X	dropenvelope(CurEnv);
X
X	/* post statistics */
X	poststats(StatFile);
X
X	/* and exit */
X# ifdef LOG
X	if (LogLevel > 11)
X		syslog(LOG_DEBUG, "finis, pid=%d", getpid());
X# endif LOG
X	if (ExitStat == EX_TEMPFAIL)
X		ExitStat = EX_OK;
X	exit(ExitStat);
X}
X/*
X**  INTSIG -- clean up on interrupt
X**
X**	This just arranges to exit.  It pessimises in that it
X**	may resend a message.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Unlocks the current job.
X*/
X
Xintsig()
X{
X	FileName = NULL;
X	unlockqueue(CurEnv);
X	exit(EX_OK);
X}
X/*
X**  INITMACROS -- initialize the macro system
X**
X**	This just involves defining some macros that are actually
X**	used internally as metasymbols to be themselves.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		initializes several macros to be themselves.
X*/
X
Xstruct metamac
X{
X	char	metaname;
X	char	metaval;
X};
X
Xstruct metamac	MetaMacros[] =
X{
X	/* LHS pattern matching characters */
X	'*', MATCHZANY,	'+', MATCHANY,	'-', MATCHONE,	'=', MATCHCLASS,
X	'~', MATCHNCLASS,
X
X	/* these are RHS metasymbols */
X	'#', CANONNET,	'@', CANONHOST,	':', CANONUSER,	'>', CALLSUBR,
X
X	/* the conditional operations */
X	'?', CONDIF,	'|', CONDELSE,	'.', CONDFI,
X
X	/* and finally the hostname lookup characters */
X	'[', HOSTBEGIN,	']', HOSTEND,
X
X	'\0'
X};
X
Xinitmacros()
X{
X	register struct metamac *m;
X	char buf[5];
X	register int c;
X
X	for (m = MetaMacros; m->metaname != '\0'; m++)
X	{
X		buf[0] = m->metaval;
X		buf[1] = '\0';
X		define(m->metaname, newstr(buf), CurEnv);
X	}
X	buf[0] = MATCHREPL;
X	buf[2] = '\0';
X	for (c = '0'; c <= '9'; c++)
X	{
X		buf[1] = c;
X		define(c, newstr(buf), CurEnv);
X	}
X}
X/*
X**  FREEZE -- freeze BSS & allocated memory
X**
X**	This will be used to efficiently load the configuration file.
X**
X**	Parameters:
X**		freezefile -- the name of the file to freeze to.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Writes BSS and malloc'ed memory to freezefile
X*/
X
Xunion frz
X{
X	char		frzpad[BUFSIZ];	/* insure we are on a BUFSIZ boundary */
X	struct
X	{
X		time_t	frzstamp;	/* timestamp on this freeze */
X		char	*frzbrk;	/* the current break */
X		char	*frzedata;	/* address of edata */
X		char	*frzend;	/* address of end */
X		char	frzver[252];	/* sendmail version */
X	} frzinfo;
X};
X
Xfreeze(freezefile)
X	char *freezefile;
X{
X	int f;
X	union frz fhdr;
X	extern char edata, end;
X	extern char *sbrk();
X	extern char Version[];
X
X	if (freezefile == NULL)
X		return;
X
X	/* try to open the freeze file */
X	f = creat(freezefile, FileMode);
X	if (f < 0)
X	{
X		syserr("Cannot freeze");
X		errno = 0;
X		return;
X	}
X
X	/* build the freeze header */
X	fhdr.frzinfo.frzstamp = curtime();
X	fhdr.frzinfo.frzbrk = sbrk(0);
X	fhdr.frzinfo.frzedata = &edata;
X	fhdr.frzinfo.frzend = &end;
X	(void) strcpy(fhdr.frzinfo.frzver, Version);
X
X	/* write out the freeze header */
X	if (write(f, (char *) &fhdr, sizeof fhdr) != sizeof fhdr ||
X	    write(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - &edata)) !=
X					(int) (fhdr.frzinfo.frzbrk - &edata))
X	{
X		syserr("Cannot freeze");
X	}
X
X	/* fine, clean up */
X	(void) close(f);
X}
X/*
X**  THAW -- read in the frozen configuration file.
X**
X**	Parameters:
X**		freezefile -- the name of the file to thaw from.
X**
X**	Returns:
X**		TRUE if it successfully read the freeze file.
X**		FALSE otherwise.
X**
X**	Side Effects:
X**		reads freezefile in to BSS area.
X*/
X
Xthaw(freezefile)
X	char *freezefile;
X{
X	int f;
X	union frz fhdr;
X	extern char edata;
X	extern char Version[];
X	extern caddr_t brk();
X
X	if (freezefile == NULL)
X		return (FALSE);
X
X	/* open the freeze file */
X	f = open(freezefile, 0);
X	if (f < 0)
X	{
X		errno = 0;
X		return (FALSE);
X	}
X
X	/* read in the header */
X	if (read(f, (char *) &fhdr, sizeof fhdr) < sizeof fhdr ||
X	    fhdr.frzinfo.frzedata != &edata ||
X	    fhdr.frzinfo.frzend != &end ||
X	    strcmp(fhdr.frzinfo.frzver, Version) != 0)
X	{
X		(void) close(f);
X		return (FALSE);
X	}
X
X	/* arrange to have enough space */
X	if (brk(fhdr.frzinfo.frzbrk) == (caddr_t) -1)
X	{
X		syserr("Cannot break to %x", fhdr.frzinfo.frzbrk);
X		(void) close(f);
X		return (FALSE);
X	}
X
X	/* now read in the freeze file */
X	if (read(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - &edata)) !=
X					(int) (fhdr.frzinfo.frzbrk - &edata))
X	{
X		/* oops!  we have trashed memory..... */
X		(void) write(2, "Cannot read freeze file\n", 24);
X		_exit(EX_SOFTWARE);
X	}
X
X	(void) close(f);
X	return (TRUE);
X}
X/*
X**  DISCONNECT -- remove our connection with any foreground process
X**
X**	Parameters:
X**		fulldrop -- if set, we should also drop the controlling
X**			TTY if possible -- this should only be done when
X**			setting up the daemon since otherwise UUCP can
X**			leave us trying to open a dialin, and we will
X**			wait for the carrier.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		Trys to insure that we are immune to vagaries of
X**		the controlling tty.
X*/
X
Xdisconnect(fulldrop)
X	bool fulldrop;
X{
X	int fd;
X
X#ifdef DEBUG
X	if (tTd(52, 1))
X		printf("disconnect: In %d Out %d\n", fileno(InChannel),
X						fileno(OutChannel));
X	if (tTd(52, 5))
X	{
X		printf("don't\n");
X		return;
X	}
X#endif DEBUG
X
X	/* be sure we don't get nasty signals */
X	(void) signal(SIGHUP, SIG_IGN);
X	(void) signal(SIGINT, SIG_IGN);
X	(void) signal(SIGQUIT, SIG_IGN);
X
X	/* we can't communicate with our caller, so.... */
X	HoldErrs = TRUE;
X	ErrorMode = EM_MAIL;
X	Verbose = FALSE;
X
X	/* all input from /dev/null */
X	if (InChannel != stdin)
X	{
X		(void) fclose(InChannel);
X		InChannel = stdin;
X	}
X	(void) freopen("/dev/null", "r", stdin);
X
X	/* output to the transcript */
X	if (OutChannel != stdout)
X	{
X		(void) fclose(OutChannel);
X		OutChannel = stdout;
X	}
X	if (CurEnv->e_xfp == NULL)
X		CurEnv->e_xfp = fopen("/dev/null", "w");
X	(void) fflush(stdout);
X	(void) close(1);
X	(void) close(2);
X	while ((fd = dup(fileno(CurEnv->e_xfp))) < 2 && fd > 0)
X		continue;
X
X#ifdef TIOCNOTTY
X	/* drop our controlling TTY completely if possible */
X	if (fulldrop)
X	{
X		fd = open("/dev/tty", 2);
X		if (fd >= 0)
X		{
X			(void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
X			(void) close(fd);
X		}
X		(void) setpgrp(0, 0);
X		errno = 0;
X	}
X#endif TIOCNOTTY
X
X# ifdef LOG
X	if (LogLevel > 11)
X		syslog(LOG_DEBUG, "in background, pid=%d", getpid());
X# endif LOG
X
X	errno = 0;
X}
END_OF_FILE
if test 21371 -ne `wc -c <'src/main.c'`; then
    echo shar: \"'src/main.c'\" unpacked with wrong size!
fi
# end of 'src/main.c'
fi
if test -f 'src/parseaddr.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/parseaddr.c'\"
else
echo shar: Extracting \"'src/parseaddr.c'\" \(24032 characters\)
sed "s/^X//" >'src/parseaddr.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)parseaddr.c	5.6 (Berkeley) 4/2/86";
X#endif not lint
X
X# include "sendmail.h"
X
X/*
X**  PARSEADDR -- Parse an address
X**
X**	Parses an address and breaks it up into three parts: a
X**	net to transmit the message on, the host to transmit it
X**	to, and a user on that host.  These are loaded into an
X**	ADDRESS header with the values squirreled away if necessary.
X**	The "user" part may not be a real user; the process may
X**	just reoccur on that machine.  For example, on a machine
X**	with an arpanet connection, the address
X**		csvax.bill@berkeley
X**	will break up to a "user" of 'csvax.bill' and a host
X**	of 'berkeley' -- to be transmitted over the arpanet.
X**
X**	Parameters:
X**		addr -- the address to parse.
X**		a -- a pointer to the address descriptor buffer.
X**			If NULL, a header will be created.
X**		copyf -- determines what shall be copied:
X**			-1 -- don't copy anything.  The printname
X**				(q_paddr) is just addr, and the
X**				user & host are allocated internally
X**				to parse.
X**			0 -- copy out the parsed user & host, but
X**				don't copy the printname.
X**			+1 -- copy everything.
X**		delim -- the character to terminate the address, passed
X**			to prescan.
X**
X**	Returns:
X**		A pointer to the address descriptor header (`a' if
X**			`a' is non-NULL).
X**		NULL on error.
X**
X**	Side Effects:
X**		none
X*/
X
X/* following delimiters are inherent to the internal algorithms */
X# define DELIMCHARS	"\001()<>,;\\\"\r\n"	/* word delimiters */
X
XADDRESS *
Xparseaddr(addr, a, copyf, delim)
X	char *addr;
X	register ADDRESS *a;
X	int copyf;
X	char delim;
X{
X	register char **pvp;
X	register struct mailer *m;
X	char pvpbuf[PSBUFSIZE];
X	extern char **prescan();
X	extern ADDRESS *buildaddr();
X
X	/*
X	**  Initialize and prescan address.
X	*/
X
X	CurEnv->e_to = addr;
X# ifdef DEBUG
X	if (tTd(20, 1))
X		printf("\n--parseaddr(%s)\n", addr);
X# endif DEBUG
X
X	pvp = prescan(addr, delim, pvpbuf);
X	if (pvp == NULL)
X		return (NULL);
X
X	/*
X	**  Apply rewriting rules.
X	**	Ruleset 0 does basic parsing.  It must resolve.
X	*/
X
X	rewrite(pvp, 3);
X	rewrite(pvp, 0);
X
X	/*
X	**  See if we resolved to a real mailer.
X	*/
X
X	if (pvp[0][0] != CANONNET)
X	{
X		setstat(EX_USAGE);
X		usrerr("cannot resolve name");
X		return (NULL);
X	}
X
X	/*
X	**  Build canonical address from pvp.
X	*/
X
X	a = buildaddr(pvp, a);
X	if (a == NULL)
X		return (NULL);
X	m = a->q_mailer;
X
X	/*
X	**  Make local copies of the host & user and then
X	**  transport them out.
X	*/
X
X	if (copyf > 0)
X	{
X		extern char *DelimChar;
X		char savec = *DelimChar;
X
X		*DelimChar = '\0';
X		a->q_paddr = newstr(addr);
X		*DelimChar = savec;
X	}
X	else
X		a->q_paddr = addr;
X
X	if (a->q_user == NULL)
X		a->q_user = "";
X	if (a->q_host == NULL)
X		a->q_host = "";
X
X	if (copyf >= 0)
X	{
X		a->q_host = newstr(a->q_host);
X		if (a->q_user != a->q_paddr)
X			a->q_user = newstr(a->q_user);
X	}
X
X	/*
X	**  Convert host name to lower case if requested.
X	**	User name will be done later.
X	*/
X
X	if (!bitnset(M_HST_UPPER, m->m_flags))
X		makelower(a->q_host);
X
X	/*
X	**  Compute return value.
X	*/
X
X# ifdef DEBUG
X	if (tTd(20, 1))
X	{
X		printf("parseaddr-->");
X		printaddr(a, FALSE);
X	}
X# endif DEBUG
X
X	return (a);
X}
X/*
X**  LOWERADDR -- map UPPER->lower case on addresses as requested.
X**
X**	Parameters:
X**		a -- address to be mapped.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
Xloweraddr(a)
X	register ADDRESS *a;
X{
X	register MAILER *m = a->q_mailer;
X
X	if (!bitnset(M_USR_UPPER, m->m_flags))
X		makelower(a->q_user);
X}
X/*
X**  PRESCAN -- Prescan name and make it canonical
X**
X**	Scans a name and turns it into a set of tokens.  This process
X**	deletes blanks and comments (in parentheses).
X**
X**	This routine knows about quoted strings and angle brackets.
X**
X**	There are certain subtleties to this routine.  The one that
X**	comes to mind now is that backslashes on the ends of names
X**	are silently stripped off; this is intentional.  The problem
X**	is that some versions of sndmsg (like at LBL) set the kill
X**	character to something other than @ when reading addresses;
X**	so people type "csvax.eric\@berkeley" -- which screws up the
X**	berknet mailer.
X**
X**	Parameters:
X**		addr -- the name to chomp.
X**		delim -- the delimiter for the address, normally
X**			'\0' or ','; \0 is accepted in any case.
X**			If '\t' then we are reading the .cf file.
X**		pvpbuf -- place to put the saved text -- note that
X**			the pointers are static.
X**
X**	Returns:
X**		A pointer to a vector of tokens.
X**		NULL on error.
X**
X**	Side Effects:
X**		sets DelimChar to point to the character matching 'delim'.
X*/
X
X/* states and character types */
X# define OPR		0	/* operator */
X# define ATM		1	/* atom */
X# define QST		2	/* in quoted string */
X# define SPC		3	/* chewing up spaces */
X# define ONE		4	/* pick up one character */
X
X# define NSTATES	5	/* number of states */
X# define TYPE		017	/* mask to select state type */
X
X/* meta bits for table */
X# define M		020	/* meta character; don't pass through */
X# define B		040	/* cause a break */
X# define MB		M|B	/* meta-break */
X
Xstatic short StateTab[NSTATES][NSTATES] =
X{
X   /*	oldst	chtype>	OPR	ATM	QST	SPC	ONE	*/
X	/*OPR*/		OPR|B,	ATM|B,	QST|B,	SPC|MB,	ONE|B,
X	/*ATM*/		OPR|B,	ATM,	QST|B,	SPC|MB,	ONE|B,
X	/*QST*/		QST,	QST,	OPR,	QST,	QST,
X	/*SPC*/		OPR,	ATM,	QST,	SPC|M,	ONE,
X	/*ONE*/		OPR,	OPR,	OPR,	OPR,	OPR,
X};
X
X# define NOCHAR		-1	/* signal nothing in lookahead token */
X
Xchar	*DelimChar;		/* set to point to the delimiter */
X
Xchar **
Xprescan(addr, delim, pvpbuf)
X	char *addr;
X	char delim;
X	char pvpbuf[];
X{
X	register char *p;
X	register char *q;
X	register int c;
X	char **avp;
X	bool bslashmode;
X	int cmntcnt;
X	int anglecnt;
X	char *tok;
X	int state;
X	int newstate;
X	static char *av[MAXATOM+1];
X	extern int errno;
X
X	/* make sure error messages don't have garbage on them */
X	errno = 0;
X
X	q = pvpbuf;
X	bslashmode = FALSE;
X	cmntcnt = 0;
X	anglecnt = 0;
X	avp = av;
X	state = OPR;
X	c = NOCHAR;
X	p = addr;
X# ifdef DEBUG
X	if (tTd(22, 45))
X	{
X		printf("prescan: ");
X		xputs(p);
X		(void) putchar('\n');
X	}
X# endif DEBUG
X
X	do
X	{
X		/* read a token */
X		tok = q;
X		for (;;)
X		{
X			/* store away any old lookahead character */
X			if (c != NOCHAR)
X			{
X				/* see if there is room */
X				if (q >= &pvpbuf[PSBUFSIZE - 5])
X				{
X					usrerr("Address too long");
X					DelimChar = p;
X					return (NULL);
X				}
X
X				/* squirrel it away */
X				*q++ = c;
X			}
X
X			/* read a new input character */
X			c = *p++;
X			if (c == '\0')
X				break;
X			c &= ~0200;
X
X# ifdef DEBUG
X			if (tTd(22, 101))
X				printf("c=%c, s=%d; ", c, state);
X# endif DEBUG
X
X			/* chew up special characters */
X			*q = '\0';
X			if (bslashmode)
X			{
X				/* kludge \! for naive users */
X				if (c != '!')
X					c |= 0200;
X				bslashmode = FALSE;
X			}
X			else if (c == '\\')
X			{
X				bslashmode = TRUE;
X				c = NOCHAR;
X			}
X			else if (state == QST)
X			{
X				/* do nothing, just avoid next clauses */
X			}
X			else if (c == '(')
X			{
X				cmntcnt++;
X				c = NOCHAR;
X			}
X			else if (c == ')')
X			{
X				if (cmntcnt <= 0)
X				{
X					usrerr("Unbalanced ')'");
X					DelimChar = p;
X					return (NULL);
X				}
X				else
X					cmntcnt--;
X			}
X			else if (cmntcnt > 0)
X				c = NOCHAR;
X			else if (c == '<')
X				anglecnt++;
X			else if (c == '>')
X			{
X				if (anglecnt <= 0)
X				{
X					usrerr("Unbalanced '>'");
X					DelimChar = p;
X					return (NULL);
X				}
X				anglecnt--;
X			}
X			else if (delim == ' ' && isspace(c))
X				c = ' ';
X
X			if (c == NOCHAR)
X				continue;
X
X			/* see if this is end of input */
X			if (c == delim && anglecnt <= 0 && state != QST)
X				break;
X
X			newstate = StateTab[state][toktype(c)];
X# ifdef DEBUG
X			if (tTd(22, 101))
X				printf("ns=%02o\n", newstate);
X# endif DEBUG
X			state = newstate & TYPE;
X			if (bitset(M, newstate))
X				c = NOCHAR;
X			if (bitset(B, newstate))
X				break;
X		}
X
X		/* new token */
X		if (tok != q)
X		{
X			*q++ = '\0';
X# ifdef DEBUG
X			if (tTd(22, 36))
X			{
X				printf("tok=");
X				xputs(tok);
X				(void) putchar('\n');
X			}
X# endif DEBUG
X			if (avp >= &av[MAXATOM])
X			{
X				syserr("prescan: too many tokens");
X				DelimChar = p;
X				return (NULL);
X			}
X			*avp++ = tok;
X		}
X	} while (c != '\0' && (c != delim || anglecnt > 0));
X	*avp = NULL;
X	DelimChar = --p;
X	if (cmntcnt > 0)
X		usrerr("Unbalanced '('");
X	else if (anglecnt > 0)
X		usrerr("Unbalanced '<'");
X	else if (state == QST)
X		usrerr("Unbalanced '\"'");
X	else if (av[0] != NULL)
X		return (av);
X	return (NULL);
X}
X/*
X**  TOKTYPE -- return token type
X**
X**	Parameters:
X**		c -- the character in question.
X**
X**	Returns:
X**		Its type.
X**
X**	Side Effects:
X**		none.
X*/
X
Xtoktype(c)
X	register char c;
X{
X	static char buf[50];
X	static bool firstime = TRUE;
X
X	if (firstime)
X	{
X		firstime = FALSE;
X		expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
X		(void) strcat(buf, DELIMCHARS);
X	}
X	if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
X		return (ONE);
X	if (c == '"')
X		return (QST);
X	if (!isascii(c))
X		return (ATM);
X	if (isspace(c) || c == ')')
X		return (SPC);
X	if (iscntrl(c) || index(buf, c) != NULL)
X		return (OPR);
X	return (ATM);
X}
X/*
X**  REWRITE -- apply rewrite rules to token vector.
X**
X**	This routine is an ordered production system.  Each rewrite
X**	rule has a LHS (called the pattern) and a RHS (called the
X**	rewrite); 'rwr' points the the current rewrite rule.
X**
X**	For each rewrite rule, 'avp' points the address vector we
X**	are trying to match against, and 'pvp' points to the pattern.
X**	If pvp points to a special match value (MATCHZANY, MATCHANY,
X**	MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
X**	matched is saved away in the match vector (pointed to by 'mvp').
X**
X**	When a match between avp & pvp does not match, we try to
X**	back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
X**	we must also back out the match in mvp.  If we reach a
X**	MATCHANY or MATCHZANY we just extend the match and start
X**	over again.
X**
X**	When we finally match, we rewrite the address vector
X**	and try over again.
X**
X**	Parameters:
X**		pvp -- pointer to token vector.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		pvp is modified.
X*/
X
Xstruct match
X{
X	char	**first;	/* first token matched */
X	char	**last;		/* last token matched */
X};
X
X# define MAXMATCH	9	/* max params per rewrite */
X
X
Xrewrite(pvp, ruleset)
X	char **pvp;
X	int ruleset;
X{
X	register char *ap;		/* address pointer */
X	register char *rp;		/* rewrite pointer */
X	register char **avp;		/* address vector pointer */
X	register char **rvp;		/* rewrite vector pointer */
X	register struct match *mlp;	/* cur ptr into mlist */
X	register struct rewrite *rwr;	/* pointer to current rewrite rule */
X	struct match mlist[MAXMATCH];	/* stores match on LHS */
X	char *npvp[MAXATOM+1];		/* temporary space for rebuild */
X	extern bool sameword();
X
X	if (OpMode == MD_TEST || tTd(21, 2))
X	{
X		printf("rewrite: ruleset %2d   input:", ruleset);
X		printav(pvp);
X	}
X	if (pvp == NULL)
X		return;
X
X	/*
X	**  Run through the list of rewrite rules, applying
X	**	any that match.
X	*/
X
X	for (rwr = RewriteRules[ruleset]; rwr != NULL; )
X	{
X# ifdef DEBUG
X		if (tTd(21, 12))
X		{
X			printf("-----trying rule:");
X			printav(rwr->r_lhs);
X		}
X# endif DEBUG
X
X		/* try to match on this rule */
X		mlp = mlist;
X		rvp = rwr->r_lhs;
X		avp = pvp;
X		while ((ap = *avp) != NULL || *rvp != NULL)
X		{
X			rp = *rvp;
X# ifdef DEBUG
X			if (tTd(21, 35))
X			{
X				printf("ap=");
X				xputs(ap);
X				printf(", rp=");
X				xputs(rp);
X				printf("\n");
X			}
X# endif DEBUG
X			if (rp == NULL)
X			{
X				/* end-of-pattern before end-of-address */
X				goto backup;
X			}
X			if (ap == NULL && *rp != MATCHZANY)
X			{
X				/* end-of-input */
X				break;
X			}
X
X			switch (*rp)
X			{
X				register STAB *s;
X
X			  case MATCHCLASS:
X			  case MATCHNCLASS:
X				/* match any token in (not in) a class */
X				s = stab(ap, ST_CLASS, ST_FIND);
X				if (s == NULL || !bitnset(rp[1], s->s_class))
X				{
X					if (*rp == MATCHCLASS)
X						goto backup;
X				}
X				else if (*rp == MATCHNCLASS)
X					goto backup;
X
X				/* explicit fall-through */
X
X			  case MATCHONE:
X			  case MATCHANY:
X				/* match exactly one token */
X				mlp->first = avp;
X				mlp->last = avp++;
X				mlp++;
X				break;
X
X			  case MATCHZANY:
X				/* match zero or more tokens */
X				mlp->first = avp;
X				mlp->last = avp - 1;
X				mlp++;
X				break;
X
X			  default:
X				/* must have exact match */
X				if (!sameword(rp, ap))
X					goto backup;
X				avp++;
X				break;
X			}
X
X			/* successful match on this token */
X			rvp++;
X			continue;
X
X		  backup:
X			/* match failed -- back up */
X			while (--rvp >= rwr->r_lhs)
X			{
X				rp = *rvp;
X				if (*rp == MATCHANY || *rp == MATCHZANY)
X				{
X					/* extend binding and continue */
X					avp = ++mlp[-1].last;
X					avp++;
X					rvp++;
X					break;
X				}
X				avp--;
X				if (*rp == MATCHONE || *rp == MATCHCLASS ||
X				    *rp == MATCHNCLASS)
X				{
X					/* back out binding */
X					mlp--;
X				}
X			}
X
X			if (rvp < rwr->r_lhs)
X			{
X				/* total failure to match */
X				break;
X			}
X		}
X
X		/*
X		**  See if we successfully matched
X		*/
X
X		if (rvp < rwr->r_lhs || *rvp != NULL)
X		{
X# ifdef DEBUG
X			if (tTd(21, 10))
X				printf("----- rule fails\n");
X# endif DEBUG
X			rwr = rwr->r_next;
X			continue;
X		}
X
X		rvp = rwr->r_rhs;
X# ifdef DEBUG
X		if (tTd(21, 12))
X		{
X			printf("-----rule matches:");
X			printav(rvp);
X		}
X# endif DEBUG
X
X		rp = *rvp;
X		if (*rp == CANONUSER)
X		{
X			rvp++;
X			rwr = rwr->r_next;
X		}
X		else if (*rp == CANONHOST)
X		{
X			rvp++;
X			rwr = NULL;
X		}
X		else if (*rp == CANONNET)
X			rwr = NULL;
X
X		/* substitute */
X		for (avp = npvp; *rvp != NULL; rvp++)
X		{
X			register struct match *m;
X			register char **pp;
X
X			rp = *rvp;
X			if (*rp == MATCHREPL)
X			{
X				/* substitute from LHS */
X				m = &mlist[rp[1] - '1'];
X				if (m >= mlp)
X				{
X					syserr("rewrite: ruleset %d: replacement out of bounds", ruleset);
X					return;
X				}
X# ifdef DEBUG
X				if (tTd(21, 15))
X				{
X					printf("$%c:", rp[1]);
X					pp = m->first;
X					while (pp <= m->last)
X					{
X						printf(" %x=\"", *pp);
X						(void) fflush(stdout);
X						printf("%s\"", *pp++);
X					}
X					printf("\n");
X				}
X# endif DEBUG
X				pp = m->first;
X				while (pp <= m->last)
X				{
X					if (avp >= &npvp[MAXATOM])
X					{
X						syserr("rewrite: expansion too long");
X						return;
X					}
X					*avp++ = *pp++;
X				}
X			}
X			else
X			{
X				/* vanilla replacement */
X				if (avp >= &npvp[MAXATOM])
X				{
X	toolong:
X					syserr("rewrite: expansion too long");
X					return;
X				}
X				*avp++ = rp;
X			}
X		}
X		*avp++ = NULL;
X
X		/*
X		**  Check for any hostname lookups.
X		*/
X
X		for (rvp = npvp; *rvp != NULL; rvp++)
X		{
X			char **hbrvp;
X			char **xpvp;
X			int trsize;
X			char *olddelimchar;
X			char buf[MAXNAME + 1];
X			char *pvpb1[MAXATOM + 1];
X			char pvpbuf[PSBUFSIZE];
X			extern char *DelimChar;
X
X			if (**rvp != HOSTBEGIN)
X				continue;
X
X			/*
X			**  Got a hostname lookup.
X			**
X			**	This could be optimized fairly easily.
X			*/
X
X			hbrvp = rvp;
X
X			/* extract the match part */
X			while (*++rvp != NULL && **rvp != HOSTEND)
X				continue;
X			if (*rvp != NULL)
X				*rvp++ = NULL;
X
X			/* save the remainder of the input string */
X			trsize = (int) (avp - rvp + 1) * sizeof *rvp;
X			bcopy((char *) rvp, (char *) pvpb1, trsize);
X
X			/* look it up */
X			cataddr(++hbrvp, buf, sizeof buf);
X			maphostname(buf, sizeof buf);
X
X			/* scan the new host name */
X			olddelimchar = DelimChar;
X			xpvp = prescan(buf, '\0', pvpbuf);
X			DelimChar = olddelimchar;
X			if (xpvp == NULL)
X			{
X				syserr("rewrite: cannot prescan canonical hostname: %s", buf);
X				return;
X			}
X
X			/* append it to the token list */
X			for (avp = --hbrvp; *xpvp != NULL; xpvp++)
X			{
X				*avp++ = newstr(*xpvp);
X				if (avp >= &npvp[MAXATOM])
X					goto toolong;
X			}
X
X			/* restore the old trailing information */
X			for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
X				if (avp >= &npvp[MAXATOM])
X					goto toolong;
X
X			break;
X		}
X
X		/*
X		**  Check for subroutine calls.
X		*/
X
X		if (*npvp != NULL && **npvp == CALLSUBR)
X		{
X			bcopy((char *) &npvp[2], (char *) pvp,
X				(int) (avp - npvp - 2) * sizeof *avp);
X# ifdef DEBUG
X			if (tTd(21, 3))
X				printf("-----callsubr %s\n", npvp[1]);
X# endif DEBUG
X			rewrite(pvp, atoi(npvp[1]));
X		}
X		else
X		{
X			bcopy((char *) npvp, (char *) pvp,
X				(int) (avp - npvp) * sizeof *avp);
X		}
X# ifdef DEBUG
X		if (tTd(21, 4))
X		{
X			printf("rewritten as:");
X			printav(pvp);
X		}
X# endif DEBUG
X	}
X
X	if (OpMode == MD_TEST || tTd(21, 2))
X	{
X		printf("rewrite: ruleset %2d returns:", ruleset);
X		printav(pvp);
X	}
X}
X/*
X**  BUILDADDR -- build address from token vector.
X**
X**	Parameters:
X**		tv -- token vector.
X**		a -- pointer to address descriptor to fill.
X**			If NULL, one will be allocated.
X**
X**	Returns:
X**		NULL if there was an error.
X**		'a' otherwise.
X**
X**	Side Effects:
X**		fills in 'a'
X*/
X
XADDRESS *
Xbuildaddr(tv, a)
X	register char **tv;
X	register ADDRESS *a;
X{
X	static char buf[MAXNAME];
X	struct mailer **mp;
X	register struct mailer *m;
X	extern bool sameword();
X
X	if (a == NULL)
X		a = (ADDRESS *) xalloc(sizeof *a);
X	bzero((char *) a, sizeof *a);
X
X	/* figure out what net/mailer to use */
X	if (**tv != CANONNET)
X	{
X		syserr("buildaddr: no net");
X		return (NULL);
X	}
X	tv++;
X	if (sameword(*tv, "error"))
X	{
X		if (**++tv == CANONHOST)
X		{
X			setstat(atoi(*++tv));
X			tv++;
X		}
X		if (**tv != CANONUSER)
X			syserr("buildaddr: error: no user");
X		buf[0] = '\0';
X		while (*++tv != NULL)
X		{
X			if (buf[0] != '\0')
X				(void) strcat(buf, " ");
X			(void) strcat(buf, *tv);
X		}
X		usrerr(buf);
X		return (NULL);
X	}
X	for (mp = Mailer; (m = *mp++) != NULL; )
X	{
X		if (sameword(m->m_name, *tv))
X			break;
X	}
X	if (m == NULL)
X	{
X		syserr("buildaddr: unknown mailer %s", *tv);
X		return (NULL);
X	}
X	a->q_mailer = m;
X
X	/* figure out what host (if any) */
X	tv++;
X	if (!bitnset(M_LOCAL, m->m_flags))
X	{
X		if (**tv++ != CANONHOST)
X		{
X			syserr("buildaddr: no host");
X			return (NULL);
X		}
X		buf[0] = '\0';
X		while (*tv != NULL && **tv != CANONUSER)
X			(void) strcat(buf, *tv++);
X		a->q_host = newstr(buf);
X	}
X	else
X		a->q_host = NULL;
X
X	/* figure out the user */
X	if (**tv != CANONUSER)
X	{
X		syserr("buildaddr: no user");
X		return (NULL);
X	}
X
X	/* rewrite according recipient mailer rewriting rules */
X	rewrite(++tv, 2);
X	if (m->m_r_rwset > 0)
X		rewrite(tv, m->m_r_rwset);
X	rewrite(tv, 4);
X
X	/* save the result for the command line/RCPT argument */
X	cataddr(tv, buf, sizeof buf);
X	a->q_user = buf;
X
X	return (a);
X}
X/*
X**  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
X**
X**	Parameters:
X**		pvp -- parameter vector to rebuild.
X**		buf -- buffer to build the string into.
X**		sz -- size of buf.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Destroys buf.
X*/
X
Xcataddr(pvp, buf, sz)
X	char **pvp;
X	char *buf;
X	register int sz;
X{
X	bool oatomtok = FALSE;
X	bool natomtok = FALSE;
X	register int i;
X	register char *p;
X
X	if (pvp == NULL)
X	{
X		(void) strcpy(buf, "");
X		return;
X	}
X	p = buf;
X	sz -= 2;
X	while (*pvp != NULL && (i = strlen(*pvp)) < sz)
X	{
X		natomtok = (toktype(**pvp) == ATM);
X		if (oatomtok && natomtok)
X			*p++ = SpaceSub;
X		(void) strcpy(p, *pvp);
X		oatomtok = natomtok;
X		p += i;
X		sz -= i + 1;
X		pvp++;
X	}
X	*p = '\0';
X}
X/*
X**  SAMEADDR -- Determine if two addresses are the same
X**
X**	This is not just a straight comparison -- if the mailer doesn't
X**	care about the host we just ignore it, etc.
X**
X**	Parameters:
X**		a, b -- pointers to the internal forms to compare.
X**
X**	Returns:
X**		TRUE -- they represent the same mailbox.
X**		FALSE -- they don't.
X**
X**	Side Effects:
X**		none.
X*/
X
Xbool
Xsameaddr(a, b)
X	register ADDRESS *a;
X	register ADDRESS *b;
X{
X	/* if they don't have the same mailer, forget it */
X	if (a->q_mailer != b->q_mailer)
X		return (FALSE);
X
X	/* if the user isn't the same, we can drop out */
X	if (strcmp(a->q_user, b->q_user) != 0)
X		return (FALSE);
X
X	/* if the mailer ignores hosts, we have succeeded! */
X	if (bitnset(M_LOCAL, a->q_mailer->m_flags))
X		return (TRUE);
X
X	/* otherwise compare hosts (but be careful for NULL ptrs) */
X	if (a->q_host == NULL || b->q_host == NULL)
X		return (FALSE);
X	if (strcmp(a->q_host, b->q_host) != 0)
X		return (FALSE);
X
X	return (TRUE);
X}
X/*
X**  PRINTADDR -- print address (for debugging)
X**
X**	Parameters:
X**		a -- the address to print
X**		follow -- follow the q_next chain.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		none.
X*/
X
X# ifdef DEBUG
X
Xprintaddr(a, follow)
X	register ADDRESS *a;
X	bool follow;
X{
X	bool first = TRUE;
X
X	while (a != NULL)
X	{
X		first = FALSE;
X		printf("%x=", a);
X		(void) fflush(stdout);
X		printf("%s: mailer %d (%s), host `%s', user `%s'\n", a->q_paddr,
X		       a->q_mailer->m_mno, a->q_mailer->m_name, a->q_host,
X		       a->q_user);
X		printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags,
X		       a->q_alias);
X		printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home,
X		       a->q_fullname);
X
X		if (!follow)
X			return;
X		a = a->q_next;
X	}
X	if (first)
X		printf("[NULL]\n");
X}
X
X# endif DEBUG
X/*
X**  REMOTENAME -- return the name relative to the current mailer
X**
X**	Parameters:
X**		name -- the name to translate.
X**		m -- the mailer that we want to do rewriting relative
X**			to.
X**		senderaddress -- if set, uses the sender rewriting rules
X**			rather than the recipient rewriting rules.
X**		canonical -- if set, strip out any comment information,
X**			etc.
X**
X**	Returns:
X**		the text string representing this address relative to
X**			the receiving mailer.
X**
X**	Side Effects:
X**		none.
X**
X**	Warnings:
X**		The text string returned is tucked away locally;
X**			copy it if you intend to save it.
X*/
X
Xchar *
Xremotename(name, m, senderaddress, canonical)
X	char *name;
X	struct mailer *m;
X	bool senderaddress;
X	bool canonical;
X{
X	register char **pvp;
X	char *fancy;
X	extern char *macvalue();
X	char *oldg = macvalue('g', CurEnv);
X	static char buf[MAXNAME];
X	char lbuf[MAXNAME];
X	char pvpbuf[PSBUFSIZE];
X	extern char **prescan();
X	extern char *crackaddr();
X
X# ifdef DEBUG
X	if (tTd(12, 1))
X		printf("remotename(%s)\n", name);
X# endif DEBUG
X
X	/* don't do anything if we are tagging it as special */
X	if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
X		return (name);
X
X	/*
X	**  Do a heuristic crack of this name to extract any comment info.
X	**	This will leave the name as a comment and a $g macro.
X	*/
X
X	if (canonical)
X		fancy = "\001g";
X	else
X		fancy = crackaddr(name);
X
X	/*
X	**  Turn the name into canonical form.
X	**	Normally this will be RFC 822 style, i.e., "user@domain".
X	**	If this only resolves to "user", and the "C" flag is
X	**	specified in the sending mailer, then the sender's
X	**	domain will be appended.
X	*/
X
X	pvp = prescan(name, '\0', pvpbuf);
X	if (pvp == NULL)
X		return (name);
X	rewrite(pvp, 3);
X	if (CurEnv->e_fromdomain != NULL)
X	{
X		/* append from domain to this address */
X		register char **pxp = pvp;
X
X		/* see if there is an "@domain" in the current name */
X		while (*pxp != NULL && strcmp(*pxp, "@") != 0)
X			pxp++;
X		if (*pxp == NULL)
X		{
X			/* no.... append the "@domain" from the sender */
X			register char **qxq = CurEnv->e_fromdomain;
X
X			while ((*pxp++ = *qxq++) != NULL)
X				continue;
X			rewrite(pvp, 3);
X		}
X	}
X
X	/*
X	**  Do more specific rewriting.
X	**	Rewrite using ruleset 1 or 2 depending on whether this is
X	**		a sender address or not.
X	**	Then run it through any receiving-mailer-specific rulesets.
X	*/
X
X	if (senderaddress)
X	{
X		rewrite(pvp, 1);
X		if (m->m_s_rwset > 0)
X			rewrite(pvp, m->m_s_rwset);
X	}
X	else
X	{
X		rewrite(pvp, 2);
X		if (m->m_r_rwset > 0)
X			rewrite(pvp, m->m_r_rwset);
X	}
X
X	/*
X	**  Do any final sanitation the address may require.
X	**	This will normally be used to turn internal forms
X	**	(e.g., user@host.LOCAL) into external form.  This
X	**	may be used as a default to the above rules.
X	*/
X
X	rewrite(pvp, 4);
X
X	/*
X	**  Now restore the comment information we had at the beginning.
X	*/
X
X	cataddr(pvp, lbuf, sizeof lbuf);
X	define('g', lbuf, CurEnv);
X	expand(fancy, buf, &buf[sizeof buf - 1], CurEnv);
X	define('g', oldg, CurEnv);
X
X# ifdef DEBUG
X	if (tTd(12, 1))
X		printf("remotename => `%s'\n", buf);
X# endif DEBUG
X	return (buf);
X}
END_OF_FILE
if test 24032 -ne `wc -c <'src/parseaddr.c'`; then
    echo shar: \"'src/parseaddr.c'\" unpacked with wrong size!
fi
# end of 'src/parseaddr.c'
fi
echo shar: End of archive 7 \(of 8\).
cp /dev/null ark7isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
David H. Brierley
Home: dave@galaxia.Newport.RI.US   {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb@rayssd.ray.com           {sun,decuac,gatech,necntc,ukma}!rayssd!dhb

dave@galaxia.Newport.RI.US (David H. Brierley) (02/25/89)

----- cut here and feed to /bin/sh -----
#! /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 archive 8 (of 8)."
# Contents:  src/deliver.c
# Wrapped by dave@galaxia on Fri Feb 24 20:24:22 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/deliver.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/deliver.c'\"
else
echo shar: Extracting \"'src/deliver.c'\" \(31247 characters\)
sed "s/^X//" >'src/deliver.c' <<'END_OF_FILE'
X/*
X**  Sendmail
X**  Copyright (c) 1983  Eric P. Allman
X**  Berkeley, California
X**
X**  Copyright (c) 1983 Regents of the University of California.
X**  All rights reserved.  The Berkeley software License Agreement
X**  specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char	SccsId[] = "@(#)deliver.c	5.15 (Berkeley) 12/12/87";
X#endif not lint
X
X# include <signal.h>
X# include <errno.h>
X# include "sendmail.h"
X# include <sys/stat.h>
X
X#ifdef	SMTP
X# include <netdb.h>
X#endif
X
X/*
X**  DELIVER -- Deliver a message to a list of addresses.
X**
X**	This routine delivers to everyone on the same host as the
X**	user on the head of the list.  It is clever about mailers
X**	that don't handle multiple users.  It is NOT guaranteed
X**	that it will deliver to all these addresses however -- so
X**	deliver should be called once for each address on the
X**	list.
X**
X**	Parameters:
X**		e -- the envelope to deliver.
X**		firstto -- head of the address list to deliver to.
X**
X**	Returns:
X**		zero -- successfully delivered.
X**		else -- some failure, see ExitStat for more info.
X**
X**	Side Effects:
X**		The standard input is passed off to someone.
X*/
X
Xdeliver(e, firstto)
X	register ENVELOPE *e;
X	ADDRESS *firstto;
X{
X	char *host;			/* host being sent to */
X	char *user;			/* user being sent to */
X	char **pvp;
X	register char **mvp;
X	register char *p;
X	register MAILER *m;		/* mailer for this recipient */
X	ADDRESS *ctladdr;
X	register ADDRESS *to = firstto;
X	bool clever = FALSE;		/* running user smtp to this mailer */
X	ADDRESS *tochain = NULL;	/* chain of users in this mailer call */
X	register int rcode;		/* response code */
X	char *pv[MAXPV+1];
X	char tobuf[MAXLINE-50];		/* text line of to people */
X	char buf[MAXNAME];
X	char tfrombuf[MAXNAME];		/* translated from person */
X	extern bool checkcompat();
X	extern ADDRESS *getctladdr();
X	extern char *remotename();
X
X	errno = 0;
X	if (bitset(QDONTSEND, to->q_flags))
X		return (0);
X
X	m = to->q_mailer;
X	host = to->q_host;
X
X# ifdef DEBUG
X	if (tTd(10, 1))
X		printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n",
X			m->m_mno, host, to->q_user);
X# endif DEBUG
X
X	/*
X	**  If this mailer is expensive, and if we don't want to make
X	**  connections now, just mark these addresses and return.
X	**	This is useful if we want to batch connections to
X	**	reduce load.  This will cause the messages to be
X	**	queued up, and a daemon will come along to send the
X	**	messages later.
X	**		This should be on a per-mailer basis.
X	*/
X
X	if (NoConnect && !QueueRun && bitnset(M_EXPENSIVE, m->m_flags) &&
X	    !Verbose)
X	{
X		for (; to != NULL; to = to->q_next)
X		{
X			if (bitset(QDONTSEND, to->q_flags) || to->q_mailer != m)
X				continue;
X			to->q_flags |= QQUEUEUP|QDONTSEND;
X			e->e_to = to->q_paddr;
X			message(Arpa_Info, "queued");
X			if (LogLevel > 4)
X				logdelivery("queued");
X		}
X		e->e_to = NULL;
X		return (0);
X	}
X
X	/*
X	**  Do initial argv setup.
X	**	Insert the mailer name.  Notice that $x expansion is
X	**	NOT done on the mailer name.  Then, if the mailer has
X	**	a picky -f flag, we insert it as appropriate.  This
X	**	code does not check for 'pv' overflow; this places a
X	**	manifest lower limit of 4 for MAXPV.
X	**		The from address rewrite is expected to make
X	**		the address relative to the other end.
X	*/
X
X	/* rewrite from address, using rewriting rules */
X	expand("\001f", buf, &buf[sizeof buf - 1], e);
X	(void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE));
X
X	define('g', tfrombuf, e);		/* translated sender address */
X	define('h', host, e);			/* to host */
X	Errors = 0;
X	pvp = pv;
X	*pvp++ = m->m_argv[0];
X
X	/* insert -f or -r flag as appropriate */
X	if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags)))
X	{
X		if (bitnset(M_FOPT, m->m_flags))
X			*pvp++ = "-f";
X		else
X			*pvp++ = "-r";
X		expand("\001g", buf, &buf[sizeof buf - 1], e);
X		*pvp++ = newstr(buf);
X	}
X
X	/*
X	**  Append the other fixed parts of the argv.  These run
X	**  up to the first entry containing "$u".  There can only
X	**  be one of these, and there are only a few more slots
X	**  in the pv after it.
X	*/
X
X	for (mvp = m->m_argv; (p = *++mvp) != NULL; )
X	{
X		while ((p = index(p, '\001')) != NULL)
X			if (*++p == 'u')
X				break;
X		if (p != NULL)
X			break;
X
X		/* this entry is safe -- go ahead and process it */
X		expand(*mvp, buf, &buf[sizeof buf - 1], e);
X		*pvp++ = newstr(buf);
X		if (pvp >= &pv[MAXPV - 3])
X		{
X			syserr("Too many parameters to %s before $u", pv[0]);
X			return (-1);
X		}
X	}
X
X	/*
X	**  If we have no substitution for the user name in the argument
X	**  list, we know that we must supply the names otherwise -- and
X	**  SMTP is the answer!!
X	*/
X
X	if (*mvp == NULL)
X	{
X		/* running SMTP */
X# ifdef SMTP
X		clever = TRUE;
X		*pvp = NULL;
X# else SMTP
X		/* oops!  we don't implement SMTP */
X		syserr("SMTP style mailer");
X		return (EX_SOFTWARE);
X# endif SMTP
X	}
X
X	/*
X	**  At this point *mvp points to the argument with $u.  We
X	**  run through our address list and append all the addresses
X	**  we can.  If we run out of space, do not fret!  We can
X	**  always send another copy later.
X	*/
X
X	tobuf[0] = '\0';
X	e->e_to = tobuf;
X	ctladdr = NULL;
X	for (; to != NULL; to = to->q_next)
X	{
X		/* avoid sending multiple recipients to dumb mailers */
X		if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags))
X			break;
X
X		/* if already sent or not for this host, don't send */
X		if (bitset(QDONTSEND, to->q_flags) ||
X		    strcmp(to->q_host, host) != 0 ||
X		    to->q_mailer != firstto->q_mailer)
X			continue;
X
X		/* avoid overflowing tobuf */
X		if (sizeof tobuf - (strlen(to->q_paddr) + strlen(tobuf) + 2) < 0)
X			break;
X
X# ifdef DEBUG
X		if (tTd(10, 1))
X		{
X			printf("\nsend to ");
X			printaddr(to, FALSE);
X		}
X# endif DEBUG
X
X		/* compute effective uid/gid when sending */
X		if (to->q_mailer == ProgMailer)
X			ctladdr = getctladdr(to);
X
X		user = to->q_user;
X		e->e_to = to->q_paddr;
X		to->q_flags |= QDONTSEND;
X
X		/*
X		**  Check to see that these people are allowed to
X		**  talk to each other.
X		*/
X
X		if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize)
X		{
X			NoReturn = TRUE;
X			usrerr("Message is too large; %ld bytes max", m->m_maxsize);
X			giveresponse(EX_UNAVAILABLE, m, e);
X			continue;
X		}
X		if (!checkcompat(to))
X		{
X			giveresponse(EX_UNAVAILABLE, m, e);
X			continue;
X		}
X
X		/*
X		**  Strip quote bits from names if the mailer is dumb
X		**	about them.
X		*/
X
X		if (bitnset(M_STRIPQ, m->m_flags))
X		{
X			stripquotes(user, TRUE);
X			stripquotes(host, TRUE);
X		}
X		else
X		{
X			stripquotes(user, FALSE);
X			stripquotes(host, FALSE);
X		}
X
X		/* hack attack -- delivermail compatibility */
X		if (m == ProgMailer && *user == '|')
X			user++;
X
X		/*
X		**  If an error message has already been given, don't
X		**	bother to send to this address.
X		**
X		**	>>>>>>>>>> This clause assumes that the local mailer
X		**	>> NOTE >> cannot do any further aliasing; that
X		**	>>>>>>>>>> function is subsumed by sendmail.
X		*/
X
X		if (bitset(QBADADDR|QQUEUEUP, to->q_flags))
X			continue;
X
X		/* save statistics.... */
X		markstats(e, to);
X
X		/*
X		**  See if this user name is "special".
X		**	If the user name has a slash in it, assume that this
X		**	is a file -- send it off without further ado.  Note
X		**	that this type of addresses is not processed along
X		**	with the others, so we fudge on the To person.
X		*/
X
X		if (m == LocalMailer)
X		{
X			if (user[0] == '/')
X			{
X				rcode = mailfile(user, getctladdr(to));
X				giveresponse(rcode, m, e);
X				continue;
X			}
X		}
X
X		/*
X		**  Address is verified -- add this user to mailer
X		**  argv, and add it to the print list of recipients.
X		*/
X
X		/* link together the chain of recipients */
X		to->q_tchain = tochain;
X		tochain = to;
X
X		/* create list of users for error messages */
X		(void) strcat(tobuf, ",");
X		(void) strcat(tobuf, to->q_paddr);
X		define('u', user, e);		/* to user */
X		define('z', to->q_home, e);	/* user's home */
X
X		/*
X		**  Expand out this user into argument list.
X		*/
X
X		if (!clever)
X		{
X			expand(*mvp, buf, &buf[sizeof buf - 1], e);
X			*pvp++ = newstr(buf);
X			if (pvp >= &pv[MAXPV - 2])
X			{
X				/* allow some space for trailing parms */
X				break;
X			}
X		}
X	}
X
X	/* see if any addresses still exist */
X	if (tobuf[0] == '\0')
X	{
X		define('g', (char *) NULL, e);
X		return (0);
X	}
X
X	/* print out messages as full list */
X	e->e_to = tobuf + 1;
X
X	/*
X	**  Fill out any parameters after the $u parameter.
X	*/
X
X	while (!clever && *++mvp != NULL)
X	{
X		expand(*mvp, buf, &buf[sizeof buf - 1], e);
X		*pvp++ = newstr(buf);
X		if (pvp >= &pv[MAXPV])
X			syserr("deliver: pv overflow after $u for %s", pv[0]);
X	}
X	*pvp++ = NULL;
X
X	/*
X	**  Call the mailer.
X	**	The argument vector gets built, pipes
X	**	are created as necessary, and we fork & exec as
X	**	appropriate.
X	**	If we are running SMTP, we just need to clean up.
X	*/
X
X	message(Arpa_Info, "Connecting to %s.%s...", host, m->m_name);
X
X	if (ctladdr == NULL)
X		ctladdr = &e->e_from;
X# ifdef SMTP
X	if (clever)
X	{
X# ifdef MXDOMAIN
X		expand("\001w", buf, &buf[sizeof buf - 1], e);
X		if (host[0] == '[')
X		{
X			Nmx = 1;
X			MxHosts[0] = host;
X			rcode = EX_OK;
X		}
X		else if ((Nmx = getmxrr(host, MxHosts, MAXMXHOSTS, buf)) < 0)
X		{
X			/*
X			 * Map errors into standard values
X			 */
X			if (Nmx == -1)
X				rcode = EX_TEMPFAIL;
X			else if (Nmx == -3)
X				rcode = EX_NOHOST;
X			else
X				rcode = EX_UNAVAILABLE;
X		}
X		else
X			rcode = EX_OK;
X#else MXDOMAIN
X		Nmx = 1;
X		MxHosts[0] = host;
X		rcode = EX_OK;
X#endif
X		/* send the initial SMTP protocol */
X		if (rcode == EX_OK)
X			rcode = smtpinit(m, pv);
X
X		if (rcode == EX_OK)
X		{
X			/* send the recipient list */
X			tobuf[0] = '\0';
X			for (to = tochain; to != NULL; to = to->q_tchain)
X			{
X				int i;
X
X				e->e_to = to->q_paddr;
X				i = smtprcpt(to, m);
X				if (i != EX_OK)
X				{
X					markfailure(e, to, i);
X					giveresponse(i, m, e);
X				}
X				else
X				{
X					(void) strcat(tobuf, ",");
X					(void) strcat(tobuf, to->q_paddr);
X				}
X			}
X
X			/* now send the data */
X			if (tobuf[0] == '\0')
X				e->e_to = NULL;
X			else
X			{
X				e->e_to = tobuf + 1;
X				rcode = smtpdata(m, e);
X			}
X
X			/* now close the connection */
X			smtpquit(m);
X		}
X	}
X	else
X# endif SMTP
X		rcode = sendoff(e, m, pv, ctladdr);
X
X	/*
X	**  Do final status disposal.
X	**	We check for something in tobuf for the SMTP case.
X	**	If we got a temporary failure, arrange to queue the
X	**		addressees.
X	*/
X
X	if (tobuf[0] != '\0')
X		giveresponse(rcode, m, e);
X	if (rcode != EX_OK)
X	{
X		for (to = tochain; to != NULL; to = to->q_tchain)
X			markfailure(e, to, rcode);
X	}
X
X	errno = 0;
X	define('g', (char *) NULL, e);
X	return (rcode);
X}
X/*
X**  MARKFAILURE -- mark a failure on a specific address.
X**
X**	Parameters:
X**		e -- the envelope we are sending.
X**		q -- the address to mark.
X**		rcode -- the code signifying the particular failure.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		marks the address (and possibly the envelope) with the
X**			failure so that an error will be returned or
X**			the message will be queued, as appropriate.
X*/
X
Xmarkfailure(e, q, rcode)
X	register ENVELOPE *e;
X	register ADDRESS *q;
X	int rcode;
X{
X	if (rcode == EX_OK)
X		return;
X	else if (rcode != EX_TEMPFAIL)
X		q->q_flags |= QBADADDR;
X	else if (curtime() > e->e_ctime + TimeOut)
X	{
X		extern char *pintvl();
X		char buf[MAXLINE];
X
X		if (!bitset(EF_TIMEOUT, e->e_flags))
X		{
X			(void) sprintf(buf, "Cannot send message for %s",
X				pintvl(TimeOut, FALSE));
X			if (e->e_message != NULL)
X				free(e->e_message);
X			e->e_message = newstr(buf);
X			message(Arpa_Info, buf);
X		}
X		q->q_flags |= QBADADDR;
X		e->e_flags |= EF_TIMEOUT;
X	}
X	else
X		q->q_flags |= QQUEUEUP;
X}
X/*
X**  DOFORK -- do a fork, retrying a couple of times on failure.
X**
X**	This MUST be a macro, since after a vfork we are running
X**	two processes on the same stack!!!
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		From a macro???  You've got to be kidding!
X**
X**	Side Effects:
X**		Modifies the ==> LOCAL <== variable 'pid', leaving:
X**			pid of child in parent, zero in child.
X**			-1 on unrecoverable error.
X**
X**	Notes:
X**		I'm awfully sorry this looks so awful.  That's
X**		vfork for you.....
X*/
X
X# define NFORKTRIES	5
X# ifdef VMUNIX
X# define XFORK	vfork
X# else VMUNIX
X# define XFORK	fork
X# endif VMUNIX
X
X# define DOFORK(fORKfN) \
X{\
X	register int i;\
X\
X	for (i = NFORKTRIES; --i >= 0; )\
X	{\
X		pid = fORKfN();\
X		if (pid >= 0)\
X			break;\
X		if (i > 0)\
X			sleep((unsigned) NFORKTRIES - i);\
X	}\
X}
X/*
X**  DOFORK -- simple fork interface to DOFORK.
X**
X**	Parameters:
X**		none.
X**
X**	Returns:
X**		pid of child in parent.
X**		zero in child.
X**		-1 on error.
X**
X**	Side Effects:
X**		returns twice, once in parent and once in child.
X*/
X
Xdofork()
X{
X	register int pid;
X
X	DOFORK(fork);
X	return (pid);
X}
X/*
X**  SENDOFF -- send off call to mailer & collect response.
X**
X**	Parameters:
X**		e -- the envelope to mail.
X**		m -- mailer descriptor.
X**		pvp -- parameter vector to send to it.
X**		ctladdr -- an address pointer controlling the
X**			user/groupid etc. of the mailer.
X**
X**	Returns:
X**		exit status of mailer.
X**
X**	Side Effects:
X**		none.
X*/
X
Xsendoff(e, m, pvp, ctladdr)
X	register ENVELOPE *e;
X	MAILER *m;
X	char **pvp;
X	ADDRESS *ctladdr;
X{
X	auto FILE *mfile;
X	auto FILE *rfile;
X	register int i;
X	int pid;
X
X	/*
X	**  Create connection to mailer.
X	*/
X
X	pid = openmailer(m, pvp, ctladdr, FALSE, &mfile, &rfile);
X	if (pid < 0)
X		return (-1);
X
X	/*
X	**  Format and send message.
X	*/
X
X	putfromline(mfile, m);
X	(*e->e_puthdr)(mfile, m, e);
X	putline("\n", mfile, m);
X	(*e->e_putbody)(mfile, m, e);
X	(void) fclose(mfile);
X
X	i = endmailer(pid, pvp[0]);
X
X	/* arrange a return receipt if requested */
X	if (e->e_receiptto != NULL && bitnset(M_LOCAL, m->m_flags))
X	{
X		e->e_flags |= EF_SENDRECEIPT;
X		/* do we want to send back more info? */
X	}
X
X	return (i);
X}
X/*
X**  ENDMAILER -- Wait for mailer to terminate.
X**
X**	We should never get fatal errors (e.g., segmentation
X**	violation), so we report those specially.  For other
X**	errors, we choose a status message (into statmsg),
X**	and if it represents an error, we print it.
X**
X**	Parameters:
X**		pid -- pid of mailer.
X**		name -- name of mailer (for error messages).
X**
X**	Returns:
X**		exit code of mailer.
X**
X**	Side Effects:
X**		none.
X*/
X
Xendmailer(pid, name)
X	int pid;
X	char *name;
X{
X	int st;
X
X	/* in the IPC case there is nothing to wait for */
X	if (pid == 0)
X		return (EX_OK);
X
X	/* wait for the mailer process to die and collect status */
X	st = waitfor(pid);
X	if (st == -1)
X	{
X		syserr("endmailer %s: wait", name);
X		return (EX_SOFTWARE);
X	}
X
X	/* see if it died a horrid death */
X	if ((st & 0377) != 0)
X	{
X		syserr("mailer %s died with signal %o", name, st);
X		ExitStat = EX_TEMPFAIL;
X		return (EX_TEMPFAIL);
X	}
X
X	/* normal death -- return status */
X	st = (st >> 8) & 0377;
X	return (st);
X}
X/*
X**  OPENMAILER -- open connection to mailer.
X**
X**	Parameters:
X**		m -- mailer descriptor.
X**		pvp -- parameter vector to pass to mailer.
X**		ctladdr -- controlling address for user.
X**		clever -- create a full duplex connection.
X**		pmfile -- pointer to mfile (to mailer) connection.
X**		prfile -- pointer to rfile (from mailer) connection.
X**
X**	Returns:
X**		pid of mailer ( > 0 ).
X**		-1 on error.
X**		zero on an IPC connection.
X**
X**	Side Effects:
X**		creates a mailer in a subprocess.
X*/
X
Xopenmailer(m, pvp, ctladdr, clever, pmfile, prfile)
X	MAILER *m;
X	char **pvp;
X	ADDRESS *ctladdr;
X	bool clever;
X	FILE **pmfile;
X	FILE **prfile;
X{
X	int pid;
X	int mpvect[2];
X	int rpvect[2];
X	FILE *mfile;
X	FILE *rfile;
X	extern FILE *fdopen();
X
X# ifdef DEBUG
X	if (tTd(11, 1))
X	{
X		printf("openmailer:");
X		printav(pvp);
X	}
X# endif DEBUG
X	errno = 0;
X
X	CurHostName = m->m_mailer;
X
X	/*
X	**  Deal with the special case of mail handled through an IPC
X	**  connection.
X	**	In this case we don't actually fork.  We must be
X	**	running SMTP for this to work.  We will return a
X	**	zero pid to indicate that we are running IPC.
X	**  We also handle a debug version that just talks to stdin/out.
X	*/
X
X#ifdef DEBUG
X	/* check for Local Person Communication -- not for mortals!!! */
X	if (strcmp(m->m_mailer, "[LPC]") == 0)
X	{
X		*pmfile = stdout;
X		*prfile = stdin;
X		return (0);
X	}
X#endif DEBUG
X
X	if (strcmp(m->m_mailer, "[IPC]") == 0)
X	{
X#ifdef HOSTINFO
X		register STAB *st;
X		extern STAB *stab();
X#endif HOSTINFO
X#ifdef DAEMON
X		register int i, j;
X		register u_short port;
X
X		CurHostName = pvp[1];
X		if (!clever)
X			syserr("non-clever IPC");
X		if (pvp[2] != NULL)
X			port = atoi(pvp[2]);
X		else
X			port = 0;
X		for (j = 0; j < Nmx; j++)
X		{
X			CurHostName = MxHosts[j];
X#ifdef HOSTINFO
X		/* see if we have already determined that this host is fried */
X			st = stab(MxHosts[j], ST_HOST, ST_FIND);
X			if (st == NULL || st->s_host.ho_exitstat == EX_OK)
X				i = makeconnection(MxHosts[j], port, pmfile, prfile);
X			else
X			{
X				i = st->s_host.ho_exitstat;
X				errno = st->s_host.ho_errno;
X			}
X#else HOSTINFO
X			i = makeconnection(MxHosts[j], port, pmfile, prfile);
X#endif HOSTINFO
X			if (i != EX_OK)
X			{
X#ifdef HOSTINFO
X				/* enter status of this host */
X				if (st == NULL)
X					st = stab(MxHosts[j], ST_HOST, ST_ENTER);
X				st->s_host.ho_exitstat = i;
X				st->s_host.ho_errno = errno;
X#endif HOSTINFO
X				ExitStat = i;
X				continue;
X			}
X			else
X				return (0);
X		}
X		return (-1);
X#else DAEMON
X		syserr("openmailer: no IPC");
X		return (-1);
X#endif DAEMON
X	}
X
X	/* create a pipe to shove the mail through */
X	if (pipe(mpvect) < 0)
X	{
X		syserr("openmailer: pipe (to mailer)");
X		return (-1);
X	}
X
X#ifdef SMTP
X	/* if this mailer speaks smtp, create a return pipe */
X	if (clever && pipe(rpvect) < 0)
X	{
X		syserr("openmailer: pipe (from mailer)");
X		(void) close(mpvect[0]);
X		(void) close(mpvect[1]);
X		return (-1);
X	}
X#endif SMTP
X
X	/*
X	**  Actually fork the mailer process.
X	**	DOFORK is clever about retrying.
X	**
X	**	Dispose of SIGCHLD signal catchers that may be laying
X	**	around so that endmail will get it.
X	*/
X
X	if (CurEnv->e_xfp != NULL)
X		(void) fflush(CurEnv->e_xfp);		/* for debugging */
X	(void) fflush(stdout);
X# ifdef SIGCHLD
X	(void) signal(SIGCHLD, SIG_DFL);
X# endif SIGCHLD
X	DOFORK(XFORK);
X	/* pid is set by DOFORK */
X	if (pid < 0)
X	{
X		/* failure */
X		syserr("openmailer: cannot fork");
X		(void) close(mpvect[0]);
X		(void) close(mpvect[1]);
X#ifdef SMTP
X		if (clever)
X		{
X			(void) close(rpvect[0]);
X			(void) close(rpvect[1]);
X		}
X#endif SMTP
X		return (-1);
X	}
X	else if (pid == 0)
X	{
X		int i;
X		extern int DtableSize;
X
X		/* child -- set up input & exec mailer */
X		/* make diagnostic output be standard output */
X		(void) signal(SIGINT, SIG_IGN);
X		(void) signal(SIGHUP, SIG_IGN);
X		(void) signal(SIGTERM, SIG_DFL);
X
X		/* arrange to filter standard & diag output of command */
X		if (clever)
X		{
X			(void) close(rpvect[0]);
X			(void) close(1);
X			(void) dup(rpvect[1]);
X			(void) close(rpvect[1]);
X		}
X		else if (OpMode == MD_SMTP || HoldErrs)
X		{
X			/* put mailer output in transcript */
X			(void) close(1);
X			(void) dup(fileno(CurEnv->e_xfp));
X		}
X		(void) close(2);
X		(void) dup(1);
X
X		/* arrange to get standard input */
X		(void) close(mpvect[1]);
X		(void) close(0);
X		if (dup(mpvect[0]) < 0)
X		{
X			syserr("Cannot dup to zero!");
X			_exit(EX_OSERR);
X		}
X		(void) close(mpvect[0]);
X		if (!bitnset(M_RESTR, m->m_flags))
X		{
X			if (ctladdr == NULL || ctladdr->q_uid == 0)
X			{
X				(void) setgid(DefGid);
X				(void) setuid(DefUid);
X			}
X			else
X			{
X				(void) setgid(ctladdr->q_gid);
X				(void) setuid(ctladdr->q_uid);
X			}
X		}
X
X		/* arrange for all the files to be closed */
X		for (i = 3; i < DtableSize; i++)
X#ifdef FIOCLEX
X			(void) ioctl(i, FIOCLEX, 0);
X#else FIOCLEX
X			(void) close(i);
X#endif FIOCLEX
X
X		/* try to execute the mailer */
X		execve(m->m_mailer, pvp, UserEnviron);
X
X#ifdef FIOCLEX
X		syserr("Cannot exec %s", m->m_mailer);
X#else FIOCLEX
X		printf("Cannot exec '%s' errno=%d\n", m->m_mailer, errno);
X		(void) fflush(stdout);
X#endif FIOCLEX
X#ifdef EPROCLIM
X		if (m == LocalMailer || errno == EIO || errno == EAGAIN ||
X		    errno == ENOMEM || errno == EPROCLIM)
X#else
X		if (m == LocalMailer || errno == EIO || errno == EAGAIN ||
X		    errno == ENOMEM)
X#endif
X			_exit(EX_TEMPFAIL);
X		else
X			_exit(EX_UNAVAILABLE);
X	}
X
X	/*
X	**  Set up return value.
X	*/
X
X	(void) close(mpvect[0]);
X	mfile = fdopen(mpvect[1], "w");
X	if (clever)
X	{
X		(void) close(rpvect[1]);
X		rfile = fdopen(rpvect[0], "r");
X	}
X
X	*pmfile = mfile;
X	*prfile = rfile;
X
X	return (pid);
X}
X/*
X**  GIVERESPONSE -- Interpret an error response from a mailer
X**
X**	Parameters:
X**		stat -- the status code from the mailer (high byte
X**			only; core dumps must have been taken care of
X**			already).
X**		m -- the mailer descriptor for this mailer.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Errors may be incremented.
X**		ExitStat may be set.
X*/
X
Xgiveresponse(stat, m, e)
X	int stat;
X	register MAILER *m;
X	ENVELOPE *e;
X{
X	register char *statmsg;
X	extern char *SysExMsg[];
X	register int i;
X	extern int N_SysEx;
X#if	defined(DAEMON) || defined(MXDOMAIN)
X	extern int h_errno;
X#endif
X	char buf[MAXLINE];
X
X#ifdef lint
X	if (m == NULL)
X		return;
X#endif lint
X
X	/*
X	**  Compute status message from code.
X	*/
X
X	i = stat - EX__BASE;
X	if (stat == 0)
X		statmsg = "250 Sent";
X	else if (i < 0 || i > N_SysEx)
X	{
X		(void) sprintf(buf, "554 unknown mailer error %d", stat);
X		stat = EX_UNAVAILABLE;
X		statmsg = buf;
X	}
X	else if (stat == EX_TEMPFAIL)
X	{
X		(void) strcpy(buf, SysExMsg[i]);
X#if	defined(TRY_AGAIN) && (defined(DAEMON) || defined(MXDOMAIN))
X		if (h_errno == TRY_AGAIN)
X		{
X			extern char *errstring();
X
X			statmsg = errstring(h_errno+MAX_ERRNO);
X		}
X		else
X#endif
X		{
X			if (errno != 0)
X			{
X				extern char *errstring();
X
X				statmsg = errstring(errno);
X			}
X			else
X			{
X#ifdef SMTP
X				extern char SmtpError[];
X
X				statmsg = SmtpError;
X#else SMTP
X				statmsg = NULL;
X#endif SMTP
X			}
X		}
X		if (statmsg != NULL && statmsg[0] != '\0')
X		{
X			(void) strcat(buf, ": ");
X			(void) strcat(buf, statmsg);
X		}
X		statmsg = buf;
X	}
X	else
X	{
X		statmsg = SysExMsg[i];
X	}
X
X	/*
X	**  Print the message as appropriate
X	*/
X
X	if (stat == EX_OK || stat == EX_TEMPFAIL)
X		message(Arpa_Info, &statmsg[4]);
X	else
X	{
X		Errors++;
X		usrerr(statmsg);
X	}
X
X	/*
X	**  Final cleanup.
X	**	Log a record of the transaction.  Compute the new
X	**	ExitStat -- if we already had an error, stick with
X	**	that.
X	*/
X
X	if (LogLevel > ((stat == 0 || stat == EX_TEMPFAIL) ? 3 : 2))
X		logdelivery(&statmsg[4]);
X
X	if (stat != EX_TEMPFAIL)
X		setstat(stat);
X	if (stat != EX_OK)
X	{
X		if (e->e_message != NULL)
X			free(e->e_message);
X		e->e_message = newstr(&statmsg[4]);
X	}
X	errno = 0;
X#if	defined(DAEMON) || defined(MXDOMAIN)
X	h_errno = 0;
X#endif
X}
X/*
X**  LOGDELIVERY -- log the delivery in the system log
X**
X**	Parameters:
X**		stat -- the message to print for the status
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		none
X*/
X
Xlogdelivery(stat)
X	char *stat;
X{
X	extern char *pintvl();
X
X# ifdef LOG
X	syslog(LOG_INFO, "%s: to=%s, delay=%s, stat=%s", CurEnv->e_id,
X	       CurEnv->e_to, pintvl(curtime() - CurEnv->e_ctime, TRUE), stat);
X# endif LOG
X}
X/*
X**  PUTFROMLINE -- output a UNIX-style from line (or whatever)
X**
X**	This can be made an arbitrary message separator by changing $l
X**
X**	One of the ugliest hacks seen by human eyes is contained herein:
X**	UUCP wants those stupid "remote from <host>" lines.  Why oh why
X**	does a well-meaning programmer such as myself have to deal with
X**	this kind of antique garbage????
X**
X**	Parameters:
X**		fp -- the file to output to.
X**		m -- the mailer describing this entry.
X**
X**	Returns:
X**		none
X**
X**	Side Effects:
X**		outputs some text to fp.
X*/
X
Xputfromline(fp, m)
X	register FILE *fp;
X	register MAILER *m;
X{
X	char *template = "\001l\n";
X	char buf[MAXLINE];
X
X	if (bitnset(M_NHDR, m->m_flags))
X		return;
X
X# ifdef UGLYUUCP
X	if (bitnset(M_UGLYUUCP, m->m_flags))
X	{
X		char *bang;
X		char xbuf[MAXLINE];
X
X		expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
X		bang = index(buf, '!');
X		if (bang == NULL)
X			syserr("No ! in UUCP! (%s)", buf);
X		else
X		{
X			*bang++ = '\0';
X			(void) sprintf(xbuf, "From %s  \001d remote from %s\n", bang, buf);
X			template = xbuf;
X		}
X	}
X# endif UGLYUUCP
X	expand(template, buf, &buf[sizeof buf - 1], CurEnv);
X	putline(buf, fp, m);
X}
X/*
X**  PUTBODY -- put the body of a message.
X**
X**	Parameters:
X**		fp -- file to output onto.
X**		m -- a mailer descriptor to control output format.
X**		e -- the envelope to put out.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		The message is written onto fp.
X*/
X
Xputbody(fp, m, e)
X	FILE *fp;
X	MAILER *m;
X	register ENVELOPE *e;
X{
X	char buf[MAXLINE];
X
X	/*
X	**  Output the body of the message
X	*/
X
X	if (e->e_dfp == NULL)
X	{
X		if (e->e_df != NULL)
X		{
X			e->e_dfp = fopen(e->e_df, "r");
X			if (e->e_dfp == NULL)
X				syserr("Cannot open %s", e->e_df);
X		}
X		else
X			putline("<<< No Message Collected >>>", fp, m);
X	}
X	if (e->e_dfp != NULL)
X	{
X		rewind(e->e_dfp);
X		while (!ferror(fp) && fgets(buf, sizeof buf, e->e_dfp) != NULL)
X		{
X			if (buf[0] == 'F' && bitnset(M_ESCFROM, m->m_flags) &&
X			    strncmp(buf, "From", 4) == 0)
X				(void) putc('>', fp);
X			putline(buf, fp, m);
X		}
X
X		if (ferror(e->e_dfp))
X		{
X			syserr("putbody: read error");
X			ExitStat = EX_IOERR;
X		}
X	}
X
X	(void) fflush(fp);
X	if (ferror(fp) && errno != EPIPE)
X	{
X		syserr("putbody: write error");
X		ExitStat = EX_IOERR;
X	}
X	errno = 0;
X}
X/*
X**  MAILFILE -- Send a message to a file.
X**
X**	If the file has the setuid/setgid bits set, but NO execute
X**	bits, sendmail will try to become the owner of that file
X**	rather than the real user.  Obviously, this only works if
X**	sendmail runs as root.
X**
X**	This could be done as a subordinate mailer, except that it
X**	is used implicitly to save messages in ~/dead.letter.  We
X**	view this as being sufficiently important as to include it
X**	here.  For example, if the system is dying, we shouldn't have
X**	to create another process plus some pipes to save the message.
X**
X**	Parameters:
X**		filename -- the name of the file to send to.
X**		ctladdr -- the controlling address header -- includes
X**			the userid/groupid to be when sending.
X**
X**	Returns:
X**		The exit code associated with the operation.
X**
X**	Side Effects:
X**		none.
X*/
X
Xmailfile(filename, ctladdr)
X	char *filename;
X	ADDRESS *ctladdr;
X{
X	register FILE *f;
X	register int pid;
X
X	/*
X	**  Fork so we can change permissions here.
X	**	Note that we MUST use fork, not vfork, because of
X	**	the complications of calling subroutines, etc.
X	*/
X
X	DOFORK(fork);
X
X	if (pid < 0)
X		return (EX_OSERR);
X	else if (pid == 0)
X	{
X		/* child -- actually write to file */
X		struct stat stb;
X
X		(void) signal(SIGINT, SIG_DFL);
X		(void) signal(SIGHUP, SIG_DFL);
X		(void) signal(SIGTERM, SIG_DFL);
X		(void) umask(OldUmask);
X		if (stat(filename, &stb) < 0)
X		{
X			errno = 0;
X			stb.st_mode = 0666;
X		}
X		if (bitset(0111, stb.st_mode))
X			exit(EX_CANTCREAT);
X		if (ctladdr == NULL)
X			ctladdr = &CurEnv->e_from;
X		if (!bitset(S_ISGID, stb.st_mode) || setgid(stb.st_gid) < 0)
X		{
X			if (ctladdr->q_uid == 0)
X				(void) setgid(DefGid);
X			else
X				(void) setgid(ctladdr->q_gid);
X		}
X		if (!bitset(S_ISUID, stb.st_mode) || setuid(stb.st_uid) < 0)
X		{
X			if (ctladdr->q_uid == 0)
X				(void) setuid(DefUid);
X			else
X				(void) setuid(ctladdr->q_uid);
X		}
X		f = dfopen(filename, "a");
X		if (f == NULL)
X			exit(EX_CANTCREAT);
X
X		putfromline(f, ProgMailer);
X		(*CurEnv->e_puthdr)(f, ProgMailer, CurEnv);
X		putline("\n", f, ProgMailer);
X		(*CurEnv->e_putbody)(f, ProgMailer, CurEnv);
X		putline("\n", f, ProgMailer);
X		(void) fclose(f);
X		(void) fflush(stdout);
X
X		/* reset ISUID & ISGID bits for paranoid systems */
X		(void) chmod(filename, (int) stb.st_mode);
X		exit(EX_OK);
X		/*NOTREACHED*/
X	}
X	else
X	{
X		/* parent -- wait for exit status */
X		int st;
X
X		st = waitfor(pid);
X		if ((st & 0377) != 0)
X			return (EX_UNAVAILABLE);
X		else
X			return ((st >> 8) & 0377);
X	}
X}
X/*
X**  SENDALL -- actually send all the messages.
X**
X**	Parameters:
X**		e -- the envelope to send.
X**		mode -- the delivery mode to use.  If SM_DEFAULT, use
X**			the current SendMode.
X**
X**	Returns:
X**		none.
X**
X**	Side Effects:
X**		Scans the send lists and sends everything it finds.
X**		Delivers any appropriate error messages.
X**		If we are running in a non-interactive mode, takes the
X**			appropriate action.
X*/
X
Xsendall(e, mode)
X	ENVELOPE *e;
X	char mode;
X{
X	register ADDRESS *q;
X	bool oldverbose;
X	int pid;
X
X	/* determine actual delivery mode */
X	if (mode == SM_DEFAULT)
X	{
X		extern bool shouldqueue();
X
X		if (shouldqueue(e->e_msgpriority))
X			mode = SM_QUEUE;
X		else
X			mode = SendMode;
X	}
X
X#ifdef DEBUG
X	if (tTd(13, 1))
X	{
X		printf("\nSENDALL: mode %c, sendqueue:\n", mode);
X		printaddr(e->e_sendqueue, TRUE);
X	}
X#endif DEBUG
X
X	/*
X	**  Do any preprocessing necessary for the mode we are running.
X	**	Check to make sure the hop count is reasonable.
X	**	Delete sends to the sender in mailing lists.
X	*/
X
X	CurEnv = e;
X
X	if (e->e_hopcount > MAXHOP)
X	{
X		syserr("sendall: too many hops (%d max)", MAXHOP);
X		return;
X	}
X
X	if (!MeToo)
X	{
X		extern ADDRESS *recipient();
X
X		e->e_from.q_flags |= QDONTSEND;
X		(void) recipient(&e->e_from, &e->e_sendqueue);
X	}
X
X# ifdef QUEUE
X	if ((mode == SM_QUEUE || mode == SM_FORK ||
X	     (mode != SM_VERIFY && SuperSafe)) &&
X	    !bitset(EF_INQUEUE, e->e_flags))
X		queueup(e, TRUE, mode == SM_QUEUE);
X#endif QUEUE
X
X	oldverbose = Verbose;
X	switch (mode)
X	{
X	  case SM_VERIFY:
X		Verbose = TRUE;
X		break;
X
X	  case SM_QUEUE:
X		e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
X		return;
X
X	  case SM_FORK:
X		if (e->e_xfp != NULL)
X			(void) fflush(e->e_xfp);
X		pid = fork();
X		if (pid < 0)
X		{
X			mode = SM_DELIVER;
X			break;
X		}
X		else if (pid > 0)
X		{
X			/* be sure we leave the temp files to our child */
X			e->e_id = e->e_df = NULL;
X			return;
X		}
X
X		/* double fork to avoid zombies */
X		if (fork() > 0)
X			exit(EX_OK);
X
X		/* be sure we are immune from the terminal */
X		disconnect(FALSE);
X
X		break;
X	}
X
X	/*
X	**  Run through the list and send everything.
X	*/
X
X	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
X	{
X		if (mode == SM_VERIFY)
X		{
X			e->e_to = q->q_paddr;
X			if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
X				message(Arpa_Info, "deliverable");
X		}
X		else
X			(void) deliver(e, q);
X	}
X	Verbose = oldverbose;
X
X	/*
X	**  Now run through and check for errors.
X	*/
X
X	if (mode == SM_VERIFY)
X		return;
X
X	for (q = e->e_sendqueue; q != NULL; q = q->q_next)
X	{
X		register ADDRESS *qq;
X
X# ifdef DEBUG
X		if (tTd(13, 3))
X		{
X			printf("Checking ");
X			printaddr(q, FALSE);
X		}
X# endif DEBUG
X
X		/* only send errors if the message failed */
X		if (!bitset(QBADADDR, q->q_flags))
X			continue;
X
X		/* we have an address that failed -- find the parent */
X		for (qq = q; qq != NULL; qq = qq->q_alias)
X		{
X			char obuf[MAXNAME + 6];
X			extern char *aliaslookup();
X
X			/* we can only have owners for local addresses */
X			if (!bitnset(M_LOCAL, qq->q_mailer->m_flags))
X				continue;
X
X			/* see if the owner list exists */
X			(void) strcpy(obuf, "owner-");
X			if (strncmp(qq->q_user, "owner-", 6) == 0)
X				(void) strcat(obuf, "owner");
X			else
X				(void) strcat(obuf, qq->q_user);
X			if (aliaslookup(obuf) == NULL)
X				continue;
X
X# ifdef DEBUG
X			if (tTd(13, 4))
X				printf("Errors to %s\n", obuf);
X# endif DEBUG
X
X			/* owner list exists -- add it to the error queue */
X			sendtolist(obuf, (ADDRESS *) NULL, &e->e_errorqueue);
X			ErrorMode = EM_MAIL;
X			break;
X		}
X
X		/* if we did not find an owner, send to the sender */
X		if (qq == NULL && bitset(QBADADDR, q->q_flags))
X			sendtolist(e->e_from.q_paddr, qq, &e->e_errorqueue);
X	}
X
X	if (mode == SM_FORK)
X		finis();
X}
END_OF_FILE
if test 31247 -ne `wc -c <'src/deliver.c'`; then
    echo shar: \"'src/deliver.c'\" unpacked with wrong size!
fi
# end of 'src/deliver.c'
fi
echo shar: End of archive 8 \(of 8\).
cp /dev/null ark8isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 8 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
David H. Brierley
Home: dave@galaxia.Newport.RI.US   {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb@rayssd.ray.com           {sun,decuac,gatech,necntc,ukma}!rayssd!dhb

kevin@kosman.UUCP (Kevin O'Gorman) (02/27/89)

This kit has a problem unpacking src/rename.c and src/stab.c.  Try it
yourself, and watch what happens.  This also happens on the stuff I got
at USENIX directly on floppy.

The problem is that rename.c ends with a ^L character with no trailing
linefeed.  This confuses the shar, which puts the end-of-file string on
the same line, where the here-document reader can't see it.  This gets
very messed up.

The problem is seen in the fourth line of the following extract.  Fix
with your favorite editor.  Meanwhile, David: check your shar for dealing
with incomplete last lines.

STUPID
INEWS
FODDER
STUPID
INEWS
FODDER

> 		return(-1);
> 	return(unlink(oldname));
> }
> END_OF_FILE
> if test 281 -ne `wc -c <'src/rename.c'`; then
>     echo shar: \"'src/rename.c'\" unpacked with wrong size!
> fi
> # end of 'src/rename.c'
> fi
> if test -f 'src/stab.c' -a "${1}" != "-c" ; then 
>   echo shar: Will not clobber existing file \"'src/stab.c'\"
> else
> echo shar: Extracting \"'src/stab.c'\" \(2239 characters\)
> sed "s/^X//" >'src/stab.c' <<'END_OF_FILE'
> /*
> **  Sendmail
> **  Copyright (c) 1983  Eric P. Allman