--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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();
+}
--- /dev/null
+/*
+ * 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 */
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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 */
+};
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*-
+ * 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";
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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");
+}
--- /dev/null
+/*
+ * 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();
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/*
+ * 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]);
+}
--- /dev/null
+/*
+ * 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;
+ }
+}
--- /dev/null
+/*
+ * 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();
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
+}