SCCSID(@
(#)readcf.c 3.35 %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 p f s r a Define mailer. n - internal name,
** p - pathname, f - flags, s - rewriting
** ruleset for sender, s - rewriting ruleset
** for recipients, a - argument vector.
** Oxvalue Set option x to value.
** Pname=value Set precedence name to value.
** cfname -- control file name.
** safe -- set if this is a system configuration file.
** Non-system configuration files can not do
** certain things (e.g., leave the SUID bit on
** when executing mailers).
** Builds several internal tables.
struct rewrite
*rwp
= NULL
;
extern char **copyplist();
syserr("cannot open %s", cfname
);
while (fgetfolded(buf
, sizeof buf
, cf
) != NULL
)
case 'R': /* rewriting rule */
for (p
= &buf
[1]; *p
!= '\0' && *p
!= '\t'; p
++)
syserr("line %d: invalid rewrite line \"%s\"",
/* 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');
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');
rwp
->r_rhs
= copyplist(rwp
->r_rhs
, TRUE
);
case 'S': /* select rewriting set */
if (ruleset
>= MAXRWSETS
|| ruleset
< 0)
syserr("readcf: line %d: bad ruleset %d (%d max)",
LineNumber
, ruleset
, MAXRWSETS
);
case 'D': /* macro definition */
define(buf
[1], newstr(&buf
[2]));
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(class, &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
))
s
= stab(wd
, ST_CLASS
, ST_ENTER
);
s
->s_class
|= 1L << class;
case 'M': /* define mailer */
makemailer(&buf
[1], safe
);
case 'O': /* set option */
setoption(buf
[1], &buf
[2], safe
, FALSE
);
case 'P': /* set precedence */
if (NumPriorities
>= MAXPRIORITIES
)
syserr("readcf: line %d: too many P lines, %d max",
LineNumber
, MAXPRIORITIES
);
for (p
= &buf
[1]; *p
!= '\0' && *p
!= '='; p
++)
Priorities
[NumPriorities
].pri_name
= newstr(&buf
[1]);
Priorities
[NumPriorities
].pri_val
= atoi(++p
);
syserr("readcf: line %d: unknown control line \"%s\"",
** 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)
s
= stab(wordbuf
, ST_CLASS
, ST_ENTER
);
s
->s_class
|= 1L << class;
** MAKEMAILER -- define a new mailer.
** line -- description of mailer. This is in tokens
** separated by white space. The fields are:
** * the name of the mailer, as refered to
** in the rewriting rules.
** * the pathname of the program to fork to
** * the options needed by this program.
** * the macro string needed to translate
** a local "from" name to one that can be
** returned to this machine.
** * the argument vector (a series of parameters).
** safe -- set if this is a safe configuration file.
** enters the mailer into the mailer table.
while (*p != '\0' && isspace(*p)) \
while (*p != '\0' && !isspace(*p)) \
register struct mailer
*m
;
extern u_long
mfencode();
if (NextMailer
>= MAXMAILERS
)
syserr("readcf: line %d: too many mailers defined (%d max)",
/* collect initial information */
syserr("readcf: line %d: invalid M line in configuration file",
if (msset
>= MAXRWSETS
|| mrset
>= MAXRWSETS
)
syserr("readcf: line %d: invalid rewrite set, %d max",
m
= (struct mailer
*) xalloc(sizeof *m
);
m
->m_name
= newstr(mname
);
m
->m_mailer
= newstr(mpath
);
m
->m_badstat
= EX_UNAVAILABLE
;
Mailer
[NextMailer
++] = m
;
/* collect the argument vector */
for (i
= 0; i
< MAXPV
- 1 && *p
!= '\0'; i
++)
m
->m_argv
= (char **) xalloc(sizeof margv
[0] * i
);
bmove((char *) margv
, (char *) m
->m_argv
, sizeof margv
[0] * i
);
s
= stab(m
->m_name
, ST_MAILER
, ST_ENTER
);
** 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
)
** MFENCODE -- crack mailer options
** These options modify the functioning of the mailer
** from the configuration table.
** p -- pointer to vector of options.
** option list in binary.
char opt_name
; /* external name of option */
u_long opt_value
; /* internal name of option */
struct optlist OptList
[] =
register struct optlist
*o
;
register u_long opts
= 0;
for (o
= OptList
; o
->opt_name
!= '\0' && o
->opt_name
!= *p
; o
++)
syserr("bad mailer option %c", *p
);
** MFDECODE -- decode mailer flags into external form.
** flags -- value of flags to decode.
** f -- file to write them onto.
register struct optlist
*o
;
for (o
= OptList
; o
->opt_name
!= '\0'; o
++)
if ((o
->opt_value
& flags
) == o
->opt_value
)
** SETOPTION -- set global processing option
** val -- option value (as a text string).
** safe -- if set, this came from a system configuration file.
** sticky -- if set, don't let other setoptions override
** Sets options as implied by the arguments.
static int StickyOpt
[128 / sizeof (int)];
setoption(opt
, val
, safe
, sticky
)
printf("setoption %c=%s\n", opt
, val
);
** See if this option is preset for us.
smask
= 1 << (sindex
% sizeof (int));
if (bitset(smask
, StickyOpt
[sindex
]))
StickyOpt
[sindex
] |= smask
;
** Encode this option as appropriate.
if (index("rT", opt
) != NULL
)
else if (index("gLu", opt
) != NULL
)
else if (index("cfimosv", opt
) != NULL
)
else if (index("be", opt
) != NULL
)
** Now do the actual assignment.
case 'A': /* set default alias file */
if (AliasFile
[0] == '\0')
case 'b': /* operations mode */
case MD_DAEMON
: /* run as a daemon */
syserr("Daemon mode not implemented");
case '\0': /* default: do full delivery */
/* fall through....... */
case MD_DELIVER
: /* do everything (default) */
case MD_FORK
: /* fork after verification */
case MD_QUEUE
: /* queue only */
case MD_VERIFY
: /* verify only */
syserr("Unknown operation mode -b%c", Mode
);
case 'c': /* don't connect to "expensive" mailers */
case 'e': /* set error processing mode */
case 'p': /* print errors normally */
case 'q': /* be silent about it */
(void) freopen("/dev/null", "w", stdout
);
case 'm': /* mail back */
case 'e': /* do berknet error processing */
case 'w': /* write back (or mail) */
case 'f': /* save Unix-style From lines on front */
case 'g': /* default gid */
case 'H': /* help file */
HelpFile
= "sendmail.hf";
case 'i': /* ignore dot lines in message */
case 'L': /* log level */
case 'M': /* define macro */
case 'm': /* send to me too */
case 'o': /* assume old style headers */
CurEnv
->e_oldstyle
= bval
;
case 'Q': /* queue directory */
case 'r': /* read timeout */
case 'S': /* status file */
StatFile
= "sendmail.st";
case 's': /* be super safe, even if expensive */
case 'T': /* queue timeout */
case 't': /* time zone name */
DstTimezone
= index(val
, ',');
case 'u': /* set default uid */
case 'v': /* run in verbose mode */
syserr("setoption: line %d: syntax error on \"%c%s\"",