Sendmail source for the 3B1/7300 (part 4/8)

David H. Brierley dave at galaxia.Newport.RI.US
Sat Feb 25 12:28:39 AEST 1989


----- 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 at 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 at galaxia.Newport.RI.US   {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb at rayssd.ray.com           {sun,decuac,gatech,necntc,ukma}!rayssd!dhb



More information about the Unix-pc.sources mailing list