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