386BSD 0.1 development
authorWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Sat, 20 Apr 1991 22:55:16 +0000 (14:55 -0800)
committerWilliam F. Jolitz <wjolitz@soda.berkeley.edu>
Sat, 20 Apr 1991 22:55:16 +0000 (14:55 -0800)
Work on file usr/src/usr.sbin/sendmail/src/arpadate.c
Work on file usr/src/usr.sbin/sendmail/src/clock.c
Work on file usr/src/usr.sbin/sendmail/src/collect.c
Work on file usr/src/usr.sbin/sendmail/src/convtime.c
Work on file usr/src/usr.sbin/sendmail/src/daemon.c
Work on file usr/src/usr.sbin/sendmail/src/deliver.c
Work on file usr/src/usr.sbin/sendmail/src/envelope.c
Work on file usr/src/usr.sbin/sendmail/src/domain.c
Work on file usr/src/usr.sbin/sendmail/src/headers.c
Work on file usr/src/usr.sbin/sendmail/src/err.c
Work on file usr/src/usr.sbin/sendmail/src/macro.c
Work on file usr/src/usr.sbin/sendmail/src/mailstats.h
Work on file usr/src/usr.sbin/sendmail/src/main.c
Work on file usr/src/usr.sbin/sendmail/src/parseaddr.c
Work on file usr/src/usr.sbin/sendmail/src/pathnames.h
Work on file usr/src/usr.sbin/sendmail/src/queue.c
Work on file usr/src/usr.sbin/sendmail/src/recipient.c
Work on file usr/src/usr.sbin/sendmail/src/readcf.c
Work on file usr/src/usr.sbin/sendmail/src/savemail.c
Work on file usr/src/usr.sbin/sendmail/src/sendmail.h
Work on file usr/src/usr.sbin/sendmail/src/stats.c
Work on file usr/src/usr.sbin/sendmail/src/stab.c
Work on file usr/src/usr.sbin/sendmail/src/useful.h
Work on file usr/src/usr.sbin/sendmail/src/trace.c
Work on file usr/src/usr.sbin/sendmail/src/sysexits.c
Work on file usr/src/usr.sbin/sendmail/src/util.c
Work on file usr/src/usr.sbin/sendmail/src/usersmtp.c

Co-Authored-By: Lynne Greer Jolitz <ljolitz@cardio.ucsf.edu>
Synthesized-from: 386BSD-0.1

27 files changed:
usr/src/usr.sbin/sendmail/src/arpadate.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/clock.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/collect.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/convtime.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/daemon.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/deliver.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/domain.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/envelope.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/err.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/headers.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/macro.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/mailstats.h [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/main.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/parseaddr.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/pathnames.h [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/queue.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/readcf.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/recipient.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/savemail.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/sendmail.h [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/stab.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/stats.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/sysexits.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/trace.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/useful.h [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/usersmtp.c [new file with mode: 0644]
usr/src/usr.sbin/sendmail/src/util.c [new file with mode: 0644]

diff --git a/usr/src/usr.sbin/sendmail/src/arpadate.c b/usr/src/usr.sbin/sendmail/src/arpadate.c
new file mode 100644 (file)
index 0000000..29bf644
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)arpadate.c 5.11 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+# include "conf.h"
+# include <time.h>
+# include <sys/types.h>
+# include "useful.h"
+
+/*
+**  ARPADATE -- Create date in ARPANET format
+**
+**     Parameters:
+**             ud -- unix style date string.  if NULL, one is created.
+**
+**     Returns:
+**             pointer to an ARPANET date field
+**
+**     Side Effects:
+**             none
+**
+**     WARNING:
+**             date is stored in a local buffer -- subsequent
+**             calls will overwrite.
+**
+**     Bugs:
+**             Timezone is computed from local time, rather than
+**             from whereever (and whenever) the message was sent.
+**             To do better is very hard.
+**
+**             Some sites are now inserting the timezone into the
+**             local date.  This routine should figure out what
+**             the format is and work appropriately.
+*/
+
+char *
+arpadate(ud)
+       register char *ud;
+{
+       register char *p;
+       register char *q;
+       register int off;
+       register int i;
+       register struct tm *lt;
+       time_t t;
+       struct tm gmt;
+       static char b[40];
+       extern struct tm *localtime(), *gmtime();
+       extern char *ctime();
+       extern time_t time();
+
+       /*
+       **  Get current time.
+       **      This will be used if a null argument is passed and
+       **      to resolve the timezone.
+       */
+
+       (void) time(&t);
+       if (ud == NULL)
+               ud = ctime(&t);
+
+       /*
+       **  Crack the UNIX date line in a singularly unoriginal way.
+       */
+
+       q = b;
+
+       p = &ud[0];             /* Mon */
+       *q++ = *p++;
+       *q++ = *p++;
+       *q++ = *p++;
+       *q++ = ',';
+       *q++ = ' ';
+
+       p = &ud[8];             /* 16 */
+       if (*p == ' ')
+               p++;
+       else
+               *q++ = *p++;
+       *q++ = *p++;
+       *q++ = ' ';
+
+       p = &ud[4];             /* Sep */
+       *q++ = *p++;
+       *q++ = *p++;
+       *q++ = *p++;
+       *q++ = ' ';
+
+       p = &ud[22];            /* 79 */
+       *q++ = *p++;
+       *q++ = *p++;
+       *q++ = ' ';
+
+       p = &ud[11];            /* 01:03:52 */
+       for (i = 8; i > 0; i--)
+               *q++ = *p++;
+
+       /*
+        * should really get the timezone from the time in "ud" (which
+        * is only different if a non-null arg was passed which is different
+        * from the current time), but for all practical purposes, returning
+        * the current local zone will do (its all that is ever needed).
+        */
+       gmt = *gmtime(&t);
+       lt = localtime(&t);
+
+       off = (lt->tm_hour - gmt.tm_hour) * 60 + lt->tm_min - gmt.tm_min;
+
+       /* assume that offset isn't more than a day ... */
+       if (lt->tm_year < gmt.tm_year)
+               off -= 24 * 60;
+       else if (lt->tm_year > gmt.tm_year)
+               off += 24 * 60;
+       else if (lt->tm_yday < gmt.tm_yday)
+               off -= 24 * 60;
+       else if (lt->tm_yday > gmt.tm_yday)
+               off += 24 * 60;
+
+       *q++ = ' ';
+       if (off == 0) {
+               *q++ = 'G';
+               *q++ = 'M';
+               *q++ = 'T';
+       } else {
+               if (off < 0) {
+                       off = -off;
+                       *q++ = '-';
+               } else
+                       *q++ = '+';
+
+               if (off >= 24*60)               /* should be impossible */
+                       off = 23*60+59;         /* if not, insert silly value */
+
+               *q++ = (off / 600) + '0';
+               *q++ = (off / 60) % 10 + '0';
+               off %= 60;
+               *q++ = (off / 10) + '0';
+               *q++ = (off % 10) + '0';
+       }
+       *q = '\0';
+
+       return (b);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/clock.c b/usr/src/usr.sbin/sendmail/src/clock.c
new file mode 100644 (file)
index 0000000..890bfc0
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)clock.c    5.10 (Berkeley) 3/4/91";
+#endif /* not lint */
+
+# include "sendmail.h"
+# include <signal.h>
+
+/*
+**  SETEVENT -- set an event to happen at a specific time.
+**
+**     Events are stored in a sorted list for fast processing.
+**     An event only applies to the process that set it.
+**
+**     Parameters:
+**             intvl -- intvl until next event occurs.
+**             func -- function to call on event.
+**             arg -- argument to func on event.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             none.
+*/
+
+static void tick();
+
+EVENT *
+setevent(intvl, func, arg)
+       time_t intvl;
+       int (*func)();
+       int arg;
+{
+       register EVENT **evp;
+       register EVENT *ev;
+       auto time_t now;
+
+       if (intvl <= 0)
+       {
+               syserr("setevent: intvl=%ld\n", intvl);
+               return (NULL);
+       }
+
+       (void) time(&now);
+
+       /* search event queue for correct position */
+       for (evp = &EventQueue; (ev = *evp) != NULL; evp = &ev->ev_link)
+       {
+               if (ev->ev_time >= now + intvl)
+                       break;
+       }
+
+       /* insert new event */
+       ev = (EVENT *) xalloc(sizeof *ev);
+       ev->ev_time = now + intvl;
+       ev->ev_func = func;
+       ev->ev_arg = arg;
+       ev->ev_pid = getpid();
+       ev->ev_link = *evp;
+       *evp = ev;
+
+       if (tTd(5, 5))
+               printf("setevent: intvl=%ld, for=%ld, func=%x, arg=%d, ev=%x\n",
+                       intvl, now + intvl, func, arg, ev);
+
+       tick();
+       return (ev);
+}
+\f/*
+**  CLREVENT -- remove an event from the event queue.
+**
+**     Parameters:
+**             ev -- pointer to event to remove.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             arranges for event ev to not happen.
+*/
+
+clrevent(ev)
+       register EVENT *ev;
+{
+       register EVENT **evp;
+
+       if (tTd(5, 5))
+               printf("clrevent: ev=%x\n", ev);
+       if (ev == NULL)
+               return;
+
+       /* find the parent event */
+       (void) signal(SIGALRM, SIG_IGN);
+       for (evp = &EventQueue; *evp != NULL; evp = &(*evp)->ev_link)
+       {
+               if (*evp == ev)
+                       break;
+       }
+
+       /* now remove it */
+       if (*evp != NULL)
+       {
+               *evp = ev->ev_link;
+               free((char *) ev);
+       }
+
+       /* restore clocks and pick up anything spare */
+       tick();
+}
+\f/*
+**  TICK -- take a clock tick
+**
+**     Called by the alarm clock.  This routine runs events as needed.
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             calls the next function in EventQueue.
+*/
+
+static void
+tick()
+{
+       register time_t now;
+       register EVENT *ev;
+       int mypid = getpid();
+
+       (void) signal(SIGALRM, SIG_IGN);
+       (void) alarm(0);
+       now = curtime();
+
+       if (tTd(5, 4))
+               printf("tick: now=%ld\n", now);
+
+       while ((ev = EventQueue) != NULL &&
+              (ev->ev_time <= now || ev->ev_pid != mypid))
+       {
+               int (*f)();
+               int arg;
+               int pid;
+
+               /* process the event on the top of the queue */
+               ev = EventQueue;
+               EventQueue = EventQueue->ev_link;
+               if (tTd(5, 6))
+                       printf("tick: ev=%x, func=%x, arg=%d, pid=%d\n", ev,
+                               ev->ev_func, ev->ev_arg, ev->ev_pid);
+
+               /* we must be careful in here because ev_func may not return */
+               (void) signal(SIGALRM, tick);
+#ifdef SIGVTALRM
+               /* reset 4.2bsd signal mask to allow future alarms */
+               (void) sigsetmask(sigblock(0) & ~sigmask(SIGALRM));
+#endif SIGVTALRM
+
+               f = ev->ev_func;
+               arg = ev->ev_arg;
+               pid = ev->ev_pid;
+               free((char *) ev);
+               if (pid != getpid())
+                       continue;
+               if (EventQueue != NULL)
+               {
+                       if (EventQueue->ev_time > now)
+                               (void) alarm((unsigned) (EventQueue->ev_time - now));
+                       else
+                               (void) alarm(3);
+               }
+               (*f)(arg);
+               (void) alarm(0);
+               now = curtime();
+       }
+       (void) signal(SIGALRM, tick);
+       if (EventQueue != NULL)
+               (void) alarm((unsigned) (EventQueue->ev_time - now));
+}
+\f/*
+**  SLEEP -- a version of sleep that works with this stuff
+**
+**     Because sleep uses the alarm facility, I must reimplement
+**     it here.
+**
+**     Parameters:
+**             intvl -- time to sleep.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             waits for intvl time.  However, other events can
+**             be run during that interval.
+*/
+
+static bool    SleepDone;
+
+sleep(intvl)
+       unsigned int intvl;
+{
+       static int endsleep();
+
+       if (intvl == 0)
+               return;
+       SleepDone = FALSE;
+       (void) setevent((time_t) intvl, endsleep, 0);
+       while (!SleepDone)
+               pause();
+}
+
+static
+endsleep()
+{
+       SleepDone = TRUE;
+}
diff --git a/usr/src/usr.sbin/sendmail/src/collect.c b/usr/src/usr.sbin/sendmail/src/collect.c
new file mode 100644 (file)
index 0000000..a96df15
--- /dev/null
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)collect.c  5.9 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+# include <errno.h>
+# include "sendmail.h"
+
+/*
+**  COLLECT -- read & parse message header & make temp file.
+**
+**     Creates a temporary file name and copies the standard
+**     input to that file.  Leading UNIX-style "From" lines are
+**     stripped off (after important information is extracted).
+**
+**     Parameters:
+**             sayok -- if set, give an ARPANET style message
+**                     to say we are ready to collect input.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Temp file is created and filled.
+**             The from person may be set.
+*/
+
+collect(sayok)
+       bool sayok;
+{
+       register FILE *tf;
+       char buf[MAXFIELD], buf2[MAXFIELD];
+       register char *workbuf, *freebuf;
+       register int workbuflen;
+       extern char *hvalue();
+       extern bool isheader(), flusheol();
+
+       /*
+       **  Create the temp file name and create the file.
+       */
+
+       CurEnv->e_df = newstr(queuename(CurEnv, 'd'));
+       if ((tf = dfopen(CurEnv->e_df, "w")) == NULL)
+       {
+               syserr("Cannot create %s", CurEnv->e_df);
+               NoReturn = TRUE;
+               finis();
+       }
+       (void) chmod(CurEnv->e_df, FileMode);
+
+       /*
+       **  Tell ARPANET to go ahead.
+       */
+
+       if (sayok)
+               message("354", "Enter mail, end with \".\" on a line by itself");
+
+       /*
+       **  Try to read a UNIX-style From line
+       */
+
+       if (sfgets(buf, MAXFIELD, InChannel) == NULL)
+               goto readerr;
+       fixcrlf(buf, FALSE);
+# ifndef NOTUNIX
+       if (!SaveFrom && strncmp(buf, "From ", 5) == 0)
+       {
+               if (!flusheol(buf, InChannel))
+                       goto readerr;
+               eatfrom(buf);
+               if (sfgets(buf, MAXFIELD, InChannel) == NULL)
+                       goto readerr;
+               fixcrlf(buf, FALSE);
+       }
+# endif NOTUNIX
+
+       /*
+       **  Copy InChannel to temp file & do message editing.
+       **      To keep certain mailers from getting confused,
+       **      and to keep the output clean, lines that look
+       **      like UNIX "From" lines are deleted in the header.
+       */
+
+       workbuf = buf;          /* `workbuf' contains a header field */
+       freebuf = buf2;         /* `freebuf' can be used for read-ahead */
+       for (;;)
+       {
+               /* first, see if the header is over */
+               if (!isheader(workbuf))
+               {
+                       fixcrlf(workbuf, TRUE);
+                       break;
+               }
+
+               /* if the line is too long, throw the rest away */
+               if (!flusheol(workbuf, InChannel))
+                       goto readerr;
+
+               /* it's okay to toss '\n' now (flusheol() needed it) */
+               fixcrlf(workbuf, TRUE);
+
+               workbuflen = strlen(workbuf);
+
+               /* get the rest of this field */
+               for (;;)
+               {
+                       if (sfgets(freebuf, MAXFIELD, InChannel) == NULL)
+                               goto readerr;
+
+                       /* is this a continuation line? */
+                       if (*freebuf != ' ' && *freebuf != '\t')
+                               break;
+
+                       if (!flusheol(freebuf, InChannel))
+                               goto readerr;
+
+                       /* yes; append line to `workbuf' if there's room */
+                       if (workbuflen < MAXFIELD-3)
+                       {
+                               register char *p = workbuf + workbuflen;
+                               register char *q = freebuf;
+
+                               /* we have room for more of this field */
+                               fixcrlf(freebuf, TRUE);
+                               *p++ = '\n'; workbuflen++;
+                               while(*q != '\0' && workbuflen < MAXFIELD-1)
+                               {
+                                       *p++ = *q++;
+                                       workbuflen++;
+                               }
+                               *p = '\0';
+                       }
+               }
+
+               CurEnv->e_msgsize += workbuflen;
+
+               /*
+               **  The working buffer now becomes the free buffer, since
+               **  the free buffer contains a new header field.
+               **
+               **  This is premature, since we still havent called
+               **  chompheader() to process the field we just created
+               **  (so the call to chompheader() will use `freebuf').
+               **  This convolution is necessary so that if we break out
+               **  of the loop due to H_EOH, `workbuf' will always be
+               **  the next unprocessed buffer.
+               */
+
+               {
+                       register char *tmp = workbuf;
+                       workbuf = freebuf;
+                       freebuf = tmp;
+               }
+
+               /*
+               **  Snarf header away.
+               */
+
+               if (bitset(H_EOH, chompheader(freebuf, FALSE)))
+                       break;
+       }
+
+       if (tTd(30, 1))
+               printf("EOH\n");
+
+       if (*workbuf == '\0')
+       {
+               /* throw away a blank line */
+               if (sfgets(buf, MAXFIELD, InChannel) == NULL)
+                       goto readerr;
+       }
+       else if (workbuf == buf2)       /* guarantee `buf' contains data */
+               (void) strcpy(buf, buf2);
+
+       /*
+       **  Collect the body of the message.
+       */
+
+       do
+       {
+               register char *bp = buf;
+
+               fixcrlf(buf, TRUE);
+
+               /* check for end-of-message */
+               if (!IgnrDot && buf[0] == '.' && (buf[1] == '\n' || buf[1] == '\0'))
+                       break;
+
+               /* check for transparent dot */
+               if (OpMode == MD_SMTP && !IgnrDot && bp[0] == '.' && bp[1] == '.')
+                       bp++;
+
+               /*
+               **  Figure message length, output the line to the temp
+               **  file, and insert a newline if missing.
+               */
+
+               CurEnv->e_msgsize += strlen(bp) + 1;
+               fputs(bp, tf);
+               fputs("\n", tf);
+               if (ferror(tf))
+                       tferror(tf);
+       } while (sfgets(buf, MAXFIELD, InChannel) != NULL);
+
+readerr:
+       if (fflush(tf) != 0)
+               tferror(tf);
+       (void) fclose(tf);
+
+       /* An EOF when running SMTP is an error */
+       if ((feof(InChannel) || ferror(InChannel)) && OpMode == MD_SMTP)
+       {
+               int usrerr(), syserr();
+# ifdef LOG
+               if (RealHostName != NULL && LogLevel > 0)
+                       syslog(LOG_NOTICE,
+                           "collect: unexpected close on connection from %s: %m\n",
+                           CurEnv->e_from.q_paddr, RealHostName);
+# endif
+               (feof(InChannel) ? usrerr: syserr)
+                       ("collect: unexpected close, from=%s", CurEnv->e_from.q_paddr);
+
+               /* don't return an error indication */
+               CurEnv->e_to = NULL;
+               CurEnv->e_flags &= ~EF_FATALERRS;
+
+               /* and don't try to deliver the partial message either */
+               finis();
+       }
+
+       /*
+       **  Find out some information from the headers.
+       **      Examples are who is the from person & the date.
+       */
+
+       eatheader(CurEnv);
+
+       /*
+       **  Add an Apparently-To: line if we have no recipient lines.
+       */
+
+       if (hvalue("to") == NULL && hvalue("cc") == NULL &&
+           hvalue("bcc") == NULL && hvalue("apparently-to") == NULL)
+       {
+               register ADDRESS *q;
+
+               /* create an Apparently-To: field */
+               /*    that or reject the message.... */
+               for (q = CurEnv->e_sendqueue; q != NULL; q = q->q_next)
+               {
+                       if (q->q_alias != NULL)
+                               continue;
+                       if (tTd(30, 3))
+                               printf("Adding Apparently-To: %s\n", q->q_paddr);
+                       addheader("apparently-to", q->q_paddr, CurEnv);
+               }
+       }
+
+       if ((CurEnv->e_dfp = fopen(CurEnv->e_df, "r")) == NULL)
+               syserr("Cannot reopen %s", CurEnv->e_df);
+}
+\f/*
+**  FLUSHEOL -- if not at EOL, throw away rest of input line.
+**
+**     Parameters:
+**             buf -- last line read in (checked for '\n'),
+**             fp -- file to be read from.
+**
+**     Returns:
+**             FALSE on error from sfgets(), TRUE otherwise.
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+flusheol(buf, fp)
+       char *buf;
+       FILE *fp;
+{
+       char junkbuf[MAXLINE], *sfgets();
+       register char *p = buf;
+
+       while (index(p, '\n') == NULL) {
+               if (sfgets(junkbuf,MAXLINE,fp) == NULL)
+                       return(FALSE);
+               p = junkbuf;
+       }
+
+       return(TRUE);
+}
+\f/*
+**  TFERROR -- signal error on writing the temporary file.
+**
+**     Parameters:
+**             tf -- the file pointer for the temporary file.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Gives an error message.
+**             Arranges for following output to go elsewhere.
+*/
+
+tferror(tf)
+       FILE *tf;
+{
+       if (errno == ENOSPC)
+       {
+               (void) freopen(CurEnv->e_df, "w", tf);
+               fputs("\nMAIL DELETED BECAUSE OF LACK OF DISK SPACE\n\n", tf);
+               usrerr("452 Out of disk space for temp file");
+       }
+       else
+               syserr("collect: Cannot write %s", CurEnv->e_df);
+       (void) freopen("/dev/null", "w", tf);
+}
+\f/*
+**  EATFROM -- chew up a UNIX style from line and process
+**
+**     This does indeed make some assumptions about the format
+**     of UNIX messages.
+**
+**     Parameters:
+**             fm -- the from line.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             extracts what information it can from the header,
+**             such as the date.
+*/
+
+# ifndef NOTUNIX
+
+char   *DowList[] =
+{
+       "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", NULL
+};
+
+char   *MonthList[] =
+{
+       "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+       "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+       NULL
+};
+
+eatfrom(fm)
+       char *fm;
+{
+       register char *p;
+       register char **dt;
+
+       if (tTd(30, 2))
+               printf("eatfrom(%s)\n", fm);
+
+       /* find the date part */
+       p = fm;
+       while (*p != '\0')
+       {
+               /* skip a word */
+               while (*p != '\0' && *p != ' ')
+                       p++;
+               while (*p == ' ')
+                       p++;
+               if (!isupper(*p) || p[3] != ' ' || p[13] != ':' || p[16] != ':')
+                       continue;
+
+               /* we have a possible date */
+               for (dt = DowList; *dt != NULL; dt++)
+                       if (strncmp(*dt, p, 3) == 0)
+                               break;
+               if (*dt == NULL)
+                       continue;
+
+               for (dt = MonthList; *dt != NULL; dt++)
+                       if (strncmp(*dt, &p[4], 3) == 0)
+                               break;
+               if (*dt != NULL)
+                       break;
+       }
+
+       if (*p != NULL)
+       {
+               char *q;
+               extern char *arpadate();
+
+               /* we have found a date */
+               q = xalloc(25);
+               (void) strncpy(q, p, 25);
+               q[24] = '\0';
+               define('d', q, CurEnv);
+               q = arpadate(q);
+               define('a', newstr(q), CurEnv);
+       }
+}
+
+# endif NOTUNIX
diff --git a/usr/src/usr.sbin/sendmail/src/convtime.c b/usr/src/usr.sbin/sendmail/src/convtime.c
new file mode 100644 (file)
index 0000000..499421b
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)convtime.c 5.4 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+# include <ctype.h>
+# include "useful.h"
+
+/*
+**  CONVTIME -- convert time
+**
+**     Takes a time as an ascii string with a trailing character
+**     giving units:
+**       s -- seconds
+**       m -- minutes
+**       h -- hours
+**       d -- days (default)
+**       w -- weeks
+**     For example, "3d12h" is three and a half days.
+**
+**     Parameters:
+**             p -- pointer to ascii time.
+**
+**     Returns:
+**             time in seconds.
+**
+**     Side Effects:
+**             none.
+*/
+
+time_t
+convtime(p)
+       char *p;
+{
+       register time_t t, r;
+       register char c;
+
+       r = 0;
+       while (*p != '\0')
+       {
+               t = 0;
+               while (isdigit(c = *p++))
+                       t = t * 10 + (c - '0');
+               if (c == '\0')
+                       p--;
+               switch (c)
+               {
+                 case 'w':             /* weeks */
+                       t *= 7;
+
+                 case 'd':             /* days */
+                 default:
+                       t *= 24;
+
+                 case 'h':             /* hours */
+                       t *= 60;
+
+                 case 'm':             /* minutes */
+                       t *= 60;
+
+                 case 's':             /* seconds */
+                       break;
+               }
+               r += t;
+       }
+
+       return (r);
+}
+\f/*
+**  PINTVL -- produce printable version of a time interval
+**
+**     Parameters:
+**             intvl -- the interval to be converted
+**             brief -- if TRUE, print this in an extremely compact form
+**                     (basically used for logging).
+**
+**     Returns:
+**             A pointer to a string version of intvl suitable for
+**                     printing or framing.
+**
+**     Side Effects:
+**             none.
+**
+**     Warning:
+**             The string returned is in a static buffer.
+*/
+
+# define PLURAL(n)     ((n) == 1 ? "" : "s")
+
+char *
+pintvl(intvl, brief)
+       time_t intvl;
+       bool brief;
+{
+       static char buf[256];
+       register char *p;
+       int wk, dy, hr, mi, se;
+
+       if (intvl == 0 && !brief)
+               return ("zero seconds");
+
+       /* decode the interval into weeks, days, hours, minutes, seconds */
+       se = intvl % 60;
+       intvl /= 60;
+       mi = intvl % 60;
+       intvl /= 60;
+       hr = intvl % 24;
+       intvl /= 24;
+       if (brief)
+               dy = intvl;
+       else
+       {
+               dy = intvl % 7;
+               intvl /= 7;
+               wk = intvl;
+       }
+
+       /* now turn it into a sexy form */
+       p = buf;
+       if (brief)
+       {
+               if (dy > 0)
+               {
+                       (void) sprintf(p, "%d+", dy);
+                       p += strlen(p);
+               }
+               (void) sprintf(p, "%02d:%02d:%02d", hr, mi, se);
+               return (buf);
+       }
+
+       /* use the verbose form */
+       if (wk > 0)
+       {
+               (void) sprintf(p, ", %d week%s", wk, PLURAL(wk));
+               p += strlen(p);
+       }
+       if (dy > 0)
+       {
+               (void) sprintf(p, ", %d day%s", dy, PLURAL(dy));
+               p += strlen(p);
+       }
+       if (hr > 0)
+       {
+               (void) sprintf(p, ", %d hour%s", hr, PLURAL(hr));
+               p += strlen(p);
+       }
+       if (mi > 0)
+       {
+               (void) sprintf(p, ", %d minute%s", mi, PLURAL(mi));
+               p += strlen(p);
+       }
+       if (se > 0)
+       {
+               (void) sprintf(p, ", %d second%s", se, PLURAL(se));
+               p += strlen(p);
+       }
+
+       return (buf + 2);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/daemon.c b/usr/src/usr.sbin/sendmail/src/daemon.c
new file mode 100644 (file)
index 0000000..dd1b4bf
--- /dev/null
@@ -0,0 +1,612 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include "sendmail.h"
+
+#ifndef lint
+#ifdef DAEMON
+static char sccsid[] = "@(#)daemon.c   5.37 (Berkeley) 3/2/91 (with daemon mode)";
+#else
+static char sccsid[] = "@(#)daemon.c   5.37 (Berkeley) 3/2/91 (without daemon mode)";
+#endif
+#endif /* not lint */
+
+int la;        /* load average */
+
+#ifdef DAEMON
+
+# include <netdb.h>
+# include <sys/signal.h>
+# include <sys/wait.h>
+# include <sys/time.h>
+# include <sys/resource.h>
+
+/*
+**  DAEMON.C -- routines to use when running as a daemon.
+**
+**     This entire file is highly dependent on the 4.2 BSD
+**     interprocess communication primitives.  No attempt has
+**     been made to make this file portable to Version 7,
+**     Version 6, MPX files, etc.  If you should try such a
+**     thing yourself, I recommend chucking the entire file
+**     and starting from scratch.  Basic semantics are:
+**
+**     getrequests()
+**             Opens a port and initiates a connection.
+**             Returns in a child.  Must set InChannel and
+**             OutChannel appropriately.
+**     clrdaemon()
+**             Close any open files associated with getting
+**             the connection; this is used when running the queue,
+**             etc., to avoid having extra file descriptors during
+**             the queue run and to avoid confusing the network
+**             code (if it cares).
+**     makeconnection(host, port, outfile, infile)
+**             Make a connection to the named host on the given
+**             port.  Set *outfile and *infile to the files
+**             appropriate for communication.  Returns zero on
+**             success, else an exit status describing the
+**             error.
+**     maphostname(hbuf, hbufsize)
+**             Convert the entry in hbuf into a canonical form.  It
+**             may not be larger than hbufsize.
+*/
+\f/*
+**  GETREQUESTS -- open mail IPC port and get requests.
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Waits until some interesting activity occurs.  When
+**             it does, a child is created to process it, and the
+**             parent waits for completion.  Return from this
+**             routine is always in the child.  The file pointers
+**             "InChannel" and "OutChannel" should be set to point
+**             to the communication channel.
+*/
+
+struct sockaddr_in     SendmailAddress;/* internet address of sendmail */
+
+int    DaemonSocket    = -1;           /* fd describing socket */
+char   *NetName;                       /* name of home (local?) network */
+
+getrequests()
+{
+       int t;
+       register struct servent *sp;
+       int on = 1;
+       extern void reapchild();
+
+       /*
+       **  Set up the address for the mailer.
+       */
+
+       sp = getservbyname("smtp", "tcp");
+       if (sp == NULL)
+       {
+               syserr("server \"smtp\" unknown");
+               goto severe;
+       }
+       SendmailAddress.sin_family = AF_INET;
+       SendmailAddress.sin_addr.s_addr = INADDR_ANY;
+       SendmailAddress.sin_port = sp->s_port;
+
+       /*
+       **  Try to actually open the connection.
+       */
+
+       if (tTd(15, 1))
+               printf("getrequests: port 0x%x\n", SendmailAddress.sin_port);
+
+       /* get a socket for the SMTP connection */
+       DaemonSocket = socket(AF_INET, SOCK_STREAM, 0);
+       if (DaemonSocket < 0)
+       {
+               /* probably another daemon already */
+               syserr("getrequests: can't create socket");
+         severe:
+# ifdef LOG
+               if (LogLevel > 0)
+                       syslog(LOG_ALERT, "cannot get connection");
+# endif LOG
+               finis();
+       }
+
+       /* turn on network debugging? */
+       if (tTd(15, 15))
+               (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
+
+       (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof on);
+       (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_KEEPALIVE, (char *)&on, sizeof on);
+
+       if (bind(DaemonSocket,
+           (struct sockaddr *)&SendmailAddress, sizeof SendmailAddress) < 0)
+       {
+               syserr("getrequests: cannot bind");
+               (void) close(DaemonSocket);
+               goto severe;
+       }
+       if (listen(DaemonSocket, 10) < 0)
+       {
+               syserr("getrequests: cannot listen");
+               (void) close(DaemonSocket);
+               goto severe;
+       }
+
+       (void) signal(SIGCHLD, reapchild);
+
+       if (tTd(15, 1))
+               printf("getrequests: %d\n", DaemonSocket);
+
+       for (;;)
+       {
+               register int pid;
+               auto int lotherend;
+               extern int RefuseLA;
+
+               /* see if we are rejecting connections */
+               while ((la = getla()) > RefuseLA)
+               {
+                       setproctitle("rejecting connections: load average: %.2f", (double)la);
+                       sleep(5);
+               }
+
+               /* wait for a connection */
+               setproctitle("accepting connections");
+               do
+               {
+                       errno = 0;
+                       lotherend = sizeof RealHostAddr;
+                       t = accept(DaemonSocket,
+                           (struct sockaddr *)&RealHostAddr, &lotherend);
+               } while (t < 0 && errno == EINTR);
+               if (t < 0)
+               {
+                       syserr("getrequests: accept");
+                       sleep(5);
+                       continue;
+               }
+
+               /*
+               **  Create a subprocess to process the mail.
+               */
+
+               if (tTd(15, 2))
+                       printf("getrequests: forking (fd = %d)\n", t);
+
+               pid = fork();
+               if (pid < 0)
+               {
+                       syserr("daemon: cannot fork");
+                       sleep(10);
+                       (void) close(t);
+                       continue;
+               }
+
+               if (pid == 0)
+               {
+                       extern struct hostent *gethostbyaddr();
+                       register struct hostent *hp;
+                       char buf[MAXNAME];
+
+                       /*
+                       **  CHILD -- return to caller.
+                       **      Collect verified idea of sending host.
+                       **      Verify calling user id if possible here.
+                       */
+
+                       (void) signal(SIGCHLD, SIG_DFL);
+
+                       /* determine host name */
+                       hp = gethostbyaddr((char *) &RealHostAddr.sin_addr, sizeof RealHostAddr.sin_addr, AF_INET);
+                       if (hp != NULL)
+                               (void) strcpy(buf, hp->h_name);
+                       else
+                       {
+                               extern char *inet_ntoa();
+
+                               /* produce a dotted quad */
+                               (void) sprintf(buf, "[%s]",
+                                       inet_ntoa(RealHostAddr.sin_addr));
+                       }
+
+                       /* should we check for illegal connection here? XXX */
+
+                       RealHostName = newstr(buf);
+
+                       (void) close(DaemonSocket);
+                       InChannel = fdopen(t, "r");
+                       OutChannel = fdopen(dup(t), "w");
+                       if (tTd(15, 2))
+                               printf("getreq: returning\n");
+# ifdef LOG
+                       if (LogLevel > 11)
+                               syslog(LOG_DEBUG, "connected, pid=%d", getpid());
+# endif LOG
+                       return;
+               }
+
+               /* close the port so that others will hang (for a while) */
+               (void) close(t);
+       }
+       /*NOTREACHED*/
+}
+\f/*
+**  CLRDAEMON -- reset the daemon connection
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             releases any resources used by the passive daemon.
+*/
+
+clrdaemon()
+{
+       if (DaemonSocket >= 0)
+               (void) close(DaemonSocket);
+       DaemonSocket = -1;
+}
+\f/*
+**  MAKECONNECTION -- make a connection to an SMTP socket on another machine.
+**
+**     Parameters:
+**             host -- the name of the host.
+**             port -- the port number to connect to.
+**             outfile -- a pointer to a place to put the outfile
+**                     descriptor.
+**             infile -- ditto for infile.
+**
+**     Returns:
+**             An exit code telling whether the connection could be
+**                     made and if not why not.
+**
+**     Side Effects:
+**             none.
+*/
+
+makeconnection(host, port, outfile, infile)
+       char *host;
+       u_short port;
+       FILE **outfile;
+       FILE **infile;
+{
+       register int i, s;
+       register struct hostent *hp = (struct hostent *)NULL;
+       extern char *inet_ntoa();
+       int sav_errno;
+#ifdef NAMED_BIND
+       extern int h_errno;
+#endif
+
+       /*
+       **  Set up the address for the mailer.
+       **      Accept "[a.b.c.d]" syntax for host name.
+       */
+
+#ifdef NAMED_BIND
+       h_errno = 0;
+#endif
+       errno = 0;
+
+       if (host[0] == '[')
+       {
+               long hid;
+               register char *p = index(host, ']');
+
+               if (p != NULL)
+               {
+                       *p = '\0';
+                       hid = inet_addr(&host[1]);
+                       *p = ']';
+               }
+               if (p == NULL || hid == -1)
+               {
+                       usrerr("Invalid numeric domain spec \"%s\"", host);
+                       return (EX_NOHOST);
+               }
+               SendmailAddress.sin_addr.s_addr = hid;
+       }
+       else
+       {
+               hp = gethostbyname(host);
+               if (hp == NULL)
+               {
+#ifdef NAMED_BIND
+                       if (errno == ETIMEDOUT || h_errno == TRY_AGAIN)
+                               return (EX_TEMPFAIL);
+
+                       /* if name server is specified, assume temp fail */
+                       if (errno == ECONNREFUSED && UseNameServer)
+                               return (EX_TEMPFAIL);
+#endif
+
+                       /*
+                       **  XXX Should look for mail forwarder record here
+                       **  XXX if (h_errno == NO_ADDRESS).
+                       */
+
+                       return (EX_NOHOST);
+               }
+               bcopy(hp->h_addr, (char *) &SendmailAddress.sin_addr, hp->h_length);
+               i = 1;
+       }
+
+       /*
+       **  Determine the port number.
+       */
+
+       if (port != 0)
+               SendmailAddress.sin_port = htons(port);
+       else
+       {
+               register struct servent *sp = getservbyname("smtp", "tcp");
+
+               if (sp == NULL)
+               {
+                       syserr("makeconnection: server \"smtp\" unknown");
+                       return (EX_OSFILE);
+               }
+               SendmailAddress.sin_port = sp->s_port;
+       }
+
+       /*
+       **  Try to actually open the connection.
+       */
+
+again:
+       if (tTd(16, 1))
+               printf("makeconnection (%s [%s])\n", host,
+                   inet_ntoa(SendmailAddress.sin_addr.s_addr));
+
+       s = socket(AF_INET, SOCK_STREAM, 0);
+       if (s < 0)
+       {
+               syserr("makeconnection: no socket");
+               sav_errno = errno;
+               goto failure;
+       }
+
+       if (tTd(16, 1))
+               printf("makeconnection: %d\n", s);
+
+       /* turn on network debugging? */
+       if (tTd(16, 14))
+       {
+               int on = 1;
+               (void) setsockopt(DaemonSocket, SOL_SOCKET, SO_DEBUG, (char *)&on, sizeof on);
+       }
+       if (CurEnv->e_xfp != NULL)
+               (void) fflush(CurEnv->e_xfp);           /* for debugging */
+       errno = 0;                                      /* for debugging */
+       SendmailAddress.sin_family = AF_INET;
+       if (connect(s,
+           (struct sockaddr *)&SendmailAddress, sizeof SendmailAddress) < 0)
+       {
+               sav_errno = errno;
+               (void) close(s);
+               if (hp && hp->h_addr_list[i])
+               {
+                       bcopy(hp->h_addr_list[i++],
+                           (char *)&SendmailAddress.sin_addr, hp->h_length);
+                       goto again;
+               }
+
+               /* failure, decide if temporary or not */
+       failure:
+               switch (sav_errno)
+               {
+                 case EISCONN:
+                 case ETIMEDOUT:
+                 case EINPROGRESS:
+                 case EALREADY:
+                 case EADDRINUSE:
+                 case EHOSTDOWN:
+                 case ENETDOWN:
+                 case ENETRESET:
+                 case ENOBUFS:
+                 case ECONNREFUSED:
+                 case ECONNRESET:
+                 case EHOSTUNREACH:
+                 case ENETUNREACH:
+                       /* there are others, I'm sure..... */
+                       return (EX_TEMPFAIL);
+
+                 case EPERM:
+                       /* why is this happening? */
+                       syserr("makeconnection: funny failure, addr=%lx, port=%x",
+                               SendmailAddress.sin_addr.s_addr, SendmailAddress.sin_port);
+                       return (EX_TEMPFAIL);
+
+                 default:
+                       {
+                               extern char *errstring();
+
+                               message(Arpa_Info, "%s", errstring(sav_errno));
+                               return (EX_UNAVAILABLE);
+                       }
+               }
+       }
+
+       /* connection ok, put it into canonical form */
+       *outfile = fdopen(s, "w");
+       *infile = fdopen(dup(s), "r");
+
+       return (EX_OK);
+}
+\f/*
+**  MYHOSTNAME -- return the name of this host.
+**
+**     Parameters:
+**             hostbuf -- a place to return the name of this host.
+**             size -- the size of hostbuf.
+**
+**     Returns:
+**             A list of aliases for this host.
+**
+**     Side Effects:
+**             none.
+*/
+
+char **
+myhostname(hostbuf, size)
+       char hostbuf[];
+       int size;
+{
+       extern struct hostent *gethostbyname();
+       struct hostent *hp;
+
+       if (gethostname(hostbuf, size) < 0)
+       {
+               (void) strcpy(hostbuf, "localhost");
+       }
+       hp = gethostbyname(hostbuf);
+       if (hp != NULL)
+       {
+               (void) strcpy(hostbuf, hp->h_name);
+               return (hp->h_aliases);
+       }
+       else
+               return (NULL);
+}
+
+/*
+ *  MAPHOSTNAME -- turn a hostname into canonical form
+ *
+ *     Parameters:
+ *             hbuf -- a buffer containing a hostname.
+ *             hbsize -- the size of hbuf.
+ *
+ *     Returns:
+ *             none.
+ *
+ *     Side Effects:
+ *             Looks up the host specified in hbuf.  If it is not
+ *             the canonical name for that host, replace it with
+ *             the canonical name.  If the name is unknown, or it
+ *             is already the canonical name, leave it unchanged.
+ */
+maphostname(hbuf, hbsize)
+       char *hbuf;
+       int hbsize;
+{
+       register struct hostent *hp;
+       u_long in_addr;
+       char ptr[256], *cp;
+       struct hostent *gethostbyaddr();
+
+       /*
+        * If first character is a bracket, then it is an address
+        * lookup.  Address is copied into a temporary buffer to
+        * strip the brackets and to preserve hbuf if address is
+        * unknown.
+        */
+       if (*hbuf != '[') {
+               getcanonname(hbuf, hbsize);
+               return;
+       }
+       if ((cp = index(strcpy(ptr, hbuf), ']')) == NULL)
+               return;
+       *cp = '\0';
+       in_addr = inet_addr(&ptr[1]);
+       hp = gethostbyaddr((char *)&in_addr, sizeof(struct in_addr), AF_INET);
+       if (hp == NULL)
+               return;
+       if (strlen(hp->h_name) >= hbsize)
+               hp->h_name[hbsize - 1] = '\0';
+       (void)strcpy(hbuf, hp->h_name);
+}
+
+# else DAEMON
+/* code for systems without sophisticated networking */
+
+/*
+**  MYHOSTNAME -- stub version for case of no daemon code.
+**
+**     Can't convert to upper case here because might be a UUCP name.
+**
+**     Mark, you can change this to be anything you want......
+*/
+
+char **
+myhostname(hostbuf, size)
+       char hostbuf[];
+       int size;
+{
+       register FILE *f;
+
+       hostbuf[0] = '\0';
+       f = fopen("/usr/include/whoami", "r");
+       if (f != NULL)
+       {
+               (void) fgets(hostbuf, size, f);
+               fixcrlf(hostbuf, TRUE);
+               (void) fclose(f);
+       }
+       return (NULL);
+}
+\f/*
+**  MAPHOSTNAME -- turn a hostname into canonical form
+**
+**     Parameters:
+**             hbuf -- a buffer containing a hostname.
+**             hbsize -- the size of hbuf.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Looks up the host specified in hbuf.  If it is not
+**             the canonical name for that host, replace it with
+**             the canonical name.  If the name is unknown, or it
+**             is already the canonical name, leave it unchanged.
+*/
+
+/*ARGSUSED*/
+maphostname(hbuf, hbsize)
+       char *hbuf;
+       int hbsize;
+{
+       return;
+}
+
+#endif DAEMON
diff --git a/usr/src/usr.sbin/sendmail/src/deliver.c b/usr/src/usr.sbin/sendmail/src/deliver.c
new file mode 100644 (file)
index 0000000..c8e05f1
--- /dev/null
@@ -0,0 +1,1553 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)deliver.c  5.41 (Berkeley) 3/21/91";
+#endif /* not lint */
+
+#include "sendmail.h"
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <netdb.h>
+#include <fcntl.h>
+#include <errno.h>
+#ifdef NAMED_BIND
+#include <sys/param.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#endif
+
+/*
+**  DELIVER -- Deliver a message to a list of addresses.
+**
+**     This routine delivers to everyone on the same host as the
+**     user on the head of the list.  It is clever about mailers
+**     that don't handle multiple users.  It is NOT guaranteed
+**     that it will deliver to all these addresses however -- so
+**     deliver should be called once for each address on the
+**     list.
+**
+**     Parameters:
+**             e -- the envelope to deliver.
+**             firstto -- head of the address list to deliver to.
+**
+**     Returns:
+**             zero -- successfully delivered.
+**             else -- some failure, see ExitStat for more info.
+**
+**     Side Effects:
+**             The standard input is passed off to someone.
+*/
+
+deliver(e, firstto)
+       register ENVELOPE *e;
+       ADDRESS *firstto;
+{
+       char *host;                     /* host being sent to */
+       char *user;                     /* user being sent to */
+       char **pvp;
+       register char **mvp;
+       register char *p;
+       register MAILER *m;             /* mailer for this recipient */
+       ADDRESS *ctladdr;
+       register ADDRESS *to = firstto;
+       bool clever = FALSE;            /* running user smtp to this mailer */
+       ADDRESS *tochain = NULL;        /* chain of users in this mailer call */
+       int rcode;              /* response code */
+       char *pv[MAXPV+1];
+       char tobuf[MAXLINE-50];         /* text line of to people */
+       char buf[MAXNAME];
+       char tfrombuf[MAXNAME];         /* translated from person */
+       extern bool checkcompat();
+       extern ADDRESS *getctladdr();
+       extern char *remotename();
+
+       errno = 0;
+       if (bitset(QDONTSEND, to->q_flags))
+               return (0);
+
+#ifdef NAMED_BIND
+       /* unless interactive, try twice, over a minute */
+       if (OpMode == MD_DAEMON || OpMode == MD_SMTP) {
+               _res.retrans = 30;
+               _res.retry = 2;
+       }
+#endif 
+
+       m = to->q_mailer;
+       host = to->q_host;
+
+       if (tTd(10, 1))
+               printf("\n--deliver, mailer=%d, host=`%s', first user=`%s'\n",
+                       m->m_mno, host, to->q_user);
+
+       /*
+       **  If this mailer is expensive, and if we don't want to make
+       **  connections now, just mark these addresses and return.
+       **      This is useful if we want to batch connections to
+       **      reduce load.  This will cause the messages to be
+       **      queued up, and a daemon will come along to send the
+       **      messages later.
+       **              This should be on a per-mailer basis.
+       */
+
+       if (NoConnect && !QueueRun && bitnset(M_EXPENSIVE, m->m_flags) &&
+           !Verbose)
+       {
+               for (; to != NULL; to = to->q_next)
+               {
+                       if (bitset(QDONTSEND, to->q_flags) || to->q_mailer != m)
+                               continue;
+                       to->q_flags |= QQUEUEUP|QDONTSEND;
+                       e->e_to = to->q_paddr;
+                       message(Arpa_Info, "queued");
+                       if (LogLevel > 4)
+                               logdelivery("queued");
+               }
+               e->e_to = NULL;
+               return (0);
+       }
+
+       /*
+       **  Do initial argv setup.
+       **      Insert the mailer name.  Notice that $x expansion is
+       **      NOT done on the mailer name.  Then, if the mailer has
+       **      a picky -f flag, we insert it as appropriate.  This
+       **      code does not check for 'pv' overflow; this places a
+       **      manifest lower limit of 4 for MAXPV.
+       **              The from address rewrite is expected to make
+       **              the address relative to the other end.
+       */
+
+       /* rewrite from address, using rewriting rules */
+       expand("\001f", buf, &buf[sizeof buf - 1], e);
+       (void) strcpy(tfrombuf, remotename(buf, m, TRUE, TRUE));
+
+       define('g', tfrombuf, e);               /* translated sender address */
+       define('h', host, e);                   /* to host */
+       Errors = 0;
+       pvp = pv;
+       *pvp++ = m->m_argv[0];
+
+       /* insert -f or -r flag as appropriate */
+       if (FromFlag && (bitnset(M_FOPT, m->m_flags) || bitnset(M_ROPT, m->m_flags)))
+       {
+               if (bitnset(M_FOPT, m->m_flags))
+                       *pvp++ = "-f";
+               else
+                       *pvp++ = "-r";
+               expand("\001g", buf, &buf[sizeof buf - 1], e);
+               *pvp++ = newstr(buf);
+       }
+
+       /*
+       **  Append the other fixed parts of the argv.  These run
+       **  up to the first entry containing "$u".  There can only
+       **  be one of these, and there are only a few more slots
+       **  in the pv after it.
+       */
+
+       for (mvp = m->m_argv; (p = *++mvp) != NULL; )
+       {
+               while ((p = index(p, '\001')) != NULL)
+                       if (*++p == 'u')
+                               break;
+               if (p != NULL)
+                       break;
+
+               /* this entry is safe -- go ahead and process it */
+               expand(*mvp, buf, &buf[sizeof buf - 1], e);
+               *pvp++ = newstr(buf);
+               if (pvp >= &pv[MAXPV - 3])
+               {
+                       syserr("Too many parameters to %s before $u", pv[0]);
+                       return (-1);
+               }
+       }
+
+       /*
+       **  If we have no substitution for the user name in the argument
+       **  list, we know that we must supply the names otherwise -- and
+       **  SMTP is the answer!!
+       */
+
+       if (*mvp == NULL)
+       {
+               /* running SMTP */
+# ifdef SMTP
+               clever = TRUE;
+               *pvp = NULL;
+# else SMTP
+               /* oops!  we don't implement SMTP */
+               syserr("SMTP style mailer");
+               return (EX_SOFTWARE);
+# endif SMTP
+       }
+
+       /*
+       **  At this point *mvp points to the argument with $u.  We
+       **  run through our address list and append all the addresses
+       **  we can.  If we run out of space, do not fret!  We can
+       **  always send another copy later.
+       */
+
+       tobuf[0] = '\0';
+       e->e_to = tobuf;
+       ctladdr = NULL;
+       for (; to != NULL; to = to->q_next)
+       {
+               /* avoid sending multiple recipients to dumb mailers */
+               if (tobuf[0] != '\0' && !bitnset(M_MUSER, m->m_flags))
+                       break;
+
+               /* if already sent or not for this host, don't send */
+               if (bitset(QDONTSEND, to->q_flags) ||
+                   strcmp(to->q_host, host) != 0 ||
+                   to->q_mailer != firstto->q_mailer)
+                       continue;
+
+               /* avoid overflowing tobuf */
+               if (sizeof tobuf < (strlen(to->q_paddr) + strlen(tobuf) + 2))
+                       break;
+
+               if (tTd(10, 1))
+               {
+                       printf("\nsend to ");
+                       printaddr(to, FALSE);
+               }
+
+               /* compute effective uid/gid when sending */
+               if (to->q_mailer == ProgMailer)
+                       ctladdr = getctladdr(to);
+
+               user = to->q_user;
+               e->e_to = to->q_paddr;
+               to->q_flags |= QDONTSEND;
+
+               /*
+               **  Check to see that these people are allowed to
+               **  talk to each other.
+               */
+
+               if (m->m_maxsize != 0 && e->e_msgsize > m->m_maxsize)
+               {
+                       NoReturn = TRUE;
+                       usrerr("Message is too large; %ld bytes max", m->m_maxsize);
+                       giveresponse(EX_UNAVAILABLE, m, e);
+                       continue;
+               }
+               if (!checkcompat(to))
+               {
+                       giveresponse(EX_UNAVAILABLE, m, e);
+                       continue;
+               }
+
+               /*
+               **  Strip quote bits from names if the mailer is dumb
+               **      about them.
+               */
+
+               if (bitnset(M_STRIPQ, m->m_flags))
+               {
+                       stripquotes(user, TRUE);
+                       stripquotes(host, TRUE);
+               }
+               else
+               {
+                       stripquotes(user, FALSE);
+                       stripquotes(host, FALSE);
+               }
+
+               /* hack attack -- delivermail compatibility */
+               if (m == ProgMailer && *user == '|')
+                       user++;
+
+               /*
+               **  If an error message has already been given, don't
+               **      bother to send to this address.
+               **
+               **      >>>>>>>>>> This clause assumes that the local mailer
+               **      >> NOTE >> cannot do any further aliasing; that
+               **      >>>>>>>>>> function is subsumed by sendmail.
+               */
+
+               if (bitset(QBADADDR|QQUEUEUP, to->q_flags))
+                       continue;
+
+               /* save statistics.... */
+               markstats(e, to);
+
+               /*
+               **  See if this user name is "special".
+               **      If the user name has a slash in it, assume that this
+               **      is a file -- send it off without further ado.  Note
+               **      that this type of addresses is not processed along
+               **      with the others, so we fudge on the To person.
+               */
+
+               if (m == LocalMailer)
+               {
+                       if (user[0] == '/')
+                       {
+                               rcode = mailfile(user, getctladdr(to));
+                               giveresponse(rcode, m, e);
+                               if (rcode == EX_OK)
+                                       to->q_flags |= QSENT;
+                               continue;
+                       }
+               }
+
+               /*
+               **  Address is verified -- add this user to mailer
+               **  argv, and add it to the print list of recipients.
+               */
+
+               /* link together the chain of recipients */
+               to->q_tchain = tochain;
+               tochain = to;
+
+               /* create list of users for error messages */
+               (void) strcat(tobuf, ",");
+               (void) strcat(tobuf, to->q_paddr);
+               define('u', user, e);           /* to user */
+               define('z', to->q_home, e);     /* user's home */
+
+               /*
+               **  Expand out this user into argument list.
+               */
+
+               if (!clever)
+               {
+                       expand(*mvp, buf, &buf[sizeof buf - 1], e);
+                       *pvp++ = newstr(buf);
+                       if (pvp >= &pv[MAXPV - 2])
+                       {
+                               /* allow some space for trailing parms */
+                               break;
+                       }
+               }
+       }
+
+       /* see if any addresses still exist */
+       if (tobuf[0] == '\0')
+       {
+               define('g', (char *) NULL, e);
+               return (0);
+       }
+
+       /* print out messages as full list */
+       e->e_to = tobuf + 1;
+
+       /*
+       **  Fill out any parameters after the $u parameter.
+       */
+
+       while (!clever && *++mvp != NULL)
+       {
+               expand(*mvp, buf, &buf[sizeof buf - 1], e);
+               *pvp++ = newstr(buf);
+               if (pvp >= &pv[MAXPV])
+                       syserr("deliver: pv overflow after $u for %s", pv[0]);
+       }
+       *pvp++ = NULL;
+
+       /*
+       **  Call the mailer.
+       **      The argument vector gets built, pipes
+       **      are created as necessary, and we fork & exec as
+       **      appropriate.
+       **      If we are running SMTP, we just need to clean up.
+       */
+
+       if (ctladdr == NULL)
+               ctladdr = &e->e_from;
+#ifdef NAMED_BIND
+       _res.options &= ~(RES_DEFNAMES | RES_DNSRCH);           /* XXX */
+#endif
+#ifdef SMTP
+       if (clever)
+       {
+               rcode = EX_OK;
+#ifdef NAMED_BIND
+               if (host[0] && host[0] != '[')
+               {
+                       expand("\001w", buf, &buf[sizeof(buf) - 1], e);
+                       Nmx = getmxrr(host, MxHosts, buf, &rcode);
+               }
+               else
+#endif
+               {
+                       Nmx = 1;
+                       MxHosts[0] = host;
+               }
+               if (Nmx >= 0)
+               {
+                       message(Arpa_Info, "Connecting to %s (%s)...",
+                           MxHosts[0], m->m_name);
+                       if ((rcode = smtpinit(m, pv)) == EX_OK) {
+                               register char *t = tobuf;
+                               register int i;
+
+                               /* send the recipient list */
+                               tobuf[0] = '\0';
+                               for (to = tochain; to; to = to->q_tchain) {
+                                       e->e_to = to->q_paddr;
+                                       if ((i = smtprcpt(to, m)) != EX_OK) {
+                                               markfailure(e, to, i);
+                                               giveresponse(i, m, e);
+                                       }
+                                       else {
+                                               *t++ = ',';
+                                               for (p = to->q_paddr; *p; *t++ = *p++);
+                                       }
+                               }
+
+                               /* now send the data */
+                               if (tobuf[0] == '\0')
+                                       e->e_to = NULL;
+                               else {
+                                       e->e_to = tobuf + 1;
+                                       rcode = smtpdata(m, e);
+                               }
+
+                               /* now close the connection */
+                               smtpquit(m);
+                       }
+               }
+       }
+       else
+#endif /* SMTP */
+       {
+               static int sendoff();
+
+               message(Arpa_Info, "Connecting to %s (%s)...", host, m->m_name);
+               rcode = sendoff(e, m, pv, ctladdr);
+       }
+#ifdef NAMED_BIND
+       _res.options |= RES_DEFNAMES | RES_DNSRCH;      /* XXX */
+#endif
+
+       /*
+       **  Do final status disposal.
+       **      We check for something in tobuf for the SMTP case.
+       **      If we got a temporary failure, arrange to queue the
+       **              addressees.
+       */
+
+       if (tobuf[0] != '\0')
+               giveresponse(rcode, m, e);
+       for (to = tochain; to != NULL; to = to->q_tchain)
+               if (rcode != EX_OK)
+                       markfailure(e, to, rcode);
+               else
+                       to->q_flags |= QSENT;
+
+       errno = 0;
+       define('g', (char *) NULL, e);
+       return (rcode);
+}
+\f/*
+**  MARKFAILURE -- mark a failure on a specific address.
+**
+**     Parameters:
+**             e -- the envelope we are sending.
+**             q -- the address to mark.
+**             rcode -- the code signifying the particular failure.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             marks the address (and possibly the envelope) with the
+**                     failure so that an error will be returned or
+**                     the message will be queued, as appropriate.
+*/
+
+markfailure(e, q, rcode)
+       register ENVELOPE *e;
+       register ADDRESS *q;
+       int rcode;
+{
+       if (rcode == EX_OK)
+               return;
+       else if (rcode != EX_TEMPFAIL && rcode != EX_IOERR && rcode != EX_OSERR)
+               q->q_flags |= QBADADDR;
+       else if (curtime() > e->e_ctime + TimeOut)
+       {
+               extern char *pintvl();
+               char buf[MAXLINE];
+
+               if (!bitset(EF_TIMEOUT, e->e_flags))
+               {
+                       (void) sprintf(buf, "Cannot send message for %s",
+                               pintvl(TimeOut, FALSE));
+                       if (e->e_message != NULL)
+                               free(e->e_message);
+                       e->e_message = newstr(buf);
+                       message(Arpa_Info, buf);
+               }
+               q->q_flags |= QBADADDR;
+               e->e_flags |= EF_TIMEOUT;
+       }
+       else
+               q->q_flags |= QQUEUEUP;
+}
+\f/*
+**  DOFORK -- do a fork, retrying a couple of times on failure.
+**
+**     This MUST be a macro, since after a vfork we are running
+**     two processes on the same stack!!!
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             From a macro???  You've got to be kidding!
+**
+**     Side Effects:
+**             Modifies the ==> LOCAL <== variable 'pid', leaving:
+**                     pid of child in parent, zero in child.
+**                     -1 on unrecoverable error.
+**
+**     Notes:
+**             I'm awfully sorry this looks so awful.  That's
+**             vfork for you.....
+*/
+
+# define NFORKTRIES    5
+# ifdef VMUNIX
+# define XFORK vfork
+# else VMUNIX
+# define XFORK fork
+# endif VMUNIX
+
+# define DOFORK(fORKfN) \
+{\
+       register int i;\
+\
+       for (i = NFORKTRIES; --i >= 0; )\
+       {\
+               pid = fORKfN();\
+               if (pid >= 0)\
+                       break;\
+               if (i > 0)\
+                       sleep((unsigned) NFORKTRIES - i);\
+       }\
+}
+\f/*
+**  DOFORK -- simple fork interface to DOFORK.
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             pid of child in parent.
+**             zero in child.
+**             -1 on error.
+**
+**     Side Effects:
+**             returns twice, once in parent and once in child.
+*/
+
+dofork()
+{
+       register int pid;
+
+       DOFORK(fork);
+       return (pid);
+}
+\f/*
+**  SENDOFF -- send off call to mailer & collect response.
+**
+**     Parameters:
+**             e -- the envelope to mail.
+**             m -- mailer descriptor.
+**             pvp -- parameter vector to send to it.
+**             ctladdr -- an address pointer controlling the
+**                     user/groupid etc. of the mailer.
+**
+**     Returns:
+**             exit status of mailer.
+**
+**     Side Effects:
+**             none.
+*/
+static
+sendoff(e, m, pvp, ctladdr)
+       register ENVELOPE *e;
+       MAILER *m;
+       char **pvp;
+       ADDRESS *ctladdr;
+{
+       auto FILE *mfile;
+       auto FILE *rfile;
+       register int i;
+       int pid;
+
+       /*
+       **  Create connection to mailer.
+       */
+
+       pid = openmailer(m, pvp, ctladdr, FALSE, &mfile, &rfile);
+       if (pid < 0)
+               return (-1);
+
+       /*
+       **  Format and send message.
+       */
+
+       putfromline(mfile, m);
+       (*e->e_puthdr)(mfile, m, e);
+       putline("\n", mfile, m);
+       (*e->e_putbody)(mfile, m, e);
+       (void) fclose(mfile);
+       if (rfile != NULL)
+               (void) fclose(rfile);
+
+       i = endmailer(pid, pvp[0]);
+
+       /* arrange a return receipt if requested */
+       if (e->e_receiptto != NULL && bitnset(M_LOCAL, m->m_flags))
+       {
+               e->e_flags |= EF_SENDRECEIPT;
+               /* do we want to send back more info? */
+       }
+
+       return (i);
+}
+\f/*
+**  ENDMAILER -- Wait for mailer to terminate.
+**
+**     We should never get fatal errors (e.g., segmentation
+**     violation), so we report those specially.  For other
+**     errors, we choose a status message (into statmsg),
+**     and if it represents an error, we print it.
+**
+**     Parameters:
+**             pid -- pid of mailer.
+**             name -- name of mailer (for error messages).
+**
+**     Returns:
+**             exit code of mailer.
+**
+**     Side Effects:
+**             none.
+*/
+
+endmailer(pid, name)
+       int pid;
+       char *name;
+{
+       int st;
+
+       /* in the IPC case there is nothing to wait for */
+       if (pid == 0)
+               return (EX_OK);
+
+       /* wait for the mailer process to die and collect status */
+       st = waitfor(pid);
+       if (st == -1)
+       {
+               syserr("endmailer %s: wait", name);
+               return (EX_SOFTWARE);
+       }
+
+       /* see if it died a horrid death */
+       if ((st & 0377) != 0)
+       {
+               syserr("mailer %s died with signal %o", name, st);
+               ExitStat = EX_TEMPFAIL;
+               return (EX_TEMPFAIL);
+       }
+
+       /* normal death -- return status */
+       st = (st >> 8) & 0377;
+       return (st);
+}
+\f/*
+**  OPENMAILER -- open connection to mailer.
+**
+**     Parameters:
+**             m -- mailer descriptor.
+**             pvp -- parameter vector to pass to mailer.
+**             ctladdr -- controlling address for user.
+**             clever -- create a full duplex connection.
+**             pmfile -- pointer to mfile (to mailer) connection.
+**             prfile -- pointer to rfile (from mailer) connection.
+**
+**     Returns:
+**             pid of mailer ( > 0 ).
+**             -1 on error.
+**             zero on an IPC connection.
+**
+**     Side Effects:
+**             creates a mailer in a subprocess.
+*/
+
+openmailer(m, pvp, ctladdr, clever, pmfile, prfile)
+       MAILER *m;
+       char **pvp;
+       ADDRESS *ctladdr;
+       bool clever;
+       FILE **pmfile;
+       FILE **prfile;
+{
+       int pid;
+       int mpvect[2];
+       int rpvect[2];
+       FILE *mfile = NULL;
+       FILE *rfile = NULL;
+       extern FILE *fdopen();
+
+       if (tTd(11, 1))
+       {
+               printf("openmailer:");
+               printav(pvp);
+       }
+       errno = 0;
+
+       CurHostName = m->m_mailer;
+
+       /*
+       **  Deal with the special case of mail handled through an IPC
+       **  connection.
+       **      In this case we don't actually fork.  We must be
+       **      running SMTP for this to work.  We will return a
+       **      zero pid to indicate that we are running IPC.
+       **  We also handle a debug version that just talks to stdin/out.
+       */
+
+       /* check for Local Person Communication -- not for mortals!!! */
+       if (strcmp(m->m_mailer, "[LPC]") == 0)
+       {
+               *pmfile = stdout;
+               *prfile = stdin;
+               return (0);
+       }
+
+       if (strcmp(m->m_mailer, "[IPC]") == 0)
+       {
+#ifdef HOSTINFO
+               register STAB *st;
+               extern STAB *stab();
+#endif HOSTINFO
+#ifdef DAEMON
+               register int i, j;
+               register u_short port;
+
+               CurHostName = pvp[1];
+               if (!clever)
+                       syserr("non-clever IPC");
+               if (pvp[2] != NULL)
+                       port = atoi(pvp[2]);
+               else
+                       port = 0;
+               for (j = 0; j < Nmx; j++)
+               {
+                       CurHostName = MxHosts[j];
+#ifdef HOSTINFO
+               /* see if we have already determined that this host is fried */
+                       st = stab(MxHosts[j], ST_HOST, ST_FIND);
+                       if (st == NULL || st->s_host.ho_exitstat == EX_OK) {
+                               if (j > 1)
+                                       message(Arpa_Info,
+                                           "Connecting to %s (%s)...",
+                                           MxHosts[j], m->m_name);
+                               i = makeconnection(MxHosts[j], port, pmfile, prfile);
+                       }
+                       else
+                       {
+                               i = st->s_host.ho_exitstat;
+                               errno = st->s_host.ho_errno;
+                       }
+#else HOSTINFO
+                       i = makeconnection(MxHosts[j], port, pmfile, prfile);
+#endif HOSTINFO
+                       if (i != EX_OK)
+                       {
+#ifdef HOSTINFO
+                               /* enter status of this host */
+                               if (st == NULL)
+                                       st = stab(MxHosts[j], ST_HOST, ST_ENTER);
+                               st->s_host.ho_exitstat = i;
+                               st->s_host.ho_errno = errno;
+#endif HOSTINFO
+                               ExitStat = i;
+                               continue;
+                       }
+                       else
+                               return (0);
+               }
+               return (-1);
+#else DAEMON
+               syserr("openmailer: no IPC");
+               return (-1);
+#endif DAEMON
+       }
+
+       /* create a pipe to shove the mail through */
+       if (pipe(mpvect) < 0)
+       {
+               syserr("openmailer: pipe (to mailer)");
+               return (-1);
+       }
+
+#ifdef SMTP
+       /* if this mailer speaks smtp, create a return pipe */
+       if (clever && pipe(rpvect) < 0)
+       {
+               syserr("openmailer: pipe (from mailer)");
+               (void) close(mpvect[0]);
+               (void) close(mpvect[1]);
+               return (-1);
+       }
+#endif SMTP
+
+       /*
+       **  Actually fork the mailer process.
+       **      DOFORK is clever about retrying.
+       **
+       **      Dispose of SIGCHLD signal catchers that may be laying
+       **      around so that endmail will get it.
+       */
+
+       if (CurEnv->e_xfp != NULL)
+               (void) fflush(CurEnv->e_xfp);           /* for debugging */
+       (void) fflush(stdout);
+# ifdef SIGCHLD
+       (void) signal(SIGCHLD, SIG_DFL);
+# endif SIGCHLD
+       DOFORK(XFORK);
+       /* pid is set by DOFORK */
+       if (pid < 0)
+       {
+               /* failure */
+               syserr("openmailer: cannot fork");
+               (void) close(mpvect[0]);
+               (void) close(mpvect[1]);
+#ifdef SMTP
+               if (clever)
+               {
+                       (void) close(rpvect[0]);
+                       (void) close(rpvect[1]);
+               }
+#endif SMTP
+               return (-1);
+       }
+       else if (pid == 0)
+       {
+               int i;
+               extern int DtableSize;
+
+               /* child -- set up input & exec mailer */
+               /* make diagnostic output be standard output */
+               (void) signal(SIGINT, SIG_IGN);
+               (void) signal(SIGHUP, SIG_IGN);
+               (void) signal(SIGTERM, SIG_DFL);
+
+               /* arrange to filter standard & diag output of command */
+               if (clever)
+               {
+                       (void) close(rpvect[0]);
+                       (void) close(1);
+                       (void) dup(rpvect[1]);
+                       (void) close(rpvect[1]);
+               }
+               else if (OpMode == MD_SMTP || HoldErrs)
+               {
+                       /* put mailer output in transcript */
+                       (void) close(1);
+                       (void) dup(fileno(CurEnv->e_xfp));
+               }
+               (void) close(2);
+               (void) dup(1);
+
+               /* arrange to get standard input */
+               (void) close(mpvect[1]);
+               (void) close(0);
+               if (dup(mpvect[0]) < 0)
+               {
+                       syserr("Cannot dup to zero!");
+                       _exit(EX_OSERR);
+               }
+               (void) close(mpvect[0]);
+               if (!bitnset(M_RESTR, m->m_flags))
+               {
+                       if (ctladdr == NULL || ctladdr->q_uid == 0)
+                       {
+                               (void) setgid(DefGid);
+                               (void) initgroups(DefUser, DefGid);
+                               (void) setuid(DefUid);
+                       }
+                       else
+                       {
+                               (void) setgid(ctladdr->q_gid);
+                               (void) initgroups(ctladdr->q_ruser?
+                                       ctladdr->q_ruser: ctladdr->q_user,
+                                       ctladdr->q_gid);
+                               (void) setuid(ctladdr->q_uid);
+                       }
+               }
+
+               /* arrange for all the files to be closed */
+               for (i = 3; i < DtableSize; i++) {
+                       register int j;
+                       if ((j = fcntl(i, F_GETFD, 0)) != -1)
+                               (void)fcntl(i, F_SETFD, j|1);
+               }
+
+               /* try to execute the mailer */
+               execve(m->m_mailer, pvp, UserEnviron);
+               syserr("Cannot exec %s", m->m_mailer);
+               if (m == LocalMailer || errno == EIO || errno == EAGAIN ||
+                   errno == ENOMEM || errno == EPROCLIM)
+                       _exit(EX_TEMPFAIL);
+               else
+                       _exit(EX_UNAVAILABLE);
+       }
+
+       /*
+       **  Set up return value.
+       */
+
+       (void) close(mpvect[0]);
+       mfile = fdopen(mpvect[1], "w");
+       if (clever)
+       {
+               (void) close(rpvect[1]);
+               rfile = fdopen(rpvect[0], "r");
+       } else
+               rfile = NULL;
+
+       *pmfile = mfile;
+       *prfile = rfile;
+
+       return (pid);
+}
+\f/*
+**  GIVERESPONSE -- Interpret an error response from a mailer
+**
+**     Parameters:
+**             stat -- the status code from the mailer (high byte
+**                     only; core dumps must have been taken care of
+**                     already).
+**             m -- the mailer descriptor for this mailer.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Errors may be incremented.
+**             ExitStat may be set.
+*/
+
+giveresponse(stat, m, e)
+       int stat;
+       register MAILER *m;
+       ENVELOPE *e;
+{
+       register char *statmsg;
+       extern char *SysExMsg[];
+       register int i;
+       extern int N_SysEx;
+#ifdef NAMED_BIND
+       extern int h_errno;
+#endif
+       char buf[MAXLINE];
+
+#ifdef lint
+       if (m == NULL)
+               return;
+#endif lint
+
+       /*
+       **  Compute status message from code.
+       */
+
+       i = stat - EX__BASE;
+       if (stat == 0)
+               statmsg = "250 Sent";
+       else if (i < 0 || i > N_SysEx)
+       {
+               (void) sprintf(buf, "554 unknown mailer error %d", stat);
+               stat = EX_UNAVAILABLE;
+               statmsg = buf;
+       }
+       else if (stat == EX_TEMPFAIL)
+       {
+               (void) strcpy(buf, SysExMsg[i]);
+#ifdef NAMED_BIND
+               if (h_errno == TRY_AGAIN)
+               {
+                       extern char *errstring();
+
+                       statmsg = errstring(h_errno+MAX_ERRNO);
+               }
+               else
+#endif
+               {
+                       if (errno != 0)
+                       {
+                               extern char *errstring();
+
+                               statmsg = errstring(errno);
+                       }
+                       else
+                       {
+#ifdef SMTP
+                               extern char SmtpError[];
+
+                               statmsg = SmtpError;
+#else SMTP
+                               statmsg = NULL;
+#endif SMTP
+                       }
+               }
+               if (statmsg != NULL && statmsg[0] != '\0')
+               {
+                       (void) strcat(buf, ": ");
+                       (void) strcat(buf, statmsg);
+               }
+               statmsg = buf;
+       }
+       else
+       {
+               statmsg = SysExMsg[i];
+       }
+
+       /*
+       **  Print the message as appropriate
+       */
+
+       if (stat == EX_OK || stat == EX_TEMPFAIL)
+               message(Arpa_Info, &statmsg[4]);
+       else
+       {
+               Errors++;
+               usrerr(statmsg);
+       }
+
+       /*
+       **  Final cleanup.
+       **      Log a record of the transaction.  Compute the new
+       **      ExitStat -- if we already had an error, stick with
+       **      that.
+       */
+
+       if (LogLevel > ((stat == 0 || stat == EX_TEMPFAIL) ? 3 : 2))
+               logdelivery(&statmsg[4]);
+
+       if (stat != EX_TEMPFAIL)
+               setstat(stat);
+       if (stat != EX_OK)
+       {
+               if (e->e_message != NULL)
+                       free(e->e_message);
+               e->e_message = newstr(&statmsg[4]);
+       }
+       errno = 0;
+#ifdef NAMED_BIND
+       h_errno = 0;
+#endif
+}
+\f/*
+**  LOGDELIVERY -- log the delivery in the system log
+**
+**     Parameters:
+**             stat -- the message to print for the status
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             none
+*/
+
+logdelivery(stat)
+       char *stat;
+{
+       extern char *pintvl();
+
+# ifdef LOG
+       syslog(LOG_INFO, "%s: to=%s, delay=%s, stat=%s", CurEnv->e_id,
+              CurEnv->e_to, pintvl(curtime() - CurEnv->e_ctime, TRUE), stat);
+# endif LOG
+}
+\f/*
+**  PUTFROMLINE -- output a UNIX-style from line (or whatever)
+**
+**     This can be made an arbitrary message separator by changing $l
+**
+**     One of the ugliest hacks seen by human eyes is contained herein:
+**     UUCP wants those stupid "remote from <host>" lines.  Why oh why
+**     does a well-meaning programmer such as myself have to deal with
+**     this kind of antique garbage????
+**
+**     Parameters:
+**             fp -- the file to output to.
+**             m -- the mailer describing this entry.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             outputs some text to fp.
+*/
+
+putfromline(fp, m)
+       register FILE *fp;
+       register MAILER *m;
+{
+       char *template = "\001l\n";
+       char buf[MAXLINE];
+
+       if (bitnset(M_NHDR, m->m_flags))
+               return;
+
+# ifdef UGLYUUCP
+       if (bitnset(M_UGLYUUCP, m->m_flags))
+       {
+               char *bang;
+               char xbuf[MAXLINE];
+
+               expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
+               bang = index(buf, '!');
+               if (bang == NULL)
+                       syserr("No ! in UUCP! (%s)", buf);
+               else
+               {
+                       *bang++ = '\0';
+                       (void) sprintf(xbuf, "From %s  \001d remote from %s\n", bang, buf);
+                       template = xbuf;
+               }
+       }
+# endif UGLYUUCP
+       expand(template, buf, &buf[sizeof buf - 1], CurEnv);
+       putline(buf, fp, m);
+}
+\f/*
+**  PUTBODY -- put the body of a message.
+**
+**     Parameters:
+**             fp -- file to output onto.
+**             m -- a mailer descriptor to control output format.
+**             e -- the envelope to put out.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             The message is written onto fp.
+*/
+
+putbody(fp, m, e)
+       FILE *fp;
+       MAILER *m;
+       register ENVELOPE *e;
+{
+       char buf[MAXLINE];
+
+       /*
+       **  Output the body of the message
+       */
+
+       if (e->e_dfp == NULL)
+       {
+               if (e->e_df != NULL)
+               {
+                       e->e_dfp = fopen(e->e_df, "r");
+                       if (e->e_dfp == NULL)
+                               syserr("putbody: Cannot open %s for %s from %s",
+                               e->e_df, e->e_to, e->e_from);
+               }
+               else
+                       putline("<<< No Message Collected >>>", fp, m);
+       }
+       if (e->e_dfp != NULL)
+       {
+               rewind(e->e_dfp);
+               while (!ferror(fp) && fgets(buf, sizeof buf, e->e_dfp) != NULL)
+               {
+                       if (buf[0] == 'F' && bitnset(M_ESCFROM, m->m_flags) &&
+                           strncmp(buf, "From ", 5) == 0)
+                               (void) putc('>', fp);
+                       putline(buf, fp, m);
+               }
+
+               if (ferror(e->e_dfp))
+               {
+                       syserr("putbody: read error");
+                       ExitStat = EX_IOERR;
+               }
+       }
+
+       (void) fflush(fp);
+       if (ferror(fp) && errno != EPIPE)
+       {
+               syserr("putbody: write error");
+               ExitStat = EX_IOERR;
+       }
+       errno = 0;
+}
+\f/*
+**  MAILFILE -- Send a message to a file.
+**
+**     If the file has the setuid/setgid bits set, but NO execute
+**     bits, sendmail will try to become the owner of that file
+**     rather than the real user.  Obviously, this only works if
+**     sendmail runs as root.
+**
+**     This could be done as a subordinate mailer, except that it
+**     is used implicitly to save messages in ~/dead.letter.  We
+**     view this as being sufficiently important as to include it
+**     here.  For example, if the system is dying, we shouldn't have
+**     to create another process plus some pipes to save the message.
+**
+**     Parameters:
+**             filename -- the name of the file to send to.
+**             ctladdr -- the controlling address header -- includes
+**                     the userid/groupid to be when sending.
+**
+**     Returns:
+**             The exit code associated with the operation.
+**
+**     Side Effects:
+**             none.
+*/
+
+mailfile(filename, ctladdr)
+       char *filename;
+       ADDRESS *ctladdr;
+{
+       register FILE *f;
+       register int pid;
+       ENVELOPE *e = CurEnv;
+
+       /*
+       **  Fork so we can change permissions here.
+       **      Note that we MUST use fork, not vfork, because of
+       **      the complications of calling subroutines, etc.
+       */
+
+       DOFORK(fork);
+
+       if (pid < 0)
+               return (EX_OSERR);
+       else if (pid == 0)
+       {
+               /* child -- actually write to file */
+               struct stat stb;
+
+               (void) signal(SIGINT, SIG_DFL);
+               (void) signal(SIGHUP, SIG_DFL);
+               (void) signal(SIGTERM, SIG_DFL);
+               (void) umask(OldUmask);
+               if (stat(filename, &stb) < 0)
+               {
+                       errno = 0;
+                       stb.st_mode = 0666;
+               }
+               if (bitset(0111, stb.st_mode))
+                       exit(EX_CANTCREAT);
+               if (ctladdr == NULL)
+                       ctladdr = &e->e_from;
+               /* we have to open the dfile BEFORE setuid */
+               if (e->e_dfp == NULL &&  e->e_df != NULL)
+               {
+                       e->e_dfp = fopen(e->e_df, "r");
+                       if (e->e_dfp == NULL) {
+                               syserr("mailfile: Cannot open %s for %s from %s",
+                               e->e_df, e->e_to, e->e_from);
+                       }
+               }
+
+               if (!bitset(S_ISGID, stb.st_mode) || setgid(stb.st_gid) < 0)
+               {
+                       if (ctladdr->q_uid == 0) {
+                               (void) setgid(DefGid);
+                               (void) initgroups(DefUser, DefGid);
+                       } else {
+                               (void) setgid(ctladdr->q_gid);
+                               (void) initgroups(ctladdr->q_ruser?
+                                       ctladdr->q_ruser: ctladdr->q_user,
+                                       ctladdr->q_gid);
+                       }
+               }
+               if (!bitset(S_ISUID, stb.st_mode) || setuid(stb.st_uid) < 0)
+               {
+                       if (ctladdr->q_uid == 0)
+                               (void) setuid(DefUid);
+                       else
+                               (void) setuid(ctladdr->q_uid);
+               }
+               f = dfopen(filename, "a");
+               if (f == NULL)
+                       exit(EX_CANTCREAT);
+
+               putfromline(f, ProgMailer);
+               (*CurEnv->e_puthdr)(f, ProgMailer, CurEnv);
+               putline("\n", f, ProgMailer);
+               (*CurEnv->e_putbody)(f, ProgMailer, CurEnv);
+               putline("\n", f, ProgMailer);
+               (void) fclose(f);
+               (void) fflush(stdout);
+
+               /* reset ISUID & ISGID bits for paranoid systems */
+               (void) chmod(filename, (int) stb.st_mode);
+               exit(EX_OK);
+               /*NOTREACHED*/
+       }
+       else
+       {
+               /* parent -- wait for exit status */
+               int st;
+
+               st = waitfor(pid);
+               if ((st & 0377) != 0)
+                       return (EX_UNAVAILABLE);
+               else
+                       return ((st >> 8) & 0377);
+               /*NOTREACHED*/
+       }
+}
+\f/*
+**  SENDALL -- actually send all the messages.
+**
+**     Parameters:
+**             e -- the envelope to send.
+**             mode -- the delivery mode to use.  If SM_DEFAULT, use
+**                     the current SendMode.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Scans the send lists and sends everything it finds.
+**             Delivers any appropriate error messages.
+**             If we are running in a non-interactive mode, takes the
+**                     appropriate action.
+*/
+
+sendall(e, mode)
+       ENVELOPE *e;
+       char mode;
+{
+       register ADDRESS *q;
+       bool oldverbose;
+       int pid;
+       int nsent;
+       FILE *lockfp = NULL, *queueup();
+
+       /* determine actual delivery mode */
+       if (mode == SM_DEFAULT)
+       {
+               extern bool shouldqueue();
+
+               if (shouldqueue(e->e_msgpriority))
+                       mode = SM_QUEUE;
+               else
+                       mode = SendMode;
+       }
+
+       if (tTd(13, 1))
+       {
+               printf("\nSENDALL: mode %c, sendqueue:\n", mode);
+               printaddr(e->e_sendqueue, TRUE);
+       }
+
+       /*
+       **  Do any preprocessing necessary for the mode we are running.
+       **      Check to make sure the hop count is reasonable.
+       **      Delete sends to the sender in mailing lists.
+       */
+
+       CurEnv = e;
+
+       if (e->e_hopcount > MAXHOP)
+       {
+               errno = 0;
+               syserr("sendall: too many hops %d (%d max): from %s, to %s",
+                       e->e_hopcount, MAXHOP, e->e_from, e->e_to);
+               return;
+       }
+
+       if (!MeToo)
+       {
+               extern ADDRESS *recipient();
+
+               e->e_from.q_flags |= QDONTSEND;
+               (void) recipient(&e->e_from, &e->e_sendqueue);
+       }
+
+# ifdef QUEUE
+       if ((mode == SM_QUEUE || mode == SM_FORK ||
+            (mode != SM_VERIFY && SuperSafe)) &&
+           !bitset(EF_INQUEUE, e->e_flags))
+               lockfp = queueup(e, TRUE, mode == SM_QUEUE);
+#endif QUEUE
+
+       oldverbose = Verbose;
+       switch (mode)
+       {
+         case SM_VERIFY:
+               Verbose = TRUE;
+               break;
+
+         case SM_QUEUE:
+               e->e_flags |= EF_INQUEUE|EF_KEEPQUEUE;
+               return;
+
+         case SM_FORK:
+               if (e->e_xfp != NULL)
+                       (void) fflush(e->e_xfp);
+               pid = fork();
+               if (pid < 0)
+               {
+                       mode = SM_DELIVER;
+                       break;
+               }
+               else if (pid > 0)
+               {
+                       /* be sure we leave the temp files to our child */
+                       e->e_id = e->e_df = NULL;
+                       if (lockfp != NULL)
+                               (void) fclose(lockfp);
+                       return;
+               }
+
+               /* double fork to avoid zombies */
+               if (fork() > 0)
+                       exit(EX_OK);
+
+               /* be sure we are immune from the terminal */
+               disconnect(FALSE);
+
+               break;
+       }
+
+       /*
+       **  Run through the list and send everything.
+       */
+
+       nsent = 0;
+       for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+       {
+               if (mode == SM_VERIFY)
+               {
+                       e->e_to = q->q_paddr;
+                       if (!bitset(QDONTSEND|QBADADDR, q->q_flags))
+                               message(Arpa_Info, "deliverable");
+               }
+               else if (!bitset(QDONTSEND, q->q_flags))
+               {
+                       /*
+                       **  Checkpoint the send list every few addresses
+                       */
+
+                       if (nsent >= CheckpointInterval)
+                       {
+                               queueup(e, TRUE, FALSE);
+                               nsent = 0;
+                       }
+                       if (deliver(e, q) == EX_OK)
+                               nsent++;
+               }
+       }
+       Verbose = oldverbose;
+
+       /*
+       **  Now run through and check for errors.
+       */
+
+       if (mode == SM_VERIFY) {
+               if (lockfp != NULL)
+                       (void) fclose(lockfp);
+               return;
+       }
+
+       for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+       {
+               register ADDRESS *qq;
+
+               if (tTd(13, 3))
+               {
+                       printf("Checking ");
+                       printaddr(q, FALSE);
+               }
+
+               /* only send errors if the message failed */
+               if (!bitset(QBADADDR, q->q_flags))
+                       continue;
+
+               /* we have an address that failed -- find the parent */
+               for (qq = q; qq != NULL; qq = qq->q_alias)
+               {
+                       char obuf[MAXNAME + 6];
+                       extern char *aliaslookup();
+
+                       /* we can only have owners for local addresses */
+                       if (!bitnset(M_LOCAL, qq->q_mailer->m_flags))
+                               continue;
+
+                       /* see if the owner list exists */
+                       (void) strcpy(obuf, "owner-");
+                       if (strncmp(qq->q_user, "owner-", 6) == 0)
+                               (void) strcat(obuf, "owner");
+                       else
+                               (void) strcat(obuf, qq->q_user);
+                       makelower(obuf);
+                       if (aliaslookup(obuf) == NULL)
+                               continue;
+
+                       if (tTd(13, 4))
+                               printf("Errors to %s\n", obuf);
+
+                       /* owner list exists -- add it to the error queue */
+                       sendtolist(obuf, (ADDRESS *) NULL, &e->e_errorqueue);
+                       ErrorMode = EM_MAIL;
+                       break;
+               }
+
+               /* if we did not find an owner, send to the sender */
+               if (qq == NULL && bitset(QBADADDR, q->q_flags))
+                       sendtolist(e->e_from.q_paddr, qq, &e->e_errorqueue);
+       }
+
+       /* this removes the lock on the file */
+       if (lockfp != NULL)
+               (void) fclose(lockfp);
+
+       if (mode == SM_FORK)
+               finis();
+}
diff --git a/usr/src/usr.sbin/sendmail/src/domain.c b/usr/src/usr.sbin/sendmail/src/domain.c
new file mode 100644 (file)
index 0000000..1bef478
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (c) 1986 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "sendmail.h"
+
+#ifndef lint
+#ifdef NAMED_BIND
+static char sccsid[] = "@(#)domain.c   5.23 (Berkeley) 3/2/91 (with name server)";
+#else
+static char sccsid[] = "@(#)domain.c   5.23 (Berkeley) 3/2/91 (without name server)";
+#endif
+#endif /* not lint */
+
+#ifdef NAMED_BIND
+
+#include <sys/param.h>
+#include <errno.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <netdb.h>
+
+typedef union {
+       HEADER qb1;
+       char qb2[PACKETSZ];
+} querybuf;
+
+static char hostbuf[MAXMXHOSTS*PACKETSZ];
+
+getmxrr(host, mxhosts, localhost, rcode)
+       char *host, **mxhosts, *localhost;
+       int *rcode;
+{
+       extern int h_errno;
+       register u_char *eom, *cp;
+       register int i, j, n, nmx;
+       register char *bp;
+       HEADER *hp;
+       querybuf answer;
+       int ancount, qdcount, buflen, seenlocal;
+       u_short pref, localpref, type, prefer[MAXMXHOSTS];
+
+       errno = 0;
+       n = res_search(host, C_IN, T_MX, (char *)&answer, sizeof(answer));
+       if (n < 0)
+       {
+               if (tTd(8, 1))
+                       printf("getmxrr: res_search failed (errno=%d, h_errno=%d)\n",
+                           errno, h_errno);
+               switch (h_errno)
+               {
+                 case NO_DATA:
+                 case NO_RECOVERY:
+                       /* no MX data on this host */
+                       goto punt;
+
+                 case HOST_NOT_FOUND:
+                       /* the host just doesn't exist */
+                       *rcode = EX_NOHOST;
+                       break;
+
+                 case TRY_AGAIN:
+                       /* couldn't connect to the name server */
+                       if (!UseNameServer && errno == ECONNREFUSED)
+                               goto punt;
+
+                       /* it might come up later; better queue it up */
+                       *rcode = EX_TEMPFAIL;
+                       break;
+               }
+
+               /* irreconcilable differences */
+               return (-1);
+       }
+
+       /* find first satisfactory answer */
+       hp = (HEADER *)&answer;
+       cp = (u_char *)&answer + sizeof(HEADER);
+       eom = (u_char *)&answer + n;
+       for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ)
+               if ((n = __dn_skipname(cp, eom)) < 0)
+                       goto punt;
+       nmx = 0;
+       seenlocal = 0;
+       buflen = sizeof(hostbuf);
+       bp = hostbuf;
+       ancount = ntohs(hp->ancount);
+       while (--ancount >= 0 && cp < eom && nmx < MAXMXHOSTS) {
+               if ((n = dn_expand((u_char *)&answer,
+                   eom, cp, (u_char *)bp, buflen)) < 0)
+                       break;
+               cp += n;
+               GETSHORT(type, cp);
+               cp += sizeof(u_short) + sizeof(u_long);
+               GETSHORT(n, cp);
+               if (type != T_MX)  {
+                       if (tTd(8, 1) || _res.options & RES_DEBUG)
+                               printf("unexpected answer type %d, size %d\n",
+                                   type, n);
+                       cp += n;
+                       continue;
+               }
+               GETSHORT(pref, cp);
+               if ((n = dn_expand((u_char *)&answer,
+                   eom, cp, (u_char *)bp, buflen)) < 0)
+                       break;
+               cp += n;
+               if (!strcasecmp(bp, localhost)) {
+                       if (seenlocal == 0 || pref < localpref)
+                               localpref = pref;
+                       seenlocal = 1;
+                       continue;
+               }
+               prefer[nmx] = pref;
+               mxhosts[nmx++] = bp;
+               n = strlen(bp) + 1;
+               bp += n;
+               buflen -= n;
+       }
+       if (nmx == 0) {
+punt:          mxhosts[0] = strcpy(hostbuf, host);
+               return(1);
+       }
+
+       /* sort the records */
+       for (i = 0; i < nmx; i++) {
+               for (j = i + 1; j < nmx; j++) {
+                       if (prefer[i] > prefer[j] ||
+                           (prefer[i] == prefer[j] && rand() % 1 == 0)) {
+                               register int temp;
+                               register char *temp1;
+
+                               temp = prefer[i];
+                               prefer[i] = prefer[j];
+                               prefer[j] = temp;
+                               temp1 = mxhosts[i];
+                               mxhosts[i] = mxhosts[j];
+                               mxhosts[j] = temp1;
+                       }
+               }
+               if (seenlocal && prefer[i] >= localpref) {
+                       /*
+                        * truncate higher pref part of list; if we're
+                        * the best choice left, we should have realized
+                        * awhile ago that this was a local delivery.
+                        */
+                       if (i == 0) {
+                               *rcode = EX_CONFIG;
+                               return(-1);
+                       }
+                       nmx = i;
+                       break;
+               }
+       }
+       return(nmx);
+}
+
+getcanonname(host, hbsize)
+       char *host;
+       int hbsize;
+{
+       extern int h_errno;
+       register u_char *eom, *cp;
+       register int n; 
+       HEADER *hp;
+       querybuf answer;
+       u_short type;
+       int first, ancount, qdcount, loopcnt;
+       char nbuf[PACKETSZ];
+
+       loopcnt = 0;
+loop:
+       /*
+        * Use query type of ANY if possible (NO_WILDCARD_MX), which will
+        * find types CNAME, A, and MX, and will cause all existing records
+        * to be cached by our local server.  If there is (might be) a
+        * wildcard MX record in the local domain or its parents that are
+        * searched, we can't use ANY; it would cause fully-qualified names
+        * to match as names in a local domain.
+        */
+# ifdef NO_WILDCARD_MX
+       n = res_search(host, C_IN, T_ANY, (char *)&answer, sizeof(answer));
+# else
+       n = res_search(host, C_IN, T_CNAME, (char *)&answer, sizeof(answer));
+# endif
+       if (n < 0) {
+               if (tTd(8, 1))
+                       printf("getcanonname:  res_search failed (errno=%d, h_errno=%d)\n",
+                           errno, h_errno);
+               return;
+       }
+
+       /* find first satisfactory answer */
+       hp = (HEADER *)&answer;
+       ancount = ntohs(hp->ancount);
+
+       /* we don't care about errors here, only if we got an answer */
+       if (ancount == 0) {
+               if (tTd(8, 1))
+                       printf("rcode = %d, ancount=%d\n", hp->rcode, ancount);
+               return;
+       }
+       cp = (u_char *)&answer + sizeof(HEADER);
+       eom = (u_char *)&answer + n;
+       for (qdcount = ntohs(hp->qdcount); qdcount--; cp += n + QFIXEDSZ)
+               if ((n = __dn_skipname(cp, eom)) < 0)
+                       return;
+
+       /*
+        * just in case someone puts a CNAME record after another record,
+        * check all records for CNAME; otherwise, just take the first
+        * name found.
+        */
+       for (first = 1; --ancount >= 0 && cp < eom; cp += n) {
+               if ((n = dn_expand((u_char *)&answer,
+                   eom, cp, (u_char *)nbuf, sizeof(nbuf))) < 0)
+                       break;
+               if (first) {                    /* XXX */
+                       (void)strncpy(host, nbuf, hbsize);
+                       host[hbsize - 1] = '\0';
+                       first = 0;
+               }
+               cp += n;
+               GETSHORT(type, cp);
+               cp += sizeof(u_short) + sizeof(u_long);
+               GETSHORT(n, cp);
+               if (type == T_CNAME)  {
+                       /*
+                        * assume that only one cname will be found.  More
+                        * than one is undefined.  Copy so that if dn_expand
+                        * fails, `host' is still okay.
+                        */
+                       if ((n = dn_expand((u_char *)&answer,
+                           eom, cp, (u_char *)nbuf, sizeof(nbuf))) < 0)
+                               break;
+                       (void)strncpy(host, nbuf, hbsize); /* XXX */
+                       host[hbsize - 1] = '\0';
+                       if (++loopcnt > 8)      /* never be more than 1 */
+                               return;
+                       goto loop;
+               }
+       }
+}
+
+#else /* not NAMED_BIND */
+
+#include <netdb.h>
+
+getcanonname(host, hbsize)
+       char *host;
+       int hbsize;
+{
+       struct hostent *hp;
+
+       hp = gethostbyname(host);
+       if (hp == NULL)
+               return;
+
+       if (strlen(hp->h_name) >= hbsize)
+               return;
+
+       (void) strcpy(host, hp->h_name);
+}
+
+#endif /* not NAMED_BIND */
diff --git a/usr/src/usr.sbin/sendmail/src/envelope.c b/usr/src/usr.sbin/sendmail/src/envelope.c
new file mode 100644 (file)
index 0000000..24b914a
--- /dev/null
@@ -0,0 +1,616 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)envelope.c 5.22 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <sys/file.h>
+#include "sendmail.h"
+
+/*
+**  NEWENVELOPE -- allocate a new envelope
+**
+**     Supports inheritance.
+**
+**     Parameters:
+**             e -- the new envelope to fill in.
+**
+**     Returns:
+**             e.
+**
+**     Side Effects:
+**             none.
+*/
+
+ENVELOPE *
+newenvelope(e)
+       register ENVELOPE *e;
+{
+       register ENVELOPE *parent;
+       extern putheader(), putbody();
+       extern ENVELOPE BlankEnvelope;
+
+       parent = CurEnv;
+       if (e == CurEnv)
+               parent = e->e_parent;
+       clearenvelope(e, TRUE);
+       if (e == CurEnv)
+               bcopy((char *) &NullAddress, (char *) &e->e_from, sizeof e->e_from);
+       else
+               bcopy((char *) &CurEnv->e_from, (char *) &e->e_from, sizeof e->e_from);
+       e->e_parent = parent;
+       e->e_ctime = curtime();
+       e->e_msgpriority = parent->e_msgsize;
+       e->e_puthdr = putheader;
+       e->e_putbody = putbody;
+       if (CurEnv->e_xfp != NULL)
+               (void) fflush(CurEnv->e_xfp);
+
+       return (e);
+}
+\f/*
+**  DROPENVELOPE -- deallocate an envelope.
+**
+**     Parameters:
+**             e -- the envelope to deallocate.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             housekeeping necessary to dispose of an envelope.
+**             Unlocks this queue file.
+*/
+
+dropenvelope(e)
+       register ENVELOPE *e;
+{
+       bool queueit = FALSE;
+       register ADDRESS *q;
+
+       if (tTd(50, 1))
+       {
+               printf("dropenvelope %x id=", e);
+               xputs(e->e_id);
+               printf(" flags=%o\n", e->e_flags);
+       }
+#ifdef LOG
+       if (LogLevel > 10)
+               syslog(LOG_DEBUG, "dropenvelope, id=%s, flags=%o, pid=%d",
+                                 e->e_id == NULL ? "(none)" : e->e_id,
+                                 e->e_flags, getpid());
+#endif LOG
+
+       /* we must have an id to remove disk files */
+       if (e->e_id == NULL)
+               return;
+
+       /*
+       **  Extract state information from dregs of send list.
+       */
+
+       for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+       {
+               if (bitset(QQUEUEUP, q->q_flags))
+                       queueit = TRUE;
+       }
+
+       /*
+       **  Send back return receipts as requested.
+       */
+
+       if (e->e_receiptto != NULL && bitset(EF_SENDRECEIPT, e->e_flags))
+       {
+               auto ADDRESS *rlist = NULL;
+
+               sendtolist(CurEnv->e_receiptto, (ADDRESS *) NULL, &rlist);
+               (void) returntosender("Return receipt", rlist, FALSE);
+       }
+
+       /*
+       **  Arrange to send error messages if there are fatal errors.
+       */
+
+       if (bitset(EF_FATALERRS|EF_TIMEOUT, e->e_flags) && ErrorMode != EM_QUIET)
+               savemail(e);
+
+       /*
+       **  Instantiate or deinstantiate the queue.
+       */
+
+       if ((!queueit && !bitset(EF_KEEPQUEUE, e->e_flags)) ||
+           bitset(EF_CLRQUEUE, e->e_flags))
+       {
+               if (e->e_df != NULL)
+                       xunlink(e->e_df);
+               xunlink(queuename(e, 'q'));
+       }
+       else if (queueit || !bitset(EF_INQUEUE, e->e_flags))
+       {
+#ifdef QUEUE
+               FILE *lockfp, *queueup();
+               lockfp = queueup(e, FALSE, FALSE);
+               if (lockfp != NULL)
+                       (void) fclose(lockfp);
+#else QUEUE
+               syserr("dropenvelope: queueup");
+#endif QUEUE
+       }
+
+       /* now unlock the job */
+       closexscript(e);
+       unlockqueue(e);
+
+       /* make sure that this envelope is marked unused */
+       e->e_id = e->e_df = NULL;
+       if (e->e_dfp != NULL)
+               (void) fclose(e->e_dfp);
+       e->e_dfp = NULL;
+}
+\f/*
+**  CLEARENVELOPE -- clear an envelope without unlocking
+**
+**     This is normally used by a child process to get a clean
+**     envelope without disturbing the parent.
+**
+**     Parameters:
+**             e -- the envelope to clear.
+**             fullclear - if set, the current envelope is total
+**                     garbage and should be ignored; otherwise,
+**                     release any resources it may indicate.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Closes files associated with the envelope.
+**             Marks the envelope as unallocated.
+*/
+
+clearenvelope(e, fullclear)
+       register ENVELOPE *e;
+       bool fullclear;
+{
+       register HDR *bh;
+       register HDR **nhp;
+       extern ENVELOPE BlankEnvelope;
+
+       if (!fullclear)
+       {
+               /* clear out any file information */
+               if (e->e_xfp != NULL)
+                       (void) fclose(e->e_xfp);
+               if (e->e_dfp != NULL)
+                       (void) fclose(e->e_dfp);
+       }
+
+       /* now clear out the data */
+       STRUCTCOPY(BlankEnvelope, *e);
+       bh = BlankEnvelope.e_header;
+       nhp = &e->e_header;
+       while (bh != NULL)
+       {
+               *nhp = (HDR *) xalloc(sizeof *bh);
+               bcopy((char *) bh, (char *) *nhp, sizeof *bh);
+               bh = bh->h_link;
+               nhp = &(*nhp)->h_link;
+       }
+}
+\f/*
+**  INITSYS -- initialize instantiation of system
+**
+**     In Daemon mode, this is done in the child.
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Initializes the system macros, some global variables,
+**             etc.  In particular, the current time in various
+**             forms is set.
+*/
+
+initsys()
+{
+       static char cbuf[5];                    /* holds hop count */
+       static char pbuf[10];                   /* holds pid */
+#ifdef TTYNAME
+       static char ybuf[10];                   /* holds tty id */
+       register char *p;
+#endif TTYNAME
+       extern char *ttyname();
+       extern char *macvalue();
+       extern char Version[];
+
+       /*
+       **  Give this envelope a reality.
+       **      I.e., an id, a transcript, and a creation time.
+       */
+
+       openxscript(CurEnv);
+       CurEnv->e_ctime = curtime();
+
+       /*
+       **  Set OutChannel to something useful if stdout isn't it.
+       **      This arranges that any extra stuff the mailer produces
+       **      gets sent back to the user on error (because it is
+       **      tucked away in the transcript).
+       */
+
+       if (OpMode == MD_DAEMON && QueueRun)
+               OutChannel = CurEnv->e_xfp;
+
+       /*
+       **  Set up some basic system macros.
+       */
+
+       /* process id */
+       (void) sprintf(pbuf, "%d", getpid());
+       define('p', pbuf, CurEnv);
+
+       /* hop count */
+       (void) sprintf(cbuf, "%d", CurEnv->e_hopcount);
+       define('c', cbuf, CurEnv);
+
+       /* time as integer, unix time, arpa time */
+       settime();
+
+#ifdef TTYNAME
+       /* tty name */
+       if (macvalue('y', CurEnv) == NULL)
+       {
+               p = ttyname(2);
+               if (p != NULL)
+               {
+                       if (rindex(p, '/') != NULL)
+                               p = rindex(p, '/') + 1;
+                       (void) strcpy(ybuf, p);
+                       define('y', ybuf, CurEnv);
+               }
+       }
+#endif TTYNAME
+}
+\f/*
+**  SETTIME -- set the current time.
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Sets the various time macros -- $a, $b, $d, $t.
+*/
+
+settime()
+{
+       register char *p;
+       auto time_t now;
+       static char tbuf[20];                   /* holds "current" time */
+       static char dbuf[30];                   /* holds ctime(tbuf) */
+       register struct tm *tm;
+       extern char *arpadate();
+       extern struct tm *gmtime();
+       extern char *macvalue();
+
+       now = curtime();
+       tm = gmtime(&now);
+       (void) sprintf(tbuf, "%02d%02d%02d%02d%02d", tm->tm_year, tm->tm_mon+1,
+                       tm->tm_mday, tm->tm_hour, tm->tm_min);
+       define('t', tbuf, CurEnv);
+       (void) strcpy(dbuf, ctime(&now));
+       *index(dbuf, '\n') = '\0';
+       if (macvalue('d', CurEnv) == NULL)
+               define('d', dbuf, CurEnv);
+       p = newstr(arpadate(dbuf));
+       if (macvalue('a', CurEnv) == NULL)
+               define('a', p, CurEnv);
+       define('b', p, CurEnv);
+}
+\f/*
+**  OPENXSCRIPT -- Open transcript file
+**
+**     Creates a transcript file for possible eventual mailing or
+**     sending back.
+**
+**     Parameters:
+**             e -- the envelope to create the transcript in/for.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             Creates the transcript file.
+*/
+
+openxscript(e)
+       register ENVELOPE *e;
+{
+       register char *p;
+       int fd;
+
+# ifdef LOG
+       if (LogLevel > 19)
+               syslog(LOG_DEBUG, "%s: openx%s", e->e_id, e->e_xfp == NULL ? "" : " (no)");
+# endif LOG
+       if (e->e_xfp != NULL)
+               return;
+       p = queuename(e, 'x');
+       fd = open(p, O_WRONLY|O_CREAT, 0644);
+       if (fd < 0)
+               syserr("Can't create %s", p);
+       else
+               e->e_xfp = fdopen(fd, "w");
+}
+\f/*
+**  CLOSEXSCRIPT -- close the transcript file.
+**
+**     Parameters:
+**             e -- the envelope containing the transcript to close.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             none.
+*/
+
+closexscript(e)
+       register ENVELOPE *e;
+{
+       if (e->e_xfp == NULL)
+               return;
+       (void) fclose(e->e_xfp);
+       e->e_xfp = NULL;
+}
+\f/*
+**  SETSENDER -- set the person who this message is from
+**
+**     Under certain circumstances allow the user to say who
+**     s/he is (using -f or -r).  These are:
+**     1.  The user's uid is zero (root).
+**     2.  The user's login name is in an approved list (typically
+**         from a network server).
+**     3.  The address the user is trying to claim has a
+**         "!" character in it (since #2 doesn't do it for
+**         us if we are dialing out for UUCP).
+**     A better check to replace #3 would be if the
+**     effective uid is "UUCP" -- this would require me
+**     to rewrite getpwent to "grab" uucp as it went by,
+**     make getname more nasty, do another passwd file
+**     scan, or compile the UID of "UUCP" into the code,
+**     all of which are reprehensible.
+**
+**     Assuming all of these fail, we figure out something
+**     ourselves.
+**
+**     Parameters:
+**             from -- the person we would like to believe this message
+**                     is from, as specified on the command line.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             sets sendmail's notion of who the from person is.
+*/
+
+setsender(from)
+       char *from;
+{
+       register char **pvp;
+       char *realname = NULL;
+       register struct passwd *pw;
+       char buf[MAXNAME];
+       char pvpbuf[PSBUFSIZE];
+       extern struct passwd *getpwnam();
+       extern char *macvalue();
+       extern char **prescan();
+       extern bool safefile();
+       extern char *FullName;
+
+       if (tTd(45, 1))
+               printf("setsender(%s)\n", from == NULL ? "" : from);
+
+       /*
+       **  Figure out the real user executing us.
+       **      Username can return errno != 0 on non-errors.
+       */
+
+       if (QueueRun || OpMode == MD_SMTP || OpMode == MD_ARPAFTP)
+               realname = from;
+       if (realname == NULL || realname[0] == '\0')
+       {
+               extern char *username();
+
+               realname = username();
+       }
+
+       /*
+       **  Determine if this real person is allowed to alias themselves.
+       */
+
+       if (from != NULL)
+       {
+               extern bool trusteduser();
+
+               if (!trusteduser(realname) && getuid() != geteuid() &&
+                   index(from, '!') == NULL && getuid() != 0)
+               {
+                       /* network sends -r regardless (why why why?) */
+                       /* syserr("%s, you cannot use the -f flag", realname); */
+                       from = NULL;
+               }
+       }
+
+       SuprErrs = TRUE;
+       if (from == NULL || parseaddr(from, &CurEnv->e_from, 1, '\0') == NULL)
+       {
+               /* log garbage addresses for traceback */
+               if (from != NULL)
+               {
+# ifdef LOG
+                       if (LogLevel >= 1)
+                           if (realname == from && RealHostName != NULL)
+                               syslog(LOG_NOTICE,
+                                   "from=%s unparseable, received from %s",
+                                   from, RealHostName);
+                           else
+                               syslog(LOG_NOTICE,
+                                   "Unparseable username %s wants from=%s",
+                                   realname, from);
+# endif LOG
+               }
+               from = newstr(realname);
+               if (parseaddr(from, &CurEnv->e_from, 1, '\0') == NULL &&
+                   parseaddr("postmaster", &CurEnv->e_from, 1, '\0') == NULL)
+               {
+                       syserr("setsender: can't even parse postmaster!");
+               }
+       }
+       else
+               FromFlag = TRUE;
+       CurEnv->e_from.q_flags |= QDONTSEND;
+       loweraddr(&CurEnv->e_from);
+       SuprErrs = FALSE;
+
+       if (CurEnv->e_from.q_mailer == LocalMailer &&
+           (pw = getpwnam(CurEnv->e_from.q_user)) != NULL)
+       {
+               /*
+               **  Process passwd file entry.
+               */
+
+
+               /* extract home directory */
+               CurEnv->e_from.q_home = newstr(pw->pw_dir);
+               define('z', CurEnv->e_from.q_home, CurEnv);
+
+               /* extract user and group id */
+               CurEnv->e_from.q_uid = pw->pw_uid;
+               CurEnv->e_from.q_gid = pw->pw_gid;
+
+               /* if the user has given fullname already, don't redefine */
+               if (FullName == NULL)
+                       FullName = macvalue('x', CurEnv);
+               if (FullName != NULL && FullName[0] == '\0')
+                       FullName = NULL;
+
+               /* extract full name from passwd file */
+               if (FullName == NULL && pw->pw_gecos != NULL &&
+                   strcmp(pw->pw_name, CurEnv->e_from.q_user) == 0)
+               {
+                       buildfname(pw->pw_gecos, CurEnv->e_from.q_user, buf);
+                       if (buf[0] != '\0')
+                               FullName = newstr(buf);
+               }
+               if (FullName != NULL)
+                       define('x', FullName, CurEnv);
+       }
+       else
+       {
+               if (CurEnv->e_from.q_home == NULL)
+                       CurEnv->e_from.q_home = getenv("HOME");
+               CurEnv->e_from.q_uid = getuid();
+               CurEnv->e_from.q_gid = getgid();
+       }
+
+       /*
+       **  Rewrite the from person to dispose of possible implicit
+       **      links in the net.
+       */
+
+       pvp = prescan(from, '\0', pvpbuf);
+       if (pvp == NULL)
+       {
+# ifdef LOG
+               if (LogLevel >= 1)
+                       syslog(LOG_NOTICE, "cannot prescan from (%s)", from);
+# endif
+               usrerr("cannot prescan from (%s)", from);
+               finis();
+       }
+       rewrite(pvp, 3);
+       rewrite(pvp, 1);
+       rewrite(pvp, 4);
+       cataddr(pvp, buf, sizeof buf);
+       define('f', newstr(buf), CurEnv);
+
+       /* save the domain spec if this mailer wants it */
+       if (CurEnv->e_from.q_mailer != NULL &&
+           bitnset(M_CANONICAL, CurEnv->e_from.q_mailer->m_flags))
+       {
+               extern char **copyplist();
+
+               while (*pvp != NULL && strcmp(*pvp, "@") != 0)
+                       pvp++;
+               if (*pvp != NULL)
+                       CurEnv->e_fromdomain = copyplist(pvp, TRUE);
+       }
+}
+\f/*
+**  TRUSTEDUSER -- tell us if this user is to be trusted.
+**
+**     Parameters:
+**             user -- the user to be checked.
+**
+**     Returns:
+**             TRUE if the user is in an approved list.
+**             FALSE otherwise.
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+trusteduser(user)
+       char *user;
+{
+       register char **ulist;
+       extern char *TrustedUsers[];
+
+       for (ulist = TrustedUsers; *ulist != NULL; ulist++)
+               if (strcmp(*ulist, user) == 0)
+                       return (TRUE);
+       return (FALSE);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/err.c b/usr/src/usr.sbin/sendmail/src/err.c
new file mode 100644 (file)
index 0000000..3242da0
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)err.c      5.11 (Berkeley) 3/2/91";
+#endif /* not lint */
+
+# include "sendmail.h"
+# include <errno.h>
+# include <netdb.h>
+
+/*
+**  SYSERR -- Print error message.
+**
+**     Prints an error message via printf to the diagnostic
+**     output.  If LOG is defined, it logs it also.
+**
+**     Parameters:
+**             f -- the format string
+**             a, b, c, d, e -- parameters
+**
+**     Returns:
+**             none
+**             Through TopFrame if QuickAbort is set.
+**
+**     Side Effects:
+**             increments Errors.
+**             sets ExitStat.
+*/
+
+# ifdef lint
+int    sys_nerr;
+char   *sys_errlist[];
+# endif lint
+char   MsgBuf[BUFSIZ*2];       /* text of most recent message */
+
+static void fmtmsg();
+
+/*VARARGS1*/
+syserr(fmt, a, b, c, d, e)
+       char *fmt;
+{
+       register char *p;
+       int olderrno = errno;
+       extern char Arpa_PSyserr[];
+       extern char Arpa_TSyserr[];
+
+       /* format and output the error message */
+       if (olderrno == 0)
+               p = Arpa_PSyserr;
+       else
+               p = Arpa_TSyserr;
+       fmtmsg(MsgBuf, (char *) NULL, p, olderrno, fmt, a, b, c, d, e);
+       puterrmsg(MsgBuf);
+
+       /* determine exit status if not already set */
+       if (ExitStat == EX_OK)
+       {
+               if (olderrno == 0)
+                       ExitStat = EX_SOFTWARE;
+               else
+                       ExitStat = EX_OSERR;
+       }
+
+# ifdef LOG
+       if (LogLevel > 0)
+               syslog(LOG_CRIT, "%s: SYSERR: %s",
+                       CurEnv->e_id == NULL ? "NOQUEUE" : CurEnv->e_id,
+                       &MsgBuf[4]);
+# endif LOG
+       errno = 0;
+       if (QuickAbort)
+               longjmp(TopFrame, 2);
+}
+\f/*
+**  USRERR -- Signal user error.
+**
+**     This is much like syserr except it is for user errors.
+**
+**     Parameters:
+**             fmt, a, b, c, d -- printf strings
+**
+**     Returns:
+**             none
+**             Through TopFrame if QuickAbort is set.
+**
+**     Side Effects:
+**             increments Errors.
+*/
+
+/*VARARGS1*/
+usrerr(fmt, a, b, c, d, e)
+       char *fmt;
+{
+       extern char SuprErrs;
+       extern char Arpa_Usrerr[];
+       extern int errno;
+
+       if (SuprErrs)
+               return;
+
+       fmtmsg(MsgBuf, CurEnv->e_to, Arpa_Usrerr, errno, fmt, a, b, c, d, e);
+       puterrmsg(MsgBuf);
+
+       if (QuickAbort)
+               longjmp(TopFrame, 1);
+}
+\f/*
+**  MESSAGE -- print message (not necessarily an error)
+**
+**     Parameters:
+**             num -- the default ARPANET error number (in ascii)
+**             msg -- the message (printf fmt) -- if it begins
+**                     with a digit, this number overrides num.
+**             a, b, c, d, e -- printf arguments
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             none.
+*/
+
+/*VARARGS2*/
+message(num, msg, a, b, c, d, e)
+       register char *num;
+       register char *msg;
+{
+       errno = 0;
+       fmtmsg(MsgBuf, CurEnv->e_to, num, 0, msg, a, b, c, d, e);
+       putmsg(MsgBuf, FALSE);
+}
+\f/*
+**  NMESSAGE -- print message (not necessarily an error)
+**
+**     Just like "message" except it never puts the to... tag on.
+**
+**     Parameters:
+**             num -- the default ARPANET error number (in ascii)
+**             msg -- the message (printf fmt) -- if it begins
+**                     with three digits, this number overrides num.
+**             a, b, c, d, e -- printf arguments
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             none.
+*/
+
+/*VARARGS2*/
+nmessage(num, msg, a, b, c, d, e)
+       register char *num;
+       register char *msg;
+{
+       errno = 0;
+       fmtmsg(MsgBuf, (char *) NULL, num, 0, msg, a, b, c, d, e);
+       putmsg(MsgBuf, FALSE);
+}
+\f/*
+**  PUTMSG -- output error message to transcript and channel
+**
+**     Parameters:
+**             msg -- message to output (in SMTP format).
+**             holdmsg -- if TRUE, don't output a copy of the message to
+**                     our output channel.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Outputs msg to the transcript.
+**             If appropriate, outputs it to the channel.
+**             Deletes SMTP reply code number as appropriate.
+*/
+
+putmsg(msg, holdmsg)
+       char *msg;
+       bool holdmsg;
+{
+       /* output to transcript if serious */
+       if (CurEnv->e_xfp != NULL && (msg[0] == '4' || msg[0] == '5'))
+               fprintf(CurEnv->e_xfp, "%s\n", msg);
+
+       /* output to channel if appropriate */
+       if (!holdmsg && (Verbose || msg[0] != '0'))
+       {
+               (void) fflush(stdout);
+               if (OpMode == MD_SMTP || OpMode == MD_ARPAFTP)
+                       fprintf(OutChannel, "%s\r\n", msg);
+               else
+                       fprintf(OutChannel, "%s\n", &msg[4]);
+               (void) fflush(OutChannel);
+       }
+}
+\f/*
+**  PUTERRMSG -- like putmsg, but does special processing for error messages
+**
+**     Parameters:
+**             msg -- the message to output.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Sets the fatal error bit in the envelope as appropriate.
+*/
+
+puterrmsg(msg)
+       char *msg;
+{
+       /* output the message as usual */
+       putmsg(msg, HoldErrs);
+
+       /* signal the error */
+       Errors++;
+       if (msg[0] == '5')
+               CurEnv->e_flags |= EF_FATALERRS;
+}
+\f/*
+**  FMTMSG -- format a message into buffer.
+**
+**     Parameters:
+**             eb -- error buffer to get result.
+**             to -- the recipient tag for this message.
+**             num -- arpanet error number.
+**             en -- the error number to display.
+**             fmt -- format of string.
+**             a, b, c, d, e -- arguments.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             none.
+*/
+
+/*VARARGS5*/
+static void
+fmtmsg(eb, to, num, eno, fmt, a, b, c, d, e)
+       register char *eb;
+       char *to;
+       char *num;
+       int eno;
+       char *fmt;
+{
+       char del;
+
+       /* output the reply code */
+       if (isdigit(fmt[0]) && isdigit(fmt[1]) && isdigit(fmt[2]))
+       {
+               num = fmt;
+               fmt += 4;
+       }
+       if (num[3] == '-')
+               del = '-';
+       else
+               del = ' ';
+       (void) sprintf(eb, "%3.3s%c", num, del);
+       eb += 4;
+
+       /* output the file name and line number */
+       if (FileName != NULL)
+       {
+               (void) sprintf(eb, "%s: line %d: ", FileName, LineNumber);
+               eb += strlen(eb);
+       }
+
+       /* output the "to" person */
+       if (to != NULL && to[0] != '\0')
+       {
+               (void) sprintf(eb, "%s... ", to);
+               while (*eb != '\0')
+                       *eb++ &= 0177;
+       }
+
+       /* output the message */
+       (void) sprintf(eb, fmt, a, b, c, d, e);
+       while (*eb != '\0')
+               *eb++ &= 0177;
+
+       /* output the error code, if any */
+       if (eno != 0)
+       {
+               extern char *errstring();
+
+               (void) sprintf(eb, ": %s", errstring(eno));
+               eb += strlen(eb);
+       }
+}
+\f/*
+**  ERRSTRING -- return string description of error code
+**
+**     Parameters:
+**             errno -- the error number to translate
+**
+**     Returns:
+**             A string description of errno.
+**
+**     Side Effects:
+**             none.
+*/
+
+char *
+errstring(errno)
+       int errno;
+{
+       extern char *sys_errlist[];
+       extern int sys_nerr;
+       static char buf[100];
+# ifdef SMTP
+       extern char *SmtpPhase;
+# endif SMTP
+
+# ifdef DAEMON
+# ifdef VMUNIX
+       /*
+       **  Handle special network error codes.
+       **
+       **      These are 4.2/4.3bsd specific; they should be in daemon.c.
+       */
+
+       switch (errno)
+       {
+         case ETIMEDOUT:
+         case ECONNRESET:
+               (void) strcpy(buf, sys_errlist[errno]);
+               if (SmtpPhase != NULL)
+               {
+                       (void) strcat(buf, " during ");
+                       (void) strcat(buf, SmtpPhase);
+               }
+               if (CurHostName != NULL)
+               {
+                       (void) strcat(buf, " with ");
+                       (void) strcat(buf, CurHostName);
+               }
+               return (buf);
+
+         case EHOSTDOWN:
+               if (CurHostName == NULL)
+                       break;
+               (void) sprintf(buf, "Host %s is down", CurHostName);
+               return (buf);
+
+         case ECONNREFUSED:
+               if (CurHostName == NULL)
+                       break;
+               (void) sprintf(buf, "Connection refused by %s", CurHostName);
+               return (buf);
+
+         case (TRY_AGAIN+MAX_ERRNO):
+               (void) sprintf(buf, "Host Name Lookup Failure");
+               return (buf);
+       }
+# endif VMUNIX
+# endif DAEMON
+
+       if (errno > 0 && errno < sys_nerr)
+               return (sys_errlist[errno]);
+
+       (void) sprintf(buf, "Error %d", errno);
+       return (buf);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/headers.c b/usr/src/usr.sbin/sendmail/src/headers.c
new file mode 100644 (file)
index 0000000..02950f0
--- /dev/null
@@ -0,0 +1,864 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)headers.c  5.15 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+# include <sys/param.h>
+# include <errno.h>
+# include "sendmail.h"
+
+/*
+**  CHOMPHEADER -- process and save a header line.
+**
+**     Called by collect and by readcf to deal with header lines.
+**
+**     Parameters:
+**             line -- header as a text line.
+**             def -- if set, this is a default value.
+**
+**     Returns:
+**             flags for this header.
+**
+**     Side Effects:
+**             The header is saved on the header list.
+**             Contents of 'line' are destroyed.
+*/
+
+chompheader(line, def)
+       char *line;
+       bool def;
+{
+       register char *p;
+       register HDR *h;
+       HDR **hp;
+       char *fname;
+       char *fvalue;
+       struct hdrinfo *hi;
+       bool cond = FALSE;
+       BITMAP mopts;
+       extern char *crackaddr();
+
+       if (tTd(31, 6))
+               printf("chompheader: %s\n", line);
+
+       /* strip off options */
+       clrbitmap(mopts);
+       p = line;
+       if (*p == '?')
+       {
+               /* have some */
+               register char *q = index(p + 1, *p);
+               
+               if (q != NULL)
+               {
+                       *q++ = '\0';
+                       while (*++p != '\0')
+                               setbitn(*p, mopts);
+                       p = q;
+               }
+               else
+                       usrerr("chompheader: syntax error, line \"%s\"", line);
+               cond = TRUE;
+       }
+
+       /* find canonical name */
+       fname = p;
+       p = index(p, ':');
+       if (p == NULL)
+       {
+               syserr("chompheader: syntax error, line \"%s\"", line);
+               return (0);
+       }
+       fvalue = &p[1];
+       while (isspace(*--p))
+               continue;
+       *++p = '\0';
+       makelower(fname);
+
+       /* strip field value on front */
+       if (*fvalue == ' ')
+               fvalue++;
+
+       /* see if it is a known type */
+       for (hi = HdrInfo; hi->hi_field != NULL; hi++)
+       {
+               if (strcmp(hi->hi_field, fname) == 0)
+                       break;
+       }
+
+       /* see if this is a resent message */
+       if (!def && bitset(H_RESENT, hi->hi_flags))
+               CurEnv->e_flags |= EF_RESENT;
+
+       /* if this means "end of header" quit now */
+       if (bitset(H_EOH, hi->hi_flags))
+               return (hi->hi_flags);
+
+       /* drop explicit From: if same as what we would generate -- for MH */
+       p = "resent-from";
+       if (!bitset(EF_RESENT, CurEnv->e_flags))
+               p += 7;
+       if (!def && !QueueRun && strcmp(fname, p) == 0)
+       {
+               if (CurEnv->e_from.q_paddr != NULL &&
+                   strcmp(fvalue, CurEnv->e_from.q_paddr) == 0)
+                       return (hi->hi_flags);
+       }
+
+       /* delete default value for this header */
+       for (hp = &CurEnv->e_header; (h = *hp) != NULL; hp = &h->h_link)
+       {
+               if (strcmp(fname, h->h_field) == 0 &&
+                   bitset(H_DEFAULT, h->h_flags) &&
+                   !bitset(H_FORCE, h->h_flags))
+                       h->h_value = NULL;
+       }
+
+       /* create a new node */
+       h = (HDR *) xalloc(sizeof *h);
+       h->h_field = newstr(fname);
+       h->h_value = NULL;
+       h->h_link = NULL;
+       bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts);
+       *hp = h;
+       h->h_flags = hi->hi_flags;
+       if (def)
+               h->h_flags |= H_DEFAULT;
+       if (cond)
+               h->h_flags |= H_CHECK;
+       if (h->h_value != NULL)
+               free((char *) h->h_value);
+       h->h_value = newstr(fvalue);
+
+       /* hack to see if this is a new format message */
+       if (!def && bitset(H_RCPT|H_FROM, h->h_flags) &&
+           (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL ||
+            index(fvalue, '<') != NULL || index(fvalue, ';') != NULL))
+       {
+               CurEnv->e_flags &= ~EF_OLDSTYLE;
+       }
+
+       return (h->h_flags);
+}
+\f/*
+**  ADDHEADER -- add a header entry to the end of the queue.
+**
+**     This bypasses the special checking of chompheader.
+**
+**     Parameters:
+**             field -- the name of the header field.
+**             value -- the value of the field.  It must be lower-cased.
+**             e -- the envelope to add them to.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             adds the field on the list of headers for this envelope.
+*/
+
+addheader(field, value, e)
+       char *field;
+       char *value;
+       ENVELOPE *e;
+{
+       register HDR *h;
+       register struct hdrinfo *hi;
+       HDR **hp;
+
+       /* find info struct */
+       for (hi = HdrInfo; hi->hi_field != NULL; hi++)
+       {
+               if (strcmp(field, hi->hi_field) == 0)
+                       break;
+       }
+
+       /* find current place in list -- keep back pointer? */
+       for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
+       {
+               if (strcmp(field, h->h_field) == 0)
+                       break;
+       }
+
+       /* allocate space for new header */
+       h = (HDR *) xalloc(sizeof *h);
+       h->h_field = field;
+       h->h_value = newstr(value);
+       h->h_link = *hp;
+       h->h_flags = hi->hi_flags | H_DEFAULT;
+       clrbitmap(h->h_mflags);
+       *hp = h;
+}
+\f/*
+**  HVALUE -- return value of a header.
+**
+**     Only "real" fields (i.e., ones that have not been supplied
+**     as a default) are used.
+**
+**     Parameters:
+**             field -- the field name.
+**
+**     Returns:
+**             pointer to the value part.
+**             NULL if not found.
+**
+**     Side Effects:
+**             none.
+*/
+
+char *
+hvalue(field)
+       char *field;
+{
+       register HDR *h;
+
+       for (h = CurEnv->e_header; h != NULL; h = h->h_link)
+       {
+               if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0)
+                       return (h->h_value);
+       }
+       return (NULL);
+}
+\f/*
+**  ISHEADER -- predicate telling if argument is a header.
+**
+**     A line is a header if it has a single word followed by
+**     optional white space followed by a colon.
+**
+**     Parameters:
+**             s -- string to check for possible headerness.
+**
+**     Returns:
+**             TRUE if s is a header.
+**             FALSE otherwise.
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+isheader(s)
+       register char *s;
+{
+       while (*s > ' ' && *s != ':' && *s != '\0')
+               s++;
+
+       /* following technically violates RFC822 */
+       while (isspace(*s))
+               s++;
+
+       return (*s == ':');
+}
+\f/*
+**  EATHEADER -- run through the stored header and extract info.
+**
+**     Parameters:
+**             e -- the envelope to process.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Sets a bunch of global variables from information
+**                     in the collected header.
+**             Aborts the message if the hop count is exceeded.
+*/
+
+eatheader(e)
+       register ENVELOPE *e;
+{
+       register HDR *h;
+       register char *p;
+       int hopcnt = 0;
+
+       if (tTd(32, 1))
+               printf("----- collected header -----\n");
+       for (h = e->e_header; h != NULL; h = h->h_link)
+       {
+               extern char *capitalize();
+
+               if (tTd(32, 1))
+                       printf("%s: %s\n", capitalize(h->h_field), h->h_value);
+               /* count the number of times it has been processed */
+               if (bitset(H_TRACE, h->h_flags))
+                       hopcnt++;
+
+               /* send to this person if we so desire */
+               if (GrabTo && bitset(H_RCPT, h->h_flags) &&
+                   !bitset(H_DEFAULT, h->h_flags) &&
+                   (!bitset(EF_RESENT, CurEnv->e_flags) || bitset(H_RESENT, h->h_flags)))
+               {
+                       sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
+               }
+
+               /* log the message-id */
+#ifdef LOG
+               if (!QueueRun && LogLevel > 8 && h->h_value != NULL &&
+                   strcmp(h->h_field, "message-id") == 0)
+               {
+                       char buf[MAXNAME];
+
+                       p = h->h_value;
+                       if (bitset(H_DEFAULT, h->h_flags))
+                       {
+                               expand(p, buf, &buf[sizeof buf], e);
+                               p = buf;
+                       }
+                       syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p);
+               }
+#endif LOG
+       }
+       if (tTd(32, 1))
+               printf("----------------------------\n");
+
+       /* store hop count */
+       if (hopcnt > e->e_hopcount)
+               e->e_hopcount = hopcnt;
+
+       /* message priority */
+       p = hvalue("precedence");
+       if (p != NULL)
+               e->e_class = priencode(p);
+       if (!QueueRun)
+               e->e_msgpriority = e->e_msgsize
+                                - e->e_class * WkClassFact
+                                + e->e_nrcpts * WkRecipFact;
+
+       /* return receipt to */
+       p = hvalue("return-receipt-to");
+       if (p != NULL)
+               e->e_receiptto = p;
+
+       /* errors to */
+       p = hvalue("errors-to");
+       if (p != NULL)
+               sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue);
+
+       /* from person */
+       if (OpMode == MD_ARPAFTP)
+       {
+               register struct hdrinfo *hi = HdrInfo;
+
+               for (p = NULL; p == NULL && hi->hi_field != NULL; hi++)
+               {
+                       if (bitset(H_FROM, hi->hi_flags))
+                               p = hvalue(hi->hi_field);
+               }
+               if (p != NULL)
+                       setsender(p);
+       }
+
+       /* full name of from person */
+       p = hvalue("full-name");
+       if (p != NULL)
+               define('x', p, e);
+
+       /* date message originated */
+       p = hvalue("posted-date");
+       if (p == NULL)
+               p = hvalue("date");
+       if (p != NULL)
+       {
+               define('a', p, e);
+               /* we don't have a good way to do canonical conversion ....
+               define('d', newstr(arpatounix(p)), e);
+               .... so we will ignore the problem for the time being */
+       }
+
+       /*
+       **  Log collection information.
+       */
+
+# ifdef LOG
+       if (!QueueRun && LogLevel > 1)
+       {
+               char hbuf[100], *name = hbuf;
+
+               if (RealHostName == NULL)
+                       name = "local";
+               else if (RealHostName[0] == '[')
+                       name = RealHostName;
+               else
+                       (void)sprintf(hbuf, "%.90s (%s)", 
+                           RealHostName, inet_ntoa(RealHostAddr.sin_addr));
+               syslog(LOG_INFO,
+                   "%s: from=%s, size=%ld, class=%d, received from %s\n",
+                   CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize,
+                   CurEnv->e_class, name);
+       }
+# endif LOG
+}
+\f/*
+**  PRIENCODE -- encode external priority names into internal values.
+**
+**     Parameters:
+**             p -- priority in ascii.
+**
+**     Returns:
+**             priority as a numeric level.
+**
+**     Side Effects:
+**             none.
+*/
+
+priencode(p)
+       char *p;
+{
+       register int i;
+
+       for (i = 0; i < NumPriorities; i++)
+       {
+               if (!strcasecmp(p, Priorities[i].pri_name))
+                       return (Priorities[i].pri_val);
+       }
+
+       /* unknown priority */
+       return (0);
+}
+\f/*
+**  CRACKADDR -- parse an address and turn it into a macro
+**
+**     This doesn't actually parse the address -- it just extracts
+**     it and replaces it with "$g".  The parse is totally ad hoc
+**     and isn't even guaranteed to leave something syntactically
+**     identical to what it started with.  However, it does leave
+**     something semantically identical.
+**
+**     The process is kind of strange.  There are a number of
+**     interesting cases:
+**             1.  comment <address> comment   ==> comment <$g> comment
+**             2.  address                     ==> address
+**             3.  address (comment)           ==> $g (comment)
+**             4.  (comment) address           ==> (comment) $g
+**     And then there are the hard cases....
+**             5.  add (comment) ress          ==> $g (comment)
+**             6.  comment <address (comment)> ==> comment <$g (comment)>
+**             7.    .... etc ....
+**
+**     Parameters:
+**             addr -- the address to be cracked.
+**
+**     Returns:
+**             a pointer to the new version.
+**
+**     Side Effects:
+**             none.
+**
+**     Warning:
+**             The return value is saved in local storage and should
+**             be copied if it is to be reused.
+*/
+
+char *
+crackaddr(addr)
+       register char *addr;
+{
+       register char *p;
+       register int i;
+       static char buf[MAXNAME];
+       char *rhs;
+       bool gotaddr;
+       register char *bp;
+
+       if (tTd(33, 1))
+               printf("crackaddr(%s)\n", addr);
+
+       (void) strcpy(buf, "");
+       rhs = NULL;
+
+       /* strip leading spaces */
+       while (*addr != '\0' && isspace(*addr))
+               addr++;
+
+       /*
+       **  See if we have anything in angle brackets.  If so, that is
+       **  the address part, and the rest is the comment.
+       */
+
+       p = index(addr, '<');
+       if (p != NULL)
+       {
+               /* copy the beginning of the addr field to the buffer */
+               *p = '\0';
+               (void) strcpy(buf, addr);
+               (void) strcat(buf, "<");
+               *p++ = '<';
+
+               /* skip spaces */
+               while (isspace(*p))
+                       p++;
+
+               /* find the matching right angle bracket */
+               addr = p;
+               for (i = 0; *p != '\0'; p++)
+               {
+                       switch (*p)
+                       {
+                         case '<':
+                               i++;
+                               break;
+
+                         case '>':
+                               i--;
+                               break;
+                       }
+                       if (i < 0)
+                               break;
+               }
+
+               /* p now points to the closing quote (or a null byte) */
+               if (*p != '\0')
+               {
+                       /* make rhs point to the extra stuff at the end */
+                       rhs = p;
+                       *p++ = '\0';
+               }
+       }
+
+       /*
+       **  Now parse the real address part.  "addr" points to the (null
+       **  terminated) version of what we are inerested in; rhs points
+       **  to the extra stuff at the end of the line, if any.
+       */
+
+       p = addr;
+
+       /* now strip out comments */
+       bp = &buf[strlen(buf)];
+       gotaddr = FALSE;
+       for (; *p != '\0'; p++)
+       {
+               if (*p == '(')
+               {
+                       /* copy to matching close paren */
+                       *bp++ = *p++;
+                       for (i = 0; *p != '\0'; p++)
+                       {
+                               *bp++ = *p;
+                               switch (*p)
+                               {
+                                 case '(':
+                                       i++;
+                                       break;
+
+                                 case ')':
+                                       i--;
+                                       break;
+                               }
+                               if (i < 0)
+                                       break;
+                       }
+                       continue;
+               }
+
+               /*
+               **  If this is the first "real" character we have seen,
+               **  then we put the "$g" in the buffer now.
+               */
+
+               if (isspace(*p))
+                       *bp++ = *p;
+               else if (!gotaddr)
+               {
+                       (void) strcpy(bp, "\001g");
+                       bp += 2;
+                       gotaddr = TRUE;
+               }
+       }
+
+       /* hack, hack.... strip trailing blanks */
+       do
+       {
+               *bp-- = '\0';
+       } while (isspace(*bp));
+       bp++;
+
+       /* put any right hand side back on */
+       if (rhs != NULL)
+       {
+               *rhs = '>';
+               (void) strcpy(bp, rhs);
+       }
+
+       if (tTd(33, 1))
+               printf("crackaddr=>`%s'\n", buf);
+
+       return (buf);
+}
+\f/*
+**  PUTHEADER -- put the header part of a message from the in-core copy
+**
+**     Parameters:
+**             fp -- file to put it on.
+**             m -- mailer to use.
+**             e -- envelope to use.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             none.
+*/
+
+putheader(fp, m, e)
+       register FILE *fp;
+       register MAILER *m;
+       register ENVELOPE *e;
+{
+       char buf[MAX(MAXFIELD,BUFSIZ)];
+       register HDR *h;
+       extern char *arpadate();
+       extern char *capitalize();
+       char obuf[MAX(MAXFIELD,MAXLINE)];
+
+       for (h = e->e_header; h != NULL; h = h->h_link)
+       {
+               register char *p;
+               extern bool bitintersect();
+
+               if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
+                   !bitintersect(h->h_mflags, m->m_flags))
+                       continue;
+
+               /* handle Resent-... headers specially */
+               if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
+                       continue;
+
+               p = h->h_value;
+               if (bitset(H_DEFAULT, h->h_flags))
+               {
+                       /* macro expand value if generated internally */
+                       expand(p, buf, &buf[sizeof buf], e);
+                       p = buf;
+                       if (p == NULL || *p == '\0')
+                               continue;
+               }
+
+               if (bitset(H_FROM|H_RCPT, h->h_flags))
+               {
+                       /* address field */
+                       bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
+
+                       if (bitset(H_FROM, h->h_flags))
+                               oldstyle = FALSE;
+                       commaize(h, p, fp, oldstyle, m);
+               }
+               else
+               {
+                       /* vanilla header line */
+                       register char *nlp;
+
+                       (void) sprintf(obuf, "%s: ", capitalize(h->h_field));
+                       while ((nlp = index(p, '\n')) != NULL)
+                       {
+                               *nlp = '\0';
+                               (void) strcat(obuf, p);
+                               *nlp = '\n';
+                               putline(obuf, fp, m);
+                               p = ++nlp;
+                               obuf[0] = '\0';
+                       }
+                       (void) strcat(obuf, p);
+                       putline(obuf, fp, m);
+               }
+       }
+}
+\f/*
+**  COMMAIZE -- output a header field, making a comma-translated list.
+**
+**     Parameters:
+**             h -- the header field to output.
+**             p -- the value to put in it.
+**             fp -- file to put it to.
+**             oldstyle -- TRUE if this is an old style header.
+**             m -- a pointer to the mailer descriptor.  If NULL,
+**                     don't transform the name at all.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             outputs "p" to file "fp".
+*/
+
+commaize(h, p, fp, oldstyle, m)
+       register HDR *h;
+       register char *p;
+       FILE *fp;
+       bool oldstyle;
+       register MAILER *m;
+{
+       register char *obp;
+       int opos;
+       bool firstone = TRUE;
+       char obuf[MAXLINE + 3];
+
+       /*
+       **  Output the address list translated by the
+       **  mailer and with commas.
+       */
+
+       if (tTd(14, 2))
+               printf("commaize(%s: %s)\n", h->h_field, p);
+
+       obp = obuf;
+       (void) sprintf(obp, "%s: ", capitalize(h->h_field));
+       opos = strlen(h->h_field) + 2;
+       obp += opos;
+
+       /*
+       **  Run through the list of values.
+       */
+
+       while (*p != '\0')
+       {
+               register char *name;
+               char savechar;
+               extern char *remotename();
+               extern char *DelimChar;         /* defined in prescan */
+
+               /*
+               **  Find the end of the name.  New style names
+               **  end with a comma, old style names end with
+               **  a space character.  However, spaces do not
+               **  necessarily delimit an old-style name -- at
+               **  signs mean keep going.
+               */
+
+               /* find end of name */
+               while (isspace(*p) || *p == ',')
+                       p++;
+               name = p;
+               for (;;)
+               {
+                       char *oldp;
+                       char pvpbuf[PSBUFSIZE];
+                       extern bool isatword();
+                       extern char **prescan();
+
+                       (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf);
+                       p = DelimChar;
+
+                       /* look to see if we have an at sign */
+                       oldp = p;
+                       while (*p != '\0' && isspace(*p))
+                               p++;
+
+                       if (*p != '@' && !isatword(p))
+                       {
+                               p = oldp;
+                               break;
+                       }
+                       p += *p == '@' ? 1 : 2;
+                       while (*p != '\0' && isspace(*p))
+                               p++;
+               }
+               /* at the end of one complete name */
+
+               /* strip off trailing white space */
+               while (p >= name && (isspace(*p) || *p == ',' || *p == '\0'))
+                       p--;
+               if (++p == name)
+                       continue;
+               savechar = *p;
+               *p = '\0';
+
+               /* translate the name to be relative */
+               name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE);
+               if (*name == '\0')
+               {
+                       *p = savechar;
+                       continue;
+               }
+
+               /* output the name with nice formatting */
+               opos += qstrlen(name);
+               if (!firstone)
+                       opos += 2;
+               if (opos > 78 && !firstone)
+               {
+                       (void) strcpy(obp, ",\n");
+                       putline(obuf, fp, m);
+                       obp = obuf;
+                       (void) sprintf(obp, "        ");
+                       opos = strlen(obp);
+                       obp += opos;
+                       opos += qstrlen(name);
+               }
+               else if (!firstone)
+               {
+                       (void) sprintf(obp, ", ");
+                       obp += 2;
+               }
+
+               /* strip off quote bits as we output */
+               while (*name != '\0' && obp < &obuf[MAXLINE])
+               {
+                       if (bitset(0200, *name))
+                               *obp++ = '\\';
+                       *obp++ = *name++ & ~0200;
+               }
+               firstone = FALSE;
+               *p = savechar;
+       }
+       (void) strcpy(obp, "\n");
+       putline(obuf, fp, m);
+}
+\f/*
+**  ISATWORD -- tell if the word we are pointing to is "at".
+**
+**     Parameters:
+**             p -- word to check.
+**
+**     Returns:
+**             TRUE -- if p is the word at.
+**             FALSE -- otherwise.
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+isatword(p)
+       register char *p;
+{
+       extern char lower();
+
+       if (lower(p[0]) == 'a' && lower(p[1]) == 't' &&
+           p[2] != '\0' && isspace(p[2]))
+               return (TRUE);
+       return (FALSE);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/macro.c b/usr/src/usr.sbin/sendmail/src/macro.c
new file mode 100644 (file)
index 0000000..da35b46
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)macro.c    5.7 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+/*
+**  EXPAND -- macro expand a string using $x escapes.
+**
+**     Parameters:
+**             s -- the string to expand.
+**             buf -- the place to put the expansion.
+**             buflim -- the buffer limit, i.e., the address
+**                     of the last usable position in buf.
+**             e -- envelope in which to work.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             none.
+*/
+
+expand(s, buf, buflim, e)
+       register char *s;
+       register char *buf;
+       char *buflim;
+       register ENVELOPE *e;
+{
+       register char *xp;
+       register char *q;
+       bool skipping;          /* set if conditionally skipping output */
+       bool recurse = FALSE;   /* set if recursion required */
+       int i;
+       char xbuf[BUFSIZ];
+       extern char *macvalue();
+
+       if (tTd(35, 24))
+       {
+               printf("expand(");
+               xputs(s);
+               printf(")\n");
+       }
+
+       skipping = FALSE;
+       if (s == NULL)
+               s = "";
+       for (xp = xbuf; *s != '\0'; s++)
+       {
+               char c;
+
+               /*
+               **  Check for non-ordinary (special?) character.
+               **      'q' will be the interpolated quantity.
+               */
+
+               q = NULL;
+               c = *s;
+               switch (c)
+               {
+                 case CONDIF:          /* see if var set */
+                       c = *++s;
+                       skipping = macvalue(c, e) == NULL;
+                       continue;
+
+                 case CONDELSE:        /* change state of skipping */
+                       skipping = !skipping;
+                       continue;
+
+                 case CONDFI:          /* stop skipping */
+                       skipping = FALSE;
+                       continue;
+
+                 case '\001':          /* macro interpolation */
+                       c = *++s;
+                       q = macvalue(c & 0177, e);
+                       if (q == NULL)
+                               continue;
+                       break;
+               }
+
+               /*
+               **  Interpolate q or output one character
+               */
+
+               if (skipping || xp >= &xbuf[sizeof xbuf])
+                       continue;
+               if (q == NULL)
+                       *xp++ = c;
+               else
+               {
+                       /* copy to end of q or max space remaining in buf */
+                       while ((c = *q++) != '\0' && xp < &xbuf[sizeof xbuf - 1])
+                       {
+                               if (iscntrl(c) && !isspace(c))
+                                       recurse = TRUE;
+                               *xp++ = c;
+                       }
+               }
+       }
+       *xp = '\0';
+
+       if (tTd(35, 24))
+       {
+               printf("expand ==> ");
+               xputs(xbuf);
+               printf("\n");
+       }
+
+       /* recurse as appropriate */
+       if (recurse)
+       {
+               expand(xbuf, buf, buflim, e);
+               return;
+       }
+
+       /* copy results out */
+       i = buflim - buf - 1;
+       if (i > xp - xbuf)
+               i = xp - xbuf;
+       bcopy(xbuf, buf, i);
+       buf[i] = '\0';
+}
+\f/*
+**  DEFINE -- define a macro.
+**
+**     this would be better done using a #define macro.
+**
+**     Parameters:
+**             n -- the macro name.
+**             v -- the macro value.
+**             e -- the envelope to store the definition in.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             e->e_macro[n] is defined.
+**
+**     Notes:
+**             There is one macro for each ASCII character,
+**             although they are not all used.  The currently
+**             defined macros are:
+**
+**             $a   date in ARPANET format (preferring the Date: line
+**                  of the message)
+**             $b   the current date (as opposed to the date as found
+**                  the message) in ARPANET format
+**             $c   hop count
+**             $d   (current) date in UNIX (ctime) format
+**             $e   the SMTP entry message+
+**             $f   raw from address
+**             $g   translated from address
+**             $h   to host
+**             $i   queue id
+**             $j   official SMTP hostname, used in messages+
+**             $l   UNIX-style from line+
+**             $n   name of sendmail ("MAILER-DAEMON" on local
+**                  net typically)+
+**             $o   delimiters ("operators") for address tokens+
+**             $p   my process id in decimal
+**             $q   the string that becomes an address -- this is
+**                  normally used to combine $g & $x.
+**             $r   protocol used to talk to sender
+**             $s   sender's host name
+**             $t   the current time in seconds since 1/1/1970
+**             $u   to user
+**             $v   version number of sendmail
+**             $w   our host name (if it can be determined)
+**             $x   signature (full name) of from person
+**             $y   the tty id of our terminal
+**             $z   home directory of to person
+**
+**             Macros marked with + must be defined in the
+**             configuration file and are used internally, but
+**             are not set.
+**
+**             There are also some macros that can be used
+**             arbitrarily to make the configuration file
+**             cleaner.  In general all upper-case letters
+**             are available.
+*/
+
+define(n, v, e)
+       char n;
+       char *v;
+       register ENVELOPE *e;
+{
+       if (tTd(35, 9))
+       {
+               printf("define(%c as ", n);
+               xputs(v);
+               printf(")\n");
+       }
+       e->e_macro[n & 0177] = v;
+}
+\f/*
+**  MACVALUE -- return uninterpreted value of a macro.
+**
+**     Parameters:
+**             n -- the name of the macro.
+**
+**     Returns:
+**             The value of n.
+**
+**     Side Effects:
+**             none.
+*/
+
+char *
+macvalue(n, e)
+       char n;
+       register ENVELOPE *e;
+{
+       n &= 0177;
+       while (e != NULL)
+       {
+               register char *p = e->e_macro[n];
+
+               if (p != NULL)
+                       return (p);
+               e = e->e_parent;
+       }
+       return (NULL);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/mailstats.h b/usr/src/usr.sbin/sendmail/src/mailstats.h
new file mode 100644 (file)
index 0000000..a67ff58
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)mailstats.h 5.4 (Berkeley) 6/1/90
+ */
+
+/*
+**  Statistics structure.
+*/
+
+struct statistics
+{
+       time_t  stat_itime;             /* file initialization time */
+       short   stat_size;              /* size of this structure */
+       long    stat_nf[MAXMAILERS];    /* # msgs from each mailer */
+       long    stat_bf[MAXMAILERS];    /* kbytes from each mailer */
+       long    stat_nt[MAXMAILERS];    /* # msgs to each mailer */
+       long    stat_bt[MAXMAILERS];    /* kbytes to each mailer */
+};
diff --git a/usr/src/usr.sbin/sendmail/src/main.c b/usr/src/usr.sbin/sendmail/src/main.c
new file mode 100644 (file)
index 0000000..895a589
--- /dev/null
@@ -0,0 +1,1099 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+char copyright[] =
+"@(#) Copyright (c) 1988 Regents of the University of California.\n\
+ All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c     5.32 (Berkeley) 3/2/91";
+#endif /* not lint */
+
+#define        _DEFINE
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <signal.h>
+#include <sgtty.h>
+#include "sendmail.h"
+#include <arpa/nameser.h>
+#include <resolv.h>
+
+# ifdef lint
+char   edata, end;
+# endif lint
+
+/*
+**  SENDMAIL -- Post mail to a set of destinations.
+**
+**     This is the basic mail router.  All user mail programs should
+**     call this routine to actually deliver mail.  Sendmail in
+**     turn calls a bunch of mail servers that do the real work of
+**     delivering the mail.
+**
+**     Sendmail is driven by tables read in from /usr/lib/sendmail.cf
+**     (read by readcf.c).  Some more static configuration info,
+**     including some code that you may want to tailor for your
+**     installation, is in conf.c.  You may also want to touch
+**     daemon.c (if you have some other IPC mechanism), acct.c
+**     (to change your accounting), names.c (to adjust the name
+**     server mechanism).
+**
+**     Usage:
+**             /usr/lib/sendmail [flags] addr ...
+**
+**             See the associated documentation for details.
+**
+**     Author:
+**             Eric Allman, UCB/INGRES (until 10/81)
+**                          Britton-Lee, Inc., purveyors of fine
+**                             database computers (from 11/81)
+**             The support of the INGRES Project and Britton-Lee is
+**                     gratefully acknowledged.  Britton-Lee in
+**                     particular had absolutely nothing to gain from
+**                     my involvement in this project.
+*/
+
+
+int            NextMailer;     /* "free" index into Mailer struct */
+char           *FullName;      /* sender's full name */
+ENVELOPE       BlankEnvelope;  /* a "blank" envelope */
+ENVELOPE       MainEnvelope;   /* the envelope around the basic letter */
+ADDRESS                NullAddress =   /* a null address */
+               { "", "", NULL, "" };
+
+/*
+**  Pointers for setproctitle.
+**     This allows "ps" listings to give more useful information.
+**     These must be kept out of BSS for frozen configuration files
+**             to work.
+*/
+
+# ifdef SETPROCTITLE
+char           **Argv = NULL;          /* pointer to argument vector */
+char           *LastArgv = NULL;       /* end of argv */
+# endif SETPROCTITLE
+
+#ifdef DAEMON
+#ifndef SMTP
+ERROR %%%%   Cannot have daemon mode without SMTP   %%%% ERROR
+#endif SMTP
+#endif DAEMON
+
+main(argc, argv, envp)
+       int argc;
+       char **argv;
+       char **envp;
+{
+       register char *p;
+       char **av;
+       extern int finis();
+       extern char Version[];
+       char *from;
+       typedef int (*fnptr)();
+       STAB *st;
+       register int i;
+       bool readconfig = TRUE;
+       bool queuemode = FALSE;         /* process queue requests */
+       bool nothaw;
+       static bool reenter = FALSE;
+       char jbuf[30];                  /* holds MyHostName */
+       extern bool safefile();
+       extern time_t convtime();
+       extern putheader(), putbody();
+       extern ENVELOPE *newenvelope();
+       extern void intsig();
+       extern char **myhostname();
+       extern char *arpadate();
+       extern char **environ;
+
+       /*
+       **  Check to see if we reentered.
+       **      This would normally happen if e_putheader or e_putbody
+       **      were NULL when invoked.
+       */
+
+       if (reenter)
+       {
+               syserr("main: reentered!");
+               abort();
+       }
+       reenter = TRUE;
+
+       /* Enforce use of local time */
+       unsetenv("TZ");
+
+       /*
+       **  Be sure we have enough file descriptors.
+       **      But also be sure that 0, 1, & 2 are open.
+       */
+
+       i = open("/dev/null", O_RDWR);
+       while (i >= 0 && i < 2)
+               i = dup(i);
+       for (i = getdtablesize(); i > 2; --i)
+               (void) close(i);
+       errno = 0;
+
+#ifdef LOG_MAIL
+       openlog("sendmail", LOG_PID, LOG_MAIL);
+#else 
+       openlog("sendmail", LOG_PID);
+#endif 
+
+       /*
+       **  Set default values for variables.
+       **      These cannot be in initialized data space.
+       */
+
+       setdefaults();
+
+       /* set up the blank envelope */
+       BlankEnvelope.e_puthdr = putheader;
+       BlankEnvelope.e_putbody = putbody;
+       BlankEnvelope.e_xfp = NULL;
+       STRUCTCOPY(NullAddress, BlankEnvelope.e_from);
+       CurEnv = &BlankEnvelope;
+       STRUCTCOPY(NullAddress, MainEnvelope.e_from);
+
+       /*
+       **  Do a quick prescan of the argument list.
+       **      We do this to find out if we can potentially thaw the
+       **      configuration file.  If not, we do the thaw now so that
+       **      the argument processing applies to this run rather than
+       **      to the run that froze the configuration.
+       */
+
+       argv[argc] = NULL;
+       av = argv;
+       nothaw = FALSE;
+       while ((p = *++av) != NULL)
+       {
+               if (strncmp(p, "-C", 2) == 0)
+               {
+                       ConfFile = &p[2];
+                       if (ConfFile[0] == '\0')
+                               ConfFile = "sendmail.cf";
+                       (void) setgid(getrgid());
+                       (void) setuid(getruid());
+                       nothaw = TRUE;
+               }
+               else if (strncmp(p, "-bz", 3) == 0)
+                       nothaw = TRUE;
+               else if (strncmp(p, "-d", 2) == 0)
+               {
+                       tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
+                       tTflag(&p[2]);
+                       setbuf(stdout, (char *) NULL);
+                       printf("Version %s\n", Version);
+               }
+       }
+
+       InChannel = stdin;
+       OutChannel = stdout;
+
+       if (!nothaw)
+               readconfig = !thaw(FreezeFile);
+
+       /* reset the environment after the thaw */
+       for (i = 0; i < MAXUSERENVIRON && envp[i] != NULL; i++)
+               UserEnviron[i] = newstr(envp[i]);
+       UserEnviron[i] = NULL;
+       environ = UserEnviron;
+
+# ifdef SETPROCTITLE
+       /*
+       **  Save start and extent of argv for setproctitle.
+       */
+
+       Argv = argv;
+       if (i > 0)
+               LastArgv = envp[i - 1] + strlen(envp[i - 1]);
+       else
+               LastArgv = argv[argc - 1] + strlen(argv[argc - 1]);
+# endif SETPROCTITLE
+
+       if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+               (void) signal(SIGINT, intsig);
+       if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+               (void) signal(SIGHUP, intsig);
+       (void) signal(SIGTERM, intsig);
+       (void) signal(SIGPIPE, SIG_IGN);
+       OldUmask = umask(0);
+       OpMode = MD_DELIVER;
+       MotherPid = getpid();
+       FullName = getenv("NAME");
+
+       errno = 0;
+       from = NULL;
+
+       if (readconfig)
+       {
+               /* initialize some macros, etc. */
+               initmacros();
+
+               /* hostname */
+               av = myhostname(jbuf, sizeof jbuf);
+               if (jbuf[0] != '\0')
+               {
+                       if (tTd(0, 4))
+                               printf("canonical name: %s\n", jbuf);
+                       p = newstr(jbuf);
+                       define('w', p, CurEnv);
+                       setclass('w', p);
+               }
+               while (av != NULL && *av != NULL)
+               {
+                       if (tTd(0, 4))
+                               printf("\ta.k.a.: %s\n", *av);
+                       setclass('w', *av++);
+               }
+
+               /* version */
+               define('v', Version, CurEnv);
+       }
+
+       /* current time */
+       define('b', arpadate((char *) NULL), CurEnv);
+
+       /*
+       ** Crack argv.
+       */
+
+       av = argv;
+       p = rindex(*av, '/');
+       if (p++ == NULL)
+               p = *av;
+       if (strcmp(p, "newaliases") == 0)
+               OpMode = MD_INITALIAS;
+       else if (strcmp(p, "mailq") == 0)
+               OpMode = MD_PRINT;
+       else if (strcmp(p, "smtpd") == 0)
+               OpMode = MD_DAEMON;
+       while ((p = *++av) != NULL && p[0] == '-')
+       {
+               switch (p[1])
+               {
+                 case 'b':     /* operations mode */
+                       switch (p[2])
+                       {
+                         case MD_DAEMON:
+# ifdef DAEMON
+                               if (getuid() != 0) {
+                                       usrerr("Permission denied");
+                                       exit (EX_USAGE);
+                               }
+                               (void) unsetenv("HOSTALIASES");
+# else
+                               usrerr("Daemon mode not implemented");
+                               ExitStat = EX_USAGE;
+                               break;
+# endif DAEMON
+                         case MD_SMTP:
+# ifndef SMTP
+                               usrerr("I don't speak SMTP");
+                               ExitStat = EX_USAGE;
+                               break;
+# endif SMTP
+                         case MD_ARPAFTP:
+                         case MD_DELIVER:
+                         case MD_VERIFY:
+                         case MD_TEST:
+                         case MD_INITALIAS:
+                         case MD_PRINT:
+                         case MD_FREEZE:
+                               OpMode = p[2];
+                               break;
+
+                         default:
+                               usrerr("Invalid operation mode %c", p[2]);
+                               ExitStat = EX_USAGE;
+                               break;
+                       }
+                       break;
+
+                 case 'C':     /* select configuration file (already done) */
+                       break;
+
+                 case 'd':     /* debugging -- redo in case frozen */
+                       tTsetup(tTdvect, sizeof tTdvect, "0-99.1");
+                       tTflag(&p[2]);
+                       setbuf(stdout, (char *) NULL);
+#ifdef NAMED_BIND
+                       _res.options |= RES_DEBUG;
+#endif
+                       break;
+
+                 case 'f':     /* from address */
+                 case 'r':     /* obsolete -f flag */
+                       p += 2;
+                       if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
+                       {
+                               p = *++av;
+                               if (p == NULL || *p == '-')
+                               {
+                                       usrerr("No \"from\" person");
+                                       ExitStat = EX_USAGE;
+                                       av--;
+                                       break;
+                               }
+                       }
+                       if (from != NULL)
+                       {
+                               usrerr("More than one \"from\" person");
+                               ExitStat = EX_USAGE;
+                               break;
+                       }
+                       from = newstr(p);
+                       break;
+
+                 case 'F':     /* set full name */
+                       p += 2;
+                       if (*p == '\0' && ((p = *++av) == NULL || *p == '-'))
+                       {
+                               usrerr("Bad -F flag");
+                               ExitStat = EX_USAGE;
+                               av--;
+                               break;
+                       }
+                       FullName = newstr(p);
+                       break;
+
+                 case 'h':     /* hop count */
+                       p += 2;
+                       if (*p == '\0' && ((p = *++av) == NULL || !isdigit(*p)))
+                       {
+                               usrerr("Bad hop count (%s)", p);
+                               ExitStat = EX_USAGE;
+                               av--;
+                               break;
+                       }
+                       CurEnv->e_hopcount = atoi(p);
+                       break;
+               
+                 case 'n':     /* don't alias */
+                       NoAlias = TRUE;
+                       break;
+
+                 case 'o':     /* set option */
+                       setoption(p[2], &p[3], FALSE, TRUE);
+                       break;
+
+                 case 'q':     /* run queue files at intervals */
+# ifdef QUEUE
+                       if (getuid() != 0) {
+                               usrerr("Permission denied");
+                               exit (EX_USAGE);
+                       }
+                       (void) unsetenv("HOSTALIASES");
+                       queuemode = TRUE;
+                       QueueIntvl = convtime(&p[2]);
+# else QUEUE
+                       usrerr("I don't know about queues");
+                       ExitStat = EX_USAGE;
+# endif QUEUE
+                       break;
+
+                 case 't':     /* read recipients from message */
+                       GrabTo = TRUE;
+                       break;
+
+                       /* compatibility flags */
+                 case 'c':     /* connect to non-local mailers */
+                 case 'e':     /* error message disposition */
+                 case 'i':     /* don't let dot stop me */
+                 case 'm':     /* send to me too */
+                 case 'T':     /* set timeout interval */
+                 case 'v':     /* give blow-by-blow description */
+                       setoption(p[1], &p[2], FALSE, TRUE);
+                       break;
+
+                 case 's':     /* save From lines in headers */
+                       setoption('f', &p[2], FALSE, TRUE);
+                       break;
+
+# ifdef DBM
+                 case 'I':     /* initialize alias DBM file */
+                       OpMode = MD_INITALIAS;
+                       break;
+# endif DBM
+               }
+       }
+
+       /*
+       **  Do basic initialization.
+       **      Read system control file.
+       **      Extract special fields for local use.
+       */
+
+       if (OpMode == MD_FREEZE || readconfig)
+               readcf(ConfFile);
+
+       switch (OpMode)
+       {
+         case MD_FREEZE:
+               /* this is critical to avoid forgeries of the frozen config */
+               (void) setgid(getgid());
+               (void) setuid(getuid());
+
+               /* freeze the configuration */
+               freeze(FreezeFile);
+               exit(EX_OK);
+
+         case MD_INITALIAS:
+               Verbose = TRUE;
+               break;
+       }
+
+       /* do heuristic mode adjustment */
+       if (Verbose)
+       {
+               /* turn off noconnect option */
+               setoption('c', "F", TRUE, FALSE);
+
+               /* turn on interactive delivery */
+               setoption('d', "", TRUE, FALSE);
+       }
+
+       /* our name for SMTP codes */
+       expand("\001j", jbuf, &jbuf[sizeof jbuf - 1], CurEnv);
+       MyHostName = jbuf;
+
+       /* the indices of local and program mailers */
+       st = stab("local", ST_MAILER, ST_FIND);
+       if (st == NULL)
+               syserr("No local mailer defined");
+       else
+               LocalMailer = st->s_mailer;
+       st = stab("prog", ST_MAILER, ST_FIND);
+       if (st == NULL)
+               syserr("No prog mailer defined");
+       else
+               ProgMailer = st->s_mailer;
+
+       /* operate in queue directory */
+       if (chdir(QueueDir) < 0)
+       {
+               syserr("cannot chdir(%s)", QueueDir);
+               exit(EX_SOFTWARE);
+       }
+
+       /*
+       **  Do operation-mode-dependent initialization.
+       */
+
+       switch (OpMode)
+       {
+         case MD_PRINT:
+               /* print the queue */
+#ifdef QUEUE
+               dropenvelope(CurEnv);
+               printqueue();
+               exit(EX_OK);
+#else QUEUE
+               usrerr("No queue to print");
+               finis();
+#endif QUEUE
+
+         case MD_INITALIAS:
+               /* initialize alias database */
+               initaliases(AliasFile, TRUE);
+               exit(EX_OK);
+
+         case MD_DAEMON:
+               /* don't open alias database -- done in srvrsmtp */
+               break;
+
+         default:
+               /* open the alias database */
+               initaliases(AliasFile, FALSE);
+               break;
+       }
+
+       if (tTd(0, 15))
+       {
+               /* print configuration table (or at least part of it) */
+               printrules();
+               for (i = 0; i < MAXMAILERS; i++)
+               {
+                       register struct mailer *m = Mailer[i];
+                       int j;
+
+                       if (m == NULL)
+                               continue;
+                       printf("mailer %d (%s): P=%s S=%d R=%d M=%ld F=", i, m->m_name,
+                               m->m_mailer, m->m_s_rwset, m->m_r_rwset,
+                               m->m_maxsize);
+                       for (j = '\0'; j <= '\177'; j++)
+                               if (bitnset(j, m->m_flags))
+                                       (void) putchar(j);
+                       printf(" E=");
+                       xputs(m->m_eol);
+                       printf("\n");
+               }
+       }
+
+       /*
+       **  Switch to the main envelope.
+       */
+
+       CurEnv = newenvelope(&MainEnvelope);
+       MainEnvelope.e_flags = BlankEnvelope.e_flags;
+
+       /*
+       **  If test mode, read addresses from stdin and process.
+       */
+
+       if (OpMode == MD_TEST)
+       {
+               char buf[MAXLINE];
+
+               printf("ADDRESS TEST MODE\nEnter <ruleset> <address>\n");
+               for (;;)
+               {
+                       register char **pvp;
+                       char *q;
+                       extern char *DelimChar;
+
+                       printf("> ");
+                       (void) fflush(stdout);
+                       if (fgets(buf, sizeof buf, stdin) == NULL)
+                               finis();
+                       for (p = buf; isspace(*p); p++)
+                               continue;
+                       q = p;
+                       while (*p != '\0' && !isspace(*p))
+                               p++;
+                       if (*p == '\0')
+                               continue;
+                       *p = '\0';
+                       do
+                       {
+                               extern char **prescan();
+                               char pvpbuf[PSBUFSIZE];
+
+                               pvp = prescan(++p, ',', pvpbuf);
+                               if (pvp == NULL)
+                                       continue;
+                               rewrite(pvp, 3);
+                               p = q;
+                               while (*p != '\0')
+                               {
+                                       rewrite(pvp, atoi(p));
+                                       while (*p != '\0' && *p++ != ',')
+                                               continue;
+                               }
+                       } while (*(p = DelimChar) != '\0');
+               }
+       }
+
+# ifdef QUEUE
+       /*
+       **  If collecting stuff from the queue, go start doing that.
+       */
+
+       if (queuemode && OpMode != MD_DAEMON && QueueIntvl == 0)
+       {
+               runqueue(FALSE);
+               finis();
+       }
+# endif QUEUE
+
+       /*
+       **  If a daemon, wait for a request.
+       **      getrequests will always return in a child.
+       **      If we should also be processing the queue, start
+       **              doing it in background.
+       **      We check for any errors that might have happened
+       **              during startup.
+       */
+
+       if (OpMode == MD_DAEMON || QueueIntvl != 0)
+       {
+               if (!tTd(0, 1))
+               {
+                       /* put us in background */
+                       i = fork();
+                       if (i < 0)
+                               syserr("daemon: cannot fork");
+                       if (i != 0)
+                               exit(0);
+
+                       /* get our pid right */
+                       MotherPid = getpid();
+
+                       /* disconnect from our controlling tty */
+                       disconnect(TRUE);
+               }
+
+# ifdef QUEUE
+               if (queuemode)
+               {
+                       runqueue(TRUE);
+                       if (OpMode != MD_DAEMON)
+                               for (;;)
+                                       pause();
+               }
+# endif QUEUE
+               dropenvelope(CurEnv);
+
+#ifdef DAEMON
+               getrequests();
+
+               /* at this point we are in a child: reset state */
+               OpMode = MD_SMTP;
+               (void) newenvelope(CurEnv);
+               openxscript(CurEnv);
+#endif DAEMON
+       }
+       
+# ifdef SMTP
+       /*
+       **  If running SMTP protocol, start collecting and executing
+       **  commands.  This will never return.
+       */
+
+       if (OpMode == MD_SMTP)
+               smtp();
+# endif SMTP
+
+       /*
+       **  Do basic system initialization and set the sender
+       */
+
+       initsys();
+       setsender(from);
+
+       if (OpMode != MD_ARPAFTP && *av == NULL && !GrabTo)
+       {
+               usrerr("Recipient names must be specified");
+
+               /* collect body for UUCP return */
+               if (OpMode != MD_VERIFY)
+                       collect(FALSE);
+               finis();
+       }
+       if (OpMode == MD_VERIFY)
+               SendMode = SM_VERIFY;
+
+       /*
+       **  Scan argv and deliver the message to everyone.
+       */
+
+       sendtoargv(av);
+
+       /* if we have had errors sofar, arrange a meaningful exit stat */
+       if (Errors > 0 && ExitStat == EX_OK)
+               ExitStat = EX_USAGE;
+
+       /*
+       **  Read the input mail.
+       */
+
+       CurEnv->e_to = NULL;
+       if (OpMode != MD_VERIFY || GrabTo)
+               collect(FALSE);
+       errno = 0;
+
+       /* collect statistics */
+       if (OpMode != MD_VERIFY)
+               markstats(CurEnv, (ADDRESS *) NULL);
+
+       if (tTd(1, 1))
+               printf("From person = \"%s\"\n", CurEnv->e_from.q_paddr);
+
+       /*
+       **  Actually send everything.
+       **      If verifying, just ack.
+       */
+
+       CurEnv->e_from.q_flags |= QDONTSEND;
+       CurEnv->e_to = NULL;
+       sendall(CurEnv, SM_DEFAULT);
+
+       /*
+       ** All done.
+       */
+
+       finis();
+}
+\f/*
+**  FINIS -- Clean up and exit.
+**
+**     Parameters:
+**             none
+**
+**     Returns:
+**             never
+**
+**     Side Effects:
+**             exits sendmail
+*/
+
+finis()
+{
+       if (tTd(2, 1))
+               printf("\n====finis: stat %d e_flags %o\n", ExitStat, CurEnv->e_flags);
+
+       /* clean up temp files */
+       CurEnv->e_to = NULL;
+       dropenvelope(CurEnv);
+
+       /* post statistics */
+       poststats(StatFile);
+
+       /* and exit */
+# ifdef LOG
+       if (LogLevel > 11)
+               syslog(LOG_DEBUG, "finis, pid=%d", getpid());
+# endif LOG
+       if (ExitStat == EX_TEMPFAIL)
+               ExitStat = EX_OK;
+       exit(ExitStat);
+}
+\f/*
+**  INTSIG -- clean up on interrupt
+**
+**     This just arranges to exit.  It pessimises in that it
+**     may resend a message.
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Unlocks the current job.
+*/
+
+void
+intsig()
+{
+       FileName = NULL;
+       unlockqueue(CurEnv);
+       exit(EX_OK);
+}
+\f/*
+**  INITMACROS -- initialize the macro system
+**
+**     This just involves defining some macros that are actually
+**     used internally as metasymbols to be themselves.
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             initializes several macros to be themselves.
+*/
+
+struct metamac
+{
+       char    metaname;
+       char    metaval;
+};
+
+struct metamac MetaMacros[] =
+{
+       /* LHS pattern matching characters */
+       '*', MATCHZANY, '+', MATCHANY,  '-', MATCHONE,  '=', MATCHCLASS,
+       '~', MATCHNCLASS,
+
+       /* these are RHS metasymbols */
+       '#', CANONNET,  '@', CANONHOST, ':', CANONUSER, '>', CALLSUBR,
+
+       /* the conditional operations */
+       '?', CONDIF,    '|', CONDELSE,  '.', CONDFI,
+
+       /* and finally the hostname lookup characters */
+       '[', HOSTBEGIN, ']', HOSTEND,
+
+       '\0'
+};
+
+initmacros()
+{
+       register struct metamac *m;
+       char buf[5];
+       register int c;
+
+       for (m = MetaMacros; m->metaname != '\0'; m++)
+       {
+               buf[0] = m->metaval;
+               buf[1] = '\0';
+               define(m->metaname, newstr(buf), CurEnv);
+       }
+       buf[0] = MATCHREPL;
+       buf[2] = '\0';
+       for (c = '0'; c <= '9'; c++)
+       {
+               buf[1] = c;
+               define(c, newstr(buf), CurEnv);
+       }
+}
+\f/*
+**  FREEZE -- freeze BSS & allocated memory
+**
+**     This will be used to efficiently load the configuration file.
+**
+**     Parameters:
+**             freezefile -- the name of the file to freeze to.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Writes BSS and malloc'ed memory to freezefile
+*/
+
+union frz
+{
+       char            frzpad[BUFSIZ]; /* insure we are on a BUFSIZ boundary */
+       struct
+       {
+               time_t  frzstamp;       /* timestamp on this freeze */
+               char    *frzbrk;        /* the current break */
+               char    *frzedata;      /* address of edata */
+               char    *frzend;        /* address of end */
+               char    frzver[252];    /* sendmail version */
+       } frzinfo;
+};
+
+freeze(freezefile)
+       char *freezefile;
+{
+       int f;
+       union frz fhdr;
+       extern char edata, end;
+       extern char *sbrk();
+       extern char Version[];
+
+       if (freezefile == NULL)
+               return;
+
+       /* try to open the freeze file */
+       f = creat(freezefile, FileMode);
+       if (f < 0)
+       {
+               syserr("Cannot freeze %s", freezefile);
+               errno = 0;
+               return;
+       }
+
+       /* build the freeze header */
+       fhdr.frzinfo.frzstamp = curtime();
+       fhdr.frzinfo.frzbrk = sbrk(0);
+       fhdr.frzinfo.frzedata = &edata;
+       fhdr.frzinfo.frzend = &end;
+       (void) strcpy(fhdr.frzinfo.frzver, Version);
+
+       /* write out the freeze header */
+       if (write(f, (char *) &fhdr, sizeof fhdr) != sizeof fhdr ||
+           write(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - &edata)) !=
+                                       (int) (fhdr.frzinfo.frzbrk - &edata))
+       {
+               syserr("Cannot freeze %s", freezefile);
+       }
+
+       /* fine, clean up */
+       (void) close(f);
+}
+\f/*
+**  THAW -- read in the frozen configuration file.
+**
+**     Parameters:
+**             freezefile -- the name of the file to thaw from.
+**
+**     Returns:
+**             TRUE if it successfully read the freeze file.
+**             FALSE otherwise.
+**
+**     Side Effects:
+**             reads freezefile in to BSS area.
+*/
+
+thaw(freezefile)
+       char *freezefile;
+{
+       int f;
+       union frz fhdr;
+       extern char edata, end;
+       extern char Version[];
+       extern caddr_t brk();
+
+       if (freezefile == NULL)
+               return (FALSE);
+
+       /* open the freeze file */
+       f = open(freezefile, 0);
+       if (f < 0)
+       {
+               syslog(LOG_WARNING, "Cannot open frozen config file %s: %m",
+                       freezefile);
+               errno = 0;
+               return (FALSE);
+       }
+
+       /* read in the header */
+       if (read(f, (char *) &fhdr, sizeof fhdr) < sizeof fhdr)
+       {
+               syserr("Cannot read frozen config file");
+               (void) close(f);
+               return (FALSE);
+       }
+       if ( fhdr.frzinfo.frzedata != &edata ||
+           fhdr.frzinfo.frzend != &end ||
+           strcmp(fhdr.frzinfo.frzver, Version) != 0)
+       {
+               syslog(LOG_WARNING, "Wrong version of frozen config file");
+               (void) close(f);
+               return (FALSE);
+       }
+
+       /* arrange to have enough space */
+       if (brk(fhdr.frzinfo.frzbrk) == (caddr_t) -1)
+       {
+               syserr("Cannot break to %x", fhdr.frzinfo.frzbrk);
+               (void) close(f);
+               return (FALSE);
+       }
+
+       /* now read in the freeze file */
+       if (read(f, (char *) &edata, (int) (fhdr.frzinfo.frzbrk - &edata)) !=
+                                       (int) (fhdr.frzinfo.frzbrk - &edata))
+       {
+               syserr("Cannot read frozen config file");
+               /* oops!  we have trashed memory..... */
+               (void) write(2, "Cannot read freeze file\n", 24);
+               _exit(EX_SOFTWARE);
+       }
+
+       (void) close(f);
+       return (TRUE);
+}
+\f/*
+**  DISCONNECT -- remove our connection with any foreground process
+**
+**     Parameters:
+**             fulldrop -- if set, we should also drop the controlling
+**                     TTY if possible -- this should only be done when
+**                     setting up the daemon since otherwise UUCP can
+**                     leave us trying to open a dialin, and we will
+**                     wait for the carrier.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             Trys to insure that we are immune to vagaries of
+**             the controlling tty.
+*/
+
+disconnect(fulldrop)
+       bool fulldrop;
+{
+       int fd;
+
+       if (tTd(52, 1))
+               printf("disconnect: In %d Out %d\n", fileno(InChannel),
+                                               fileno(OutChannel));
+       if (tTd(52, 5))
+       {
+               printf("don't\n");
+               return;
+       }
+
+       /* be sure we don't get nasty signals */
+       (void) signal(SIGHUP, SIG_IGN);
+       (void) signal(SIGINT, SIG_IGN);
+       (void) signal(SIGQUIT, SIG_IGN);
+
+       /* we can't communicate with our caller, so.... */
+       HoldErrs = TRUE;
+       ErrorMode = EM_MAIL;
+       Verbose = FALSE;
+
+       /* all input from /dev/null */
+       if (InChannel != stdin)
+       {
+               (void) fclose(InChannel);
+               InChannel = stdin;
+       }
+       (void) freopen("/dev/null", "r", stdin);
+
+       /* output to the transcript */
+       if (OutChannel != stdout)
+       {
+               (void) fclose(OutChannel);
+               OutChannel = stdout;
+       }
+       if (CurEnv->e_xfp == NULL)
+               CurEnv->e_xfp = fopen("/dev/null", "w");
+       (void) fflush(stdout);
+       (void) close(1);
+       (void) close(2);
+       while ((fd = dup(fileno(CurEnv->e_xfp))) < 2 && fd > 0)
+               continue;
+
+       /* drop our controlling TTY completely if possible */
+       if (fulldrop)
+       {
+#if BSD > 43
+               daemon(1, 1);
+#else
+#ifdef TIOCNOTTY
+               fd = open("/dev/tty", 2);
+               if (fd >= 0)
+               {
+                       (void) ioctl(fd, (int) TIOCNOTTY, (char *) 0);
+                       (void) close(fd);
+               }
+               (void) setpgrp(0, 0);
+#endif /* TIOCNOTTY */
+#endif /* BSD */
+               errno = 0;
+       }
+
+# ifdef LOG
+       if (LogLevel > 11)
+               syslog(LOG_DEBUG, "in background, pid=%d", getpid());
+# endif LOG
+
+       errno = 0;
+}
diff --git a/usr/src/usr.sbin/sendmail/src/parseaddr.c b/usr/src/usr.sbin/sendmail/src/parseaddr.c
new file mode 100644 (file)
index 0000000..2ee24e0
--- /dev/null
@@ -0,0 +1,1187 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)parseaddr.c        5.13 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+/*
+**  PARSEADDR -- Parse an address
+**
+**     Parses an address and breaks it up into three parts: a
+**     net to transmit the message on, the host to transmit it
+**     to, and a user on that host.  These are loaded into an
+**     ADDRESS header with the values squirreled away if necessary.
+**     The "user" part may not be a real user; the process may
+**     just reoccur on that machine.  For example, on a machine
+**     with an arpanet connection, the address
+**             csvax.bill@berkeley
+**     will break up to a "user" of 'csvax.bill' and a host
+**     of 'berkeley' -- to be transmitted over the arpanet.
+**
+**     Parameters:
+**             addr -- the address to parse.
+**             a -- a pointer to the address descriptor buffer.
+**                     If NULL, a header will be created.
+**             copyf -- determines what shall be copied:
+**                     -1 -- don't copy anything.  The printname
+**                             (q_paddr) is just addr, and the
+**                             user & host are allocated internally
+**                             to parse.
+**                     0 -- copy out the parsed user & host, but
+**                             don't copy the printname.
+**                     +1 -- copy everything.
+**             delim -- the character to terminate the address, passed
+**                     to prescan.
+**
+**     Returns:
+**             A pointer to the address descriptor header (`a' if
+**                     `a' is non-NULL).
+**             NULL on error.
+**
+**     Side Effects:
+**             none
+*/
+
+/* following delimiters are inherent to the internal algorithms */
+# define DELIMCHARS    "\001()<>,;\\\"\r\n"    /* word delimiters */
+
+ADDRESS *
+parseaddr(addr, a, copyf, delim)
+       char *addr;
+       register ADDRESS *a;
+       int copyf;
+       char delim;
+{
+       register char **pvp;
+       register struct mailer *m;
+       char pvpbuf[PSBUFSIZE];
+       extern char **prescan();
+       extern ADDRESS *buildaddr();
+
+       /*
+       **  Initialize and prescan address.
+       */
+
+       CurEnv->e_to = addr;
+       if (tTd(20, 1))
+               printf("\n--parseaddr(%s)\n", addr);
+
+       pvp = prescan(addr, delim, pvpbuf);
+       if (pvp == NULL)
+               return (NULL);
+
+       /*
+       **  Apply rewriting rules.
+       **      Ruleset 0 does basic parsing.  It must resolve.
+       */
+
+       rewrite(pvp, 3);
+       rewrite(pvp, 0);
+
+       /*
+       **  See if we resolved to a real mailer.
+       */
+
+       if (pvp[0][0] != CANONNET)
+       {
+               setstat(EX_USAGE);
+               usrerr("cannot resolve name");
+               return (NULL);
+       }
+
+       /*
+       **  Build canonical address from pvp.
+       */
+
+       a = buildaddr(pvp, a);
+       if (a == NULL)
+               return (NULL);
+       m = a->q_mailer;
+
+       /*
+       **  Make local copies of the host & user and then
+       **  transport them out.
+       */
+
+       if (copyf > 0)
+       {
+               extern char *DelimChar;
+               char savec = *DelimChar;
+
+               *DelimChar = '\0';
+               a->q_paddr = newstr(addr);
+               *DelimChar = savec;
+       }
+       else
+               a->q_paddr = addr;
+
+       if (a->q_user == NULL)
+               a->q_user = "";
+       if (a->q_host == NULL)
+               a->q_host = "";
+
+       if (copyf >= 0)
+       {
+               a->q_host = newstr(a->q_host);
+               if (a->q_user != a->q_paddr)
+                       a->q_user = newstr(a->q_user);
+       }
+
+       /*
+       **  Convert host name to lower case if requested.
+       **      User name will be done later.
+       */
+
+       if (!bitnset(M_HST_UPPER, m->m_flags))
+               makelower(a->q_host);
+
+       /*
+       **  Compute return value.
+       */
+
+       if (tTd(20, 1))
+       {
+               printf("parseaddr-->");
+               printaddr(a, FALSE);
+       }
+
+       return (a);
+}
+\f/*
+**  LOWERADDR -- map UPPER->lower case on addresses as requested.
+**
+**     Parameters:
+**             a -- address to be mapped.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             none.
+*/
+
+loweraddr(a)
+       register ADDRESS *a;
+{
+       register MAILER *m = a->q_mailer;
+
+       if (!bitnset(M_USR_UPPER, m->m_flags))
+               makelower(a->q_user);
+}
+\f/*
+**  PRESCAN -- Prescan name and make it canonical
+**
+**     Scans a name and turns it into a set of tokens.  This process
+**     deletes blanks and comments (in parentheses).
+**
+**     This routine knows about quoted strings and angle brackets.
+**
+**     There are certain subtleties to this routine.  The one that
+**     comes to mind now is that backslashes on the ends of names
+**     are silently stripped off; this is intentional.  The problem
+**     is that some versions of sndmsg (like at LBL) set the kill
+**     character to something other than @ when reading addresses;
+**     so people type "csvax.eric\@berkeley" -- which screws up the
+**     berknet mailer.
+**
+**     Parameters:
+**             addr -- the name to chomp.
+**             delim -- the delimiter for the address, normally
+**                     '\0' or ','; \0 is accepted in any case.
+**                     If '\t' then we are reading the .cf file.
+**             pvpbuf -- place to put the saved text -- note that
+**                     the pointers are static.
+**
+**     Returns:
+**             A pointer to a vector of tokens.
+**             NULL on error.
+**
+**     Side Effects:
+**             sets DelimChar to point to the character matching 'delim'.
+*/
+
+/* states and character types */
+# define OPR           0       /* operator */
+# define ATM           1       /* atom */
+# define QST           2       /* in quoted string */
+# define SPC           3       /* chewing up spaces */
+# define ONE           4       /* pick up one character */
+
+# define NSTATES       5       /* number of states */
+# define TYPE          017     /* mask to select state type */
+
+/* meta bits for table */
+# define M             020     /* meta character; don't pass through */
+# define B             040     /* cause a break */
+# define MB            M|B     /* meta-break */
+
+static short StateTab[NSTATES][NSTATES] =
+{
+   /*  oldst   chtype> OPR     ATM     QST     SPC     ONE     */
+       /*OPR*/         OPR|B,  ATM|B,  QST|B,  SPC|MB, ONE|B,
+       /*ATM*/         OPR|B,  ATM,    QST|B,  SPC|MB, ONE|B,
+       /*QST*/         QST,    QST,    OPR,    QST,    QST,
+       /*SPC*/         OPR,    ATM,    QST,    SPC|M,  ONE,
+       /*ONE*/         OPR,    OPR,    OPR,    OPR,    OPR,
+};
+
+# define NOCHAR                -1      /* signal nothing in lookahead token */
+
+char   *DelimChar;             /* set to point to the delimiter */
+
+char **
+prescan(addr, delim, pvpbuf)
+       char *addr;
+       char delim;
+       char pvpbuf[];
+{
+       register char *p;
+       register char *q;
+       register int c;
+       char **avp;
+       bool bslashmode;
+       int cmntcnt;
+       int anglecnt;
+       char *tok;
+       int state;
+       int newstate;
+       static char *av[MAXATOM+1];
+       extern int errno;
+
+       /* make sure error messages don't have garbage on them */
+       errno = 0;
+
+       q = pvpbuf;
+       bslashmode = FALSE;
+       cmntcnt = 0;
+       anglecnt = 0;
+       avp = av;
+       state = OPR;
+       c = NOCHAR;
+       p = addr;
+       if (tTd(22, 45))
+       {
+               printf("prescan: ");
+               xputs(p);
+               (void) putchar('\n');
+       }
+
+       do
+       {
+               /* read a token */
+               tok = q;
+               for (;;)
+               {
+                       /* store away any old lookahead character */
+                       if (c != NOCHAR)
+                       {
+                               /* see if there is room */
+                               if (q >= &pvpbuf[PSBUFSIZE - 5])
+                               {
+                                       usrerr("Address too long");
+                                       DelimChar = p;
+                                       return (NULL);
+                               }
+
+                               /* squirrel it away */
+                               *q++ = c;
+                       }
+
+                       /* read a new input character */
+                       c = *p++;
+                       if (c == '\0')
+                               break;
+                       c &= ~0200;
+
+                       if (tTd(22, 101))
+                               printf("c=%c, s=%d; ", c, state);
+
+                       /* chew up special characters */
+                       *q = '\0';
+                       if (bslashmode)
+                       {
+                               /* kludge \! for naive users */
+                               if (c != '!')
+                                       c |= 0200;
+                               bslashmode = FALSE;
+                       }
+                       else if (c == '\\')
+                       {
+                               bslashmode = TRUE;
+                               c = NOCHAR;
+                       }
+                       else if (state == QST)
+                       {
+                               /* do nothing, just avoid next clauses */
+                       }
+                       else if (c == '(')
+                       {
+                               cmntcnt++;
+                               c = NOCHAR;
+                       }
+                       else if (c == ')')
+                       {
+                               if (cmntcnt <= 0)
+                               {
+                                       usrerr("Unbalanced ')'");
+                                       DelimChar = p;
+                                       return (NULL);
+                               }
+                               else
+                                       cmntcnt--;
+                       }
+                       else if (cmntcnt > 0)
+                               c = NOCHAR;
+                       else if (c == '<')
+                               anglecnt++;
+                       else if (c == '>')
+                       {
+                               if (anglecnt <= 0)
+                               {
+                                       usrerr("Unbalanced '>'");
+                                       DelimChar = p;
+                                       return (NULL);
+                               }
+                               anglecnt--;
+                       }
+                       else if (delim == ' ' && isspace(c))
+                               c = ' ';
+
+                       if (c == NOCHAR)
+                               continue;
+
+                       /* see if this is end of input */
+                       if (c == delim && anglecnt <= 0 && state != QST)
+                               break;
+
+                       newstate = StateTab[state][toktype(c)];
+                       if (tTd(22, 101))
+                               printf("ns=%02o\n", newstate);
+                       state = newstate & TYPE;
+                       if (bitset(M, newstate))
+                               c = NOCHAR;
+                       if (bitset(B, newstate))
+                               break;
+               }
+
+               /* new token */
+               if (tok != q)
+               {
+                       *q++ = '\0';
+                       if (tTd(22, 36))
+                       {
+                               printf("tok=");
+                               xputs(tok);
+                               (void) putchar('\n');
+                       }
+                       if (avp >= &av[MAXATOM])
+                       {
+                               syserr("prescan: too many tokens");
+                               DelimChar = p;
+                               return (NULL);
+                       }
+                       *avp++ = tok;
+               }
+       } while (c != '\0' && (c != delim || anglecnt > 0));
+       *avp = NULL;
+       DelimChar = --p;
+       if (cmntcnt > 0)
+               usrerr("Unbalanced '('");
+       else if (anglecnt > 0)
+               usrerr("Unbalanced '<'");
+       else if (state == QST)
+               usrerr("Unbalanced '\"'");
+       else if (av[0] != NULL)
+               return (av);
+       return (NULL);
+}
+\f/*
+**  TOKTYPE -- return token type
+**
+**     Parameters:
+**             c -- the character in question.
+**
+**     Returns:
+**             Its type.
+**
+**     Side Effects:
+**             none.
+*/
+
+toktype(c)
+       register char c;
+{
+       static char buf[50];
+       static bool firstime = TRUE;
+
+       if (firstime)
+       {
+               firstime = FALSE;
+               expand("\001o", buf, &buf[sizeof buf - 1], CurEnv);
+               (void) strcat(buf, DELIMCHARS);
+       }
+       if (c == MATCHCLASS || c == MATCHREPL || c == MATCHNCLASS)
+               return (ONE);
+       if (c == '"')
+               return (QST);
+       if (!isascii(c))
+               return (ATM);
+       if (isspace(c) || c == ')')
+               return (SPC);
+       if (iscntrl(c) || index(buf, c) != NULL)
+               return (OPR);
+       return (ATM);
+}
+\f/*
+**  REWRITE -- apply rewrite rules to token vector.
+**
+**     This routine is an ordered production system.  Each rewrite
+**     rule has a LHS (called the pattern) and a RHS (called the
+**     rewrite); 'rwr' points the the current rewrite rule.
+**
+**     For each rewrite rule, 'avp' points the address vector we
+**     are trying to match against, and 'pvp' points to the pattern.
+**     If pvp points to a special match value (MATCHZANY, MATCHANY,
+**     MATCHONE, MATCHCLASS, MATCHNCLASS) then the address in avp
+**     matched is saved away in the match vector (pointed to by 'mvp').
+**
+**     When a match between avp & pvp does not match, we try to
+**     back out.  If we back up over MATCHONE, MATCHCLASS, or MATCHNCLASS
+**     we must also back out the match in mvp.  If we reach a
+**     MATCHANY or MATCHZANY we just extend the match and start
+**     over again.
+**
+**     When we finally match, we rewrite the address vector
+**     and try over again.
+**
+**     Parameters:
+**             pvp -- pointer to token vector.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             pvp is modified.
+*/
+
+struct match
+{
+       char    **first;        /* first token matched */
+       char    **last;         /* last token matched */
+};
+
+# define MAXMATCH      9       /* max params per rewrite */
+
+
+rewrite(pvp, ruleset)
+       char **pvp;
+       int ruleset;
+{
+       register char *ap;              /* address pointer */
+       register char *rp;              /* rewrite pointer */
+       register char **avp;            /* address vector pointer */
+       register char **rvp;            /* rewrite vector pointer */
+       register struct match *mlp;     /* cur ptr into mlist */
+       register struct rewrite *rwr;   /* pointer to current rewrite rule */
+       struct match mlist[MAXMATCH];   /* stores match on LHS */
+       char *npvp[MAXATOM+1];          /* temporary space for rebuild */
+
+       if (OpMode == MD_TEST || tTd(21, 2))
+       {
+               printf("rewrite: ruleset %2d   input:", ruleset);
+               printav(pvp);
+       }
+       if (pvp == NULL)
+               return;
+
+       /*
+       **  Run through the list of rewrite rules, applying
+       **      any that match.
+       */
+
+       for (rwr = RewriteRules[ruleset]; rwr != NULL; )
+       {
+               if (tTd(21, 12))
+               {
+                       printf("-----trying rule:");
+                       printav(rwr->r_lhs);
+               }
+
+               /* try to match on this rule */
+               mlp = mlist;
+               rvp = rwr->r_lhs;
+               avp = pvp;
+               while ((ap = *avp) != NULL || *rvp != NULL)
+               {
+                       rp = *rvp;
+                       if (tTd(21, 35))
+                       {
+                               printf("ap=");
+                               xputs(ap);
+                               printf(", rp=");
+                               xputs(rp);
+                               printf("\n");
+                       }
+                       if (rp == NULL)
+                       {
+                               /* end-of-pattern before end-of-address */
+                               goto backup;
+                       }
+                       if (ap == NULL && *rp != MATCHZANY)
+                       {
+                               /* end-of-input */
+                               break;
+                       }
+
+                       switch (*rp)
+                       {
+                               register STAB *s;
+
+                         case MATCHCLASS:
+                         case MATCHNCLASS:
+                               /* match any token in (not in) a class */
+                               s = stab(ap, ST_CLASS, ST_FIND);
+                               if (s == NULL || !bitnset(rp[1], s->s_class))
+                               {
+                                       if (*rp == MATCHCLASS)
+                                               goto backup;
+                               }
+                               else if (*rp == MATCHNCLASS)
+                                       goto backup;
+
+                               /* explicit fall-through */
+
+                         case MATCHONE:
+                         case MATCHANY:
+                               /* match exactly one token */
+                               mlp->first = avp;
+                               mlp->last = avp++;
+                               mlp++;
+                               break;
+
+                         case MATCHZANY:
+                               /* match zero or more tokens */
+                               mlp->first = avp;
+                               mlp->last = avp - 1;
+                               mlp++;
+                               break;
+
+                         default:
+                               /* must have exact match */
+                               if (strcasecmp(rp, ap))
+                                       goto backup;
+                               avp++;
+                               break;
+                       }
+
+                       /* successful match on this token */
+                       rvp++;
+                       continue;
+
+                 backup:
+                       /* match failed -- back up */
+                       while (--rvp >= rwr->r_lhs)
+                       {
+                               rp = *rvp;
+                               if (*rp == MATCHANY || *rp == MATCHZANY)
+                               {
+                                       /* extend binding and continue */
+                                       avp = ++mlp[-1].last;
+                                       avp++;
+                                       rvp++;
+                                       break;
+                               }
+                               avp--;
+                               if (*rp == MATCHONE || *rp == MATCHCLASS ||
+                                   *rp == MATCHNCLASS)
+                               {
+                                       /* back out binding */
+                                       mlp--;
+                               }
+                       }
+
+                       if (rvp < rwr->r_lhs)
+                       {
+                               /* total failure to match */
+                               break;
+                       }
+               }
+
+               /*
+               **  See if we successfully matched
+               */
+
+               if (rvp < rwr->r_lhs || *rvp != NULL)
+               {
+                       if (tTd(21, 10))
+                               printf("----- rule fails\n");
+                       rwr = rwr->r_next;
+                       continue;
+               }
+
+               rvp = rwr->r_rhs;
+               if (tTd(21, 12))
+               {
+                       printf("-----rule matches:");
+                       printav(rvp);
+               }
+
+               rp = *rvp;
+               if (*rp == CANONUSER)
+               {
+                       rvp++;
+                       rwr = rwr->r_next;
+               }
+               else if (*rp == CANONHOST)
+               {
+                       rvp++;
+                       rwr = NULL;
+               }
+               else if (*rp == CANONNET)
+                       rwr = NULL;
+
+               /* substitute */
+               for (avp = npvp; *rvp != NULL; rvp++)
+               {
+                       register struct match *m;
+                       register char **pp;
+
+                       rp = *rvp;
+                       if (*rp == MATCHREPL)
+                       {
+                               /* substitute from LHS */
+                               m = &mlist[rp[1] - '1'];
+                               if (m >= mlp)
+                               {
+                                       syserr("rewrite: ruleset %d: replacement out of bounds", ruleset);
+                                       return;
+                               }
+                               if (tTd(21, 15))
+                               {
+                                       printf("$%c:", rp[1]);
+                                       pp = m->first;
+                                       while (pp <= m->last)
+                                       {
+                                               printf(" %x=\"", *pp);
+                                               (void) fflush(stdout);
+                                               printf("%s\"", *pp++);
+                                       }
+                                       printf("\n");
+                               }
+                               pp = m->first;
+                               while (pp <= m->last)
+                               {
+                                       if (avp >= &npvp[MAXATOM])
+                                       {
+                                               syserr("rewrite: expansion too long");
+                                               return;
+                                       }
+                                       *avp++ = *pp++;
+                               }
+                       }
+                       else
+                       {
+                               /* vanilla replacement */
+                               if (avp >= &npvp[MAXATOM])
+                               {
+       toolong:
+                                       syserr("rewrite: expansion too long");
+                                       return;
+                               }
+                               *avp++ = rp;
+                       }
+               }
+               *avp++ = NULL;
+
+               /*
+               **  Check for any hostname lookups.
+               */
+
+               for (rvp = npvp; *rvp != NULL; rvp++)
+               {
+                       char **hbrvp;
+                       char **xpvp;
+                       int trsize;
+                       char *olddelimchar;
+                       char buf[MAXNAME + 1];
+                       char *pvpb1[MAXATOM + 1];
+                       char pvpbuf[PSBUFSIZE];
+                       extern char *DelimChar;
+
+                       if (**rvp != HOSTBEGIN)
+                               continue;
+
+                       /*
+                       **  Got a hostname lookup.
+                       **
+                       **      This could be optimized fairly easily.
+                       */
+
+                       hbrvp = rvp;
+
+                       /* extract the match part */
+                       while (*++rvp != NULL && **rvp != HOSTEND)
+                               continue;
+                       if (*rvp != NULL)
+                               *rvp++ = NULL;
+
+                       /* save the remainder of the input string */
+                       trsize = (int) (avp - rvp + 1) * sizeof *rvp;
+                       bcopy((char *) rvp, (char *) pvpb1, trsize);
+
+                       /* look it up */
+                       cataddr(++hbrvp, buf, sizeof buf);
+                       maphostname(buf, sizeof buf);
+
+                       /* scan the new host name */
+                       olddelimchar = DelimChar;
+                       xpvp = prescan(buf, '\0', pvpbuf);
+                       DelimChar = olddelimchar;
+                       if (xpvp == NULL)
+                       {
+                               syserr("rewrite: cannot prescan canonical hostname: %s", buf);
+                               return;
+                       }
+
+                       /* append it to the token list */
+                       for (avp = --hbrvp; *xpvp != NULL; xpvp++)
+                       {
+                               *avp++ = newstr(*xpvp);
+                               if (avp >= &npvp[MAXATOM])
+                                       goto toolong;
+                       }
+
+                       /* restore the old trailing information */
+                       for (xpvp = pvpb1; (*avp++ = *xpvp++) != NULL; )
+                               if (avp >= &npvp[MAXATOM])
+                                       goto toolong;
+
+                       break;
+               }
+
+               /*
+               **  Check for subroutine calls.
+               */
+
+               if (*npvp != NULL && **npvp == CALLSUBR)
+               {
+                       bcopy((char *) &npvp[2], (char *) pvp,
+                               (int) (avp - npvp - 2) * sizeof *avp);
+                       if (tTd(21, 3))
+                               printf("-----callsubr %s\n", npvp[1]);
+                       rewrite(pvp, atoi(npvp[1]));
+               }
+               else
+               {
+                       bcopy((char *) npvp, (char *) pvp,
+                               (int) (avp - npvp) * sizeof *avp);
+               }
+               if (tTd(21, 4))
+               {
+                       printf("rewritten as:");
+                       printav(pvp);
+               }
+       }
+
+       if (OpMode == MD_TEST || tTd(21, 2))
+       {
+               printf("rewrite: ruleset %2d returns:", ruleset);
+               printav(pvp);
+       }
+}
+\f/*
+**  BUILDADDR -- build address from token vector.
+**
+**     Parameters:
+**             tv -- token vector.
+**             a -- pointer to address descriptor to fill.
+**                     If NULL, one will be allocated.
+**
+**     Returns:
+**             NULL if there was an error.
+**             'a' otherwise.
+**
+**     Side Effects:
+**             fills in 'a'
+*/
+
+ADDRESS *
+buildaddr(tv, a)
+       register char **tv;
+       register ADDRESS *a;
+{
+       static char buf[MAXNAME];
+       struct mailer **mp;
+       register struct mailer *m;
+
+       if (a == NULL)
+               a = (ADDRESS *) xalloc(sizeof *a);
+       bzero((char *) a, sizeof *a);
+
+       /* figure out what net/mailer to use */
+       if (**tv != CANONNET)
+       {
+               syserr("buildaddr: no net");
+               return (NULL);
+       }
+       tv++;
+       if (!strcasecmp(*tv, "error"))
+       {
+               if (**++tv == CANONHOST)
+               {
+                       setstat(atoi(*++tv));
+                       tv++;
+               }
+               if (**tv != CANONUSER)
+                       syserr("buildaddr: error: no user");
+               buf[0] = '\0';
+               while (*++tv != NULL)
+               {
+                       if (buf[0] != '\0')
+                               (void) strcat(buf, " ");
+                       (void) strcat(buf, *tv);
+               }
+               usrerr(buf);
+               return (NULL);
+       }
+       for (mp = Mailer; (m = *mp++) != NULL; )
+       {
+               if (!strcasecmp(m->m_name, *tv))
+                       break;
+       }
+       if (m == NULL)
+       {
+               syserr("buildaddr: unknown mailer %s", *tv);
+               return (NULL);
+       }
+       a->q_mailer = m;
+
+       /* figure out what host (if any) */
+       tv++;
+       if (!bitnset(M_LOCAL, m->m_flags))
+       {
+               if (**tv++ != CANONHOST)
+               {
+                       syserr("buildaddr: no host");
+                       return (NULL);
+               }
+               buf[0] = '\0';
+               while (*tv != NULL && **tv != CANONUSER)
+                       (void) strcat(buf, *tv++);
+               a->q_host = newstr(buf);
+       }
+       else
+               a->q_host = NULL;
+
+       /* figure out the user */
+       if (*tv == NULL || **tv != CANONUSER)
+       {
+               syserr("buildaddr: no user");
+               return (NULL);
+       }
+
+       /* rewrite according recipient mailer rewriting rules */
+       rewrite(++tv, 2);
+       if (m->m_r_rwset > 0)
+               rewrite(tv, m->m_r_rwset);
+       rewrite(tv, 4);
+
+       /* save the result for the command line/RCPT argument */
+       cataddr(tv, buf, sizeof buf);
+       a->q_user = buf;
+
+       return (a);
+}
+\f/*
+**  CATADDR -- concatenate pieces of addresses (putting in <LWSP> subs)
+**
+**     Parameters:
+**             pvp -- parameter vector to rebuild.
+**             buf -- buffer to build the string into.
+**             sz -- size of buf.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Destroys buf.
+*/
+
+cataddr(pvp, buf, sz)
+       char **pvp;
+       char *buf;
+       register int sz;
+{
+       bool oatomtok = FALSE;
+       bool natomtok = FALSE;
+       register int i;
+       register char *p;
+
+       if (pvp == NULL)
+       {
+               (void) strcpy(buf, "");
+               return;
+       }
+       p = buf;
+       sz -= 2;
+       while (*pvp != NULL && (i = strlen(*pvp)) < sz)
+       {
+               natomtok = (toktype(**pvp) == ATM);
+               if (oatomtok && natomtok)
+                       *p++ = SpaceSub;
+               (void) strcpy(p, *pvp);
+               oatomtok = natomtok;
+               p += i;
+               sz -= i + 1;
+               pvp++;
+       }
+       *p = '\0';
+}
+\f/*
+**  SAMEADDR -- Determine if two addresses are the same
+**
+**     This is not just a straight comparison -- if the mailer doesn't
+**     care about the host we just ignore it, etc.
+**
+**     Parameters:
+**             a, b -- pointers to the internal forms to compare.
+**
+**     Returns:
+**             TRUE -- they represent the same mailbox.
+**             FALSE -- they don't.
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+sameaddr(a, b)
+       register ADDRESS *a;
+       register ADDRESS *b;
+{
+       /* if they don't have the same mailer, forget it */
+       if (a->q_mailer != b->q_mailer)
+               return (FALSE);
+
+       /* if the user isn't the same, we can drop out */
+       if (strcmp(a->q_user, b->q_user) != 0)
+               return (FALSE);
+
+       /* if the mailer ignores hosts, we have succeeded! */
+       if (bitnset(M_LOCAL, a->q_mailer->m_flags))
+               return (TRUE);
+
+       /* otherwise compare hosts (but be careful for NULL ptrs) */
+       if (a->q_host == NULL || b->q_host == NULL)
+               return (FALSE);
+       if (strcmp(a->q_host, b->q_host) != 0)
+               return (FALSE);
+
+       return (TRUE);
+}
+\f/*
+**  PRINTADDR -- print address (for debugging)
+**
+**     Parameters:
+**             a -- the address to print
+**             follow -- follow the q_next chain.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             none.
+*/
+
+printaddr(a, follow)
+       register ADDRESS *a;
+       bool follow;
+{
+       bool first = TRUE;
+
+       while (a != NULL)
+       {
+               first = FALSE;
+               printf("%x=", a);
+               (void) fflush(stdout);
+               printf("%s: mailer %d (%s), host `%s', user `%s', ruser `%s'\n",
+                      a->q_paddr, a->q_mailer->m_mno, a->q_mailer->m_name,
+                      a->q_host, a->q_user, a->q_ruser? a->q_ruser: "<null>");
+               printf("\tnext=%x, flags=%o, alias %x\n", a->q_next, a->q_flags,
+                      a->q_alias);
+               printf("\thome=\"%s\", fullname=\"%s\"\n", a->q_home,
+                      a->q_fullname);
+
+               if (!follow)
+                       return;
+               a = a->q_next;
+       }
+       if (first)
+               printf("[NULL]\n");
+}
+
+\f/*
+**  REMOTENAME -- return the name relative to the current mailer
+**
+**     Parameters:
+**             name -- the name to translate.
+**             m -- the mailer that we want to do rewriting relative
+**                     to.
+**             senderaddress -- if set, uses the sender rewriting rules
+**                     rather than the recipient rewriting rules.
+**             canonical -- if set, strip out any comment information,
+**                     etc.
+**
+**     Returns:
+**             the text string representing this address relative to
+**                     the receiving mailer.
+**
+**     Side Effects:
+**             none.
+**
+**     Warnings:
+**             The text string returned is tucked away locally;
+**                     copy it if you intend to save it.
+*/
+
+char *
+remotename(name, m, senderaddress, canonical)
+       char *name;
+       struct mailer *m;
+       bool senderaddress;
+       bool canonical;
+{
+       register char **pvp;
+       char *fancy;
+       extern char *macvalue();
+       char *oldg = macvalue('g', CurEnv);
+       static char buf[MAXNAME];
+       char lbuf[MAXNAME];
+       char pvpbuf[PSBUFSIZE];
+       extern char **prescan();
+       extern char *crackaddr();
+
+       if (tTd(12, 1))
+               printf("remotename(%s)\n", name);
+
+       /* don't do anything if we are tagging it as special */
+       if ((senderaddress ? m->m_s_rwset : m->m_r_rwset) < 0)
+               return (name);
+
+       /*
+       **  Do a heuristic crack of this name to extract any comment info.
+       **      This will leave the name as a comment and a $g macro.
+       */
+
+       if (canonical)
+               fancy = "\001g";
+       else
+               fancy = crackaddr(name);
+
+       /*
+       **  Turn the name into canonical form.
+       **      Normally this will be RFC 822 style, i.e., "user@domain".
+       **      If this only resolves to "user", and the "C" flag is
+       **      specified in the sending mailer, then the sender's
+       **      domain will be appended.
+       */
+
+       pvp = prescan(name, '\0', pvpbuf);
+       if (pvp == NULL)
+               return (name);
+       rewrite(pvp, 3);
+       if (CurEnv->e_fromdomain != NULL)
+       {
+               /* append from domain to this address */
+               register char **pxp = pvp;
+
+               /* see if there is an "@domain" in the current name */
+               while (*pxp != NULL && strcmp(*pxp, "@") != 0)
+                       pxp++;
+               if (*pxp == NULL)
+               {
+                       /* no.... append the "@domain" from the sender */
+                       register char **qxq = CurEnv->e_fromdomain;
+
+                       while ((*pxp++ = *qxq++) != NULL)
+                               continue;
+                       rewrite(pvp, 3);
+               }
+       }
+
+       /*
+       **  Do more specific rewriting.
+       **      Rewrite using ruleset 1 or 2 depending on whether this is
+       **              a sender address or not.
+       **      Then run it through any receiving-mailer-specific rulesets.
+       */
+
+       if (senderaddress)
+       {
+               rewrite(pvp, 1);
+               if (m->m_s_rwset > 0)
+                       rewrite(pvp, m->m_s_rwset);
+       }
+       else
+       {
+               rewrite(pvp, 2);
+               if (m->m_r_rwset > 0)
+                       rewrite(pvp, m->m_r_rwset);
+       }
+
+       /*
+       **  Do any final sanitation the address may require.
+       **      This will normally be used to turn internal forms
+       **      (e.g., user@host.LOCAL) into external form.  This
+       **      may be used as a default to the above rules.
+       */
+
+       rewrite(pvp, 4);
+
+       /*
+       **  Now restore the comment information we had at the beginning.
+       */
+
+       cataddr(pvp, lbuf, sizeof lbuf);
+       define('g', lbuf, CurEnv);
+       expand(fancy, buf, &buf[sizeof buf - 1], CurEnv);
+       define('g', oldg, CurEnv);
+
+       if (tTd(12, 1))
+               printf("remotename => `%s'\n", buf);
+       return (buf);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/pathnames.h b/usr/src/usr.sbin/sendmail/src/pathnames.h
new file mode 100644 (file)
index 0000000..588a648
--- /dev/null
@@ -0,0 +1,37 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)pathnames.h 5.1 (Berkeley) 4/19/90
+ */
+
+#define        _PATH_SENDMAILCF        "/etc/sendmail.cf";
+#define        _PATH_SENDMAILFC        "/etc/sendmail.fc";
diff --git a/usr/src/usr.sbin/sendmail/src/queue.c b/usr/src/usr.sbin/sendmail/src/queue.c
new file mode 100644 (file)
index 0000000..03b226a
--- /dev/null
@@ -0,0 +1,1235 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+# include "sendmail.h"
+
+#ifndef lint
+#ifdef QUEUE
+static char sccsid[] = "@(#)queue.c    5.32 (Berkeley) 3/12/91 (with queueing)";
+#else
+static char sccsid[] = "@(#)queue.c    5.32 (Berkeley) 3/12/91 (without queueing)";
+#endif
+#endif /* not lint */
+
+# include <sys/stat.h>
+# include <sys/dir.h>
+# include <sys/file.h>
+# include <signal.h>
+# include <errno.h>
+# include <pwd.h>
+
+# ifdef QUEUE
+
+/*
+**  Work queue.
+*/
+
+struct work
+{
+       char            *w_name;        /* name of control file */
+       long            w_pri;          /* priority of message, see below */
+       time_t          w_ctime;        /* creation time of message */
+       struct work     *w_next;        /* next in queue */
+};
+
+typedef struct work    WORK;
+extern int la;
+
+WORK   *WorkQ;                 /* queue of things to be done */
+\f/*
+**  QUEUEUP -- queue a message up for future transmission.
+**
+**     Parameters:
+**             e -- the envelope to queue up.
+**             queueall -- if TRUE, queue all addresses, rather than
+**                     just those with the QQUEUEUP flag set.
+**             announce -- if TRUE, tell when you are queueing up.
+**
+**     Returns:
+**             locked FILE* to q file
+**
+**     Side Effects:
+**             The current request are saved in a control file.
+*/
+
+FILE *
+queueup(e, queueall, announce)
+       register ENVELOPE *e;
+       bool queueall;
+       bool announce;
+{
+       char *qf;
+       char buf[MAXLINE], tf[MAXLINE];
+       register FILE *tfp;
+       register HDR *h;
+       register ADDRESS *q;
+       MAILER nullmailer;
+       int fd, ret;
+
+       /*
+       **  Create control file.
+       */
+
+       do {
+               strcpy(tf, queuename(e, 't'));
+               fd = open(tf, O_CREAT|O_WRONLY|O_EXCL, FileMode);
+               if (fd < 0) {
+                       if ( errno != EEXIST) {
+                               syserr("queueup: cannot create temp file %s",
+                                       tf);
+                               return NULL;
+                       }
+               } else {
+                       if (flock(fd, LOCK_EX|LOCK_NB) < 0) {
+                               if (errno != EWOULDBLOCK)
+                                       syserr("cannot flock(%s)", tf);
+                               close(fd);
+                               fd = -1;
+                       }
+               }
+       } while (fd < 0);
+
+       tfp = fdopen(fd, "w");
+
+       if (tTd(40, 1))
+               printf("queueing %s\n", e->e_id);
+
+       /*
+       **  If there is no data file yet, create one.
+       */
+
+       if (e->e_df == NULL)
+       {
+               register FILE *dfp;
+               extern putbody();
+
+               e->e_df = newstr(queuename(e, 'd'));
+               fd = open(e->e_df, O_WRONLY|O_CREAT, FileMode);
+               if (fd < 0)
+               {
+                       syserr("queueup: cannot create %s", e->e_df);
+                       (void) fclose(tfp);
+                       return NULL;
+               }
+               dfp = fdopen(fd, "w");
+               (*e->e_putbody)(dfp, ProgMailer, e);
+               (void) fclose(dfp);
+               e->e_putbody = putbody;
+       }
+
+       /*
+       **  Output future work requests.
+       **      Priority and creation time should be first, since
+       **      they are required by orderq.
+       */
+
+       /* output message priority */
+       fprintf(tfp, "P%ld\n", e->e_msgpriority);
+
+       /* output creation time */
+       fprintf(tfp, "T%ld\n", e->e_ctime);
+
+       /* output name of data file */
+       fprintf(tfp, "D%s\n", e->e_df);
+
+       /* message from envelope, if it exists */
+       if (e->e_message != NULL)
+               fprintf(tfp, "M%s\n", e->e_message);
+
+       /* output name of sender */
+       fprintf(tfp, "S%s\n", e->e_from.q_paddr);
+
+       /* output list of recipient addresses */
+       for (q = e->e_sendqueue; q != NULL; q = q->q_next)
+       {
+               if (queueall ? !bitset(QDONTSEND|QSENT, q->q_flags) :
+                              bitset(QQUEUEUP, q->q_flags))
+               {
+                       char *ctluser, *getctluser();
+
+                       if ((ctluser = getctluser(q)) != NULL)
+                               fprintf(tfp, "C%s\n", ctluser);
+                       fprintf(tfp, "R%s\n", q->q_paddr);
+                       if (announce)
+                       {
+                               e->e_to = q->q_paddr;
+                               message(Arpa_Info, "queued");
+                               if (LogLevel > 4)
+                                       logdelivery("queued");
+                               e->e_to = NULL;
+                       }
+                       if (tTd(40, 1))
+                       {
+                               printf("queueing ");
+                               printaddr(q, FALSE);
+                       }
+               }
+       }
+
+       /* output list of error recipients */
+       for (q = e->e_errorqueue; q != NULL; q = q->q_next)
+       {
+               if (!bitset(QDONTSEND, q->q_flags))
+               {
+                       char *ctluser, *getctluser();
+
+                       if ((ctluser = getctluser(q)) != NULL)
+                               fprintf(tfp, "C%s\n", ctluser);
+                       fprintf(tfp, "E%s\n", q->q_paddr);
+               }
+       }
+
+       /*
+       **  Output headers for this message.
+       **      Expand macros completely here.  Queue run will deal with
+       **      everything as absolute headers.
+       **              All headers that must be relative to the recipient
+       **              can be cracked later.
+       **      We set up a "null mailer" -- i.e., a mailer that will have
+       **      no effect on the addresses as they are output.
+       */
+
+       bzero((char *) &nullmailer, sizeof nullmailer);
+       nullmailer.m_r_rwset = nullmailer.m_s_rwset = -1;
+       nullmailer.m_eol = "\n";
+
+       define('g', "\001f", e);
+       for (h = e->e_header; h != NULL; h = h->h_link)
+       {
+               extern bool bitzerop();
+
+               /* don't output null headers */
+               if (h->h_value == NULL || h->h_value[0] == '\0')
+                       continue;
+
+               /* don't output resent headers on non-resent messages */
+               if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
+                       continue;
+
+               /* output this header */
+               fprintf(tfp, "H");
+
+               /* if conditional, output the set of conditions */
+               if (!bitzerop(h->h_mflags) && bitset(H_CHECK|H_ACHECK, h->h_flags))
+               {
+                       int j;
+
+                       (void) putc('?', tfp);
+                       for (j = '\0'; j <= '\177'; j++)
+                               if (bitnset(j, h->h_mflags))
+                                       (void) putc(j, tfp);
+                       (void) putc('?', tfp);
+               }
+
+               /* output the header: expand macros, convert addresses */
+               if (bitset(H_DEFAULT, h->h_flags))
+               {
+                       (void) expand(h->h_value, buf, &buf[sizeof buf], e);
+                       fprintf(tfp, "%s: %s\n", h->h_field, buf);
+               }
+               else if (bitset(H_FROM|H_RCPT, h->h_flags))
+               {
+                       commaize(h, h->h_value, tfp, bitset(EF_OLDSTYLE, e->e_flags),
+                                &nullmailer);
+               }
+               else
+                       fprintf(tfp, "%s: %s\n", h->h_field, h->h_value);
+       }
+
+       /*
+       **  Clean up.
+       */
+
+       qf = queuename(e, 'q');
+       if (rename(tf, qf) < 0)
+               syserr("cannot rename(%s, %s), df=%s", tf, qf, e->e_df);
+       errno = 0;
+
+# ifdef LOG
+       /* save log info */
+       if (LogLevel > 15)
+               syslog(LOG_DEBUG, "%s: queueup, qf=%s, df=%s\n", e->e_id, qf, e->e_df);
+# endif LOG
+       fflush(tfp);
+       return tfp;
+}
+\f/*
+**  RUNQUEUE -- run the jobs in the queue.
+**
+**     Gets the stuff out of the queue in some presumably logical
+**     order and processes them.
+**
+**     Parameters:
+**             forkflag -- TRUE if the queue scanning should be done in
+**                     a child process.  We double-fork so it is not our
+**                     child and we don't have to clean up after it.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             runs things in the mail queue.
+*/
+
+runqueue(forkflag)
+       bool forkflag;
+{
+       extern bool shouldqueue();
+
+       /*
+       **  If no work will ever be selected, don't even bother reading
+       **  the queue.
+       */
+
+       la = getla();   /* get load average */
+
+       if (shouldqueue(-100000000L))
+       {
+               if (Verbose)
+                       printf("Skipping queue run -- load average too high\n");
+
+               if (forkflag)
+                       return;
+               finis();
+       }
+
+       /*
+       **  See if we want to go off and do other useful work.
+       */
+
+       if (forkflag)
+       {
+               int pid;
+
+               pid = dofork();
+               if (pid != 0)
+               {
+                       extern void reapchild();
+
+                       /* parent -- pick up intermediate zombie */
+#ifndef SIGCHLD
+                       (void) waitfor(pid);
+#else SIGCHLD
+                       (void) signal(SIGCHLD, reapchild);
+#endif SIGCHLD
+                       if (QueueIntvl != 0)
+                               (void) setevent(QueueIntvl, runqueue, TRUE);
+                       return;
+               }
+               /* child -- double fork */
+#ifndef SIGCHLD
+               if (fork() != 0)
+                       exit(EX_OK);
+#else SIGCHLD
+               (void) signal(SIGCHLD, SIG_DFL);
+#endif SIGCHLD
+       }
+
+       setproctitle("running queue: %s", QueueDir);
+
+# ifdef LOG
+       if (LogLevel > 11)
+               syslog(LOG_DEBUG, "runqueue %s, pid=%d", QueueDir, getpid());
+# endif LOG
+
+       /*
+       **  Release any resources used by the daemon code.
+       */
+
+# ifdef DAEMON
+       clrdaemon();
+# endif DAEMON
+
+       /*
+       **  Make sure the alias database is open.
+       */
+
+       initaliases(AliasFile, FALSE);
+
+       /*
+       **  Start making passes through the queue.
+       **      First, read and sort the entire queue.
+       **      Then, process the work in that order.
+       **              But if you take too long, start over.
+       */
+
+       /* order the existing work requests */
+       (void) orderq(FALSE);
+
+       /* process them once at a time */
+       while (WorkQ != NULL)
+       {
+               WORK *w = WorkQ;
+
+               WorkQ = WorkQ->w_next;
+               dowork(w);
+               free(w->w_name);
+               free((char *) w);
+       }
+
+       /* exit without the usual cleanup */
+       exit(ExitStat);
+}
+\f/*
+**  ORDERQ -- order the work queue.
+**
+**     Parameters:
+**             doall -- if set, include everything in the queue (even
+**                     the jobs that cannot be run because the load
+**                     average is too high).  Otherwise, exclude those
+**                     jobs.
+**
+**     Returns:
+**             The number of request in the queue (not necessarily
+**             the number of requests in WorkQ however).
+**
+**     Side Effects:
+**             Sets WorkQ to the queue of available work, in order.
+*/
+
+# define NEED_P                001
+# define NEED_T                002
+
+orderq(doall)
+       bool doall;
+{
+       register struct direct *d;
+       register WORK *w;
+       DIR *f;
+       register int i;
+       WORK wlist[QUEUESIZE+1];
+       int wn = -1;
+       extern workcmpf();
+
+       /* clear out old WorkQ */
+       for (w = WorkQ; w != NULL; )
+       {
+               register WORK *nw = w->w_next;
+
+               WorkQ = nw;
+               free(w->w_name);
+               free((char *) w);
+               w = nw;
+       }
+
+       /* open the queue directory */
+       f = opendir(".");
+       if (f == NULL)
+       {
+               syserr("orderq: cannot open \"%s\" as \".\"", QueueDir);
+               return (0);
+       }
+
+       /*
+       **  Read the work directory.
+       */
+
+       while ((d = readdir(f)) != NULL)
+       {
+               FILE *cf;
+               char lbuf[MAXNAME];
+
+               /* is this an interesting entry? */
+               if (d->d_name[0] != 'q' || d->d_name[1] != 'f')
+                       continue;
+
+               /* yes -- open control file (if not too many files) */
+               if (++wn >= QUEUESIZE)
+                       continue;
+               cf = fopen(d->d_name, "r");
+               if (cf == NULL)
+               {
+                       /* this may be some random person sending hir msgs */
+                       /* syserr("orderq: cannot open %s", cbuf); */
+                       if (tTd(41, 2))
+                               printf("orderq: cannot open %s (%d)\n",
+                                       d->d_name, errno);
+                       errno = 0;
+                       wn--;
+                       continue;
+               }
+               w = &wlist[wn];
+               w->w_name = newstr(d->d_name);
+
+               /* make sure jobs in creation don't clog queue */
+               w->w_pri = 0x7fffffff;
+               w->w_ctime = 0;
+
+               /* extract useful information */
+               i = NEED_P | NEED_T;
+               while (i != 0 && fgets(lbuf, sizeof lbuf, cf) != NULL)
+               {
+                       extern long atol();
+
+                       switch (lbuf[0])
+                       {
+                         case 'P':
+                               w->w_pri = atol(&lbuf[1]);
+                               i &= ~NEED_P;
+                               break;
+
+                         case 'T':
+                               w->w_ctime = atol(&lbuf[1]);
+                               i &= ~NEED_T;
+                               break;
+                       }
+               }
+               (void) fclose(cf);
+
+               if (!doall && shouldqueue(w->w_pri))
+               {
+                       /* don't even bother sorting this job in */
+                       wn--;
+               }
+       }
+       (void) closedir(f);
+       wn++;
+
+       /*
+       **  Sort the work directory.
+       */
+
+       qsort((char *) wlist, min(wn, QUEUESIZE), sizeof *wlist, workcmpf);
+
+       /*
+       **  Convert the work list into canonical form.
+       **      Should be turning it into a list of envelopes here perhaps.
+       */
+
+       WorkQ = NULL;
+       for (i = min(wn, QUEUESIZE); --i >= 0; )
+       {
+               w = (WORK *) xalloc(sizeof *w);
+               w->w_name = wlist[i].w_name;
+               w->w_pri = wlist[i].w_pri;
+               w->w_ctime = wlist[i].w_ctime;
+               w->w_next = WorkQ;
+               WorkQ = w;
+       }
+
+       if (tTd(40, 1))
+       {
+               for (w = WorkQ; w != NULL; w = w->w_next)
+                       printf("%32s: pri=%ld\n", w->w_name, w->w_pri);
+       }
+
+       return (wn);
+}
+\f/*
+**  WORKCMPF -- compare function for ordering work.
+**
+**     Parameters:
+**             a -- the first argument.
+**             b -- the second argument.
+**
+**     Returns:
+**             -1 if a < b
+**              0 if a == b
+**             +1 if a > b
+**
+**     Side Effects:
+**             none.
+*/
+
+workcmpf(a, b)
+       register WORK *a;
+       register WORK *b;
+{
+       long pa = a->w_pri + a->w_ctime;
+       long pb = b->w_pri + b->w_ctime;
+
+       if (pa == pb)
+               return (0);
+       else if (pa > pb)
+               return (1);
+       else
+               return (-1);
+}
+\f/*
+**  DOWORK -- do a work request.
+**
+**     Parameters:
+**             w -- the work request to be satisfied.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             The work request is satisfied if possible.
+*/
+
+dowork(w)
+       register WORK *w;
+{
+       register int i;
+       extern bool shouldqueue();
+
+       if (tTd(40, 1))
+               printf("dowork: %s pri %ld\n", w->w_name, w->w_pri);
+
+       /*
+       **  Ignore jobs that are too expensive for the moment.
+       */
+
+       if (shouldqueue(w->w_pri))
+       {
+               if (Verbose)
+                       printf("\nSkipping %s\n", w->w_name + 2);
+               return;
+       }
+
+       /*
+       **  Fork for work.
+       */
+
+       if (ForkQueueRuns)
+       {
+               i = fork();
+               if (i < 0)
+               {
+                       syserr("dowork: cannot fork");
+                       return;
+               }
+       }
+       else
+       {
+               i = 0;
+       }
+
+       if (i == 0)
+       {
+               FILE *qflock, *readqf();
+               /*
+               **  CHILD
+               **      Lock the control file to avoid duplicate deliveries.
+               **              Then run the file as though we had just read it.
+               **      We save an idea of the temporary name so we
+               **              can recover on interrupt.
+               */
+
+               /* set basic modes, etc. */
+               (void) alarm(0);
+               clearenvelope(CurEnv, FALSE);
+               QueueRun = TRUE;
+               ErrorMode = EM_MAIL;
+               CurEnv->e_id = &w->w_name[2];
+# ifdef LOG
+               if (LogLevel > 11)
+                       syslog(LOG_DEBUG, "%s: dowork, pid=%d", CurEnv->e_id,
+                              getpid());
+# endif LOG
+
+               /* don't use the headers from sendmail.cf... */
+               CurEnv->e_header = NULL;
+
+               /* read the queue control file */
+               /*  and lock the control file during processing */
+               if ((qflock=readqf(CurEnv, TRUE)) == NULL)
+               {
+                       if (ForkQueueRuns)
+                               exit(EX_OK);
+                       else
+                               return;
+               }
+
+               CurEnv->e_flags |= EF_INQUEUE;
+               eatheader(CurEnv);
+
+               /* do the delivery */
+               if (!bitset(EF_FATALERRS, CurEnv->e_flags))
+                       sendall(CurEnv, SM_DELIVER);
+
+               /* finish up and exit */
+               if (ForkQueueRuns)
+                       finis();
+               else
+                       dropenvelope(CurEnv);
+               fclose(qflock);
+       }
+       else
+       {
+               /*
+               **  Parent -- pick up results.
+               */
+
+               errno = 0;
+               (void) waitfor(i);
+       }
+}
+\f/*
+**  READQF -- read queue file and set up environment.
+**
+**     Parameters:
+**             e -- the envelope of the job to run.
+**             full -- if set, read in all information.  Otherwise just
+**                     read in info needed for a queue print.
+**
+**     Returns:
+**             FILE * pointing to flock()ed fd so it can be closed
+**             after the mail is delivered
+**
+**     Side Effects:
+**             cf is read and created as the current job, as though
+**             we had been invoked by argument.
+*/
+
+FILE *
+readqf(e, full)
+       register ENVELOPE *e;
+       bool full;
+{
+       char *qf;
+       register FILE *qfp;
+       char buf[MAXFIELD];
+       extern char *fgetfolded();
+       extern long atol();
+       int gotctluser = 0;
+       int fd;
+
+       /*
+       **  Read and process the file.
+       */
+
+       qf = queuename(e, 'q');
+       qfp = fopen(qf, "r");
+       if (qfp == NULL)
+       {
+               if (errno != ENOENT)
+                       syserr("readqf: no control file %s", qf);
+               return NULL;
+       }
+
+       if (flock(fileno(qfp), LOCK_EX|LOCK_NB) < 0)
+       {
+# ifdef LOG
+               /* being processed by another queuer */
+               if (Verbose)
+                       printf("%s: locked\n", CurEnv->e_id);
+# endif LOG
+               (void) fclose(qfp);
+               return NULL;
+       }
+
+       /* do basic system initialization */
+       initsys();
+
+       FileName = qf;
+       LineNumber = 0;
+       if (Verbose && full)
+               printf("\nRunning %s\n", e->e_id);
+       while (fgetfolded(buf, sizeof buf, qfp) != NULL)
+       {
+               if (tTd(40, 4))
+                       printf("+++++ %s\n", buf);
+               switch (buf[0])
+               {
+                 case 'C':             /* specify controlling user */
+                       setctluser(&buf[1]);
+                       gotctluser = 1;
+                       break;
+
+                 case 'R':             /* specify recipient */
+                       sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_sendqueue);
+                       break;
+
+                 case 'E':             /* specify error recipient */
+                       sendtolist(&buf[1], (ADDRESS *) NULL, &e->e_errorqueue);
+                       break;
+
+                 case 'H':             /* header */
+                       if (full)
+                               (void) chompheader(&buf[1], FALSE);
+                       break;
+
+                 case 'M':             /* message */
+                       e->e_message = newstr(&buf[1]);
+                       break;
+
+                 case 'S':             /* sender */
+                       setsender(newstr(&buf[1]));
+                       break;
+
+                 case 'D':             /* data file name */
+                       if (!full)
+                               break;
+                       e->e_df = newstr(&buf[1]);
+                       e->e_dfp = fopen(e->e_df, "r");
+                       if (e->e_dfp == NULL)
+                               syserr("readqf: cannot open %s", e->e_df);
+                       break;
+
+                 case 'T':             /* init time */
+                       e->e_ctime = atol(&buf[1]);
+                       break;
+
+                 case 'P':             /* message priority */
+                       e->e_msgpriority = atol(&buf[1]) + WkTimeFact;
+                       break;
+
+                 case '\0':            /* blank line; ignore */
+                       break;
+
+                 default:
+                       syserr("readqf(%s:%d): bad line \"%s\"", e->e_id,
+                               LineNumber, buf);
+                       break;
+               }
+               /*
+               **  The `C' queue file command operates on the next line,
+               **  so we use "gotctluser" to maintain state as follows:
+               **      0 - no controlling user,
+               **      1 - controlling user has been set but not used,
+               **      2 - controlling user must be used on next iteration.
+               */
+               if (gotctluser == 1)
+                       gotctluser++;
+               else if (gotctluser == 2)
+               {
+                       clrctluser();
+                       gotctluser = 0;
+               }
+       }
+
+       /* clear controlling user in case we break out prematurely */
+       clrctluser();
+
+       FileName = NULL;
+
+       /*
+       **  If we haven't read any lines, this queue file is empty.
+       **  Arrange to remove it without referencing any null pointers.
+       */
+
+       if (LineNumber == 0)
+       {
+               errno = 0;
+               e->e_flags |= EF_CLRQUEUE | EF_FATALERRS | EF_RESPONSE;
+       }
+       return qfp;
+}
+\f/*
+**  PRINTQUEUE -- print out a representation of the mail queue
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Prints a listing of the mail queue on the standard output.
+*/
+
+printqueue()
+{
+       register WORK *w;
+       FILE *f;
+       int nrequests;
+       char buf[MAXLINE];
+       char cbuf[MAXLINE];
+
+       /*
+       **  Read and order the queue.
+       */
+
+       nrequests = orderq(TRUE);
+
+       /*
+       **  Print the work list that we have read.
+       */
+
+       /* first see if there is anything */
+       if (nrequests <= 0)
+       {
+               printf("Mail queue is empty\n");
+               return;
+       }
+
+       la = getla();   /* get load average */
+
+       printf("\t\tMail Queue (%d request%s", nrequests, nrequests == 1 ? "" : "s");
+       if (nrequests > QUEUESIZE)
+               printf(", only %d printed", QUEUESIZE);
+       if (Verbose)
+               printf(")\n--QID-- --Size-- -Priority- ---Q-Time--- -----------Sender/Recipient-----------\n");
+       else
+               printf(")\n--QID-- --Size-- -----Q-Time----- ------------Sender/Recipient------------\n");
+       for (w = WorkQ; w != NULL; w = w->w_next)
+       {
+               struct stat st;
+               auto time_t submittime = 0;
+               long dfsize = -1;
+               char message[MAXLINE];
+               extern bool shouldqueue();
+
+               f = fopen(w->w_name, "r");
+               if (f == NULL)
+               {
+                       errno = 0;
+                       continue;
+               }
+               printf("%7s", w->w_name + 2);
+               if (flock(fileno(f), LOCK_SH|LOCK_NB) < 0)
+                       printf("*");
+               else if (shouldqueue(w->w_pri))
+                       printf("X");
+               else
+                       printf(" ");
+               errno = 0;
+
+               message[0] = '\0';
+               cbuf[0] = '\0';
+               while (fgets(buf, sizeof buf, f) != NULL)
+               {
+                       fixcrlf(buf, TRUE);
+                       switch (buf[0])
+                       {
+                         case 'M':     /* error message */
+                               (void) strcpy(message, &buf[1]);
+                               break;
+
+                         case 'S':     /* sender name */
+                               if (Verbose)
+                                       printf("%8ld %10ld %.12s %.38s", dfsize,
+                                           w->w_pri, ctime(&submittime) + 4,
+                                           &buf[1]);
+                               else
+                                       printf("%8ld %.16s %.45s", dfsize,
+                                           ctime(&submittime), &buf[1]);
+                               if (message[0] != '\0')
+                                       printf("\n\t\t (%.60s)", message);
+                               break;
+                         case 'C':     /* controlling user */
+                               if (strlen(buf) < MAXLINE-3)    /* sanity */
+                                       (void) strcat(buf, ") ");
+                               cbuf[0] = cbuf[1] = '(';
+                               (void) strncpy(&cbuf[2], &buf[1], MAXLINE-1);
+                               cbuf[MAXLINE-1] = '\0';
+                               break;
+
+                         case 'R':     /* recipient name */
+                               if (cbuf[0] != '\0') {
+                                       /* prepend controlling user to `buf' */
+                                       (void) strncat(cbuf, &buf[1],
+                                                     MAXLINE-strlen(cbuf));
+                                       cbuf[MAXLINE-1] = '\0';
+                                       (void) strcpy(buf, cbuf);
+                                       cbuf[0] = '\0';
+                               }
+                               if (Verbose)
+                                       printf("\n\t\t\t\t\t %.38s", &buf[1]);
+                               else
+                                       printf("\n\t\t\t\t  %.45s", &buf[1]);
+                               break;
+
+                         case 'T':     /* creation time */
+                               submittime = atol(&buf[1]);
+                               break;
+
+                         case 'D':     /* data file name */
+                               if (stat(&buf[1], &st) >= 0)
+                                       dfsize = st.st_size;
+                               break;
+                       }
+               }
+               if (submittime == (time_t) 0)
+                       printf(" (no control file)");
+               printf("\n");
+               (void) fclose(f);
+       }
+}
+
+# endif QUEUE
+\f/*
+**  QUEUENAME -- build a file name in the queue directory for this envelope.
+**
+**     Assigns an id code if one does not already exist.
+**     This code is very careful to avoid trashing existing files
+**     under any circumstances.
+**
+**     Parameters:
+**             e -- envelope to build it in/from.
+**             type -- the file type, used as the first character
+**                     of the file name.
+**
+**     Returns:
+**             a pointer to the new file name (in a static buffer).
+**
+**     Side Effects:
+**             Will create the qf file if no id code is
+**             already assigned.  This will cause the envelope
+**             to be modified.
+*/
+
+char *
+queuename(e, type)
+       register ENVELOPE *e;
+       char type;
+{
+       static char buf[MAXNAME];
+       static int pid = -1;
+       char c1 = 'A';
+       char c2 = 'A';
+
+       if (e->e_id == NULL)
+       {
+               char qf[20];
+
+               /* find a unique id */
+               if (pid != getpid())
+               {
+                       /* new process -- start back at "AA" */
+                       pid = getpid();
+                       c1 = 'A';
+                       c2 = 'A' - 1;
+               }
+               (void) sprintf(qf, "qfAA%05d", pid);
+
+               while (c1 < '~' || c2 < 'Z')
+               {
+                       int i;
+
+                       if (c2 >= 'Z')
+                       {
+                               c1++;
+                               c2 = 'A' - 1;
+                       }
+                       qf[2] = c1;
+                       qf[3] = ++c2;
+                       if (tTd(7, 20))
+                               printf("queuename: trying \"%s\"\n", qf);
+
+                       i = open(qf, O_WRONLY|O_CREAT|O_EXCL, FileMode);
+                       if (i < 0) {
+                               if (errno != EEXIST) {
+                                       syserr("queuename: Cannot create \"%s\" in \"%s\"",
+                                               qf, QueueDir);
+                                       exit(EX_UNAVAILABLE);
+                               }
+                       } else {
+                               (void) close(i);
+                               break;
+                       }
+               }
+               if (c1 >= '~' && c2 >= 'Z')
+               {
+                       syserr("queuename: Cannot create \"%s\" in \"%s\"",
+                               qf, QueueDir);
+                       exit(EX_OSERR);
+               }
+               e->e_id = newstr(&qf[2]);
+               define('i', e->e_id, e);
+               if (tTd(7, 1))
+                       printf("queuename: assigned id %s, env=%x\n", e->e_id, e);
+# ifdef LOG
+               if (LogLevel > 16)
+                       syslog(LOG_DEBUG, "%s: assigned id", e->e_id);
+# endif LOG
+       }
+
+       if (type == '\0')
+               return (NULL);
+       (void) sprintf(buf, "%cf%s", type, e->e_id);
+       if (tTd(7, 2))
+               printf("queuename: %s\n", buf);
+       return (buf);
+}
+\f/*
+**  UNLOCKQUEUE -- unlock the queue entry for a specified envelope
+**
+**     Parameters:
+**             e -- the envelope to unlock.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             unlocks the queue for `e'.
+*/
+
+unlockqueue(e)
+       ENVELOPE *e;
+{
+       /* remove the transcript */
+# ifdef LOG
+       if (LogLevel > 19)
+               syslog(LOG_DEBUG, "%s: unlock", e->e_id);
+# endif LOG
+       if (!tTd(51, 4))
+               xunlink(queuename(e, 'x'));
+
+}
+\f/*
+**  GETCTLUSER -- return controlling user if mailing to prog or file
+**
+**     Check for a "|" or "/" at the beginning of the address.  If
+**     found, return a controlling username.
+**
+**     Parameters:
+**             a - the address to check out
+**
+**     Returns:
+**             Either NULL, if we werent mailing to a program or file,
+**             or a controlling user name (possibly in getpwuid's
+**             static buffer).
+**
+**     Side Effects:
+**             none.
+*/
+
+char *
+getctluser(a)
+       ADDRESS *a;
+{
+       extern ADDRESS *getctladdr();
+       struct passwd *pw;
+       char *retstr;
+
+       /*
+       **  Get unquoted user for file, program or user.name check.
+       **  N.B. remove this code block to always emit controlling
+       **  addresses (at the expense of backward compatibility).
+       */
+
+       {
+               char buf[MAXNAME];
+               (void) strncpy(buf, a->q_paddr, MAXNAME);
+               buf[MAXNAME-1] = '\0';
+               stripquotes(buf, TRUE);
+
+               if (buf[0] != '|' && buf[0] != '/')
+                       return((char *)NULL);
+       }
+
+       a = getctladdr(a);              /* find controlling address */
+
+       if (a != NULL && a->q_uid != 0 && (pw = getpwuid(a->q_uid)) != NULL)
+               retstr = pw->pw_name;
+       else                            /* use default user */
+               retstr = DefUser;
+
+       if (tTd(40, 5))
+               printf("Set controlling user for `%s' to `%s'\n",
+                      (a == NULL)? "<null>": a->q_paddr, retstr);
+
+       return(retstr);
+}
+\f/*
+**  SETCTLUSER - sets `CtlUser' to controlling user
+**  CLRCTLUSER - clears controlling user (no params, nothing returned)
+**
+**     These routines manipulate `CtlUser'.
+**
+**     Parameters:
+**             str  - controlling user as passed to setctluser()
+**
+**     Returns:
+**             None.
+**
+**     Side Effects:
+**             `CtlUser' is changed.
+*/
+
+static char CtlUser[MAXNAME];
+
+setctluser(str)
+register char *str;
+{
+       (void) strncpy(CtlUser, str, MAXNAME);
+       CtlUser[MAXNAME-1] = '\0';
+}
+
+clrctluser()
+{
+       CtlUser[0] = '\0';
+}
+
+\f/*
+**  SETCTLADDR -- create a controlling address
+**
+**     If global variable `CtlUser' is set and we are given a valid
+**     address, make that address a controlling address; change the
+**     `q_uid', `q_gid', and `q_ruser' fields and set QGOODUID.
+**
+**     Parameters:
+**             a - address for which control uid/gid info may apply
+**
+**     Returns:
+**             None.   
+**
+**     Side Effects:
+**             Fills in uid/gid fields in address and sets QGOODUID
+**             flag if appropriate.
+*/
+
+setctladdr(a)
+       ADDRESS *a;
+{
+       struct passwd *pw;
+
+       /*
+       **  If there is no current controlling user, or we were passed a
+       **  NULL addr ptr or we already have a controlling user, return.
+       */
+
+       if (CtlUser[0] == '\0' || a == NULL || a->q_ruser)
+               return;
+
+       /*
+       **  Set up addr fields for controlling user.  If `CtlUser' is no
+       **  longer valid, use the default user/group.
+       */
+
+       if ((pw = getpwnam(CtlUser)) != NULL)
+       {
+               if (a->q_home)
+                       free(a->q_home);
+               a->q_home = newstr(pw->pw_dir);
+               a->q_uid = pw->pw_uid;
+               a->q_gid = pw->pw_gid;
+               a->q_ruser = newstr(CtlUser);
+       }
+       else
+       {
+               a->q_uid = DefUid;
+               a->q_gid = DefGid;
+               a->q_ruser = newstr(DefUser);
+       }
+
+       a->q_flags |= QGOODUID;         /* flag as a "ctladdr"  */
+
+       if (tTd(40, 5))
+               printf("Restored controlling user for `%s' to `%s'\n",
+                      a->q_paddr, a->q_ruser);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/readcf.c b/usr/src/usr.sbin/sendmail/src/readcf.c
new file mode 100644 (file)
index 0000000..bd2bad7
--- /dev/null
@@ -0,0 +1,949 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)readcf.c   5.22 (Berkeley) 3/12/91";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+/*
+**  READCF -- read control file.
+**
+**     This routine reads the control file and builds the internal
+**     form.
+**
+**     The file is formatted as a sequence of lines, each taken
+**     atomically.  The first character of each line describes how
+**     the line is to be interpreted.  The lines are:
+**             Dxval           Define macro x to have value val.
+**             Cxword          Put word into class x.
+**             Fxfile [fmt]    Read file for lines to put into
+**                             class x.  Use scanf string 'fmt'
+**                             or "%s" if not present.  Fmt should
+**                             only produce one string-valued result.
+**             Hname: value    Define header with field-name 'name'
+**                             and value as specified; this will be
+**                             macro expanded immediately before
+**                             use.
+**             Sn              Use rewriting set n.
+**             Rlhs rhs        Rewrite addresses that match lhs to
+**                             be rhs.
+**             Mn arg=val...   Define mailer.  n is the internal name.
+**                             Args specify mailer parameters.
+**             Oxvalue         Set option x to value.
+**             Pname=value     Set precedence name to value.
+**
+**     Parameters:
+**             cfname -- control file name.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Builds several internal tables.
+*/
+
+readcf(cfname)
+       char *cfname;
+{
+       FILE *cf;
+       int ruleset = 0;
+       char *q;
+       char **pv;
+       struct rewrite *rwp = NULL;
+       char buf[MAXLINE];
+       register char *p;
+       extern char **prescan();
+       extern char **copyplist();
+       char exbuf[MAXLINE];
+       char pvpbuf[PSBUFSIZE];
+       extern char *fgetfolded();
+       extern char *munchstring();
+
+       cf = fopen(cfname, "r");
+       if (cf == NULL)
+       {
+               syserr("cannot open %s", cfname);
+               exit(EX_OSFILE);
+       }
+
+       FileName = cfname;
+       LineNumber = 0;
+       while (fgetfolded(buf, sizeof buf, cf) != NULL)
+       {
+               /* map $ into \001 (ASCII SOH) for macro expansion */
+               for (p = buf; *p != '\0'; p++)
+               {
+                       if (*p != '$')
+                               continue;
+
+                       if (p[1] == '$')
+                       {
+                               /* actual dollar sign.... */
+                               (void) strcpy(p, p + 1);
+                               continue;
+                       }
+
+                       /* convert to macro expansion character */
+                       *p = '\001';
+               }
+
+               /* interpret this line */
+               switch (buf[0])
+               {
+                 case '\0':
+                 case '#':             /* comment */
+                       break;
+
+                 case 'R':             /* rewriting rule */
+                       for (p = &buf[1]; *p != '\0' && *p != '\t'; p++)
+                               continue;
+
+                       if (*p == '\0')
+                       {
+                               syserr("invalid rewrite line \"%s\"", buf);
+                               break;
+                       }
+
+                       /* allocate space for the rule header */
+                       if (rwp == NULL)
+                       {
+                               RewriteRules[ruleset] = rwp =
+                                       (struct rewrite *) xalloc(sizeof *rwp);
+                       }
+                       else
+                       {
+                               rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
+                               rwp = rwp->r_next;
+                       }
+                       rwp->r_next = NULL;
+
+                       /* expand and save the LHS */
+                       *p = '\0';
+                       expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
+                       rwp->r_lhs = prescan(exbuf, '\t', pvpbuf);
+                       if (rwp->r_lhs != NULL)
+                               rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
+
+                       /* expand and save the RHS */
+                       while (*++p == '\t')
+                               continue;
+                       q = p;
+                       while (*p != '\0' && *p != '\t')
+                               p++;
+                       *p = '\0';
+                       expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
+                       rwp->r_rhs = prescan(exbuf, '\t', pvpbuf);
+                       if (rwp->r_rhs != NULL)
+                               rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
+                       break;
+
+                 case 'S':             /* select rewriting set */
+                       ruleset = atoi(&buf[1]);
+                       if (ruleset >= MAXRWSETS || ruleset < 0)
+                       {
+                               syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
+                               ruleset = 0;
+                       }
+                       rwp = NULL;
+                       break;
+
+                 case 'D':             /* macro definition */
+                       define(buf[1], newstr(munchstring(&buf[2])), CurEnv);
+                       break;
+
+                 case 'H':             /* required header line */
+                       (void) chompheader(&buf[1], TRUE);
+                       break;
+
+                 case 'C':             /* word class */
+                 case 'F':             /* word class from file */
+                       /* read list of words from argument or file */
+                       if (buf[0] == 'F')
+                       {
+                               /* read from file */
+                               for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
+                                       continue;
+                               if (*p == '\0')
+                                       p = "%s";
+                               else
+                               {
+                                       *p = '\0';
+                                       while (isspace(*++p))
+                                               continue;
+                               }
+                               fileclass(buf[1], &buf[2], p);
+                               break;
+                       }
+
+                       /* scan the list of words and set class for all */
+                       for (p = &buf[2]; *p != '\0'; )
+                       {
+                               register char *wd;
+                               char delim;
+
+                               while (*p != '\0' && isspace(*p))
+                                       p++;
+                               wd = p;
+                               while (*p != '\0' && !isspace(*p))
+                                       p++;
+                               delim = *p;
+                               *p = '\0';
+                               if (wd[0] != '\0')
+                                       setclass(buf[1], wd);
+                               *p = delim;
+                       }
+                       break;
+
+                 case 'M':             /* define mailer */
+                       makemailer(&buf[1]);
+                       break;
+
+                 case 'O':             /* set option */
+                       setoption(buf[1], &buf[2], TRUE, FALSE);
+                       break;
+
+                 case 'P':             /* set precedence */
+                       if (NumPriorities >= MAXPRIORITIES)
+                       {
+                               toomany('P', MAXPRIORITIES);
+                               break;
+                       }
+                       for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
+                               continue;
+                       if (*p == '\0')
+                               goto badline;
+                       *p = '\0';
+                       Priorities[NumPriorities].pri_name = newstr(&buf[1]);
+                       Priorities[NumPriorities].pri_val = atoi(++p);
+                       NumPriorities++;
+                       break;
+
+                 case 'T':             /* trusted user(s) */
+                       p = &buf[1];
+                       while (*p != '\0')
+                       {
+                               while (isspace(*p))
+                                       p++;
+                               q = p;
+                               while (*p != '\0' && !isspace(*p))
+                                       p++;
+                               if (*p != '\0')
+                                       *p++ = '\0';
+                               if (*q == '\0')
+                                       continue;
+                               for (pv = TrustedUsers; *pv != NULL; pv++)
+                                       continue;
+                               if (pv >= &TrustedUsers[MAXTRUST])
+                               {
+                                       toomany('T', MAXTRUST);
+                                       break;
+                               }
+                               *pv = newstr(q);
+                       }
+                       break;
+
+                 default:
+                 badline:
+                       syserr("unknown control line \"%s\"", buf);
+               }
+       }
+       FileName = NULL;
+}
+\f/*
+**  TOOMANY -- signal too many of some option
+**
+**     Parameters:
+**             id -- the id of the error line
+**             maxcnt -- the maximum possible values
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             gives a syserr.
+*/
+
+toomany(id, maxcnt)
+       char id;
+       int maxcnt;
+{
+       syserr("too many %c lines, %d max", id, maxcnt);
+}
+\f/*
+**  FILECLASS -- read members of a class from a file
+**
+**     Parameters:
+**             class -- class to define.
+**             filename -- name of file to read.
+**             fmt -- scanf string to use for match.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**
+**             puts all lines in filename that match a scanf into
+**                     the named class.
+*/
+
+fileclass(class, filename, fmt)
+       int class;
+       char *filename;
+       char *fmt;
+{
+       FILE *f;
+       char buf[MAXLINE];
+
+       f = fopen(filename, "r");
+       if (f == NULL)
+       {
+               syserr("cannot open %s", filename);
+               return;
+       }
+
+       while (fgets(buf, sizeof buf, f) != NULL)
+       {
+               register STAB *s;
+               register char *p;
+# ifdef SCANF
+               char wordbuf[MAXNAME+1];
+
+               if (sscanf(buf, fmt, wordbuf) != 1)
+                       continue;
+               p = wordbuf;
+# else SCANF
+               p = buf;
+# endif SCANF
+
+               /*
+               **  Break up the match into words.
+               */
+
+               while (*p != '\0')
+               {
+                       register char *q;
+
+                       /* strip leading spaces */
+                       while (isspace(*p))
+                               p++;
+                       if (*p == '\0')
+                               break;
+
+                       /* find the end of the word */
+                       q = p;
+                       while (*p != '\0' && !isspace(*p))
+                               p++;
+                       if (*p != '\0')
+                               *p++ = '\0';
+
+                       /* enter the word in the symbol table */
+                       s = stab(q, ST_CLASS, ST_ENTER);
+                       setbitn(class, s->s_class);
+               }
+       }
+
+       (void) fclose(f);
+}
+\f/*
+**  MAKEMAILER -- define a new mailer.
+**
+**     Parameters:
+**             line -- description of mailer.  This is in labeled
+**                     fields.  The fields are:
+**                        P -- the path to the mailer
+**                        F -- the flags associated with the mailer
+**                        A -- the argv for this mailer
+**                        S -- the sender rewriting set
+**                        R -- the recipient rewriting set
+**                        E -- the eol string
+**                     The first word is the canonical name of the mailer.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             enters the mailer into the mailer table.
+*/
+
+makemailer(line)
+       char *line;
+{
+       register char *p;
+       register struct mailer *m;
+       register STAB *s;
+       int i;
+       char fcode;
+       extern int NextMailer;
+       extern char **makeargv();
+       extern char *munchstring();
+       extern char *DelimChar;
+       extern long atol();
+
+       /* allocate a mailer and set up defaults */
+       m = (struct mailer *) xalloc(sizeof *m);
+       bzero((char *) m, sizeof *m);
+       m->m_mno = NextMailer;
+       m->m_eol = "\n";
+
+       /* collect the mailer name */
+       for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++)
+               continue;
+       if (*p != '\0')
+               *p++ = '\0';
+       m->m_name = newstr(line);
+
+       /* now scan through and assign info from the fields */
+       while (*p != '\0')
+       {
+               while (*p != '\0' && (*p == ',' || isspace(*p)))
+                       p++;
+
+               /* p now points to field code */
+               fcode = *p;
+               while (*p != '\0' && *p != '=' && *p != ',')
+                       p++;
+               if (*p++ != '=')
+               {
+                       syserr("`=' expected");
+                       return;
+               }
+               while (isspace(*p))
+                       p++;
+
+               /* p now points to the field body */
+               p = munchstring(p);
+
+               /* install the field into the mailer struct */
+               switch (fcode)
+               {
+                 case 'P':             /* pathname */
+                       m->m_mailer = newstr(p);
+                       break;
+
+                 case 'F':             /* flags */
+                       for (; *p != '\0'; p++)
+                               setbitn(*p, m->m_flags);
+                       break;
+
+                 case 'S':             /* sender rewriting ruleset */
+                 case 'R':             /* recipient rewriting ruleset */
+                       i = atoi(p);
+                       if (i < 0 || i >= MAXRWSETS)
+                       {
+                               syserr("invalid rewrite set, %d max", MAXRWSETS);
+                               return;
+                       }
+                       if (fcode == 'S')
+                               m->m_s_rwset = i;
+                       else
+                               m->m_r_rwset = i;
+                       break;
+
+                 case 'E':             /* end of line string */
+                       m->m_eol = newstr(p);
+                       break;
+
+                 case 'A':             /* argument vector */
+                       m->m_argv = makeargv(p);
+                       break;
+
+                 case 'M':             /* maximum message size */
+                       m->m_maxsize = atol(p);
+                       break;
+               }
+
+               p = DelimChar;
+       }
+
+       /* now store the mailer away */
+       if (NextMailer >= MAXMAILERS)
+       {
+               syserr("too many mailers defined (%d max)", MAXMAILERS);
+               return;
+       }
+       Mailer[NextMailer++] = m;
+       s = stab(m->m_name, ST_MAILER, ST_ENTER);
+       s->s_mailer = m;
+}
+\f/*
+**  MUNCHSTRING -- translate a string into internal form.
+**
+**     Parameters:
+**             p -- the string to munch.
+**
+**     Returns:
+**             the munched string.
+**
+**     Side Effects:
+**             Sets "DelimChar" to point to the string that caused us
+**             to stop.
+*/
+
+char *
+munchstring(p)
+       register char *p;
+{
+       register char *q;
+       bool backslash = FALSE;
+       bool quotemode = FALSE;
+       static char buf[MAXLINE];
+       extern char *DelimChar;
+
+       for (q = buf; *p != '\0'; p++)
+       {
+               if (backslash)
+               {
+                       /* everything is roughly literal */
+                       backslash = FALSE;
+                       switch (*p)
+                       {
+                         case 'r':             /* carriage return */
+                               *q++ = '\r';
+                               continue;
+
+                         case 'n':             /* newline */
+                               *q++ = '\n';
+                               continue;
+
+                         case 'f':             /* form feed */
+                               *q++ = '\f';
+                               continue;
+
+                         case 'b':             /* backspace */
+                               *q++ = '\b';
+                               continue;
+                       }
+                       *q++ = *p;
+               }
+               else
+               {
+                       if (*p == '\\')
+                               backslash = TRUE;
+                       else if (*p == '"')
+                               quotemode = !quotemode;
+                       else if (quotemode || *p != ',')
+                               *q++ = *p;
+                       else
+                               break;
+               }
+       }
+
+       DelimChar = p;
+       *q++ = '\0';
+       return (buf);
+}
+\f/*
+**  MAKEARGV -- break up a string into words
+**
+**     Parameters:
+**             p -- the string to break up.
+**
+**     Returns:
+**             a char **argv (dynamically allocated)
+**
+**     Side Effects:
+**             munges p.
+*/
+
+char **
+makeargv(p)
+       register char *p;
+{
+       char *q;
+       int i;
+       char **avp;
+       char *argv[MAXPV + 1];
+
+       /* take apart the words */
+       i = 0;
+       while (*p != '\0' && i < MAXPV)
+       {
+               q = p;
+               while (*p != '\0' && !isspace(*p))
+                       p++;
+               while (isspace(*p))
+                       *p++ = '\0';
+               argv[i++] = newstr(q);
+       }
+       argv[i++] = NULL;
+
+       /* now make a copy of the argv */
+       avp = (char **) xalloc(sizeof *avp * i);
+       bcopy((char *) argv, (char *) avp, sizeof *avp * i);
+
+       return (avp);
+}
+\f/*
+**  PRINTRULES -- print rewrite rules (for debugging)
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             prints rewrite rules.
+*/
+
+printrules()
+{
+       register struct rewrite *rwp;
+       register int ruleset;
+
+       for (ruleset = 0; ruleset < 10; ruleset++)
+       {
+               if (RewriteRules[ruleset] == NULL)
+                       continue;
+               printf("\n----Rule Set %d:", ruleset);
+
+               for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
+               {
+                       printf("\nLHS:");
+                       printav(rwp->r_lhs);
+                       printf("RHS:");
+                       printav(rwp->r_rhs);
+               }
+       }
+}
+
+\f/*
+**  SETOPTION -- set global processing option
+**
+**     Parameters:
+**             opt -- option name.
+**             val -- option value (as a text string).
+**             safe -- set if this came from a configuration file.
+**                     Some options (if set from the command line) will
+**                     reset the user id to avoid security problems.
+**             sticky -- if set, don't let other setoptions override
+**                     this value.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             Sets options as implied by the arguments.
+*/
+
+static BITMAP  StickyOpt;              /* set if option is stuck */
+extern char    *NetName;               /* name of home (local) network */
+
+setoption(opt, val, safe, sticky)
+       char opt;
+       char *val;
+       bool safe;
+       bool sticky;
+{
+       extern bool atobool();
+       extern time_t convtime();
+       extern int QueueLA;
+       extern int RefuseLA;
+       extern bool trusteduser();
+       extern char *username();
+
+       if (tTd(37, 1))
+               printf("setoption %c=%s", opt, val);
+
+       /*
+       **  See if this option is preset for us.
+       */
+
+       if (bitnset(opt, StickyOpt))
+       {
+               if (tTd(37, 1))
+                       printf(" (ignored)\n");
+               return;
+       }
+
+       /*
+       **  Check to see if this option can be specified by this user.
+       */
+
+       if (!safe && getuid() == 0)
+               safe = TRUE;
+       if (!safe && index("deiLmorsv", opt) == NULL)
+       {
+               if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
+               {
+                       if (tTd(37, 1))
+                               printf(" (unsafe)");
+                       if (getuid() != geteuid())
+                       {
+                               printf("(Resetting uid)\n");
+                               (void) setgid(getgid());
+                               (void) setuid(getuid());
+                       }
+               }
+       }
+       else if (tTd(37, 1))
+               printf("\n");
+
+       switch (opt)
+       {
+         case 'A':             /* set default alias file */
+               if (val[0] == '\0')
+                       AliasFile = "aliases";
+               else
+                       AliasFile = newstr(val);
+               break;
+
+         case 'a':             /* look N minutes for "@:@" in alias file */
+               if (val[0] == '\0')
+                       SafeAlias = 5;
+               else
+                       SafeAlias = atoi(val);
+               break;
+
+         case 'B':             /* substitution for blank character */
+               SpaceSub = val[0];
+               if (SpaceSub == '\0')
+                       SpaceSub = ' ';
+               break;
+
+         case 'c':             /* don't connect to "expensive" mailers */
+               NoConnect = atobool(val);
+               break;
+
+         case 'C':             /* checkpoint after N connections */
+               CheckPointLimit = atoi(val);
+               break;
+
+         case 'd':             /* delivery mode */
+               switch (*val)
+               {
+                 case '\0':
+                       SendMode = SM_DELIVER;
+                       break;
+
+                 case SM_QUEUE:        /* queue only */
+#ifndef QUEUE
+                       syserr("need QUEUE to set -odqueue");
+#endif QUEUE
+                       /* fall through..... */
+
+                 case SM_DELIVER:      /* do everything */
+                 case SM_FORK:         /* fork after verification */
+                       SendMode = *val;
+                       break;
+
+                 default:
+                       syserr("Unknown delivery mode %c", *val);
+                       exit(EX_USAGE);
+               }
+               break;
+
+         case 'D':             /* rebuild alias database as needed */
+               AutoRebuild = atobool(val);
+               break;
+
+         case 'e':             /* set error processing mode */
+               switch (*val)
+               {
+                 case EM_QUIET:        /* be silent about it */
+                 case EM_MAIL:         /* mail back */
+                 case EM_BERKNET:      /* do berknet error processing */
+                 case EM_WRITE:        /* write back (or mail) */
+                       HoldErrs = TRUE;
+                       /* fall through... */
+
+                 case EM_PRINT:        /* print errors normally (default) */
+                       ErrorMode = *val;
+                       break;
+               }
+               break;
+
+         case 'F':             /* file mode */
+               FileMode = atooct(val) & 0777;
+               break;
+
+         case 'f':             /* save Unix-style From lines on front */
+               SaveFrom = atobool(val);
+               break;
+
+         case 'g':             /* default gid */
+               DefGid = atoi(val);
+               break;
+
+         case 'H':             /* help file */
+               if (val[0] == '\0')
+                       HelpFile = "sendmail.hf";
+               else
+                       HelpFile = newstr(val);
+               break;
+
+         case 'I':             /* use internet domain name server */
+               UseNameServer = atobool(val);
+               break;
+
+         case 'i':             /* ignore dot lines in message */
+               IgnrDot = atobool(val);
+               break;
+
+         case 'k':             /* checkpoint every N addresses */
+               CheckpointInterval = atoi(val);
+               break;
+
+         case 'L':             /* log level */
+               LogLevel = atoi(val);
+               break;
+
+         case 'M':             /* define macro */
+               define(val[0], newstr(&val[1]), CurEnv);
+               sticky = FALSE;
+               break;
+
+         case 'm':             /* send to me too */
+               MeToo = atobool(val);
+               break;
+
+         case 'n':             /* validate RHS in newaliases */
+               CheckAliases = atobool(val);
+               break;
+
+# ifdef DAEMON
+         case 'N':             /* home (local?) network name */
+               NetName = newstr(val);
+               break;
+# endif DAEMON
+
+         case 'o':             /* assume old style headers */
+               if (atobool(val))
+                       CurEnv->e_flags |= EF_OLDSTYLE;
+               else
+                       CurEnv->e_flags &= ~EF_OLDSTYLE;
+               break;
+
+         case 'P':             /* postmaster copy address for returned mail */
+               PostMasterCopy = newstr(val);
+               break;
+
+         case 'q':             /* slope of queue only function */
+               QueueFactor = atoi(val);
+               break;
+
+         case 'Q':             /* queue directory */
+               if (val[0] == '\0')
+                       QueueDir = "mqueue";
+               else
+                       QueueDir = newstr(val);
+               break;
+
+         case 'r':             /* read timeout */
+               ReadTimeout = convtime(val);
+               break;
+
+         case 'S':             /* status file */
+               if (val[0] == '\0')
+                       StatFile = "sendmail.st";
+               else
+                       StatFile = newstr(val);
+               break;
+
+         case 's':             /* be super safe, even if expensive */
+               SuperSafe = atobool(val);
+               break;
+
+         case 'T':             /* queue timeout */
+               TimeOut = convtime(val);
+               /*FALLTHROUGH*/
+
+         case 't':             /* time zone name */
+               break;
+
+         case 'u':             /* set default uid */
+               DefUid = atoi(val);
+               setdefuser();
+               break;
+
+         case 'v':             /* run in verbose mode */
+               Verbose = atobool(val);
+               break;
+
+         case 'x':             /* load avg at which to auto-queue msgs */
+               QueueLA = atoi(val);
+               break;
+
+         case 'X':             /* load avg at which to auto-reject connections */
+               RefuseLA = atoi(val);
+               break;
+
+         case 'y':             /* work recipient factor */
+               WkRecipFact = atoi(val);
+               break;
+
+         case 'Y':             /* fork jobs during queue runs */
+               ForkQueueRuns = atobool(val);
+               break;
+
+         case 'z':             /* work message class factor */
+               WkClassFact = atoi(val);
+               break;
+
+         case 'Z':             /* work time factor */
+               WkTimeFact = atoi(val);
+               break;
+
+         default:
+               break;
+       }
+       if (sticky)
+               setbitn(opt, StickyOpt);
+       return;
+}
+\f/*
+**  SETCLASS -- set a word into a class
+**
+**     Parameters:
+**             class -- the class to put the word in.
+**             word -- the word to enter
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             puts the word into the symbol table.
+*/
+
+setclass(class, word)
+       int class;
+       char *word;
+{
+       register STAB *s;
+
+       s = stab(word, ST_CLASS, ST_ENTER);
+       setbitn(class, s->s_class);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/recipient.c b/usr/src/usr.sbin/sendmail/src/recipient.c
new file mode 100644 (file)
index 0000000..41634c1
--- /dev/null
@@ -0,0 +1,594 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)recipient.c        5.19 (Berkeley) 3/2/91";
+#endif /* not lint */
+
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <pwd.h>
+# include "sendmail.h"
+
+/*
+**  SENDTOLIST -- Designate a send list.
+**
+**     The parameter is a comma-separated list of people to send to.
+**     This routine arranges to send to all of them.
+**
+**     Parameters:
+**             list -- the send list.
+**             ctladdr -- the address template for the person to
+**                     send to -- effective uid/gid are important.
+**                     This is typically the alias that caused this
+**                     expansion.
+**             sendq -- a pointer to the head of a queue to put
+**                     these people into.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             none.
+*/
+
+# define MAXRCRSN      10
+
+sendtolist(list, ctladdr, sendq)
+       char *list;
+       ADDRESS *ctladdr;
+       ADDRESS **sendq;
+{
+       register char *p;
+       register ADDRESS *al;   /* list of addresses to send to */
+       bool firstone;          /* set on first address sent */
+       bool selfref;           /* set if this list includes ctladdr */
+       char delimiter;         /* the address delimiter */
+
+       if (tTd(25, 1))
+       {
+               printf("sendto: %s\n   ctladdr=", list);
+               printaddr(ctladdr, FALSE);
+       }
+
+       /* heuristic to determine old versus new style addresses */
+       if (ctladdr == NULL &&
+           (index(list, ',') != NULL || index(list, ';') != NULL ||
+            index(list, '<') != NULL || index(list, '(') != NULL))
+               CurEnv->e_flags &= ~EF_OLDSTYLE;
+       delimiter = ' ';
+       if (!bitset(EF_OLDSTYLE, CurEnv->e_flags) || ctladdr != NULL)
+               delimiter = ',';
+
+       firstone = TRUE;
+       selfref = FALSE;
+       al = NULL;
+
+       for (p = list; *p != '\0'; )
+       {
+               register ADDRESS *a;
+               extern char *DelimChar;         /* defined in prescan */
+
+               /* parse the address */
+               while (isspace(*p) || *p == ',')
+                       p++;
+               a = parseaddr(p, (ADDRESS *) NULL, 1, delimiter);
+               p = DelimChar;
+               if (a == NULL)
+                       continue;
+               a->q_next = al;
+               a->q_alias = ctladdr;
+
+               /* see if this should be marked as a primary address */
+               if (ctladdr == NULL ||
+                   (firstone && *p == '\0' && bitset(QPRIMARY, ctladdr->q_flags)))
+                       a->q_flags |= QPRIMARY;
+
+               /* put on send queue or suppress self-reference */
+               if (ctladdr != NULL && sameaddr(ctladdr, a))
+                       selfref = TRUE;
+               else
+                       al = a;
+               firstone = FALSE;
+       }
+
+       /* if this alias doesn't include itself, delete ctladdr */
+       if (!selfref && ctladdr != NULL)
+               ctladdr->q_flags |= QDONTSEND;
+
+       /* arrange to send to everyone on the local send list */
+       while (al != NULL)
+       {
+               register ADDRESS *a = al;
+               extern ADDRESS *recipient();
+
+               al = a->q_next;
+               setctladdr(a);
+               a = recipient(a, sendq);
+
+               /* arrange to inherit full name */
+               if (a->q_fullname == NULL && ctladdr != NULL)
+                       a->q_fullname = ctladdr->q_fullname;
+       }
+
+       CurEnv->e_to = NULL;
+}
+\f/*
+**  RECIPIENT -- Designate a message recipient
+**
+**     Saves the named person for future mailing.
+**
+**     Parameters:
+**             a -- the (preparsed) address header for the recipient.
+**             sendq -- a pointer to the head of a queue to put the
+**                     recipient in.  Duplicate supression is done
+**                     in this queue.
+**
+**     Returns:
+**             The actual address in the queue.  This will be "a" if
+**             the address is not a duplicate, else the original address.
+**
+**     Side Effects:
+**             none.
+*/
+
+extern ADDRESS *getctladdr();
+
+ADDRESS *
+recipient(a, sendq)
+       register ADDRESS *a;
+       register ADDRESS **sendq;
+{
+       register ADDRESS *q;
+       ADDRESS **pq;
+       register struct mailer *m;
+       register char *p;
+       bool quoted = FALSE;            /* set if the addr has a quote bit */
+       char buf[MAXNAME];              /* unquoted image of the user name */
+       extern bool safefile();
+
+       CurEnv->e_to = a->q_paddr;
+       m = a->q_mailer;
+       errno = 0;
+       if (tTd(26, 1))
+       {
+               printf("\nrecipient: ");
+               printaddr(a, FALSE);
+       }
+
+       /* break aliasing loops */
+       if (AliasLevel > MAXRCRSN)
+       {
+               usrerr("aliasing/forwarding loop broken");
+               return (a);
+       }
+
+       /*
+       **  Finish setting up address structure.
+       */
+
+       /* set the queue timeout */
+       a->q_timeout = TimeOut;
+
+       /* map user & host to lower case if requested on non-aliases */
+       if (a->q_alias == NULL)
+               loweraddr(a);
+
+       /* get unquoted user for file, program or user.name check */
+       (void) strcpy(buf, a->q_user);
+       for (p = buf; *p != '\0' && !quoted; p++)
+       {
+               if (!isascii(*p) && (*p & 0377) != (SpaceSub & 0377))
+                       quoted = TRUE;
+       }
+       stripquotes(buf, TRUE);
+
+       /* do sickly crude mapping for program mailing, etc. */
+       if (m == LocalMailer && buf[0] == '|')
+       {
+               a->q_mailer = m = ProgMailer;
+               a->q_user++;
+               if (a->q_alias == NULL && !QueueRun && !ForceMail)
+               {
+                       a->q_flags |= QDONTSEND|QBADADDR;
+                       usrerr("Cannot mail directly to programs");
+               }
+       }
+
+       /*
+       **  Look up this person in the recipient list.
+       **      If they are there already, return, otherwise continue.
+       **      If the list is empty, just add it.  Notice the cute
+       **      hack to make from addresses suppress things correctly:
+       **      the QDONTSEND bit will be set in the send list.
+       **      [Please note: the emphasis is on "hack."]
+       */
+
+       for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
+       {
+               if (!ForceMail && sameaddr(q, a))
+               {
+                       if (tTd(26, 1))
+                       {
+                               printf("%s in sendq: ", a->q_paddr);
+                               printaddr(q, FALSE);
+                       }
+                       if (!bitset(QDONTSEND, a->q_flags))
+                               message(Arpa_Info, "duplicate suppressed");
+                       if (!bitset(QPRIMARY, q->q_flags))
+                               q->q_flags |= a->q_flags;
+                       return (q);
+               }
+       }
+
+       /* add address on list */
+       *pq = a;
+       a->q_next = NULL;
+       CurEnv->e_nrcpts++;
+
+       /*
+       **  Alias the name and handle :include: specs.
+       */
+
+       if (m == LocalMailer && !bitset(QDONTSEND, a->q_flags))
+       {
+               if (strncmp(a->q_user, ":include:", 9) == 0)
+               {
+                       a->q_flags |= QDONTSEND;
+                       if (a->q_alias == NULL && !QueueRun && !ForceMail)
+                       {
+                               a->q_flags |= QBADADDR;
+                               usrerr("Cannot mail directly to :include:s");
+                       }
+                       else
+                       {
+                               message(Arpa_Info, "including file %s", &a->q_user[9]);
+                               include(&a->q_user[9], " sending", a, sendq);
+                       }
+               }
+               else
+                       alias(a, sendq);
+       }
+
+       /*
+       **  If the user is local and still being sent, verify that
+       **  the address is good.  If it is, try to forward.
+       **  If the address is already good, we have a forwarding
+       **  loop.  This can be broken by just sending directly to
+       **  the user (which is probably correct anyway).
+       */
+
+       if (!bitset(QDONTSEND, a->q_flags) && m == LocalMailer)
+       {
+               struct stat stb;
+               extern bool writable();
+
+               /* see if this is to a file */
+               if (buf[0] == '/')
+               {
+                       p = rindex(buf, '/');
+                       /* check if writable or creatable */
+                       if (a->q_alias == NULL && !QueueRun && !ForceMail)
+                       {
+                               a->q_flags |= QDONTSEND|QBADADDR;
+                               usrerr("Cannot mail directly to files");
+                       }
+                       else if ((stat(buf, &stb) >= 0) ? (!writable(&stb)) :
+                           (*p = '\0', !safefile(buf, getruid(), S_IWRITE|S_IEXEC)))
+                       {
+                               a->q_flags |= QBADADDR;
+                               giveresponse(EX_CANTCREAT, m, CurEnv);
+                       }
+               }
+               else
+               {
+                       register struct passwd *pw;
+                       extern struct passwd *finduser();
+
+                       /* warning -- finduser may trash buf */
+                       pw = finduser(buf);
+                       if (pw == NULL)
+                       {
+                               a->q_flags |= QBADADDR;
+                               giveresponse(EX_NOUSER, m, CurEnv);
+                       }
+                       else
+                       {
+                               char nbuf[MAXNAME];
+
+                               if (strcmp(a->q_user, pw->pw_name) != 0)
+                               {
+                                       a->q_user = newstr(pw->pw_name);
+                                       (void) strcpy(buf, pw->pw_name);
+                               }
+                               a->q_home = newstr(pw->pw_dir);
+                               a->q_uid = pw->pw_uid;
+                               a->q_gid = pw->pw_gid;
+                               a->q_flags |= QGOODUID;
+                               buildfname(pw->pw_gecos, pw->pw_name, nbuf);
+                               if (nbuf[0] != '\0')
+                                       a->q_fullname = newstr(nbuf);
+                               if (!quoted)
+                                       forward(a, sendq);
+                       }
+               }
+       }
+       return (a);
+}
+\f/*
+**  FINDUSER -- find the password entry for a user.
+**
+**     This looks a lot like getpwnam, except that it may want to
+**     do some fancier pattern matching in /etc/passwd.
+**
+**     This routine contains most of the time of many sendmail runs.
+**     It deserves to be optimized.
+**
+**     Parameters:
+**             name -- the name to match against.
+**
+**     Returns:
+**             A pointer to a pw struct.
+**             NULL if name is unknown or ambiguous.
+**
+**     Side Effects:
+**             may modify name.
+*/
+
+struct passwd *
+finduser(name)
+       char *name;
+{
+       register struct passwd *pw;
+       register char *p;
+       extern struct passwd *getpwent();
+       extern struct passwd *getpwnam();
+
+       /* map upper => lower case */
+       for (p = name; *p != '\0'; p++)
+       {
+               if (isascii(*p) && isupper(*p))
+                       *p = tolower(*p);
+       }
+
+       /* look up this login name using fast path */
+       if ((pw = getpwnam(name)) != NULL)
+               return (pw);
+
+       /* search for a matching full name instead */
+       for (p = name; *p != '\0'; p++)
+       {
+               if (*p == (SpaceSub & 0177) || *p == '_')
+                       *p = ' ';
+       }
+       (void) setpwent();
+       while ((pw = getpwent()) != NULL)
+       {
+               char buf[MAXNAME];
+
+               buildfname(pw->pw_gecos, pw->pw_name, buf);
+               if (index(buf, ' ') != NULL && !strcasecmp(buf, name))
+               {
+                       message(Arpa_Info, "sending to login name %s", pw->pw_name);
+                       return (pw);
+               }
+       }
+       return (NULL);
+}
+\f/*
+**  WRITABLE -- predicate returning if the file is writable.
+**
+**     This routine must duplicate the algorithm in sys/fio.c.
+**     Unfortunately, we cannot use the access call since we
+**     won't necessarily be the real uid when we try to
+**     actually open the file.
+**
+**     Notice that ANY file with ANY execute bit is automatically
+**     not writable.  This is also enforced by mailfile.
+**
+**     Parameters:
+**             s -- pointer to a stat struct for the file.
+**
+**     Returns:
+**             TRUE -- if we will be able to write this file.
+**             FALSE -- if we cannot write this file.
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+writable(s)
+       register struct stat *s;
+{
+       int euid, egid;
+       int bits;
+
+       if (bitset(0111, s->st_mode))
+               return (FALSE);
+       euid = getruid();
+       egid = getrgid();
+       if (geteuid() == 0)
+       {
+               if (bitset(S_ISUID, s->st_mode))
+                       euid = s->st_uid;
+               if (bitset(S_ISGID, s->st_mode))
+                       egid = s->st_gid;
+       }
+
+       if (euid == 0)
+               return (TRUE);
+       bits = S_IWRITE;
+       if (euid != s->st_uid)
+       {
+               bits >>= 3;
+               if (egid != s->st_gid)
+                       bits >>= 3;
+       }
+       return ((s->st_mode & bits) != 0);
+}
+\f/*
+**  INCLUDE -- handle :include: specification.
+**
+**     Parameters:
+**             fname -- filename to include.
+**             msg -- message to print in verbose mode.
+**             ctladdr -- address template to use to fill in these
+**                     addresses -- effective user/group id are
+**                     the important things.
+**             sendq -- a pointer to the head of the send queue
+**                     to put these addresses in.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             reads the :include: file and sends to everyone
+**             listed in that file.
+*/
+
+include(fname, msg, ctladdr, sendq)
+       char *fname;
+       char *msg;
+       ADDRESS *ctladdr;
+       ADDRESS **sendq;
+{
+       char buf[MAXLINE];
+       register FILE *fp;
+       char *oldto = CurEnv->e_to;
+       char *oldfilename = FileName;
+       int oldlinenumber = LineNumber;
+
+       fp = fopen(fname, "r");
+       if (fp == NULL)
+       {
+               usrerr("Cannot open %s", fname);
+               return;
+       }
+       if (getctladdr(ctladdr) == NULL)
+       {
+               struct stat st;
+
+               if (fstat(fileno(fp), &st) < 0)
+                       syserr("Cannot fstat %s!", fname);
+               ctladdr->q_uid = st.st_uid;
+               ctladdr->q_gid = st.st_gid;
+               ctladdr->q_flags |= QGOODUID;
+       }
+
+       /* read the file -- each line is a comma-separated list. */
+       FileName = fname;
+       LineNumber = 0;
+       while (fgets(buf, sizeof buf, fp) != NULL)
+       {
+               register char *p = index(buf, '\n');
+
+               LineNumber++;
+               if (p != NULL)
+                       *p = '\0';
+               if (buf[0] == '\0')
+                       continue;
+               CurEnv->e_to = oldto;
+               message(Arpa_Info, "%s to %s", msg, buf);
+               AliasLevel++;
+               sendtolist(buf, ctladdr, sendq);
+               AliasLevel--;
+       }
+
+       (void) fclose(fp);
+       FileName = oldfilename;
+       LineNumber = oldlinenumber;
+}
+\f/*
+**  SENDTOARGV -- send to an argument vector.
+**
+**     Parameters:
+**             argv -- argument vector to send to.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             puts all addresses on the argument vector onto the
+**                     send queue.
+*/
+
+sendtoargv(argv)
+       register char **argv;
+{
+       register char *p;
+
+       while ((p = *argv++) != NULL)
+       {
+               if (argv[0] != NULL && argv[1] != NULL && !strcasecmp(argv[0], "at"))
+               {
+                       char nbuf[MAXNAME];
+
+                       if (strlen(p) + strlen(argv[1]) + 2 > sizeof nbuf)
+                               usrerr("address overflow");
+                       else
+                       {
+                               (void) strcpy(nbuf, p);
+                               (void) strcat(nbuf, "@");
+                               (void) strcat(nbuf, argv[1]);
+                               p = newstr(nbuf);
+                               argv += 2;
+                       }
+               }
+               sendtolist(p, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
+       }
+}
+\f/*
+**  GETCTLADDR -- get controlling address from an address header.
+**
+**     If none, get one corresponding to the effective userid.
+**
+**     Parameters:
+**             a -- the address to find the controller of.
+**
+**     Returns:
+**             the controlling address.
+**
+**     Side Effects:
+**             none.
+*/
+
+ADDRESS *
+getctladdr(a)
+       register ADDRESS *a;
+{
+       while (a != NULL && !bitset(QGOODUID, a->q_flags))
+               a = a->q_alias;
+       return (a);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/savemail.c b/usr/src/usr.sbin/sendmail/src/savemail.c
new file mode 100644 (file)
index 0000000..1a7fbe2
--- /dev/null
@@ -0,0 +1,541 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)savemail.c 5.14 (Berkeley) 8/29/90";
+#endif /* not lint */
+
+# include <sys/types.h>
+# include <pwd.h>
+# include "sendmail.h"
+
+/*
+**  SAVEMAIL -- Save mail on error
+**
+**     If mailing back errors, mail it back to the originator
+**     together with an error message; otherwise, just put it in
+**     dead.letter in the user's home directory (if he exists on
+**     this machine).
+**
+**     Parameters:
+**             e -- the envelope containing the message in error.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             Saves the letter, by writing or mailing it back to the
+**             sender, or by putting it in dead.letter in her home
+**             directory.
+*/
+
+/* defines for state machine */
+# define ESM_REPORT    0       /* report to sender's terminal */
+# define ESM_MAIL      1       /* mail back to sender */
+# define ESM_QUIET     2       /* messages have already been returned */
+# define ESM_DEADLETTER        3       /* save in ~/dead.letter */
+# define ESM_POSTMASTER        4       /* return to postmaster */
+# define ESM_USRTMP    5       /* save in /usr/tmp/dead.letter */
+# define ESM_PANIC     6       /* leave the locked queue/transcript files */
+# define ESM_DONE      7       /* the message is successfully delivered */
+
+
+savemail(e)
+       register ENVELOPE *e;
+{
+       register struct passwd *pw;
+       register FILE *fp;
+       int state;
+       auto ADDRESS *q;
+       char buf[MAXLINE+1];
+       extern struct passwd *getpwnam();
+       register char *p;
+       extern char *ttypath();
+       typedef int (*fnptr)();
+
+       if (tTd(6, 1))
+               printf("\nsavemail, ErrorMode = %c\n", ErrorMode);
+
+       if (bitset(EF_RESPONSE, e->e_flags))
+               return;
+       if (e->e_class < 0)
+       {
+               message(Arpa_Info, "Dumping junk mail");
+               return;
+       }
+       ForceMail = TRUE;
+       e->e_flags &= ~EF_FATALERRS;
+
+       /*
+       **  In the unhappy event we don't know who to return the mail
+       **  to, make someone up.
+       */
+
+       if (e->e_from.q_paddr == NULL)
+       {
+               if (parseaddr("root", &e->e_from, 0, '\0') == NULL)
+               {
+                       syserr("Cannot parse root!");
+                       ExitStat = EX_SOFTWARE;
+                       finis();
+               }
+       }
+       e->e_to = NULL;
+
+       /*
+       **  Basic state machine.
+       **
+       **      This machine runs through the following states:
+       **
+       **      ESM_QUIET       Errors have already been printed iff the
+       **                      sender is local.
+       **      ESM_REPORT      Report directly to the sender's terminal.
+       **      ESM_MAIL        Mail response to the sender.
+       **      ESM_DEADLETTER  Save response in ~/dead.letter.
+       **      ESM_POSTMASTER  Mail response to the postmaster.
+       **      ESM_PANIC       Save response anywhere possible.
+       */
+
+       /* determine starting state */
+       switch (ErrorMode)
+       {
+         case EM_WRITE:
+               state = ESM_REPORT;
+               break;
+
+         case EM_BERKNET:
+               /* mail back, but return o.k. exit status */
+               ExitStat = EX_OK;
+
+               /* fall through.... */
+
+         case EM_MAIL:
+               state = ESM_MAIL;
+               break;
+
+         case EM_PRINT:
+         case '\0':
+               state = ESM_QUIET;
+               break;
+
+         case EM_QUIET:
+               /* no need to return anything at all */
+               return;
+
+         default:
+               syserr("savemail: ErrorMode x%x\n");
+               state = ESM_MAIL;
+               break;
+       }
+
+       while (state != ESM_DONE)
+       {
+               if (tTd(6, 5))
+                       printf("  state %d\n", state);
+
+               switch (state)
+               {
+                 case ESM_QUIET:
+                       if (e->e_from.q_mailer == LocalMailer)
+                               state = ESM_DEADLETTER;
+                       else
+                               state = ESM_MAIL;
+                       break;
+
+                 case ESM_REPORT:
+
+                       /*
+                       **  If the user is still logged in on the same terminal,
+                       **  then write the error messages back to hir (sic).
+                       */
+
+                       p = ttypath();
+                       if (p == NULL || freopen(p, "w", stdout) == NULL)
+                       {
+                               state = ESM_MAIL;
+                               break;
+                       }
+
+                       expand("\001n", buf, &buf[sizeof buf - 1], e);
+                       printf("\r\nMessage from %s...\r\n", buf);
+                       printf("Errors occurred while sending mail.\r\n");
+                       if (e->e_xfp != NULL)
+                       {
+                               (void) fflush(e->e_xfp);
+                               fp = fopen(queuename(e, 'x'), "r");
+                       }
+                       else
+                               fp = NULL;
+                       if (fp == NULL)
+                       {
+                               syserr("Cannot open %s", queuename(e, 'x'));
+                               printf("Transcript of session is unavailable.\r\n");
+                       }
+                       else
+                       {
+                               printf("Transcript follows:\r\n");
+                               while (fgets(buf, sizeof buf, fp) != NULL &&
+                                      !ferror(stdout))
+                                       fputs(buf, stdout);
+                               (void) fclose(fp);
+                       }
+                       printf("Original message will be saved in dead.letter.\r\n");
+                       state = ESM_DEADLETTER;
+                       break;
+
+                 case ESM_MAIL:
+                 case ESM_POSTMASTER:
+                       /*
+                       **  If mailing back, do it.
+                       **      Throw away all further output.  Don't alias,
+                       **      since this could cause loops, e.g., if joe
+                       **      mails to joe@x, and for some reason the network
+                       **      for @x is down, then the response gets sent to
+                       **      joe@x, which gives a response, etc.  Also force
+                       **      the mail to be delivered even if a version of
+                       **      it has already been sent to the sender.
+                       */
+
+                       if (state == ESM_MAIL)
+                       {
+                               if (e->e_errorqueue == NULL)
+                                       sendtolist(e->e_from.q_paddr,
+                                               (ADDRESS *) NULL,
+                                               &e->e_errorqueue);
+
+                               /* deliver a cc: to the postmaster if desired */
+                               if (PostMasterCopy != NULL)
+                                       sendtolist(PostMasterCopy,
+                                               (ADDRESS *) NULL,
+                                               &e->e_errorqueue);
+                               q = e->e_errorqueue;
+                       }
+                       else
+                       {
+                               if (parseaddr("postmaster", q, 0, '\0') == NULL)
+                               {
+                                       syserr("cannot parse postmaster!");
+                                       ExitStat = EX_SOFTWARE;
+                                       state = ESM_USRTMP;
+                                       break;
+                               }
+                       }
+                       if (returntosender(e->e_message != NULL ? e->e_message :
+                                          "Unable to deliver mail",
+                                          q, TRUE) == 0)
+                       {
+                               state = ESM_DONE;
+                               break;
+                       }
+
+                       state = state == ESM_MAIL ? ESM_POSTMASTER : ESM_USRTMP;
+                       break;
+
+                 case ESM_DEADLETTER:
+                       /*
+                       **  Save the message in dead.letter.
+                       **      If we weren't mailing back, and the user is
+                       **      local, we should save the message in
+                       **      ~/dead.letter so that the poor person doesn't
+                       **      have to type it over again -- and we all know
+                       **      what poor typists UNIX users are.
+                       */
+
+                       p = NULL;
+                       if (e->e_from.q_mailer == LocalMailer)
+                       {
+                               if (e->e_from.q_home != NULL)
+                                       p = e->e_from.q_home;
+                               else if ((pw = getpwnam(e->e_from.q_user)) != NULL)
+                                       p = pw->pw_dir;
+                       }
+                       if (p == NULL)
+                       {
+                               syserr("Can't return mail to %s", e->e_from.q_paddr);
+                               state = ESM_MAIL;
+                               break;
+                       }
+                       if (e->e_dfp != NULL)
+                       {
+                               auto ADDRESS *q;
+                               bool oldverb = Verbose;
+
+                               /* we have a home directory; open dead.letter */
+                               define('z', p, e);
+                               expand("\001z/dead.letter", buf, &buf[sizeof buf - 1], e);
+                               Verbose = TRUE;
+                               message(Arpa_Info, "Saving message in %s", buf);
+                               Verbose = oldverb;
+                               e->e_to = buf;
+                               q = NULL;
+                               sendtolist(buf, (ADDRESS *) NULL, &q);
+                               if (deliver(e, q) == 0)
+                                       state = ESM_DONE;
+                               else
+                                       state = ESM_MAIL;
+                       }
+                       else
+                       {
+                               /* no data file -- try mailing back */
+                               state = ESM_MAIL;
+                       }
+                       break;
+
+                 case ESM_USRTMP:
+                       /*
+                       **  Log the mail in /usr/tmp/dead.letter.
+                       */
+
+                       fp = dfopen("/usr/tmp/dead.letter", "a");
+                       if (fp == NULL)
+                       {
+                               state = ESM_PANIC;
+                               break;
+                       }
+
+                       putfromline(fp, ProgMailer);
+                       (*e->e_puthdr)(fp, ProgMailer, e);
+                       putline("\n", fp, ProgMailer);
+                       (*e->e_putbody)(fp, ProgMailer, e);
+                       putline("\n", fp, ProgMailer);
+                       (void) fflush(fp);
+                       state = ferror(fp) ? ESM_PANIC : ESM_DONE;
+                       (void) fclose(fp);
+                       break;
+
+                 default:
+                       syserr("savemail: unknown state %d", state);
+
+                       /* fall through ... */
+
+                 case ESM_PANIC:
+                       syserr("savemail: HELP!!!!");
+# ifdef LOG
+                       if (LogLevel >= 1)
+                               syslog(LOG_ALERT, "savemail: HELP!!!!");
+# endif LOG
+
+                       /* leave the locked queue & transcript files around */
+                       exit(EX_SOFTWARE);
+               }
+       }
+}
+\f/*
+**  RETURNTOSENDER -- return a message to the sender with an error.
+**
+**     Parameters:
+**             msg -- the explanatory message.
+**             returnq -- the queue of people to send the message to.
+**             sendbody -- if TRUE, also send back the body of the
+**                     message; otherwise just send the header.
+**
+**     Returns:
+**             zero -- if everything went ok.
+**             else -- some error.
+**
+**     Side Effects:
+**             Returns the current message to the sender via
+**             mail.
+*/
+
+static bool    SendBody;
+
+#define MAXRETURNS     6       /* max depth of returning messages */
+
+returntosender(msg, returnq, sendbody)
+       char *msg;
+       ADDRESS *returnq;
+       bool sendbody;
+{
+       char buf[MAXNAME];
+       extern putheader(), errbody();
+       register ENVELOPE *ee;
+       extern ENVELOPE *newenvelope();
+       ENVELOPE errenvelope;
+       static int returndepth;
+       register ADDRESS *q;
+
+       if (tTd(6, 1))
+       {
+               printf("Return To Sender: msg=\"%s\", depth=%d, CurEnv=%x,\n",
+                      msg, returndepth, CurEnv);
+               printf("\treturnq=");
+               printaddr(returnq, TRUE);
+       }
+
+       if (++returndepth >= MAXRETURNS)
+       {
+               if (returndepth != MAXRETURNS)
+                       syserr("returntosender: infinite recursion on %s", returnq->q_paddr);
+               /* don't "unrecurse" and fake a clean exit */
+               /* returndepth--; */
+               return (0);
+       }
+
+       SendBody = sendbody;
+       define('g', "\001f", CurEnv);
+       ee = newenvelope(&errenvelope);
+       define('a', "\001b", ee);
+       ee->e_puthdr = putheader;
+       ee->e_putbody = errbody;
+       ee->e_flags |= EF_RESPONSE;
+       if (!bitset(EF_OLDSTYLE, CurEnv->e_flags))
+               ee->e_flags &= ~EF_OLDSTYLE;
+       ee->e_sendqueue = returnq;
+       openxscript(ee);
+       for (q = returnq; q != NULL; q = q->q_next)
+       {
+               if (q->q_alias == NULL)
+                       addheader("to", q->q_paddr, ee);
+       }
+
+       (void) sprintf(buf, "Returned mail: %s", msg);
+       addheader("subject", buf, ee);
+
+       /* fake up an address header for the from person */
+       expand("\001n", buf, &buf[sizeof buf - 1], CurEnv);
+       if (parseaddr(buf, &ee->e_from, -1, '\0') == NULL)
+       {
+               syserr("Can't parse myself!");
+               ExitStat = EX_SOFTWARE;
+               returndepth--;
+               return (-1);
+       }
+       loweraddr(&ee->e_from);
+
+       /* push state into submessage */
+       CurEnv = ee;
+       define('f', "\001n", ee);
+       define('x', "Mail Delivery Subsystem", ee);
+       eatheader(ee);
+
+       /* actually deliver the error message */
+       sendall(ee, SM_DEFAULT);
+
+       /* restore state */
+       dropenvelope(ee);
+       CurEnv = CurEnv->e_parent;
+       returndepth--;
+
+       /* should check for delivery errors here */
+       return (0);
+}
+\f/*
+**  ERRBODY -- output the body of an error message.
+**
+**     Typically this is a copy of the transcript plus a copy of the
+**     original offending message.
+**
+**     Parameters:
+**             fp -- the output file.
+**             m -- the mailer to output to.
+**             e -- the envelope we are working in.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             Outputs the body of an error message.
+*/
+
+errbody(fp, m, e)
+       register FILE *fp;
+       register struct mailer *m;
+       register ENVELOPE *e;
+{
+       register FILE *xfile;
+       char buf[MAXLINE];
+       char *p;
+
+       /*
+       **  Output transcript of errors
+       */
+
+       (void) fflush(stdout);
+       p = queuename(e->e_parent, 'x');
+       if ((xfile = fopen(p, "r")) == NULL)
+       {
+               syserr("Cannot open %s", p);
+               fprintf(fp, "  ----- Transcript of session is unavailable -----\n");
+       }
+       else
+       {
+               fprintf(fp, "   ----- Transcript of session follows -----\n");
+               if (e->e_xfp != NULL)
+                       (void) fflush(e->e_xfp);
+               while (fgets(buf, sizeof buf, xfile) != NULL)
+                       putline(buf, fp, m);
+               (void) fclose(xfile);
+       }
+       errno = 0;
+
+       /*
+       **  Output text of original message
+       */
+
+       if (NoReturn)
+               fprintf(fp, "\n   ----- Return message suppressed -----\n\n");
+       else if (e->e_parent->e_dfp != NULL)
+       {
+               if (SendBody)
+               {
+                       putline("\n", fp, m);
+                       putline("   ----- Unsent message follows -----\n", fp, m);
+                       (void) fflush(fp);
+                       putheader(fp, m, e->e_parent);
+                       putline("\n", fp, m);
+                       putbody(fp, m, e->e_parent);
+               }
+               else
+               {
+                       putline("\n", fp, m);
+                       putline("  ----- Message header follows -----\n", fp, m);
+                       (void) fflush(fp);
+                       putheader(fp, m, e->e_parent);
+               }
+       }
+       else
+       {
+               putline("\n", fp, m);
+               putline("  ----- No message was collected -----\n", fp, m);
+               putline("\n", fp, m);
+       }
+
+       /*
+       **  Cleanup and exit
+       */
+
+       if (errno != 0)
+               syserr("errbody: I/O error");
+}
diff --git a/usr/src/usr.sbin/sendmail/src/sendmail.h b/usr/src/usr.sbin/sendmail/src/sendmail.h
new file mode 100644 (file)
index 0000000..ac757f6
--- /dev/null
@@ -0,0 +1,604 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)sendmail.h  5.17 (Berkeley) 3/12/91
+ */
+
+/*
+**  SENDMAIL.H -- Global definitions for sendmail.
+*/
+
+# ifdef _DEFINE
+# define EXTERN
+# ifndef lint
+static char SmailSccsId[] =    "@(#)sendmail.h 5.17            3/12/91";
+# endif lint
+# else  _DEFINE
+# define EXTERN extern
+# endif _DEFINE
+
+# include <stdio.h>
+# include <ctype.h>
+# include <setjmp.h>
+# include "conf.h"
+# include "useful.h"
+
+# ifdef LOG
+# include <sys/syslog.h>
+# endif LOG
+
+# ifdef DAEMON
+# ifdef VMUNIX
+# include <sys/socket.h>
+# include <netinet/in.h>
+# endif VMUNIX
+# endif DAEMON
+
+
+# define PSBUFSIZE     (MAXNAME + MAXATOM)     /* size of prescan buffer */
+
+
+/*
+**  Data structure for bit maps.
+**
+**     Each bit in this map can be referenced by an ascii character.
+**     This is 128 possible bits, or 12 8-bit bytes.
+*/
+
+#define BITMAPBYTES    16      /* number of bytes in a bit map */
+#define BYTEBITS       8       /* number of bits in a byte */
+
+/* internal macros */
+#define _BITWORD(bit)  (bit / (BYTEBITS * sizeof (int)))
+#define _BITBIT(bit)   (1 << (bit % (BYTEBITS * sizeof (int))))
+
+typedef int    BITMAP[BITMAPBYTES / sizeof (int)];
+
+/* test bit number N */
+#define bitnset(bit, map)      ((map)[_BITWORD(bit)] & _BITBIT(bit))
+
+/* set bit number N */
+#define setbitn(bit, map)      (map)[_BITWORD(bit)] |= _BITBIT(bit)
+
+/* clear bit number N */
+#define clrbitn(bit, map)      (map)[_BITWORD(bit)] &= ~_BITBIT(bit)
+
+/* clear an entire bit map */
+#define clrbitmap(map)         bzero((char *) map, BITMAPBYTES)
+\f/*
+**  Address structure.
+**     Addresses are stored internally in this structure.
+*/
+
+struct address
+{
+       char            *q_paddr;       /* the printname for the address */
+       char            *q_user;        /* user name */
+       char            *q_ruser;       /* real user name, or NULL if q_user */
+       char            *q_host;        /* host name */
+       struct mailer   *q_mailer;      /* mailer to use */
+       u_short         q_flags;        /* status flags, see below */
+       short           q_uid;          /* user-id of receiver (if known) */
+       short           q_gid;          /* group-id of receiver (if known) */
+       char            *q_home;        /* home dir (local mailer only) */
+       char            *q_fullname;    /* full name if known */
+       struct address  *q_next;        /* chain */
+       struct address  *q_alias;       /* address this results from */
+       struct address  *q_tchain;      /* temporary use chain */
+       time_t          q_timeout;      /* timeout for this address */
+};
+
+typedef struct address ADDRESS;
+
+# define QDONTSEND     000001  /* don't send to this address */
+# define QBADADDR      000002  /* this address is verified bad */
+# define QGOODUID      000004  /* the q_uid q_gid fields are good */
+# define QPRIMARY      000010  /* set from argv */
+# define QQUEUEUP      000020  /* queue for later transmission */
+# define QSENT         000040  /* has been successfully delivered */
+\f/*
+**  Mailer definition structure.
+**     Every mailer known to the system is declared in this
+**     structure.  It defines the pathname of the mailer, some
+**     flags associated with it, and the argument vector to
+**     pass to it.  The flags are defined in conf.c
+**
+**     The argument vector is expanded before actual use.  All
+**     words except the first are passed through the macro
+**     processor.
+*/
+
+struct mailer
+{
+       char    *m_name;        /* symbolic name of this mailer */
+       char    *m_mailer;      /* pathname of the mailer to use */
+       BITMAP  m_flags;        /* status flags, see below */
+       short   m_mno;          /* mailer number internally */
+       char    **m_argv;       /* template argument vector */
+       short   m_s_rwset;      /* rewriting set for sender addresses */
+       short   m_r_rwset;      /* rewriting set for recipient addresses */
+       char    *m_eol;         /* end of line string */
+       long    m_maxsize;      /* size limit on message to this mailer */
+};
+
+typedef struct mailer  MAILER;
+
+/* bits for m_flags */
+# define M_CANONICAL   'C'     /* make addresses canonical "u@dom" */
+# define M_EXPENSIVE   'e'     /* it costs to use this mailer.... */
+# define M_ESCFROM     'E'     /* escape From lines to >From */
+# define M_FOPT                'f'     /* mailer takes picky -f flag */
+# define M_HST_UPPER   'h'     /* preserve host case distinction */
+# define M_INTERNAL    'I'     /* SMTP to another sendmail site */
+# define M_LOCAL       'l'     /* delivery is to this host */
+# define M_LIMITS      'L'     /* must enforce SMTP line limits */
+# define M_MUSER       'm'     /* can handle multiple users at once */
+# define M_NHDR                'n'     /* don't insert From line */
+# define M_FROMPATH    'p'     /* use reverse-path in MAIL FROM: */
+# define M_ROPT                'r'     /* mailer takes picky -r flag */
+# define M_SECURE_PORT 'R'     /* try to send on a reserved TCP port */
+# define M_STRIPQ      's'     /* strip quote chars from user/host */
+# define M_RESTR       'S'     /* must be daemon to execute */
+# define M_USR_UPPER   'u'     /* preserve user case distinction */
+# define M_UGLYUUCP    'U'     /* this wants an ugly UUCP from line */
+# define M_XDOT                'X'     /* use hidden-dot algorithm */
+
+EXTERN MAILER  *Mailer[MAXMAILERS+1];
+
+EXTERN MAILER  *LocalMailer;           /* ptr to local mailer */
+EXTERN MAILER  *ProgMailer;            /* ptr to program mailer */
+\f/*
+**  Header structure.
+**     This structure is used internally to store header items.
+*/
+
+struct header
+{
+       char            *h_field;       /* the name of the field */
+       char            *h_value;       /* the value of that field */
+       struct header   *h_link;        /* the next header */
+       u_short         h_flags;        /* status bits, see below */
+       BITMAP          h_mflags;       /* m_flags bits needed */
+};
+
+typedef struct header  HDR;
+
+/*
+**  Header information structure.
+**     Defined in conf.c, this struct declares the header fields
+**     that have some magic meaning.
+*/
+
+struct hdrinfo
+{
+       char    *hi_field;      /* the name of the field */
+       u_short hi_flags;       /* status bits, see below */
+};
+
+extern struct hdrinfo  HdrInfo[];
+
+/* bits for h_flags and hi_flags */
+# define H_EOH         00001   /* this field terminates header */
+# define H_RCPT                00002   /* contains recipient addresses */
+# define H_DEFAULT     00004   /* if another value is found, drop this */
+# define H_RESENT      00010   /* this address is a "Resent-..." address */
+# define H_CHECK       00020   /* check h_mflags against m_flags */
+# define H_ACHECK      00040   /* ditto, but always (not just default) */
+# define H_FORCE       00100   /* force this field, even if default */
+# define H_TRACE       00200   /* this field contains trace information */
+# define H_FROM                00400   /* this is a from-type field */
+# define H_VALID       01000   /* this field has a validated value */
+\f/*
+**  Envelope structure.
+**     This structure defines the message itself.  There is usually
+**     only one of these -- for the message that we originally read
+**     and which is our primary interest -- but other envelopes can
+**     be generated during processing.  For example, error messages
+**     will have their own envelope.
+*/
+
+struct envelope
+{
+       HDR             *e_header;      /* head of header list */
+       long            e_msgpriority;  /* adjusted priority of this message */
+       time_t          e_ctime;        /* time message appeared in the queue */
+       char            *e_to;          /* the target person */
+       char            *e_receiptto;   /* return receipt address */
+       ADDRESS         e_from;         /* the person it is from */
+       char            **e_fromdomain; /* the domain part of the sender */
+       ADDRESS         *e_sendqueue;   /* list of message recipients */
+       ADDRESS         *e_errorqueue;  /* the queue for error responses */
+       long            e_msgsize;      /* size of the message in bytes */
+       int             e_nrcpts;       /* number of recipients */
+       short           e_class;        /* msg class (priority, junk, etc.) */
+       short           e_flags;        /* flags, see below */
+       short           e_hopcount;     /* number of times processed */
+       int             (*e_puthdr)();  /* function to put header of message */
+       int             (*e_putbody)(); /* function to put body of message */
+       struct envelope *e_parent;      /* the message this one encloses */
+       struct envelope *e_sibling;     /* the next envelope of interest */
+       char            *e_df;          /* location of temp file */
+       FILE            *e_dfp;         /* temporary file */
+       char            *e_id;          /* code for this entry in queue */
+       FILE            *e_xfp;         /* transcript file */
+       char            *e_message;     /* error message */
+       char            *e_macro[128];  /* macro definitions */
+};
+
+typedef struct envelope        ENVELOPE;
+
+/* values for e_flags */
+#define EF_OLDSTYLE    000001          /* use spaces (not commas) in hdrs */
+#define EF_INQUEUE     000002          /* this message is fully queued */
+#define EF_TIMEOUT     000004          /* this message is too old */
+#define EF_CLRQUEUE    000010          /* disk copy is no longer needed */
+#define EF_SENDRECEIPT 000020          /* send a return receipt */
+#define EF_FATALERRS   000040          /* fatal errors occured */
+#define EF_KEEPQUEUE   000100          /* keep queue files always */
+#define EF_RESPONSE    000200          /* this is an error or return receipt */
+#define EF_RESENT      000400          /* this message is being forwarded */
+
+EXTERN ENVELOPE        *CurEnv;        /* envelope currently being processed */
+\f/*
+**  Message priority classes.
+**
+**     The message class is read directly from the Priority: header
+**     field in the message.
+**
+**     CurEnv->e_msgpriority is the number of bytes in the message plus
+**     the creation time (so that jobs ``tend'' to be ordered correctly),
+**     adjusted by the message class, the number of recipients, and the
+**     amount of time the message has been sitting around.  This number
+**     is used to order the queue.  Higher values mean LOWER priority.
+**
+**     Each priority class point is worth WkClassFact priority points;
+**     each recipient is worth WkRecipFact priority points.  Each time
+**     we reprocess a message the priority is adjusted by WkTimeFact.
+**     WkTimeFact should normally decrease the priority so that jobs
+**     that have historically failed will be run later; thanks go to
+**     Jay Lepreau at Utah for pointing out the error in my thinking.
+**
+**     The "class" is this number, unadjusted by the age or size of
+**     this message.  Classes with negative representations will have
+**     error messages thrown away if they are not local.
+*/
+
+struct priority
+{
+       char    *pri_name;      /* external name of priority */
+       int     pri_val;        /* internal value for same */
+};
+
+EXTERN struct priority Priorities[MAXPRIORITIES];
+EXTERN int             NumPriorities;  /* pointer into Priorities */
+\f/*
+**  Rewrite rules.
+*/
+
+struct rewrite
+{
+       char    **r_lhs;        /* pattern match */
+       char    **r_rhs;        /* substitution value */
+       struct rewrite  *r_next;/* next in chain */
+};
+
+EXTERN struct rewrite  *RewriteRules[MAXRWSETS];
+
+/*
+**  Special characters in rewriting rules.
+**     These are used internally only.
+**     The COND* rules are actually used in macros rather than in
+**             rewriting rules, but are given here because they
+**             cannot conflict.
+*/
+
+/* left hand side items */
+# define MATCHZANY     '\020'  /* match zero or more tokens */
+# define MATCHANY      '\021'  /* match one or more tokens */
+# define MATCHONE      '\022'  /* match exactly one token */
+# define MATCHCLASS    '\023'  /* match one token in a class */
+# define MATCHNCLASS   '\024'  /* match anything not in class */
+# define MATCHREPL     '\025'  /* replacement on RHS for above */
+
+/* right hand side items */
+# define CANONNET      '\026'  /* canonical net, next token */
+# define CANONHOST     '\027'  /* canonical host, next token */
+# define CANONUSER     '\030'  /* canonical user, next N tokens */
+# define CALLSUBR      '\031'  /* call another rewriting set */
+
+/* conditionals in macros */
+# define CONDIF                '\032'  /* conditional if-then */
+# define CONDELSE      '\033'  /* conditional else */
+# define CONDFI                '\034'  /* conditional fi */
+
+/* bracket characters for host name lookup */
+# define HOSTBEGIN     '\035'  /* hostname lookup begin */
+# define HOSTEND       '\036'  /* hostname lookup end */
+
+/* \001 is also reserved as the macro expansion character */
+\f/*
+**  Information about hosts that we have looked up recently.
+**
+**     This stuff is 4.2/3bsd specific.
+*/
+
+# ifdef DAEMON
+# ifdef VMUNIX
+
+# define HOSTINFO      struct hostinfo
+
+HOSTINFO
+{
+       char            *ho_name;       /* name of this host */
+       struct in_addr  ho_inaddr;      /* internet address */
+       short           ho_flags;       /* flag bits, see below */
+       short           ho_errno;       /* error number on last connection */
+       short           ho_exitstat;    /* exit status from last connection */
+};
+
+
+/* flag bits */
+#define HOF_VALID      00001           /* this entry is valid */
+
+# endif VMUNIX
+# endif DAEMON
+\f/*
+**  Symbol table definitions
+*/
+
+struct symtab
+{
+       char            *s_name;        /* name to be entered */
+       char            s_type;         /* general type (see below) */
+       struct symtab   *s_next;        /* pointer to next in chain */
+       union
+       {
+               BITMAP          sv_class;       /* bit-map of word classes */
+               ADDRESS         *sv_addr;       /* pointer to address header */
+               MAILER          *sv_mailer;     /* pointer to mailer */
+               char            *sv_alias;      /* alias */
+# ifdef HOSTINFO
+               HOSTINFO        sv_host;        /* host information */
+# endif HOSTINFO
+       }       s_value;
+};
+
+typedef struct symtab  STAB;
+
+/* symbol types */
+# define ST_UNDEF      0       /* undefined type */
+# define ST_CLASS      1       /* class map */
+# define ST_ADDRESS    2       /* an address in parsed format */
+# define ST_MAILER     3       /* a mailer header */
+# define ST_ALIAS      4       /* an alias */
+# define ST_HOST       5       /* host information */
+
+# define s_class       s_value.sv_class
+# define s_address     s_value.sv_addr
+# define s_mailer      s_value.sv_mailer
+# define s_alias       s_value.sv_alias
+# define s_host                s_value.sv_host
+
+extern STAB    *stab();
+
+/* opcodes to stab */
+# define ST_FIND       0       /* find entry */
+# define ST_ENTER      1       /* enter if not there */
+\f/*
+**  STRUCT EVENT -- event queue.
+**
+**     Maintained in sorted order.
+**
+**     We store the pid of the process that set this event to insure
+**     that when we fork we will not take events intended for the parent.
+*/
+
+struct event
+{
+       time_t          ev_time;        /* time of the function call */
+       int             (*ev_func)();   /* function to call */
+       int             ev_arg;         /* argument to ev_func */
+       int             ev_pid;         /* pid that set this event */
+       struct event    *ev_link;       /* link to next item */
+};
+
+typedef struct event   EVENT;
+
+EXTERN EVENT   *EventQueue;            /* head of event queue */
+\f/*
+**  Operation, send, and error modes
+**
+**     The operation mode describes the basic operation of sendmail.
+**     This can be set from the command line, and is "send mail" by
+**     default.
+**
+**     The send mode tells how to send mail.  It can be set in the
+**     configuration file.  It's setting determines how quickly the
+**     mail will be delivered versus the load on your system.  If the
+**     -v (verbose) flag is given, it will be forced to SM_DELIVER
+**     mode.
+**
+**     The error mode tells how to return errors.
+*/
+
+EXTERN char    OpMode;         /* operation mode, see below */
+
+#define MD_DELIVER     'm'             /* be a mail sender */
+#define MD_ARPAFTP     'a'             /* old-style arpanet protocols */
+#define MD_SMTP                's'             /* run SMTP on standard input */
+#define MD_DAEMON      'd'             /* run as a daemon */
+#define MD_VERIFY      'v'             /* verify: don't collect or deliver */
+#define MD_TEST                't'             /* test mode: resolve addrs only */
+#define MD_INITALIAS   'i'             /* initialize alias database */
+#define MD_PRINT       'p'             /* print the queue */
+#define MD_FREEZE      'z'             /* freeze the configuration file */
+
+
+EXTERN char    SendMode;       /* send mode, see below */
+
+#define SM_DELIVER     'i'             /* interactive delivery */
+#define SM_QUICKD      'j'             /* deliver w/o queueing */
+#define SM_FORK                'b'             /* deliver in background */
+#define SM_QUEUE       'q'             /* queue, don't deliver */
+#define SM_VERIFY      'v'             /* verify only (used internally) */
+
+/* used only as a parameter to sendall */
+#define SM_DEFAULT     '\0'            /* unspecified, use SendMode */
+
+
+EXTERN char    ErrorMode;      /* error mode, see below */
+
+#define EM_PRINT       'p'             /* print errors */
+#define EM_MAIL                'm'             /* mail back errors */
+#define EM_WRITE       'w'             /* write back errors */
+#define EM_BERKNET     'e'             /* special berknet processing */
+#define EM_QUIET       'q'             /* don't print messages (stat only) */
+
+/* offset used to issure that the error messages for name server error
+ * codes are unique.
+ */
+#define        MAX_ERRNO       100
+\f/*
+**  Global variables.
+*/
+
+EXTERN bool    FromFlag;       /* if set, "From" person is explicit */
+EXTERN bool    NoAlias;        /* if set, don't do any aliasing */
+EXTERN bool    ForceMail;      /* if set, mail even if already got a copy */
+EXTERN bool    MeToo;          /* send to the sender also */
+EXTERN bool    IgnrDot;        /* don't let dot end messages */
+EXTERN bool    SaveFrom;       /* save leading "From" lines */
+EXTERN bool    Verbose;        /* set if blow-by-blow desired */
+EXTERN bool    GrabTo;         /* if set, get recipients from msg */
+EXTERN bool    NoReturn;       /* don't return letter to sender */
+EXTERN bool    SuprErrs;       /* set if we are suppressing errors */
+EXTERN bool    QueueRun;       /* currently running message from the queue */
+EXTERN bool    HoldErrs;       /* only output errors to transcript */
+EXTERN bool    NoConnect;      /* don't connect to non-local mailers */
+EXTERN bool    SuperSafe;      /* be extra careful, even if expensive */
+EXTERN bool    ForkQueueRuns;  /* fork for each job when running the queue */
+EXTERN bool    AutoRebuild;    /* auto-rebuild the alias database as needed */
+EXTERN bool    CheckAliases;   /* parse addresses during newaliases */
+EXTERN bool    UseNameServer;  /* use internet domain name server */
+EXTERN int     SafeAlias;      /* minutes to wait until @:@ in alias file */
+EXTERN time_t  TimeOut;        /* time until timeout */
+EXTERN FILE    *InChannel;     /* input connection */
+EXTERN FILE    *OutChannel;    /* output connection */
+EXTERN int     RealUid;        /* when Daemon, real uid of caller */
+EXTERN int     RealGid;        /* when Daemon, real gid of caller */
+EXTERN int     DefUid;         /* default uid to run as */
+EXTERN char    *DefUser;       /* default user to run as (from DefUid) */
+EXTERN int     DefGid;         /* default gid to run as */
+EXTERN int     OldUmask;       /* umask when sendmail starts up */
+EXTERN int     Errors;         /* set if errors (local to single pass) */
+EXTERN int     ExitStat;       /* exit status code */
+EXTERN int     AliasLevel;     /* depth of aliasing */
+EXTERN int     MotherPid;      /* proc id of parent process */
+EXTERN int     LineNumber;     /* line number in current input */
+EXTERN time_t  ReadTimeout;    /* timeout on reads */
+EXTERN int     LogLevel;       /* level of logging to perform */
+EXTERN int     FileMode;       /* mode on files */
+EXTERN int     QueueLA;        /* load average starting forced queueing */
+EXTERN int     RefuseLA;       /* load average refusing connections are */
+EXTERN int     QueueFactor;    /* slope of queue function */
+EXTERN time_t  QueueIntvl;     /* intervals between running the queue */
+EXTERN char    *AliasFile;     /* location of alias file */
+EXTERN char    *HelpFile;      /* location of SMTP help file */
+EXTERN char    *StatFile;      /* location of statistics summary */
+EXTERN char    *QueueDir;      /* location of queue directory */
+EXTERN char    *FileName;      /* name to print on error messages */
+EXTERN char    *SmtpPhase;     /* current phase in SMTP processing */
+EXTERN char    *MyHostName;    /* name of this host for SMTP messages */
+EXTERN char    *RealHostName;  /* name of host we are talking to */
+EXTERN struct  sockaddr_in RealHostAddr;/* address of host we are talking to */
+EXTERN char    *CurHostName;   /* current host we are dealing with */
+EXTERN jmp_buf TopFrame;       /* branch-to-top-of-loop-on-error frame */
+EXTERN bool    QuickAbort;     /*  .... but only if we want a quick abort */
+extern char    *ConfFile;      /* location of configuration file [conf.c] */
+extern char    *FreezeFile;    /* location of frozen memory image [conf.c] */
+extern char    Arpa_Info[];    /* the reply code for Arpanet info [conf.c] */
+extern ADDRESS NullAddress;    /* a null (template) address [main.c] */
+EXTERN char    SpaceSub;       /* substitution for <lwsp> */
+EXTERN int     WkClassFact;    /* multiplier for message class -> priority */
+EXTERN int     WkRecipFact;    /* multiplier for # of recipients -> priority */
+EXTERN int     WkTimeFact;     /* priority offset each time this job is run */
+EXTERN int     CheckPointLimit;        /* deliveries before checkpointing */
+EXTERN int     Nmx;                    /* number of MX RRs */
+EXTERN char    *PostMasterCopy;        /* address to get errs cc's */
+EXTERN char    *MxHosts[MAXMXHOSTS+1]; /* for MX RRs */
+EXTERN char    *TrustedUsers[MAXTRUST+1];      /* list of trusted users */
+EXTERN char    *UserEnviron[MAXUSERENVIRON+1]; /* saved user environment */
+EXTERN int     CheckpointInterval;     /* queue file checkpoint interval */
+\f/*
+**  Trace information
+*/
+
+/* trace vector and macros for debugging flags */
+EXTERN u_char  tTdvect[100];
+# define tTd(flag, level)      (tTdvect[flag] >= level)
+# define tTdlevel(flag)                (tTdvect[flag])
+\f/*
+**  Miscellaneous information.
+*/
+
+# include      <sysexits.h>
+
+
+/*
+**  Some in-line functions
+*/
+
+/* set exit status */
+#define setstat(s)     { \
+                               if (ExitStat == EX_OK || ExitStat == EX_TEMPFAIL) \
+                                       ExitStat = s; \
+                       }
+
+/* make a copy of a string */
+#define newstr(s)      strcpy(xalloc(strlen(s) + 1), s)
+
+#define STRUCTCOPY(s, d)       d = s
+
+
+/*
+**  Declarations of useful functions
+*/
+
+extern ADDRESS *parseaddr();
+extern char    *xalloc();
+extern bool    sameaddr();
+extern FILE    *dfopen();
+extern EVENT   *setevent();
+extern char    *sfgets();
+extern char    *queuename();
+extern time_t  curtime();
diff --git a/usr/src/usr.sbin/sendmail/src/stab.c b/usr/src/usr.sbin/sendmail/src/stab.c
new file mode 100644 (file)
index 0000000..aefbabf
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)stab.c     5.7 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+/*
+**  STAB -- manage the symbol table
+**
+**     Parameters:
+**             name -- the name to be looked up or inserted.
+**             type -- the type of symbol.
+**             op -- what to do:
+**                     ST_ENTER -- enter the name if not
+**                             already present.
+**                     ST_FIND -- find it only.
+**
+**     Returns:
+**             pointer to a STAB entry for this name.
+**             NULL if not found and not entered.
+**
+**     Side Effects:
+**             can update the symbol table.
+*/
+
+# define STABSIZE      400
+
+static STAB    *SymTab[STABSIZE];
+
+STAB *
+stab(name, type, op)
+       char *name;
+       int type;
+       int op;
+{
+       register STAB *s;
+       register STAB **ps;
+       register int hfunc;
+       register char *p;
+       extern char lower();
+
+       if (tTd(36, 5))
+               printf("STAB: %s %d ", name, type);
+
+       /*
+       **  Compute the hashing function
+       **
+       **      We could probably do better....
+       */
+
+       hfunc = type;
+       for (p = name; *p != '\0'; p++)
+               hfunc = (((hfunc << 7) | lower(*p)) & 077777) % STABSIZE;
+
+       if (tTd(36, 9))
+               printf("(hfunc=%d) ", hfunc);
+
+       ps = &SymTab[hfunc];
+       while ((s = *ps) != NULL && (strcasecmp(name, s->s_name) || s->s_type != type))
+               ps = &s->s_next;
+
+       /*
+       **  Dispose of the entry.
+       */
+
+       if (s != NULL || op == ST_FIND)
+       {
+               if (tTd(36, 5))
+               {
+                       if (s == NULL)
+                               printf("not found\n");
+                       else
+                       {
+                               long *lp = (long *) s->s_class;
+
+                               printf("type %d val %lx %lx %lx %lx\n",
+                                       s->s_type, lp[0], lp[1], lp[2], lp[3]);
+                       }
+               }
+               return (s);
+       }
+
+       /*
+       **  Make a new entry and link it in.
+       */
+
+       if (tTd(36, 5))
+               printf("entered\n");
+
+       /* make new entry */
+       s = (STAB *) xalloc(sizeof *s);
+       bzero((char *) s, sizeof *s);
+       s->s_name = newstr(name);
+       makelower(s->s_name);
+       s->s_type = type;
+
+       /* link it in */
+       *ps = s;
+
+       return (s);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/stats.c b/usr/src/usr.sbin/sendmail/src/stats.c
new file mode 100644 (file)
index 0000000..28e8d50
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)stats.c    5.11 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+# include "sendmail.h"
+# include "mailstats.h"
+
+struct statistics      Stat;
+
+#define ONE_K          1000            /* one thousand (twenty-four?) */
+#define KBYTES(x)      (((x) + (ONE_K - 1)) / ONE_K)
+\f/*
+**  MARKSTATS -- mark statistics
+*/
+
+markstats(e, to)
+       register ENVELOPE *e;
+       register ADDRESS *to;
+{
+       if (to == NULL)
+       {
+               if (e->e_from.q_mailer != NULL)
+               {
+                       Stat.stat_nf[e->e_from.q_mailer->m_mno]++;
+                       Stat.stat_bf[e->e_from.q_mailer->m_mno] +=
+                               KBYTES(CurEnv->e_msgsize);
+               }
+       }
+       else
+       {
+               Stat.stat_nt[to->q_mailer->m_mno]++;
+               Stat.stat_bt[to->q_mailer->m_mno] += KBYTES(CurEnv->e_msgsize);
+       }
+}
+\f/*
+**  POSTSTATS -- post statistics in the statistics file
+**
+**     Parameters:
+**             sfile -- the name of the statistics file.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             merges the Stat structure with the sfile file.
+*/
+
+poststats(sfile)
+       char *sfile;
+{
+       register int fd;
+       struct statistics stat;
+       extern off_t lseek();
+
+       if (sfile == NULL)
+               return;
+
+       (void) time(&Stat.stat_itime);
+       Stat.stat_size = sizeof Stat;
+
+       fd = open(sfile, 2);
+       if (fd < 0)
+       {
+               errno = 0;
+               return;
+       }
+       if (read(fd, (char *) &stat, sizeof stat) == sizeof stat &&
+           stat.stat_size == sizeof stat)
+       {
+               /* merge current statistics into statfile */
+               register int i;
+
+               for (i = 0; i < MAXMAILERS; i++)
+               {
+                       stat.stat_nf[i] += Stat.stat_nf[i];
+                       stat.stat_bf[i] += Stat.stat_bf[i];
+                       stat.stat_nt[i] += Stat.stat_nt[i];
+                       stat.stat_bt[i] += Stat.stat_bt[i];
+               }
+       }
+       else
+               bcopy((char *) &Stat, (char *) &stat, sizeof stat);
+
+       /* write out results */
+       (void) lseek(fd, (off_t) 0, 0);
+       (void) write(fd, (char *) &stat, sizeof stat);
+       (void) close(fd);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/sysexits.c b/usr/src/usr.sbin/sendmail/src/sysexits.c
new file mode 100644 (file)
index 0000000..4cc9c3d
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)sysexits.c 5.6 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+#include <sysexits.h>
+
+/*
+ *  SYSEXITS.C -- error messages corresponding to sysexits.h
+ */
+char *SysExMsg[] = {
+       /* 64 USAGE */          "500 Bad usage",
+       /* 65 DATAERR */        "501 Data format error",
+       /* 66 NOINPUT */        "550 Cannot open input",
+       /* 67 NOUSER */         "550 User unknown",
+       /* 68 NOHOST */         "550 Host unknown",
+       /* 69 UNAVAILABLE */    "554 Service unavailable",
+       /* 70 SOFTWARE */       "554 Internal error",
+       /* 71 OSERR */          "451 Operating system error",
+       /* 72 OSFILE */         "554 System file missing",
+       /* 73 CANTCREAT */      "550 Can't create output",
+       /* 74 IOERR */          "451 I/O error",
+       /* 75 TEMPFAIL */       "250 Deferred",
+       /* 76 PROTOCOL */       "554 Remote protocol error",
+       /* 77 NOPERM */         "550 Insufficient permission",
+       /* 78 CONFIG */         "554 Local configuration error",
+};
+
+int N_SysEx = sizeof(SysExMsg) / sizeof(SysExMsg[0]);
+
+/*
+ *  STATSTRING -- return string corresponding to an error status
+ *
+ *     Parameters:
+ *             stat -- the status to decode.
+ *
+ *     Returns:
+ *             The string corresponding to that status
+ *
+ *     Side Effects:
+ *             none.
+ */
+char *
+statstring(stat)
+       int stat;
+{
+       static char ebuf[50];
+
+       stat -= EX__BASE;
+       if (stat < 0 || stat >= N_SysEx) {
+               (void)sprintf(ebuf, "554 Unknown status %d", stat + EX__BASE);
+               return(ebuf);
+       }
+       return(SysExMsg[stat]);
+}
diff --git a/usr/src/usr.sbin/sendmail/src/trace.c b/usr/src/usr.sbin/sendmail/src/trace.c
new file mode 100644 (file)
index 0000000..45c7a6c
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)trace.c    5.6 (Berkeley) 6/1/90";
+#endif /* not lint */
+
+# include "sendmail.h"
+
+/*
+**  TtSETUP -- set up for trace package.
+**
+**     Parameters:
+**             vect -- pointer to trace vector.
+**             size -- number of flags in trace vector.
+**             defflags -- flags to set if no value given.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             environment is set up.
+*/
+
+u_char         *tTvect;
+int            tTsize;
+static char    *DefFlags;
+
+tTsetup(vect, size, defflags)
+       u_char *vect;
+       int size;
+       char *defflags;
+{
+       tTvect = vect;
+       tTsize = size;
+       DefFlags = defflags;
+}
+\f/*
+**  TtFLAG -- process an external trace flag description.
+**
+**     Parameters:
+**             s -- the trace flag.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             sets/clears trace flags.
+*/
+
+tTflag(s)
+       register char *s;
+{
+       int first, last;
+       register int i;
+
+       if (*s == '\0')
+               s = DefFlags;
+
+       for (;;)
+       {
+               /* find first flag to set */
+               i = 0;
+               while (isdigit(*s))
+                       i = i * 10 + (*s++ - '0');
+               first = i;
+
+               /* find last flag to set */
+               if (*s == '-')
+               {
+                       i = 0;
+                       while (isdigit(*++s))
+                               i = i * 10 + (*s - '0');
+               }
+               last = i;
+
+               /* find the level to set it to */
+               i = 1;
+               if (*s == '.')
+               {
+                       i = 0;
+                       while (isdigit(*++s))
+                               i = i * 10 + (*s - '0');
+               }
+
+               /* clean up args */
+               if (first >= tTsize)
+                       first = tTsize - 1;
+               if (last >= tTsize)
+                       last = tTsize - 1;
+
+               /* set the flags */
+               while (first <= last)
+                       tTvect[first++] = i;
+
+               /* more arguments? */
+               if (*s++ == '\0')
+                       return;
+       }
+}
diff --git a/usr/src/usr.sbin/sendmail/src/useful.h b/usr/src/usr.sbin/sendmail/src/useful.h
new file mode 100644 (file)
index 0000000..9a22d6a
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *     @(#)useful.h    4.6 (Berkeley) 6/1/90
+ */
+
+# include <sys/types.h>
+
+/* support for bool type */
+typedef char   bool;
+# define TRUE  1
+# define FALSE 0
+
+# ifndef NULL
+# define NULL  0
+# endif NULL
+
+/* bit hacking */
+# define bitset(bit, word)     (((word) & (bit)) != 0)
+
+/* some simple functions */
+# ifndef max
+# define max(a, b)     ((a) > (b) ? (a) : (b))
+# define min(a, b)     ((a) < (b) ? (a) : (b))
+# endif max
+
+/* assertions */
+# ifndef NASSERT
+# define ASSERT(expr, msg, parm)\
+       if (!(expr))\
+       {\
+               fprintf(stderr, "assertion botch: %s:%d: ", __FILE__, __LINE__);\
+               fprintf(stderr, msg, parm);\
+       }
+# else NASSERT
+# define ASSERT(expr, msg, parm)
+# endif NASSERT
+
+/* sccs id's */
+# ifndef lint
+# define SCCSID(arg)   static char SccsId[] = "arg";
+# else lint
+# define SCCSID(arg)
+# endif lint
+
+/* define the types of some common functions */
+extern char    *strcpy(), *strncpy();
+extern char    *strcat(), *strncat();
+extern char    *malloc();
+extern char    *index(), *rindex();
+extern int     errno;
+extern time_t  time();
+extern char    *ctime();
+extern char    *getenv();
diff --git a/usr/src/usr.sbin/sendmail/src/usersmtp.c b/usr/src/usr.sbin/sendmail/src/usersmtp.c
new file mode 100644 (file)
index 0000000..1d15c18
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+# include "sendmail.h"
+
+#ifndef lint
+#ifdef SMTP
+static char sccsid[] = "@(#)usersmtp.c 5.16 (Berkeley) 3/2/91 (with SMTP)";
+#else
+static char sccsid[] = "@(#)usersmtp.c 5.16 (Berkeley) 3/2/91 (without SMTP)";
+#endif
+#endif /* not lint */
+
+# include <sysexits.h>
+# include <errno.h>
+
+# ifdef SMTP
+
+/*
+**  USERSMTP -- run SMTP protocol from the user end.
+**
+**     This protocol is described in RFC821.
+*/
+
+#define REPLYTYPE(r)   ((r) / 100)             /* first digit of reply code */
+#define REPLYCLASS(r)  (((r) / 10) % 10)       /* second digit of reply code */
+#define SMTPCLOSING    421                     /* "Service Shutting Down" */
+
+char   SmtpMsgBuffer[MAXLINE];         /* buffer for commands */
+char   SmtpReplyBuffer[MAXLINE];       /* buffer for replies */
+char   SmtpError[MAXLINE] = "";        /* save failure error messages */
+FILE   *SmtpOut;                       /* output file */
+FILE   *SmtpIn;                        /* input file */
+int    SmtpPid;                        /* pid of mailer */
+
+/* following represents the state of the SMTP connection */
+int    SmtpState;                      /* connection state, see below */
+
+#define SMTP_CLOSED    0               /* connection is closed */
+#define SMTP_OPEN      1               /* connection is open for business */
+#define SMTP_SSD       2               /* service shutting down */
+\f/*
+**  SMTPINIT -- initialize SMTP.
+**
+**     Opens the connection and sends the initial protocol.
+**
+**     Parameters:
+**             m -- mailer to create connection to.
+**             pvp -- pointer to parameter vector to pass to
+**                     the mailer.
+**
+**     Returns:
+**             appropriate exit status -- EX_OK on success.
+**             If not EX_OK, it should close the connection.
+**
+**     Side Effects:
+**             creates connection and sends initial protocol.
+*/
+
+jmp_buf        CtxGreeting;
+
+smtpinit(m, pvp)
+       struct mailer *m;
+       char **pvp;
+{
+       register int r;
+       EVENT *gte;
+       char buf[MAXNAME];
+       static int greettimeout();
+
+       /*
+       **  Open the connection to the mailer.
+       */
+
+       if (SmtpState == SMTP_OPEN)
+               syserr("smtpinit: already open");
+
+       SmtpIn = SmtpOut = NULL;
+       SmtpState = SMTP_CLOSED;
+       SmtpError[0] = '\0';
+       SmtpPhase = "user open";
+       setproctitle("%s %s: %s", CurEnv->e_id, pvp[1], SmtpPhase);
+       SmtpPid = openmailer(m, pvp, (ADDRESS *) NULL, TRUE, &SmtpOut, &SmtpIn);
+       if (SmtpPid < 0)
+       {
+               if (tTd(18, 1))
+                       printf("smtpinit: cannot open %s: stat %d errno %d\n",
+                          pvp[0], ExitStat, errno);
+               if (CurEnv->e_xfp != NULL)
+               {
+                       register char *p;
+                       extern char *errstring();
+                       extern char *statstring();
+
+                       if (errno == 0)
+                       {
+                               p = statstring(ExitStat);
+                               fprintf(CurEnv->e_xfp,
+                                       "%.3s %s.%s... %s\n",
+                                       p, pvp[1], m->m_name, p);
+                       }
+                       else
+                       {
+                               r = errno;
+                               fprintf(CurEnv->e_xfp,
+                                       "421 %s.%s... Deferred: %s\n",
+                                       pvp[1], m->m_name, errstring(errno));
+                               errno = r;
+                       }
+               }
+               return (ExitStat);
+       }
+       SmtpState = SMTP_OPEN;
+
+       /*
+       **  Get the greeting message.
+       **      This should appear spontaneously.  Give it five minutes to
+       **      happen.
+       */
+
+       if (setjmp(CtxGreeting) != 0)
+               goto tempfail;
+       gte = setevent((time_t) 300, greettimeout, 0);
+       SmtpPhase = "greeting wait";
+       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
+       r = reply(m);
+       clrevent(gte);
+       if (r < 0 || REPLYTYPE(r) != 2)
+               goto tempfail;
+
+       /*
+       **  Send the HELO command.
+       **      My mother taught me to always introduce myself.
+       */
+
+       smtpmessage("HELO %s", m, MyHostName);
+       SmtpPhase = "HELO wait";
+       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
+       r = reply(m);
+       if (r < 0)
+               goto tempfail;
+       else if (REPLYTYPE(r) == 5)
+               goto unavailable;
+       else if (REPLYTYPE(r) != 2)
+               goto tempfail;
+
+       /*
+       **  If this is expected to be another sendmail, send some internal
+       **  commands.
+       */
+
+       if (bitnset(M_INTERNAL, m->m_flags))
+       {
+               /* tell it to be verbose */
+               smtpmessage("VERB", m);
+               r = reply(m);
+               if (r < 0)
+                       goto tempfail;
+
+               /* tell it we will be sending one transaction only */
+               smtpmessage("ONEX", m);
+               r = reply(m);
+               if (r < 0)
+                       goto tempfail;
+       }
+
+       /*
+       **  Send the MAIL command.
+       **      Designates the sender.
+       */
+
+       expand("\001g", buf, &buf[sizeof buf - 1], CurEnv);
+       if (CurEnv->e_from.q_mailer == LocalMailer ||
+           !bitnset(M_FROMPATH, m->m_flags))
+       {
+               smtpmessage("MAIL From:<%s>", m, buf);
+       }
+       else
+       {
+               smtpmessage("MAIL From:<@%s%c%s>", m, MyHostName,
+                       buf[0] == '@' ? ',' : ':', buf);
+       }
+       SmtpPhase = "MAIL wait";
+       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
+       r = reply(m);
+       if (r < 0 || REPLYTYPE(r) == 4)
+               goto tempfail;
+       else if (r == 250)
+               return (EX_OK);
+       else if (r == 552)
+               goto unavailable;
+
+       /* protocol error -- close up */
+       smtpquit(m);
+       return (EX_PROTOCOL);
+
+       /* signal a temporary failure */
+  tempfail:
+       smtpquit(m);
+       return (EX_TEMPFAIL);
+
+       /* signal service unavailable */
+  unavailable:
+       smtpquit(m);
+       return (EX_UNAVAILABLE);
+}
+
+
+static
+greettimeout()
+{
+       /* timeout reading the greeting message */
+       longjmp(CtxGreeting, 1);
+}
+\f/*
+**  SMTPRCPT -- designate recipient.
+**
+**     Parameters:
+**             to -- address of recipient.
+**             m -- the mailer we are sending to.
+**
+**     Returns:
+**             exit status corresponding to recipient status.
+**
+**     Side Effects:
+**             Sends the mail via SMTP.
+*/
+
+smtprcpt(to, m)
+       ADDRESS *to;
+       register MAILER *m;
+{
+       register int r;
+       extern char *remotename();
+
+       smtpmessage("RCPT To:<%s>", m, remotename(to->q_user, m, FALSE, TRUE));
+
+       SmtpPhase = "RCPT wait";
+       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
+       r = reply(m);
+       if (r < 0 || REPLYTYPE(r) == 4)
+               return (EX_TEMPFAIL);
+       else if (REPLYTYPE(r) == 2)
+               return (EX_OK);
+       else if (r == 550 || r == 551 || r == 553)
+               return (EX_NOUSER);
+       else if (r == 552 || r == 554)
+               return (EX_UNAVAILABLE);
+       return (EX_PROTOCOL);
+}
+\f/*
+**  SMTPDATA -- send the data and clean up the transaction.
+**
+**     Parameters:
+**             m -- mailer being sent to.
+**             e -- the envelope for this message.
+**
+**     Returns:
+**             exit status corresponding to DATA command.
+**
+**     Side Effects:
+**             none.
+*/
+
+smtpdata(m, e)
+       struct mailer *m;
+       register ENVELOPE *e;
+{
+       register int r;
+
+       /*
+       **  Send the data.
+       **      First send the command and check that it is ok.
+       **      Then send the data.
+       **      Follow it up with a dot to terminate.
+       **      Finally get the results of the transaction.
+       */
+
+       /* send the command and check ok to proceed */
+       smtpmessage("DATA", m);
+       SmtpPhase = "DATA wait";
+       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
+       r = reply(m);
+       if (r < 0 || REPLYTYPE(r) == 4)
+               return (EX_TEMPFAIL);
+       else if (r == 554)
+               return (EX_UNAVAILABLE);
+       else if (r != 354)
+               return (EX_PROTOCOL);
+
+       /* now output the actual message */
+       (*e->e_puthdr)(SmtpOut, m, CurEnv);
+       putline("\n", SmtpOut, m);
+       (*e->e_putbody)(SmtpOut, m, CurEnv);
+
+       /* terminate the message */
+       fprintf(SmtpOut, ".%s", m->m_eol);
+       if (Verbose && !HoldErrs)
+               nmessage(Arpa_Info, ">>> .");
+
+       /* check for the results of the transaction */
+       SmtpPhase = "result wait";
+       setproctitle("%s %s: %s", CurEnv->e_id, CurHostName, SmtpPhase);
+       r = reply(m);
+       if (r < 0 || REPLYTYPE(r) == 4)
+               return (EX_TEMPFAIL);
+       else if (r == 250)
+               return (EX_OK);
+       else if (r == 552 || r == 554)
+               return (EX_UNAVAILABLE);
+       return (EX_PROTOCOL);
+}
+\f/*
+**  SMTPQUIT -- close the SMTP connection.
+**
+**     Parameters:
+**             m -- a pointer to the mailer.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             sends the final protocol and closes the connection.
+*/
+
+smtpquit(m)
+       register MAILER *m;
+{
+       int i;
+
+       /* if the connection is already closed, don't bother */
+       if (SmtpIn == NULL)
+               return;
+
+       /* send the quit message if not a forced quit */
+       if (SmtpState == SMTP_OPEN || SmtpState == SMTP_SSD)
+       {
+               smtpmessage("QUIT", m);
+               (void) reply(m);
+               if (SmtpState == SMTP_CLOSED)
+                       return;
+       }
+
+       /* now actually close the connection */
+       (void) fclose(SmtpIn);
+       (void) fclose(SmtpOut);
+       SmtpIn = SmtpOut = NULL;
+       SmtpState = SMTP_CLOSED;
+
+       /* and pick up the zombie */
+       i = endmailer(SmtpPid, m->m_argv[0]);
+       if (i != EX_OK)
+               syserr("smtpquit %s: stat %d", m->m_argv[0], i);
+}
+\f/*
+**  REPLY -- read arpanet reply
+**
+**     Parameters:
+**             m -- the mailer we are reading the reply from.
+**
+**     Returns:
+**             reply code it reads.
+**
+**     Side Effects:
+**             flushes the mail file.
+*/
+
+reply(m)
+       MAILER *m;
+{
+       (void) fflush(SmtpOut);
+
+       if (tTd(18, 1))
+               printf("reply\n");
+
+       /*
+       **  Read the input line, being careful not to hang.
+       */
+
+       for (;;)
+       {
+               register int r;
+               register char *p;
+
+               /* actually do the read */
+               if (CurEnv->e_xfp != NULL)
+                       (void) fflush(CurEnv->e_xfp);   /* for debugging */
+
+               /* if we are in the process of closing just give the code */
+               if (SmtpState == SMTP_CLOSED)
+                       return (SMTPCLOSING);
+
+               /* get the line from the other side */
+               p = sfgets(SmtpReplyBuffer, sizeof SmtpReplyBuffer, SmtpIn);
+               if (p == NULL)
+               {
+                       extern char MsgBuf[];           /* err.c */
+                       extern char Arpa_TSyserr[];     /* conf.c */
+
+                       /* if the remote end closed early, fake an error */
+                       if (errno == 0)
+# ifdef ECONNRESET
+                               errno = ECONNRESET;
+# else ECONNRESET
+                               errno = EPIPE;
+# endif ECONNRESET
+
+                       message(Arpa_TSyserr, "reply: read error");
+                       /* if debugging, pause so we can see state */
+                       if (tTd(18, 100))
+                               pause();
+# ifdef LOG
+                       syslog(LOG_INFO, "%s", &MsgBuf[4]);
+# endif LOG
+                       SmtpState = SMTP_CLOSED;
+                       smtpquit(m);
+                       return (-1);
+               }
+               fixcrlf(SmtpReplyBuffer, TRUE);
+
+               if (CurEnv->e_xfp != NULL && index("45", SmtpReplyBuffer[0]) != NULL)
+               {
+                       /* serious error -- log the previous command */
+                       if (SmtpMsgBuffer[0] != '\0')
+                               fprintf(CurEnv->e_xfp, ">>> %s\n", SmtpMsgBuffer);
+                       SmtpMsgBuffer[0] = '\0';
+
+                       /* now log the message as from the other side */
+                       fprintf(CurEnv->e_xfp, "<<< %s\n", SmtpReplyBuffer);
+               }
+
+               /* display the input for verbose mode */
+               if (Verbose && !HoldErrs)
+                       nmessage(Arpa_Info, "%s", SmtpReplyBuffer);
+
+               /* if continuation is required, we can go on */
+               if (SmtpReplyBuffer[3] == '-' || !isdigit(SmtpReplyBuffer[0]))
+                       continue;
+
+               /* decode the reply code */
+               r = atoi(SmtpReplyBuffer);
+
+               /* extra semantics: 0xx codes are "informational" */
+               if (r < 100)
+                       continue;
+
+               /* reply code 421 is "Service Shutting Down" */
+               if (r == SMTPCLOSING && SmtpState != SMTP_SSD)
+               {
+                       /* send the quit protocol */
+                       SmtpState = SMTP_SSD;
+                       smtpquit(m);
+               }
+
+               /* save temporary failure messages for posterity */
+               if (SmtpReplyBuffer[0] == '4' && SmtpError[0] == '\0')
+                       (void) strcpy(SmtpError, &SmtpReplyBuffer[4]);
+
+               return (r);
+       }
+}
+\f/*
+**  SMTPMESSAGE -- send message to server
+**
+**     Parameters:
+**             f -- format
+**             m -- the mailer to control formatting.
+**             a, b, c -- parameters
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             writes message to SmtpOut.
+*/
+
+/*VARARGS1*/
+smtpmessage(f, m, a, b, c)
+       char *f;
+       MAILER *m;
+{
+       (void) sprintf(SmtpMsgBuffer, f, a, b, c);
+       if (tTd(18, 1) || (Verbose && !HoldErrs))
+               nmessage(Arpa_Info, ">>> %s", SmtpMsgBuffer);
+       if (SmtpOut != NULL)
+               fprintf(SmtpOut, "%s%s", SmtpMsgBuffer,
+                       m == 0 ? "\r\n" : m->m_eol);
+}
+
+# endif SMTP
diff --git a/usr/src/usr.sbin/sendmail/src/util.c b/usr/src/usr.sbin/sendmail/src/util.c
new file mode 100644 (file)
index 0000000..0aa5469
--- /dev/null
@@ -0,0 +1,852 @@
+/*
+ * Copyright (c) 1983 Eric P. Allman
+ * Copyright (c) 1988 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *     This product includes software developed by the University of
+ *     California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)util.c     5.20 (Berkeley) 3/8/91";
+#endif /* not lint */
+
+# include <stdio.h>
+# include <sys/types.h>
+# include <sys/stat.h>
+# include <sysexits.h>
+# include <errno.h>
+# include "sendmail.h"
+
+/*
+**  STRIPQUOTES -- Strip quotes & quote bits from a string.
+**
+**     Runs through a string and strips off unquoted quote
+**     characters and quote bits.  This is done in place.
+**
+**     Parameters:
+**             s -- the string to strip.
+**             qf -- if set, remove actual `` " '' characters
+**                     as well as the quote bits.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             none.
+**
+**     Called By:
+**             deliver
+*/
+
+stripquotes(s, qf)
+       char *s;
+       bool qf;
+{
+       register char *p;
+       register char *q;
+       register char c;
+
+       if (s == NULL)
+               return;
+
+       for (p = q = s; (c = *p++) != '\0'; )
+       {
+               if (c != '"' || !qf)
+                       *q++ = c & 0177;
+       }
+       *q = '\0';
+}
+\f/*
+**  QSTRLEN -- give me the string length assuming 0200 bits add a char
+**
+**     Parameters:
+**             s -- the string to measure.
+**
+**     Reurns:
+**             The length of s, including space for backslash escapes.
+**
+**     Side Effects:
+**             none.
+*/
+
+qstrlen(s)
+       register char *s;
+{
+       register int l = 0;
+       register char c;
+
+       while ((c = *s++) != '\0')
+       {
+               if (bitset(0200, c))
+                       l++;
+               l++;
+       }
+       return (l);
+}
+\f/*
+**  CAPITALIZE -- return a copy of a string, properly capitalized.
+**
+**     Parameters:
+**             s -- the string to capitalize.
+**
+**     Returns:
+**             a pointer to a properly capitalized string.
+**
+**     Side Effects:
+**             none.
+*/
+
+char *
+capitalize(s)
+       register char *s;
+{
+       static char buf[50];
+       register char *p;
+
+       p = buf;
+
+       for (;;)
+       {
+               while (!isalpha(*s) && *s != '\0')
+                       *p++ = *s++;
+               if (*s == '\0')
+                       break;
+               *p++ = toupper(*s);
+               s++;
+               while (isalpha(*s))
+                       *p++ = *s++;
+       }
+
+       *p = '\0';
+       return (buf);
+}
+\f/*
+**  XALLOC -- Allocate memory and bitch wildly on failure.
+**
+**     THIS IS A CLUDGE.  This should be made to give a proper
+**     error -- but after all, what can we do?
+**
+**     Parameters:
+**             sz -- size of area to allocate.
+**
+**     Returns:
+**             pointer to data region.
+**
+**     Side Effects:
+**             Memory is allocated.
+*/
+
+char *
+xalloc(sz)
+       register int sz;
+{
+       register char *p;
+       extern char *malloc();
+
+       p = malloc((unsigned) sz);
+       if (p == NULL)
+       {
+               syserr("Out of memory!!");
+               abort();
+               /* exit(EX_UNAVAILABLE); */
+       }
+       return (p);
+}
+\f/*
+**  COPYPLIST -- copy list of pointers.
+**
+**     This routine is the equivalent of newstr for lists of
+**     pointers.
+**
+**     Parameters:
+**             list -- list of pointers to copy.
+**                     Must be NULL terminated.
+**             copycont -- if TRUE, copy the contents of the vector
+**                     (which must be a string) also.
+**
+**     Returns:
+**             a copy of 'list'.
+**
+**     Side Effects:
+**             none.
+*/
+
+char **
+copyplist(list, copycont)
+       char **list;
+       bool copycont;
+{
+       register char **vp;
+       register char **newvp;
+
+       for (vp = list; *vp != NULL; vp++)
+               continue;
+
+       vp++;
+
+       newvp = (char **) xalloc((int) (vp - list) * sizeof *vp);
+       bcopy((char *) list, (char *) newvp, (int) (vp - list) * sizeof *vp);
+
+       if (copycont)
+       {
+               for (vp = newvp; *vp != NULL; vp++)
+                       *vp = newstr(*vp);
+       }
+
+       return (newvp);
+}
+\f/*
+**  PRINTAV -- print argument vector.
+**
+**     Parameters:
+**             av -- argument vector.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             prints av.
+*/
+
+printav(av)
+       register char **av;
+{
+       while (*av != NULL)
+       {
+               if (tTd(0, 44))
+                       printf("\n\t%08x=", *av);
+               else
+                       (void) putchar(' ');
+               xputs(*av++);
+       }
+       (void) putchar('\n');
+}
+\f/*
+**  LOWER -- turn letter into lower case.
+**
+**     Parameters:
+**             c -- character to turn into lower case.
+**
+**     Returns:
+**             c, in lower case.
+**
+**     Side Effects:
+**             none.
+*/
+
+char
+lower(c)
+       register char c;
+{
+       return(isascii(c) && isupper(c) ? tolower(c) : c);
+}
+\f/*
+**  XPUTS -- put string doing control escapes.
+**
+**     Parameters:
+**             s -- string to put.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             output to stdout
+*/
+
+xputs(s)
+       register char *s;
+{
+       register char c;
+
+       if (s == NULL)
+       {
+               printf("<null>");
+               return;
+       }
+       (void) putchar('"');
+       while ((c = *s++) != '\0')
+       {
+               if (!isascii(c))
+               {
+                       (void) putchar('\\');
+                       c &= 0177;
+               }
+               if (c < 040 || c >= 0177)
+               {
+                       (void) putchar('^');
+                       c ^= 0100;
+               }
+               (void) putchar(c);
+       }
+       (void) putchar('"');
+       (void) fflush(stdout);
+}
+\f/*
+**  MAKELOWER -- Translate a line into lower case
+**
+**     Parameters:
+**             p -- the string to translate.  If NULL, return is
+**                     immediate.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             String pointed to by p is translated to lower case.
+**
+**     Called By:
+**             parse
+*/
+
+makelower(p)
+       register char *p;
+{
+       register char c;
+
+       if (p == NULL)
+               return;
+       for (; (c = *p) != '\0'; p++)
+               if (isascii(c) && isupper(c))
+                       *p = tolower(c);
+}
+\f/*
+**  BUILDFNAME -- build full name from gecos style entry.
+**
+**     This routine interprets the strange entry that would appear
+**     in the GECOS field of the password file.
+**
+**     Parameters:
+**             p -- name to build.
+**             login -- the login name of this user (for &).
+**             buf -- place to put the result.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             none.
+*/
+
+buildfname(p, login, buf)
+       register char *p;
+       char *login;
+       char *buf;
+{
+       register char *bp = buf;
+
+       if (*p == '*')
+               p++;
+       while (*p != '\0' && *p != ',' && *p != ';' && *p != '%')
+       {
+               if (*p == '&')
+               {
+                       (void) strcpy(bp, login);
+                       *bp = toupper(*bp);
+                       while (*bp != '\0')
+                               bp++;
+                       p++;
+               }
+               else
+                       *bp++ = *p++;
+       }
+       *bp = '\0';
+}
+\f/*
+**  SAFEFILE -- return true if a file exists and is safe for a user.
+**
+**     Parameters:
+**             fn -- filename to check.
+**             uid -- uid to compare against.
+**             mode -- mode bits that must match.
+**
+**     Returns:
+**             TRUE if fn exists, is owned by uid, and matches mode.
+**             FALSE otherwise.
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+safefile(fn, uid, mode)
+       char *fn;
+       int uid;
+       int mode;
+{
+       struct stat stbuf;
+
+       if (stat(fn, &stbuf) >= 0 && stbuf.st_uid == uid &&
+           (stbuf.st_mode & mode) == mode)
+               return (TRUE);
+       errno = 0;
+       return (FALSE);
+}
+\f/*
+**  FIXCRLF -- fix <CR><LF> in line.
+**
+**     Looks for the <CR><LF> combination and turns it into the
+**     UNIX canonical <NL> character.  It only takes one line,
+**     i.e., it is assumed that the first <NL> found is the end
+**     of the line.
+**
+**     Parameters:
+**             line -- the line to fix.
+**             stripnl -- if true, strip the newline also.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             line is changed in place.
+*/
+
+fixcrlf(line, stripnl)
+       char *line;
+       bool stripnl;
+{
+       register char *p;
+
+       p = index(line, '\n');
+       if (p == NULL)
+               return;
+       if (p > line && p[-1] == '\r')
+               p--;
+       if (!stripnl)
+               *p++ = '\n';
+       *p = '\0';
+}
+\f/*
+**  DFOPEN -- determined file open
+**
+**     This routine has the semantics of fopen, except that it will
+**     keep trying a few times to make this happen.  The idea is that
+**     on very loaded systems, we may run out of resources (inodes,
+**     whatever), so this tries to get around it.
+*/
+
+FILE *
+dfopen(filename, mode)
+       char *filename;
+       char *mode;
+{
+       register int tries;
+       register FILE *fp;
+
+       for (tries = 0; tries < 10; tries++)
+       {
+               sleep((unsigned) (10 * tries));
+               errno = 0;
+               fp = fopen(filename, mode);
+               if (fp != NULL)
+                       break;
+               if (errno != ENFILE && errno != EINTR)
+                       break;
+       }
+       errno = 0;
+       return (fp);
+}
+\f/*
+**  PUTLINE -- put a line like fputs obeying SMTP conventions
+**
+**     This routine always guarantees outputing a newline (or CRLF,
+**     as appropriate) at the end of the string.
+**
+**     Parameters:
+**             l -- line to put.
+**             fp -- file to put it onto.
+**             m -- the mailer used to control output.
+**
+**     Returns:
+**             none
+**
+**     Side Effects:
+**             output of l to fp.
+*/
+
+# define SMTPLINELIM   990     /* maximum line length */
+
+putline(l, fp, m)
+       register char *l;
+       FILE *fp;
+       MAILER *m;
+{
+       register char *p;
+       register char svchar;
+
+       /* strip out 0200 bits -- these can look like TELNET protocol */
+       if (bitnset(M_LIMITS, m->m_flags))
+       {
+               for (p = l; svchar = *p; ++p)
+                       if (svchar & 0200)
+                               *p = svchar &~ 0200;
+       }
+
+       do
+       {
+               /* find the end of the line */
+               p = index(l, '\n');
+               if (p == NULL)
+                       p = &l[strlen(l)];
+
+               /* check for line overflow */
+               while ((p - l) > SMTPLINELIM && bitnset(M_LIMITS, m->m_flags))
+               {
+                       register char *q = &l[SMTPLINELIM - 1];
+
+                       svchar = *q;
+                       *q = '\0';
+                       if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
+                               (void) putc('.', fp);
+                       fputs(l, fp);
+                       (void) putc('!', fp);
+                       fputs(m->m_eol, fp);
+                       *q = svchar;
+                       l = q;
+               }
+
+               /* output last part */
+               if (l[0] == '.' && bitnset(M_XDOT, m->m_flags))
+                       (void) putc('.', fp);
+               for ( ; l < p; ++l)
+                       (void) putc(*l, fp);
+               fputs(m->m_eol, fp);
+               if (*l == '\n')
+                       ++l;
+       } while (l[0] != '\0');
+}
+\f/*
+**  XUNLINK -- unlink a file, doing logging as appropriate.
+**
+**     Parameters:
+**             f -- name of file to unlink.
+**
+**     Returns:
+**             none.
+**
+**     Side Effects:
+**             f is unlinked.
+*/
+
+xunlink(f)
+       char *f;
+{
+       register int i;
+
+# ifdef LOG
+       if (LogLevel > 20)
+               syslog(LOG_DEBUG, "%s: unlink %s\n", CurEnv->e_id, f);
+# endif LOG
+
+       i = unlink(f);
+# ifdef LOG
+       if (i < 0 && LogLevel > 21)
+               syslog(LOG_DEBUG, "%s: unlink-fail %d", f, errno);
+# endif LOG
+}
+\f/*
+**  SFGETS -- "safe" fgets -- times out and ignores random interrupts.
+**
+**     Parameters:
+**             buf -- place to put the input line.
+**             siz -- size of buf.
+**             fp -- file to read from.
+**
+**     Returns:
+**             NULL on error (including timeout).  This will also leave
+**                     buf containing a null string.
+**             buf otherwise.
+**
+**     Side Effects:
+**             none.
+*/
+
+static jmp_buf CtxReadTimeout;
+
+char *
+sfgets(buf, siz, fp)
+       char *buf;
+       int siz;
+       FILE *fp;
+{
+       register EVENT *ev = NULL;
+       register char *p;
+       static int readtimeout();
+
+       /* set the timeout */
+       if (ReadTimeout != 0)
+       {
+               if (setjmp(CtxReadTimeout) != 0)
+               {
+# ifdef LOG
+                       syslog(LOG_NOTICE,
+                           "timeout waiting for input from %s\n",
+                           RealHostName? RealHostName: "local");
+# endif
+                       errno = 0;
+                       usrerr("451 timeout waiting for input");
+                       buf[0] = '\0';
+                       return (NULL);
+               }
+               ev = setevent((time_t) ReadTimeout, readtimeout, 0);
+       }
+
+       /* try to read */
+       p = NULL;
+       while (p == NULL && !feof(fp) && !ferror(fp))
+       {
+               errno = 0;
+               p = fgets(buf, siz, fp);
+               if (errno == EINTR)
+                       clearerr(fp);
+       }
+
+       /* clear the event if it has not sprung */
+       clrevent(ev);
+
+       /* clean up the books and exit */
+       LineNumber++;
+       if (p == NULL)
+       {
+               buf[0] = '\0';
+               return (NULL);
+       }
+       for (p = buf; *p != '\0'; p++)
+               *p &= ~0200;
+       return (buf);
+}
+
+static
+readtimeout()
+{
+       longjmp(CtxReadTimeout, 1);
+}
+\f/*
+**  FGETFOLDED -- like fgets, but know about folded lines.
+**
+**     Parameters:
+**             buf -- place to put result.
+**             n -- bytes available.
+**             f -- file to read from.
+**
+**     Returns:
+**             buf on success, NULL on error or EOF.
+**
+**     Side Effects:
+**             buf gets lines from f, with continuation lines (lines
+**             with leading white space) appended.  CRLF's are mapped
+**             into single newlines.  Any trailing NL is stripped.
+*/
+
+char *
+fgetfolded(buf, n, f)
+       char *buf;
+       register int n;
+       FILE *f;
+{
+       register char *p = buf;
+       register int i;
+
+       n--;
+       while ((i = getc(f)) != EOF)
+       {
+               if (i == '\r')
+               {
+                       i = getc(f);
+                       if (i != '\n')
+                       {
+                               if (i != EOF)
+                                       (void) ungetc(i, f);
+                               i = '\r';
+                       }
+               }
+               if (--n > 0)
+                       *p++ = i;
+               if (i == '\n')
+               {
+                       LineNumber++;
+                       i = getc(f);
+                       if (i != EOF)
+                               (void) ungetc(i, f);
+                       if (i != ' ' && i != '\t')
+                       {
+                               *--p = '\0';
+                               return (buf);
+                       }
+               }
+       }
+       return (NULL);
+}
+\f/*
+**  CURTIME -- return current time.
+**
+**     Parameters:
+**             none.
+**
+**     Returns:
+**             the current time.
+**
+**     Side Effects:
+**             none.
+*/
+
+time_t
+curtime()
+{
+       auto time_t t;
+
+       (void) time(&t);
+       return (t);
+}
+\f/*
+**  ATOBOOL -- convert a string representation to boolean.
+**
+**     Defaults to "TRUE"
+**
+**     Parameters:
+**             s -- string to convert.  Takes "tTyY" as true,
+**                     others as false.
+**
+**     Returns:
+**             A boolean representation of the string.
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+atobool(s)
+       register char *s;
+{
+       if (*s == '\0' || index("tTyY", *s) != NULL)
+               return (TRUE);
+       return (FALSE);
+}
+\f/*
+**  ATOOCT -- convert a string representation to octal.
+**
+**     Parameters:
+**             s -- string to convert.
+**
+**     Returns:
+**             An integer representing the string interpreted as an
+**             octal number.
+**
+**     Side Effects:
+**             none.
+*/
+
+atooct(s)
+       register char *s;
+{
+       register int i = 0;
+
+       while (*s >= '0' && *s <= '7')
+               i = (i << 3) | (*s++ - '0');
+       return (i);
+}
+\f/*
+**  WAITFOR -- wait for a particular process id.
+**
+**     Parameters:
+**             pid -- process id to wait for.
+**
+**     Returns:
+**             status of pid.
+**             -1 if pid never shows up.
+**
+**     Side Effects:
+**             none.
+*/
+
+waitfor(pid)
+       int pid;
+{
+       auto int st;
+       int i;
+
+       do
+       {
+               errno = 0;
+               i = wait(&st);
+       } while ((i >= 0 || errno == EINTR) && i != pid);
+       if (i < 0)
+               st = -1;
+       return (st);
+}
+\f/*
+**  BITINTERSECT -- tell if two bitmaps intersect
+**
+**     Parameters:
+**             a, b -- the bitmaps in question
+**
+**     Returns:
+**             TRUE if they have a non-null intersection
+**             FALSE otherwise
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+bitintersect(a, b)
+       BITMAP a;
+       BITMAP b;
+{
+       int i;
+
+       for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
+               if ((a[i] & b[i]) != 0)
+                       return (TRUE);
+       return (FALSE);
+}
+\f/*
+**  BITZEROP -- tell if a bitmap is all zero
+**
+**     Parameters:
+**             map -- the bit map to check
+**
+**     Returns:
+**             TRUE if map is all zero.
+**             FALSE if there are any bits set in map.
+**
+**     Side Effects:
+**             none.
+*/
+
+bool
+bitzerop(map)
+       BITMAP map;
+{
+       int i;
+
+       for (i = BITMAPBYTES / sizeof (int); --i >= 0; )
+               if (map[i] != 0)
+                       return (FALSE);
+       return (TRUE);
+}