Sendmail source for the 3B1/7300 (part 5/8)
David H. Brierley
dave at galaxia.Newport.RI.US
Sat Feb 25 12:28:59 AEST 1989
----- cut here and feed to /bin/sh -----
#! /bin/sh
# This is a shell archive. Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file". To overwrite existing
# files, type "sh file -c". You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g.. If this archive is complete, you
# will see the following message at the end:
# "End of archive 5 (of 8)."
# Contents: src/headers.c src/readcf.c
# Wrapped by dave at galaxia on Fri Feb 24 20:24:05 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'src/headers.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/headers.c'\"
else
echo shar: Extracting \"'src/headers.c'\" \(16858 characters\)
sed "s/^X//" >'src/headers.c' <<'END_OF_FILE'
X/*
X** Sendmail
X** Copyright (c) 1983 Eric P. Allman
X** Berkeley, California
X**
X** Copyright (c) 1983 Regents of the University of California.
X** All rights reserved. The Berkeley software License Agreement
X** specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char SccsId[] = "@(#)headers.c 5.7 (Berkeley) 9/21/85";
X#endif not lint
X
X# include <errno.h>
X# include "sendmail.h"
X
X/*
X** CHOMPHEADER -- process and save a header line.
X**
X** Called by collect and by readcf to deal with header lines.
X**
X** Parameters:
X** line -- header as a text line.
X** def -- if set, this is a default value.
X**
X** Returns:
X** flags for this header.
X**
X** Side Effects:
X** The header is saved on the header list.
X** Contents of 'line' are destroyed.
X*/
X
Xchompheader(line, def)
X char *line;
X bool def;
X{
X register char *p;
X register HDR *h;
X HDR **hp;
X char *fname;
X char *fvalue;
X struct hdrinfo *hi;
X bool cond = FALSE;
X BITMAP mopts;
X extern char *crackaddr();
X
X# ifdef DEBUG
X if (tTd(31, 6))
X printf("chompheader: %s\n", line);
X# endif DEBUG
X
X /* strip off options */
X clrbitmap(mopts);
X p = line;
X if (*p == '?')
X {
X /* have some */
X register char *q = index(p + 1, *p);
X
X if (q != NULL)
X {
X *q++ = '\0';
X while (*++p != '\0')
X setbitn(*p, mopts);
X p = q;
X }
X else
X syserr("chompheader: syntax error, line \"%s\"", line);
X cond = TRUE;
X }
X
X /* find canonical name */
X fname = p;
X p = index(p, ':');
X if (p == NULL)
X {
X syserr("chompheader: syntax error, line \"%s\"", line);
X return (0);
X }
X fvalue = &p[1];
X while (isspace(*--p))
X continue;
X *++p = '\0';
X makelower(fname);
X
X /* strip field value on front */
X if (*fvalue == ' ')
X fvalue++;
X
X /* see if it is a known type */
X for (hi = HdrInfo; hi->hi_field != NULL; hi++)
X {
X if (strcmp(hi->hi_field, fname) == 0)
X break;
X }
X
X /* see if this is a resent message */
X if (!def && bitset(H_RESENT, hi->hi_flags))
X CurEnv->e_flags |= EF_RESENT;
X
X /* if this means "end of header" quit now */
X if (bitset(H_EOH, hi->hi_flags))
X return (hi->hi_flags);
X
X /* drop explicit From: if same as what we would generate -- for MH */
X p = "resent-from";
X if (!bitset(EF_RESENT, CurEnv->e_flags))
X p += 7;
X if (!def && !QueueRun && strcmp(fname, p) == 0)
X {
X if (CurEnv->e_from.q_paddr != NULL &&
X strcmp(fvalue, CurEnv->e_from.q_paddr) == 0)
X return (hi->hi_flags);
X }
X
X /* delete default value for this header */
X for (hp = &CurEnv->e_header; (h = *hp) != NULL; hp = &h->h_link)
X {
X if (strcmp(fname, h->h_field) == 0 &&
X bitset(H_DEFAULT, h->h_flags) &&
X !bitset(H_FORCE, h->h_flags))
X h->h_value = NULL;
X }
X
X /* create a new node */
X h = (HDR *) xalloc(sizeof *h);
X h->h_field = newstr(fname);
X h->h_value = NULL;
X h->h_link = NULL;
X bcopy((char *) mopts, (char *) h->h_mflags, sizeof mopts);
X *hp = h;
X h->h_flags = hi->hi_flags;
X if (def)
X h->h_flags |= H_DEFAULT;
X if (cond)
X h->h_flags |= H_CHECK;
X if (h->h_value != NULL)
X free((char *) h->h_value);
X h->h_value = newstr(fvalue);
X
X /* hack to see if this is a new format message */
X if (!def && bitset(H_RCPT|H_FROM, h->h_flags) &&
X (index(fvalue, ',') != NULL || index(fvalue, '(') != NULL ||
X index(fvalue, '<') != NULL || index(fvalue, ';') != NULL))
X {
X CurEnv->e_flags &= ~EF_OLDSTYLE;
X }
X
X return (h->h_flags);
X}
X/*
X** ADDHEADER -- add a header entry to the end of the queue.
X**
X** This bypasses the special checking of chompheader.
X**
X** Parameters:
X** field -- the name of the header field.
X** value -- the value of the field. It must be lower-cased.
X** e -- the envelope to add them to.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** adds the field on the list of headers for this envelope.
X*/
X
Xaddheader(field, value, e)
X char *field;
X char *value;
X ENVELOPE *e;
X{
X register HDR *h;
X register struct hdrinfo *hi;
X HDR **hp;
X
X /* find info struct */
X for (hi = HdrInfo; hi->hi_field != NULL; hi++)
X {
X if (strcmp(field, hi->hi_field) == 0)
X break;
X }
X
X /* find current place in list -- keep back pointer? */
X for (hp = &e->e_header; (h = *hp) != NULL; hp = &h->h_link)
X {
X if (strcmp(field, h->h_field) == 0)
X break;
X }
X
X /* allocate space for new header */
X h = (HDR *) xalloc(sizeof *h);
X h->h_field = field;
X h->h_value = newstr(value);
X h->h_link = *hp;
X h->h_flags = hi->hi_flags | H_DEFAULT;
X clrbitmap(h->h_mflags);
X *hp = h;
X}
X/*
X** HVALUE -- return value of a header.
X**
X** Only "real" fields (i.e., ones that have not been supplied
X** as a default) are used.
X**
X** Parameters:
X** field -- the field name.
X**
X** Returns:
X** pointer to the value part.
X** NULL if not found.
X**
X** Side Effects:
X** none.
X*/
X
Xchar *
Xhvalue(field)
X char *field;
X{
X register HDR *h;
X
X for (h = CurEnv->e_header; h != NULL; h = h->h_link)
X {
X if (!bitset(H_DEFAULT, h->h_flags) && strcmp(h->h_field, field) == 0)
X return (h->h_value);
X }
X return (NULL);
X}
X/*
X** ISHEADER -- predicate telling if argument is a header.
X**
X** A line is a header if it has a single word followed by
X** optional white space followed by a colon.
X**
X** Parameters:
X** s -- string to check for possible headerness.
X**
X** Returns:
X** TRUE if s is a header.
X** FALSE otherwise.
X**
X** Side Effects:
X** none.
X*/
X
Xbool
Xisheader(s)
X register char *s;
X{
X while (*s > ' ' && *s != ':' && *s != '\0')
X s++;
X
X /* following technically violates RFC822 */
X while (isspace(*s))
X s++;
X
X return (*s == ':');
X}
X/*
X** EATHEADER -- run through the stored header and extract info.
X**
X** Parameters:
X** e -- the envelope to process.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Sets a bunch of global variables from information
X** in the collected header.
X** Aborts the message if the hop count is exceeded.
X*/
X
Xeatheader(e)
X register ENVELOPE *e;
X{
X register HDR *h;
X register char *p;
X int hopcnt = 0;
X
X#ifdef DEBUG
X if (tTd(32, 1))
X printf("----- collected header -----\n");
X#endif DEBUG
X for (h = e->e_header; h != NULL; h = h->h_link)
X {
X#ifdef DEBUG
X extern char *capitalize();
X
X if (tTd(32, 1))
X printf("%s: %s\n", capitalize(h->h_field), h->h_value);
X#endif DEBUG
X /* count the number of times it has been processed */
X if (bitset(H_TRACE, h->h_flags))
X hopcnt++;
X
X /* send to this person if we so desire */
X if (GrabTo && bitset(H_RCPT, h->h_flags) &&
X !bitset(H_DEFAULT, h->h_flags) &&
X (!bitset(EF_RESENT, CurEnv->e_flags) || bitset(H_RESENT, h->h_flags)))
X {
X sendtolist(h->h_value, (ADDRESS *) NULL, &CurEnv->e_sendqueue);
X }
X
X /* log the message-id */
X#ifdef LOG
X if (!QueueRun && LogLevel > 8 && h->h_value != NULL &&
X strcmp(h->h_field, "message-id") == 0)
X {
X char buf[MAXNAME];
X
X p = h->h_value;
X if (bitset(H_DEFAULT, h->h_flags))
X {
X expand(p, buf, &buf[sizeof buf], e);
X p = buf;
X }
X syslog(LOG_INFO, "%s: message-id=%s", e->e_id, p);
X }
X#endif LOG
X }
X#ifdef DEBUG
X if (tTd(32, 1))
X printf("----------------------------\n");
X#endif DEBUG
X
X /* store hop count */
X if (hopcnt > e->e_hopcount)
X e->e_hopcount = hopcnt;
X
X /* message priority */
X p = hvalue("precedence");
X if (p != NULL)
X e->e_class = priencode(p);
X if (!QueueRun)
X e->e_msgpriority = e->e_msgsize
X - e->e_class * WkClassFact
X + e->e_nrcpts * WkRecipFact;
X
X /* return receipt to */
X p = hvalue("return-receipt-to");
X if (p != NULL)
X e->e_receiptto = p;
X
X /* errors to */
X p = hvalue("errors-to");
X if (p != NULL)
X sendtolist(p, (ADDRESS *) NULL, &e->e_errorqueue);
X
X /* from person */
X if (OpMode == MD_ARPAFTP)
X {
X register struct hdrinfo *hi = HdrInfo;
X
X for (p = NULL; p == NULL && hi->hi_field != NULL; hi++)
X {
X if (bitset(H_FROM, hi->hi_flags))
X p = hvalue(hi->hi_field);
X }
X if (p != NULL)
X setsender(p);
X }
X
X /* full name of from person */
X p = hvalue("full-name");
X if (p != NULL)
X define('x', p, e);
X
X /* date message originated */
X p = hvalue("posted-date");
X if (p == NULL)
X p = hvalue("date");
X if (p != NULL)
X {
X define('a', p, e);
X /* we don't have a good way to do canonical conversion ....
X define('d', newstr(arpatounix(p)), e);
X .... so we will ignore the problem for the time being */
X }
X
X /*
X ** Log collection information.
X */
X
X# ifdef LOG
X if (!QueueRun && LogLevel > 1)
X {
X syslog(LOG_INFO, "%s: from=%s, size=%ld, class=%d\n",
X CurEnv->e_id, CurEnv->e_from.q_paddr, CurEnv->e_msgsize,
X CurEnv->e_class);
X }
X# endif LOG
X}
X/*
X** PRIENCODE -- encode external priority names into internal values.
X**
X** Parameters:
X** p -- priority in ascii.
X**
X** Returns:
X** priority as a numeric level.
X**
X** Side Effects:
X** none.
X*/
X
Xpriencode(p)
X char *p;
X{
X register int i;
X extern bool sameword();
X
X for (i = 0; i < NumPriorities; i++)
X {
X if (sameword(p, Priorities[i].pri_name))
X return (Priorities[i].pri_val);
X }
X
X /* unknown priority */
X return (0);
X}
X/*
X** CRACKADDR -- parse an address and turn it into a macro
X**
X** This doesn't actually parse the address -- it just extracts
X** it and replaces it with "$g". The parse is totally ad hoc
X** and isn't even guaranteed to leave something syntactically
X** identical to what it started with. However, it does leave
X** something semantically identical.
X**
X** The process is kind of strange. There are a number of
X** interesting cases:
X** 1. comment <address> comment ==> comment <$g> comment
X** 2. address ==> address
X** 3. address (comment) ==> $g (comment)
X** 4. (comment) address ==> (comment) $g
X** And then there are the hard cases....
X** 5. add (comment) ress ==> $g (comment)
X** 6. comment <address (comment)> ==> comment <$g (comment)>
X** 7. .... etc ....
X**
X** Parameters:
X** addr -- the address to be cracked.
X**
X** Returns:
X** a pointer to the new version.
X**
X** Side Effects:
X** none.
X**
X** Warning:
X** The return value is saved in local storage and should
X** be copied if it is to be reused.
X*/
X
Xchar *
Xcrackaddr(addr)
X register char *addr;
X{
X register char *p;
X register int i;
X static char buf[MAXNAME];
X char *rhs;
X bool gotaddr;
X register char *bp;
X
X# ifdef DEBUG
X if (tTd(33, 1))
X printf("crackaddr(%s)\n", addr);
X# endif DEBUG
X
X (void) strcpy(buf, "");
X rhs = NULL;
X
X /* strip leading spaces */
X while (*addr != '\0' && isspace(*addr))
X addr++;
X
X /*
X ** See if we have anything in angle brackets. If so, that is
X ** the address part, and the rest is the comment.
X */
X
X p = index(addr, '<');
X if (p != NULL)
X {
X /* copy the beginning of the addr field to the buffer */
X *p = '\0';
X (void) strcpy(buf, addr);
X (void) strcat(buf, "<");
X *p++ = '<';
X
X /* skip spaces */
X while (isspace(*p))
X p++;
X
X /* find the matching right angle bracket */
X addr = p;
X for (i = 0; *p != '\0'; p++)
X {
X switch (*p)
X {
X case '<':
X i++;
X break;
X
X case '>':
X i--;
X break;
X }
X if (i < 0)
X break;
X }
X
X /* p now points to the closing quote (or a null byte) */
X if (*p != '\0')
X {
X /* make rhs point to the extra stuff at the end */
X rhs = p;
X *p++ = '\0';
X }
X }
X
X /*
X ** Now parse the real address part. "addr" points to the (null
X ** terminated) version of what we are inerested in; rhs points
X ** to the extra stuff at the end of the line, if any.
X */
X
X p = addr;
X
X /* now strip out comments */
X bp = &buf[strlen(buf)];
X gotaddr = FALSE;
X for (; *p != '\0'; p++)
X {
X if (*p == '(')
X {
X /* copy to matching close paren */
X *bp++ = *p++;
X for (i = 0; *p != '\0'; p++)
X {
X *bp++ = *p;
X switch (*p)
X {
X case '(':
X i++;
X break;
X
X case ')':
X i--;
X break;
X }
X if (i < 0)
X break;
X }
X continue;
X }
X
X /*
X ** If this is the first "real" character we have seen,
X ** then we put the "$g" in the buffer now.
X */
X
X if (isspace(*p))
X *bp++ = *p;
X else if (!gotaddr)
X {
X (void) strcpy(bp, "\001g");
X bp += 2;
X gotaddr = TRUE;
X }
X }
X
X /* hack, hack.... strip trailing blanks */
X do
X {
X *bp-- = '\0';
X } while (isspace(*bp));
X bp++;
X
X /* put any right hand side back on */
X if (rhs != NULL)
X {
X *rhs = '>';
X (void) strcpy(bp, rhs);
X }
X
X# ifdef DEBUG
X if (tTd(33, 1))
X printf("crackaddr=>`%s'\n", buf);
X# endif DEBUG
X
X return (buf);
X}
X/*
X** PUTHEADER -- put the header part of a message from the in-core copy
X**
X** Parameters:
X** fp -- file to put it on.
X** m -- mailer to use.
X** e -- envelope to use.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** none.
X*/
X
Xputheader(fp, m, e)
X register FILE *fp;
X register MAILER *m;
X register ENVELOPE *e;
X{
X char buf[BUFSIZ];
X register HDR *h;
X extern char *arpadate();
X extern char *capitalize();
X char obuf[MAXLINE];
X
X for (h = e->e_header; h != NULL; h = h->h_link)
X {
X register char *p;
X extern bool bitintersect();
X
X if (bitset(H_CHECK|H_ACHECK, h->h_flags) &&
X !bitintersect(h->h_mflags, m->m_flags))
X continue;
X
X /* handle Resent-... headers specially */
X if (bitset(H_RESENT, h->h_flags) && !bitset(EF_RESENT, e->e_flags))
X continue;
X
X p = h->h_value;
X if (bitset(H_DEFAULT, h->h_flags))
X {
X /* macro expand value if generated internally */
X expand(p, buf, &buf[sizeof buf], e);
X p = buf;
X if (p == NULL || *p == '\0')
X continue;
X }
X
X if (bitset(H_FROM|H_RCPT, h->h_flags))
X {
X /* address field */
X bool oldstyle = bitset(EF_OLDSTYLE, e->e_flags);
X
X if (bitset(H_FROM, h->h_flags))
X oldstyle = FALSE;
X commaize(h, p, fp, oldstyle, m);
X }
X else
X {
X /* vanilla header line */
X register char *nlp;
X
X (void) sprintf(obuf, "%s: ", capitalize(h->h_field));
X while ((nlp = index(p, '\n')) != NULL)
X {
X *nlp = '\0';
X (void) strcat(obuf, p);
X *nlp = '\n';
X putline(obuf, fp, m);
X p = ++nlp;
X obuf[0] = '\0';
X }
X (void) strcat(obuf, p);
X putline(obuf, fp, m);
X }
X }
X}
X/*
X** COMMAIZE -- output a header field, making a comma-translated list.
X**
X** Parameters:
X** h -- the header field to output.
X** p -- the value to put in it.
X** fp -- file to put it to.
X** oldstyle -- TRUE if this is an old style header.
X** m -- a pointer to the mailer descriptor. If NULL,
X** don't transform the name at all.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** outputs "p" to file "fp".
X*/
X
Xcommaize(h, p, fp, oldstyle, m)
X register HDR *h;
X register char *p;
X FILE *fp;
X bool oldstyle;
X register MAILER *m;
X{
X register char *obp;
X int opos;
X bool firstone = TRUE;
X char obuf[MAXLINE + 3];
X
X /*
X ** Output the address list translated by the
X ** mailer and with commas.
X */
X
X# ifdef DEBUG
X if (tTd(14, 2))
X printf("commaize(%s: %s)\n", h->h_field, p);
X# endif DEBUG
X
X obp = obuf;
X (void) sprintf(obp, "%s: ", capitalize(h->h_field));
X opos = strlen(h->h_field) + 2;
X obp += opos;
X
X /*
X ** Run through the list of values.
X */
X
X while (*p != '\0')
X {
X register char *name;
X char savechar;
X extern char *remotename();
X extern char *DelimChar; /* defined in prescan */
X
X /*
X ** Find the end of the name. New style names
X ** end with a comma, old style names end with
X ** a space character. However, spaces do not
X ** necessarily delimit an old-style name -- at
X ** signs mean keep going.
X */
X
X /* find end of name */
X while (isspace(*p) || *p == ',')
X p++;
X name = p;
X for (;;)
X {
X char *oldp;
X char pvpbuf[PSBUFSIZE];
X extern bool isatword();
X extern char **prescan();
X
X (void) prescan(p, oldstyle ? ' ' : ',', pvpbuf);
X p = DelimChar;
X
X /* look to see if we have an at sign */
X oldp = p;
X while (*p != '\0' && isspace(*p))
X p++;
X
X if (*p != '@' && !isatword(p))
X {
X p = oldp;
X break;
X }
X p += *p == '@' ? 1 : 2;
X while (*p != '\0' && isspace(*p))
X p++;
X }
X /* at the end of one complete name */
X
X /* strip off trailing white space */
X while (p >= name && (isspace(*p) || *p == ',' || *p == '\0'))
X p--;
X if (++p == name)
X continue;
X savechar = *p;
X *p = '\0';
X
X /* translate the name to be relative */
X name = remotename(name, m, bitset(H_FROM, h->h_flags), FALSE);
X if (*name == '\0')
X {
X *p = savechar;
X continue;
X }
X
X /* output the name with nice formatting */
X opos += qstrlen(name);
X if (!firstone)
X opos += 2;
X if (opos > 78 && !firstone)
X {
X (void) strcpy(obp, ",\n");
X putline(obuf, fp, m);
X obp = obuf;
X (void) sprintf(obp, " ");
X opos = strlen(obp);
X obp += opos;
X opos += qstrlen(name);
X }
X else if (!firstone)
X {
X (void) sprintf(obp, ", ");
X obp += 2;
X }
X
X /* strip off quote bits as we output */
X while (*name != '\0' && obp < &obuf[MAXLINE])
X {
X if (bitset(0200, *name))
X *obp++ = '\\';
X *obp++ = *name++ & ~0200;
X }
X firstone = FALSE;
X *p = savechar;
X }
X (void) strcpy(obp, "\n");
X putline(obuf, fp, m);
X}
X/*
X** ISATWORD -- tell if the word we are pointing to is "at".
X**
X** Parameters:
X** p -- word to check.
X**
X** Returns:
X** TRUE -- if p is the word at.
X** FALSE -- otherwise.
X**
X** Side Effects:
X** none.
X*/
X
Xbool
Xisatword(p)
X register char *p;
X{
X extern char lower();
X
X if (lower(p[0]) == 'a' && lower(p[1]) == 't' &&
X p[2] != '\0' && isspace(p[2]))
X return (TRUE);
X return (FALSE);
X}
END_OF_FILE
if test 16858 -ne `wc -c <'src/headers.c'`; then
echo shar: \"'src/headers.c'\" unpacked with wrong size!
fi
# end of 'src/headers.c'
fi
if test -f 'src/readcf.c' -a "${1}" != "-c" ; then
echo shar: Will not clobber existing file \"'src/readcf.c'\"
else
echo shar: Extracting \"'src/readcf.c'\" \(18332 characters\)
sed "s/^X//" >'src/readcf.c' <<'END_OF_FILE'
X/*
X** Sendmail
X** Copyright (c) 1983 Eric P. Allman
X** Berkeley, California
X**
X** Copyright (c) 1983 Regents of the University of California.
X** All rights reserved. The Berkeley software License Agreement
X** specifies the terms and conditions for redistribution.
X*/
X
X#ifndef lint
Xstatic char SccsId[] = "@(#)readcf.c 5.10 (Berkeley) 1/11/86";
X#endif not lint
X
X# include "sendmail.h"
X
X/*
X** READCF -- read control file.
X**
X** This routine reads the control file and builds the internal
X** form.
X**
X** The file is formatted as a sequence of lines, each taken
X** atomically. The first character of each line describes how
X** the line is to be interpreted. The lines are:
X** Dxval Define macro x to have value val.
X** Cxword Put word into class x.
X** Fxfile [fmt] Read file for lines to put into
X** class x. Use scanf string 'fmt'
X** or "%s" if not present. Fmt should
X** only produce one string-valued result.
X** Hname: value Define header with field-name 'name'
X** and value as specified; this will be
X** macro expanded immediately before
X** use.
X** Sn Use rewriting set n.
X** Rlhs rhs Rewrite addresses that match lhs to
X** be rhs.
X** Mn arg=val... Define mailer. n is the internal name.
X** Args specify mailer parameters.
X** Oxvalue Set option x to value.
X** Pname=value Set precedence name to value.
X**
X** Parameters:
X** cfname -- control file name.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Builds several internal tables.
X*/
X
Xreadcf(cfname)
X char *cfname;
X{
X FILE *cf;
X int ruleset = 0;
X char *q;
X char **pv;
X struct rewrite *rwp = NULL;
X char buf[MAXLINE];
X register char *p;
X extern char **prescan();
X extern char **copyplist();
X char exbuf[MAXLINE];
X char pvpbuf[PSBUFSIZE];
X extern char *fgetfolded();
X extern char *munchstring();
X
X cf = fopen(cfname, "r");
X if (cf == NULL)
X {
X syserr("cannot open %s", cfname);
X exit(EX_OSFILE);
X }
X
X FileName = cfname;
X LineNumber = 0;
X while (fgetfolded(buf, sizeof buf, cf) != NULL)
X {
X /* map $ into \001 (ASCII SOH) for macro expansion */
X for (p = buf; *p != '\0'; p++)
X {
X if (*p != '$')
X continue;
X
X if (p[1] == '$')
X {
X /* actual dollar sign.... */
X (void) strcpy(p, p + 1);
X continue;
X }
X
X /* convert to macro expansion character */
X *p = '\001';
X }
X
X /* interpret this line */
X switch (buf[0])
X {
X case '\0':
X case '#': /* comment */
X break;
X
X case 'R': /* rewriting rule */
X for (p = &buf[1]; *p != '\0' && *p != '\t'; p++)
X continue;
X
X if (*p == '\0')
X {
X syserr("invalid rewrite line \"%s\"", buf);
X break;
X }
X
X /* allocate space for the rule header */
X if (rwp == NULL)
X {
X RewriteRules[ruleset] = rwp =
X (struct rewrite *) xalloc(sizeof *rwp);
X }
X else
X {
X rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
X rwp = rwp->r_next;
X }
X rwp->r_next = NULL;
X
X /* expand and save the LHS */
X *p = '\0';
X expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
X rwp->r_lhs = prescan(exbuf, '\t', pvpbuf);
X if (rwp->r_lhs != NULL)
X rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
X
X /* expand and save the RHS */
X while (*++p == '\t')
X continue;
X q = p;
X while (*p != '\0' && *p != '\t')
X p++;
X *p = '\0';
X expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
X rwp->r_rhs = prescan(exbuf, '\t', pvpbuf);
X if (rwp->r_rhs != NULL)
X rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
X break;
X
X case 'S': /* select rewriting set */
X ruleset = atoi(&buf[1]);
X if (ruleset >= MAXRWSETS || ruleset < 0)
X {
X syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
X ruleset = 0;
X }
X rwp = NULL;
X break;
X
X case 'D': /* macro definition */
X define(buf[1], newstr(munchstring(&buf[2])), CurEnv);
X break;
X
X case 'H': /* required header line */
X (void) chompheader(&buf[1], TRUE);
X break;
X
X case 'C': /* word class */
X case 'F': /* word class from file */
X /* read list of words from argument or file */
X if (buf[0] == 'F')
X {
X /* read from file */
X for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
X continue;
X if (*p == '\0')
X p = "%s";
X else
X {
X *p = '\0';
X while (isspace(*++p))
X continue;
X }
X fileclass(buf[1], &buf[2], p);
X break;
X }
X
X /* scan the list of words and set class for all */
X for (p = &buf[2]; *p != '\0'; )
X {
X register char *wd;
X char delim;
X
X while (*p != '\0' && isspace(*p))
X p++;
X wd = p;
X while (*p != '\0' && !isspace(*p))
X p++;
X delim = *p;
X *p = '\0';
X if (wd[0] != '\0')
X setclass(buf[1], wd);
X *p = delim;
X }
X break;
X
X case 'M': /* define mailer */
X makemailer(&buf[1]);
X break;
X
X case 'O': /* set option */
X setoption(buf[1], &buf[2], TRUE, FALSE);
X break;
X
X case 'P': /* set precedence */
X if (NumPriorities >= MAXPRIORITIES)
X {
X toomany('P', MAXPRIORITIES);
X break;
X }
X for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
X continue;
X if (*p == '\0')
X goto badline;
X *p = '\0';
X Priorities[NumPriorities].pri_name = newstr(&buf[1]);
X Priorities[NumPriorities].pri_val = atoi(++p);
X NumPriorities++;
X break;
X
X case 'T': /* trusted user(s) */
X p = &buf[1];
X while (*p != '\0')
X {
X while (isspace(*p))
X p++;
X q = p;
X while (*p != '\0' && !isspace(*p))
X p++;
X if (*p != '\0')
X *p++ = '\0';
X if (*q == '\0')
X continue;
X for (pv = TrustedUsers; *pv != NULL; pv++)
X continue;
X if (pv >= &TrustedUsers[MAXTRUST])
X {
X toomany('T', MAXTRUST);
X break;
X }
X *pv = newstr(q);
X }
X break;
X
X default:
X badline:
X syserr("unknown control line \"%s\"", buf);
X }
X }
X FileName = NULL;
X}
X/*
X** TOOMANY -- signal too many of some option
X**
X** Parameters:
X** id -- the id of the error line
X** maxcnt -- the maximum possible values
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** gives a syserr.
X*/
X
Xtoomany(id, maxcnt)
X char id;
X int maxcnt;
X{
X syserr("too many %c lines, %d max", id, maxcnt);
X}
X/*
X** FILECLASS -- read members of a class from a file
X**
X** Parameters:
X** class -- class to define.
X** filename -- name of file to read.
X** fmt -- scanf string to use for match.
X**
X** Returns:
X** none
X**
X** Side Effects:
X**
X** puts all lines in filename that match a scanf into
X** the named class.
X*/
X
Xfileclass(class, filename, fmt)
X int class;
X char *filename;
X char *fmt;
X{
X FILE *f;
X char buf[MAXLINE];
X
X f = fopen(filename, "r");
X if (f == NULL)
X {
X syserr("cannot open %s", filename);
X return;
X }
X
X while (fgets(buf, sizeof buf, f) != NULL)
X {
X register STAB *s;
X register char *p;
X# ifdef SCANF
X char wordbuf[MAXNAME+1];
X
X if (sscanf(buf, fmt, wordbuf) != 1)
X continue;
X p = wordbuf;
X# else SCANF
X p = buf;
X# endif SCANF
X
X /*
X ** Break up the match into words.
X */
X
X while (*p != '\0')
X {
X register char *q;
X
X /* strip leading spaces */
X while (isspace(*p))
X p++;
X if (*p == '\0')
X break;
X
X /* find the end of the word */
X q = p;
X while (*p != '\0' && !isspace(*p))
X p++;
X if (*p != '\0')
X *p++ = '\0';
X
X /* enter the word in the symbol table */
X s = stab(q, ST_CLASS, ST_ENTER);
X setbitn(class, s->s_class);
X }
X }
X
X (void) fclose(f);
X}
X/*
X** MAKEMAILER -- define a new mailer.
X**
X** Parameters:
X** line -- description of mailer. This is in labeled
X** fields. The fields are:
X** P -- the path to the mailer
X** F -- the flags associated with the mailer
X** A -- the argv for this mailer
X** S -- the sender rewriting set
X** R -- the recipient rewriting set
X** E -- the eol string
X** The first word is the canonical name of the mailer.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** enters the mailer into the mailer table.
X*/
X
Xmakemailer(line)
X char *line;
X{
X register char *p;
X register struct mailer *m;
X register STAB *s;
X int i;
X char fcode;
X extern int NextMailer;
X extern char **makeargv();
X extern char *munchstring();
X extern char *DelimChar;
X extern long atol();
X
X /* allocate a mailer and set up defaults */
X m = (struct mailer *) xalloc(sizeof *m);
X bzero((char *) m, sizeof *m);
X m->m_mno = NextMailer;
X m->m_eol = "\n";
X
X /* collect the mailer name */
X for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++)
X continue;
X if (*p != '\0')
X *p++ = '\0';
X m->m_name = newstr(line);
X
X /* now scan through and assign info from the fields */
X while (*p != '\0')
X {
X while (*p != '\0' && (*p == ',' || isspace(*p)))
X p++;
X
X /* p now points to field code */
X fcode = *p;
X while (*p != '\0' && *p != '=' && *p != ',')
X p++;
X if (*p++ != '=')
X {
X syserr("`=' expected");
X return;
X }
X while (isspace(*p))
X p++;
X
X /* p now points to the field body */
X p = munchstring(p);
X
X /* install the field into the mailer struct */
X switch (fcode)
X {
X case 'P': /* pathname */
X m->m_mailer = newstr(p);
X break;
X
X case 'F': /* flags */
X for (; *p != '\0'; p++)
X setbitn(*p, m->m_flags);
X break;
X
X case 'S': /* sender rewriting ruleset */
X case 'R': /* recipient rewriting ruleset */
X i = atoi(p);
X if (i < 0 || i >= MAXRWSETS)
X {
X syserr("invalid rewrite set, %d max", MAXRWSETS);
X return;
X }
X if (fcode == 'S')
X m->m_s_rwset = i;
X else
X m->m_r_rwset = i;
X break;
X
X case 'E': /* end of line string */
X m->m_eol = newstr(p);
X break;
X
X case 'A': /* argument vector */
X m->m_argv = makeargv(p);
X break;
X
X case 'M': /* maximum message size */
X m->m_maxsize = atol(p);
X break;
X }
X
X p = DelimChar;
X }
X
X /* now store the mailer away */
X if (NextMailer >= MAXMAILERS)
X {
X syserr("too many mailers defined (%d max)", MAXMAILERS);
X return;
X }
X Mailer[NextMailer++] = m;
X s = stab(m->m_name, ST_MAILER, ST_ENTER);
X s->s_mailer = m;
X}
X/*
X** MUNCHSTRING -- translate a string into internal form.
X**
X** Parameters:
X** p -- the string to munch.
X**
X** Returns:
X** the munched string.
X**
X** Side Effects:
X** Sets "DelimChar" to point to the string that caused us
X** to stop.
X*/
X
Xchar *
Xmunchstring(p)
X register char *p;
X{
X register char *q;
X bool backslash = FALSE;
X bool quotemode = FALSE;
X static char buf[MAXLINE];
X extern char *DelimChar;
X
X for (q = buf; *p != '\0'; p++)
X {
X if (backslash)
X {
X /* everything is roughly literal */
X backslash = FALSE;
X switch (*p)
X {
X case 'r': /* carriage return */
X *q++ = '\r';
X continue;
X
X case 'n': /* newline */
X *q++ = '\n';
X continue;
X
X case 'f': /* form feed */
X *q++ = '\f';
X continue;
X
X case 'b': /* backspace */
X *q++ = '\b';
X continue;
X }
X *q++ = *p;
X }
X else
X {
X if (*p == '\\')
X backslash = TRUE;
X else if (*p == '"')
X quotemode = !quotemode;
X else if (quotemode || *p != ',')
X *q++ = *p;
X else
X break;
X }
X }
X
X DelimChar = p;
X *q++ = '\0';
X return (buf);
X}
X/*
X** MAKEARGV -- break up a string into words
X**
X** Parameters:
X** p -- the string to break up.
X**
X** Returns:
X** a char **argv (dynamically allocated)
X**
X** Side Effects:
X** munges p.
X*/
X
Xchar **
Xmakeargv(p)
X register char *p;
X{
X char *q;
X int i;
X char **avp;
X char *argv[MAXPV + 1];
X
X /* take apart the words */
X i = 0;
X while (*p != '\0' && i < MAXPV)
X {
X q = p;
X while (*p != '\0' && !isspace(*p))
X p++;
X while (isspace(*p))
X *p++ = '\0';
X argv[i++] = newstr(q);
X }
X argv[i++] = NULL;
X
X /* now make a copy of the argv */
X avp = (char **) xalloc(sizeof *avp * i);
X bcopy((char *) argv, (char *) avp, sizeof *avp * i);
X
X return (avp);
X}
X/*
X** PRINTRULES -- print rewrite rules (for debugging)
X**
X** Parameters:
X** none.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** prints rewrite rules.
X*/
X
X# ifdef DEBUG
X
Xprintrules()
X{
X register struct rewrite *rwp;
X register int ruleset;
X
X for (ruleset = 0; ruleset < 10; ruleset++)
X {
X if (RewriteRules[ruleset] == NULL)
X continue;
X printf("\n----Rule Set %d:", ruleset);
X
X for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
X {
X printf("\nLHS:");
X printav(rwp->r_lhs);
X printf("RHS:");
X printav(rwp->r_rhs);
X }
X }
X}
X
X# endif DEBUG
X/*
X** SETOPTION -- set global processing option
X**
X** Parameters:
X** opt -- option name.
X** val -- option value (as a text string).
X** safe -- set if this came from a configuration file.
X** Some options (if set from the command line) will
X** reset the user id to avoid security problems.
X** sticky -- if set, don't let other setoptions override
X** this value.
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** Sets options as implied by the arguments.
X*/
X
Xstatic BITMAP StickyOpt; /* set if option is stuck */
Xextern char *NetName; /* name of home (local) network */
X# ifdef SMTP
X# ifdef WIZ
Xextern char *WizWord; /* the stored wizard password */
X# endif WIZ
X# endif SMTP
X
Xsetoption(opt, val, safe, sticky)
X char opt;
X char *val;
X bool safe;
X bool sticky;
X{
X extern bool atobool();
X extern time_t convtime();
X extern int QueueLA;
X extern int RefuseLA;
X extern bool trusteduser();
X extern char *username();
X
X# ifdef DEBUG
X if (tTd(37, 1))
X printf("setoption %c=%s", opt, val);
X# endif DEBUG
X
X /*
X ** See if this option is preset for us.
X */
X
X if (bitnset(opt, StickyOpt))
X {
X# ifdef DEBUG
X if (tTd(37, 1))
X printf(" (ignored)\n");
X# endif DEBUG
X return;
X }
X
X /*
X ** Check to see if this option can be specified by this user.
X */
X
X if (!safe && getruid() == 0)
X safe = TRUE;
X if (!safe && index("deiLmorsv", opt) == NULL)
X {
X# ifdef DEBUG
X if (tTd(37, 1))
X printf(" (unsafe)");
X# endif DEBUG
X if (getruid() != geteuid())
X {
X printf("(Resetting uid)\n");
X (void) setgid(getgid());
X (void) setuid(getuid());
X }
X }
X#ifdef DEBUG
X else if (tTd(37, 1))
X printf("\n");
X#endif DEBUG
X
X switch (opt)
X {
X case 'A': /* set default alias file */
X if (val[0] == '\0')
X AliasFile = "aliases";
X else
X AliasFile = newstr(val);
X break;
X
X case 'a': /* look N minutes for "@:@" in alias file */
X if (val[0] == '\0')
X SafeAlias = 5;
X else
X SafeAlias = atoi(val);
X break;
X
X case 'B': /* substitution for blank character */
X SpaceSub = val[0];
X if (SpaceSub == '\0')
X SpaceSub = ' ';
X break;
X
X case 'c': /* don't connect to "expensive" mailers */
X NoConnect = atobool(val);
X break;
X
X case 'C': /* checkpoint after N connections */
X CheckPointLimit = atoi(val);
X break;
X
X case 'd': /* delivery mode */
X switch (*val)
X {
X case '\0':
X SendMode = SM_DELIVER;
X break;
X
X case SM_QUEUE: /* queue only */
X#ifndef QUEUE
X syserr("need QUEUE to set -odqueue");
X#endif QUEUE
X /* fall through..... */
X
X case SM_DELIVER: /* do everything */
X case SM_FORK: /* fork after verification */
X SendMode = *val;
X break;
X
X default:
X syserr("Unknown delivery mode %c", *val);
X exit(EX_USAGE);
X }
X break;
X
X case 'D': /* rebuild alias database as needed */
X AutoRebuild = atobool(val);
X break;
X
X case 'e': /* set error processing mode */
X switch (*val)
X {
X case EM_QUIET: /* be silent about it */
X case EM_MAIL: /* mail back */
X case EM_BERKNET: /* do berknet error processing */
X case EM_WRITE: /* write back (or mail) */
X HoldErrs = TRUE;
X /* fall through... */
X
X case EM_PRINT: /* print errors normally (default) */
X ErrorMode = *val;
X break;
X }
X break;
X
X case 'F': /* file mode */
X FileMode = atooct(val) & 0777;
X break;
X
X case 'f': /* save Unix-style From lines on front */
X SaveFrom = atobool(val);
X break;
X
X case 'g': /* default gid */
X DefGid = atoi(val);
X break;
X
X case 'H': /* help file */
X if (val[0] == '\0')
X HelpFile = "sendmail.hf";
X else
X HelpFile = newstr(val);
X break;
X
X case 'i': /* ignore dot lines in message */
X IgnrDot = atobool(val);
X break;
X
X case 'L': /* log level */
X LogLevel = atoi(val);
X break;
X
X case 'M': /* define macro */
X define(val[0], newstr(&val[1]), CurEnv);
X sticky = FALSE;
X break;
X
X case 'm': /* send to me too */
X MeToo = atobool(val);
X break;
X
X case 'n': /* validate RHS in newaliases */
X CheckAliases = atobool(val);
X break;
X
X# ifdef DAEMON
X case 'N': /* home (local?) network name */
X NetName = newstr(val);
X break;
X# endif DAEMON
X
X case 'o': /* assume old style headers */
X if (atobool(val))
X CurEnv->e_flags |= EF_OLDSTYLE;
X else
X CurEnv->e_flags &= ~EF_OLDSTYLE;
X break;
X
X case 'P': /* postmaster copy address for returned mail */
X PostMasterCopy = newstr(val);
X break;
X
X case 'q': /* slope of queue only function */
X QueueFactor = atoi(val);
X break;
X
X case 'Q': /* queue directory */
X if (val[0] == '\0')
X QueueDir = "mqueue";
X else
X QueueDir = newstr(val);
X break;
X
X case 'r': /* read timeout */
X ReadTimeout = convtime(val);
X break;
X
X case 'S': /* status file */
X if (val[0] == '\0')
X StatFile = "sendmail.st";
X else
X StatFile = newstr(val);
X break;
X
X case 's': /* be super safe, even if expensive */
X SuperSafe = atobool(val);
X break;
X
X case 'T': /* queue timeout */
X TimeOut = convtime(val);
X break;
X
X case 't': /* time zone name */
X# ifdef V6
X StdTimezone = newstr(val);
X DstTimezone = index(StdTimeZone, ',');
X if (DstTimezone == NULL)
X syserr("bad time zone spec");
X else
X *DstTimezone++ = '\0';
X# endif V6
X break;
X
X case 'u': /* set default uid */
X DefUid = atoi(val);
X break;
X
X case 'v': /* run in verbose mode */
X Verbose = atobool(val);
X break;
X
X# ifdef SMTP
X# ifdef WIZ
X case 'W': /* set the wizards password */
X WizWord = newstr(val);
X break;
X# endif WIZ
X# endif SMTP
X
X case 'x': /* load avg at which to auto-queue msgs */
X QueueLA = atoi(val);
X break;
X
X case 'X': /* load avg at which to auto-reject connections */
X RefuseLA = atoi(val);
X break;
X
X case 'y': /* work recipient factor */
X WkRecipFact = atoi(val);
X break;
X
X case 'Y': /* fork jobs during queue runs */
X ForkQueueRuns = atobool(val);
X break;
X
X case 'z': /* work message class factor */
X WkClassFact = atoi(val);
X break;
X
X case 'Z': /* work time factor */
X WkTimeFact = atoi(val);
X break;
X
X default:
X break;
X }
X if (sticky)
X setbitn(opt, StickyOpt);
X return;
X}
X/*
X** SETCLASS -- set a word into a class
X**
X** Parameters:
X** class -- the class to put the word in.
X** word -- the word to enter
X**
X** Returns:
X** none.
X**
X** Side Effects:
X** puts the word into the symbol table.
X*/
X
Xsetclass(class, word)
X int class;
X char *word;
X{
X register STAB *s;
X
X s = stab(word, ST_CLASS, ST_ENTER);
X setbitn(class, s->s_class);
X}
END_OF_FILE
if test 18332 -ne `wc -c <'src/readcf.c'`; then
echo shar: \"'src/readcf.c'\" unpacked with wrong size!
fi
# end of 'src/readcf.c'
fi
echo shar: End of archive 5 \(of 8\).
cp /dev/null ark5isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 ; do
if test ! -f ark${I}isdone ; then
MISSING="${MISSING} ${I}"
fi
done
if test "${MISSING}" = "" ; then
echo You have unpacked all 8 archives.
rm -f ark[1-9]isdone
else
echo You still need to unpack the following archives:
echo " " ${MISSING}
fi
## End of shell archive.
exit 0
--
David H. Brierley
Home: dave at galaxia.Newport.RI.US {rayssd,xanth,lazlo,jclyde}!galaxia!dave
Work: dhb at rayssd.ray.com {sun,decuac,gatech,necntc,ukma}!rayssd!dhb
More information about the Unix-pc.sources
mailing list