Sendmail source for the 3B1/7300 (part 6/8)
David H. Brierley
dave at galaxia.Newport.RI.US
Sat Feb 25 12:29:23 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 6 (of 8)."
# Contents: src/queue.c src/sendmail.h
# Wrapped by dave at 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 at 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 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