* Copyright (c) 1983 Eric P. Allman
* Copyright (c) 1988 Regents of the University of California.
* %sccs.include.redist.c%
static char sccsid
[] = "@(#)readcf.c 5.22 (Berkeley) %G%";
** READCF -- read control file.
** This routine reads the control file and builds the internal
** 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
** Sn Use rewriting set n.
** Rlhs rhs Rewrite addresses that match lhs to
** 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.
** cfname -- control file name.
** Builds several internal tables.
struct rewrite
*rwp
= NULL
;
extern char **copyplist();
extern char *fgetfolded();
extern char *munchstring();
syserr("cannot open %s", cfname
);
while (fgetfolded(buf
, sizeof buf
, cf
) != NULL
)
/* map $ into \001 (ASCII SOH) for macro expansion */
for (p
= buf
; *p
!= '\0'; p
++)
/* actual dollar sign.... */
/* convert to macro expansion character */
/* interpret this line */
case 'R': /* rewriting rule */
for (p
= &buf
[1]; *p
!= '\0' && *p
!= '\t'; p
++)
syserr("invalid rewrite line \"%s\"", buf
);
/* allocate space for the rule header */
RewriteRules
[ruleset
] = rwp
=
(struct rewrite
*) xalloc(sizeof *rwp
);
rwp
->r_next
= (struct rewrite
*) xalloc(sizeof *rwp
);
/* expand and save the LHS */
expand(&buf
[1], exbuf
, &exbuf
[sizeof exbuf
], CurEnv
);
rwp
->r_lhs
= prescan(exbuf
, '\t', pvpbuf
);
rwp
->r_lhs
= copyplist(rwp
->r_lhs
, TRUE
);
/* expand and save the RHS */
while (*p
!= '\0' && *p
!= '\t')
expand(q
, exbuf
, &exbuf
[sizeof exbuf
], CurEnv
);
rwp
->r_rhs
= prescan(exbuf
, '\t', pvpbuf
);
rwp
->r_rhs
= copyplist(rwp
->r_rhs
, TRUE
);
case 'S': /* select rewriting set */
if (ruleset
>= MAXRWSETS
|| ruleset
< 0)
syserr("bad ruleset %d (%d max)", ruleset
, MAXRWSETS
);
case 'D': /* macro definition */
define(buf
[1], newstr(munchstring(&buf
[2])), CurEnv
);
case 'H': /* required header line */
(void) chompheader(&buf
[1], TRUE
);
case 'C': /* word class */
case 'F': /* word class from file */
/* read list of words from argument or file */
for (p
= &buf
[2]; *p
!= '\0' && !isspace(*p
); p
++)
fileclass(buf
[1], &buf
[2], p
);
/* scan the list of words and set class for all */
for (p
= &buf
[2]; *p
!= '\0'; )
while (*p
!= '\0' && isspace(*p
))
while (*p
!= '\0' && !isspace(*p
))
case 'M': /* define mailer */
case 'O': /* set option */
setoption(buf
[1], &buf
[2], FALSE
);
case 'P': /* set precedence */
if (NumPriorities
>= MAXPRIORITIES
)
toomany('P', MAXPRIORITIES
);
for (p
= &buf
[1]; *p
!= '\0' && *p
!= '=' && *p
!= '\t'; p
++)
Priorities
[NumPriorities
].pri_name
= newstr(&buf
[1]);
Priorities
[NumPriorities
].pri_val
= atoi(++p
);
case 'T': /* trusted user(s) */
while (*p
!= '\0' && !isspace(*p
))
for (pv
= TrustedUsers
; *pv
!= NULL
; pv
++)
if (pv
>= &TrustedUsers
[MAXTRUST
])
syserr("unknown control line \"%s\"", buf
);
** TOOMANY -- signal too many of some option
** id -- the id of the error line
** maxcnt -- the maximum possible values
syserr("too many %c lines, %d max", id
, maxcnt
);
** FILECLASS -- read members of a class from a file
** class -- class to define.
** filename -- name of file to read.
** fmt -- scanf string to use for match.
** puts all lines in filename that match a scanf into
fileclass(class, filename
, fmt
)
f
= fopen(filename
, "r");
syserr("cannot open %s", filename
);
while (fgets(buf
, sizeof buf
, f
) != NULL
)
if (sscanf(buf
, fmt
, wordbuf
) != 1)
** Break up the match into words.
/* strip leading spaces */
/* find the end of the word */
while (*p
!= '\0' && !isspace(*p
))
/* enter the word in the symbol table */
s
= stab(q
, ST_CLASS
, ST_ENTER
);
setbitn(class, s
->s_class
);
** MAKEMAILER -- define a new mailer.
** 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
** The first word is the canonical name of the mailer.
** enters the mailer into the mailer table.
register struct mailer
*m
;
extern char **makeargv();
extern char *munchstring();
/* allocate a mailer and set up defaults */
m
= (struct mailer
*) xalloc(sizeof *m
);
bzero((char *) m
, sizeof *m
);
/* collect the mailer name */
for (p
= line
; *p
!= '\0' && *p
!= ',' && !isspace(*p
); p
++)
m
->m_name
= newstr(line
);
/* now scan through and assign info from the fields */
while (*p
!= '\0' && (*p
== ',' || isspace(*p
)))
/* p now points to field code */
while (*p
!= '\0' && *p
!= '=' && *p
!= ',')
/* p now points to the field body */
/* install the field into the mailer struct */
case 'S': /* sender rewriting ruleset */
case 'R': /* recipient rewriting ruleset */
if (i
< 0 || i
>= MAXRWSETS
)
syserr("invalid rewrite set, %d max", MAXRWSETS
);
case 'E': /* end of line string */
case 'A': /* argument vector */
case 'M': /* maximum message size */
/* now store the mailer away */
if (NextMailer
>= MAXMAILERS
)
syserr("too many mailers defined (%d max)", MAXMAILERS
);
Mailer
[NextMailer
++] = m
;
s
= stab(m
->m_name
, ST_MAILER
, ST_ENTER
);
** MUNCHSTRING -- translate a string into internal form.
** p -- the string to munch.
** Sets "DelimChar" to point to the string that caused us
static char buf
[MAXLINE
];
for (q
= buf
; *p
!= '\0'; p
++)
/* everything is roughly literal */
case 'r': /* carriage return */
case 'f': /* form feed */
case 'b': /* backspace */
else if (quotemode
|| *p
!= ',')
** MAKEARGV -- break up a string into words
** p -- the string to break up.
** a char **argv (dynamically allocated)
/* take apart the words */
while (*p
!= '\0' && i
< MAXPV
)
while (*p
!= '\0' && !isspace(*p
))
/* now make a copy of the argv */
avp
= (char **) xalloc(sizeof *avp
* i
);
bcopy((char *) argv
, (char *) avp
, sizeof *avp
* i
);
** PRINTRULES -- print rewrite rules (for debugging)
register struct rewrite
*rwp
;
for (ruleset
= 0; ruleset
< 10; ruleset
++)
if (RewriteRules
[ruleset
] == NULL
)
printf("\n----Rule Set %d:", ruleset
);
for (rwp
= RewriteRules
[ruleset
]; rwp
!= NULL
; rwp
= rwp
->r_next
)
** SETOPTION -- set global processing option
** val -- option value (as a text string).
** sticky -- if set, don't let other setoptions override
** 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
, sticky
)
extern time_t convtime();
extern bool trusteduser();
printf("setoption %c=%s", opt
, val
);
** See if this option is preset for us.
if (bitnset(opt
, StickyOpt
))
case 'A': /* set default alias file */
case 'a': /* look N minutes for "@:@" in alias file */
case 'B': /* substitution for blank character */
case 'c': /* don't connect to "expensive" mailers */
NoConnect
= atobool(val
);
case 'C': /* checkpoint after N connections */
CheckPointLimit
= atoi(val
);
case 'd': /* delivery mode */
case SM_QUEUE
: /* queue only */
syserr("need QUEUE to set -odqueue");
case SM_DELIVER
: /* do everything */
case SM_FORK
: /* fork after verification */
syserr("Unknown delivery mode %c", *val
);
case 'D': /* rebuild alias database as needed */
AutoRebuild
= atobool(val
);
case 'e': /* set error processing mode */
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) */
case EM_PRINT
: /* print errors normally (default) */
case 'F': /* file mode */
FileMode
= atooct(val
) & 0777;
case 'f': /* save Unix-style From lines on front */
case 'g': /* default gid */
case 'H': /* help file */
HelpFile
= "sendmail.hf";
case 'I': /* use internet domain name server */
UseNameServer
= atobool(val
);
case 'i': /* ignore dot lines in message */
case 'k': /* checkpoint every N addresses */
CheckpointInterval
= atoi(val
);
case 'L': /* log level */
case 'M': /* define macro */
define(val
[0], newstr(&val
[1]), CurEnv
);
case 'm': /* send to me too */
case 'n': /* validate RHS in newaliases */
CheckAliases
= atobool(val
);
case 'N': /* home (local?) network name */
case 'o': /* assume old style headers */
CurEnv
->e_flags
|= EF_OLDSTYLE
;
CurEnv
->e_flags
&= ~EF_OLDSTYLE
;
case 'P': /* postmaster copy address for returned mail */
PostMasterCopy
= newstr(val
);
case 'q': /* slope of queue only function */
case 'Q': /* queue directory */
case 'r': /* read timeout */
ReadTimeout
= convtime(val
);
case 'S': /* status file */
StatFile
= "sendmail.st";
case 's': /* be super safe, even if expensive */
SuperSafe
= atobool(val
);
case 'T': /* queue timeout */
case 't': /* time zone name */
case 'u': /* set default uid */
case 'v': /* run in verbose mode */
case 'x': /* load avg at which to auto-queue msgs */
case 'X': /* load avg at which to auto-reject connections */
case 'y': /* work recipient factor */
case 'Y': /* fork jobs during queue runs */
ForkQueueRuns
= atobool(val
);
case 'z': /* work message class factor */
case 'Z': /* work time factor */
** SETCLASS -- set a word into a class
** class -- the class to put the word in.
** word -- the word to enter
** puts the word into the symbol table.
s
= stab(word
, ST_CLASS
, ST_ENTER
);
setbitn(class, s
->s_class
);