122709Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 362530Sbostic * Copyright (c) 1988, 1993 462530Sbostic * The Regents of the University of California. All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822709Sdist 922709Sdist #ifndef lint 10*67730Seric static char sccsid[] = "@(#)readcf.c 8.35 (Berkeley) 08/21/94"; 1133731Sbostic #endif /* not lint */ 1222709Sdist 133313Seric # include "sendmail.h" 1464133Seric # include <pwd.h> 1564133Seric # include <grp.h> 1666334Seric #if NAMED_BIND 1757207Seric # include <resolv.h> 1857207Seric #endif 193308Seric 203308Seric /* 213308Seric ** READCF -- read control file. 223308Seric ** 233308Seric ** This routine reads the control file and builds the internal 243308Seric ** form. 253308Seric ** 264432Seric ** The file is formatted as a sequence of lines, each taken 274432Seric ** atomically. The first character of each line describes how 284432Seric ** the line is to be interpreted. The lines are: 294432Seric ** Dxval Define macro x to have value val. 304432Seric ** Cxword Put word into class x. 314432Seric ** Fxfile [fmt] Read file for lines to put into 324432Seric ** class x. Use scanf string 'fmt' 334432Seric ** or "%s" if not present. Fmt should 344432Seric ** only produce one string-valued result. 354432Seric ** Hname: value Define header with field-name 'name' 364432Seric ** and value as specified; this will be 374432Seric ** macro expanded immediately before 384432Seric ** use. 394432Seric ** Sn Use rewriting set n. 404432Seric ** Rlhs rhs Rewrite addresses that match lhs to 414432Seric ** be rhs. 4224944Seric ** Mn arg=val... Define mailer. n is the internal name. 4324944Seric ** Args specify mailer parameters. 448252Seric ** Oxvalue Set option x to value. 458252Seric ** Pname=value Set precedence name to value. 4664718Seric ** Vversioncode[/vendorcode] 4764718Seric ** Version level/vendor name of 4864718Seric ** configuration syntax. 4953654Seric ** Kmapname mapclass arguments.... 5053654Seric ** Define keyed lookup of a given class. 5153654Seric ** Arguments are class dependent. 524432Seric ** 533308Seric ** Parameters: 543308Seric ** cfname -- control file name. 5554973Seric ** safe -- TRUE if this is the system config file; 5654973Seric ** FALSE otherwise. 5755012Seric ** e -- the main envelope. 583308Seric ** 593308Seric ** Returns: 603308Seric ** none. 613308Seric ** 623308Seric ** Side Effects: 633308Seric ** Builds several internal tables. 643308Seric */ 653308Seric 6655012Seric readcf(cfname, safe, e) 673308Seric char *cfname; 6854973Seric bool safe; 6955012Seric register ENVELOPE *e; 703308Seric { 713308Seric FILE *cf; 728547Seric int ruleset = 0; 738547Seric char *q; 749350Seric struct rewrite *rwp = NULL; 7557135Seric char *bp; 7664718Seric auto char *ep; 7757589Seric int nfuzzy; 7864133Seric char *file; 7964133Seric bool optional; 803308Seric char buf[MAXLINE]; 813308Seric register char *p; 823308Seric extern char **copyplist(); 8352647Seric struct stat statb; 845909Seric char exbuf[MAXLINE]; 8565066Seric char pvpbuf[MAXLINE + MAXATOM]; 8610709Seric extern char *munchstring(); 8753654Seric extern void makemapentry(); 883308Seric 8952647Seric FileName = cfname; 9052647Seric LineNumber = 0; 9152647Seric 923308Seric cf = fopen(cfname, "r"); 933308Seric if (cf == NULL) 943308Seric { 9552647Seric syserr("cannot open"); 963308Seric exit(EX_OSFILE); 973308Seric } 983308Seric 9952647Seric if (fstat(fileno(cf), &statb) < 0) 10052647Seric { 10152647Seric syserr("cannot fstat"); 10252647Seric exit(EX_OSFILE); 10352647Seric } 10452647Seric 10552647Seric if (!S_ISREG(statb.st_mode)) 10652647Seric { 10752647Seric syserr("not a plain file"); 10852647Seric exit(EX_OSFILE); 10952647Seric } 11052647Seric 11152647Seric if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 11252647Seric { 11353037Seric if (OpMode == MD_DAEMON || OpMode == MD_FREEZE) 11453037Seric fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 11553037Seric FileName); 11653037Seric #ifdef LOG 11753037Seric if (LogLevel > 0) 11853037Seric syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", 11953037Seric FileName); 12053037Seric #endif 12152647Seric } 12252647Seric 12359254Seric #ifdef XLA 12459254Seric xla_zero(); 12559254Seric #endif 12659254Seric 12757135Seric while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 1283308Seric { 12957135Seric if (bp[0] == '#') 13057135Seric { 13157135Seric if (bp != buf) 13257135Seric free(bp); 13352637Seric continue; 13457135Seric } 13552637Seric 13658050Seric /* map $ into \201 for macro expansion */ 13757135Seric for (p = bp; *p != '\0'; p++) 13816157Seric { 13957135Seric if (*p == '#' && p > bp && ConfigLevel >= 3) 14052647Seric { 14152647Seric /* this is an on-line comment */ 14252647Seric register char *e; 14352647Seric 14458050Seric switch (*--p & 0377) 14552647Seric { 14658050Seric case MACROEXPAND: 14752647Seric /* it's from $# -- let it go through */ 14852647Seric p++; 14952647Seric break; 15052647Seric 15152647Seric case '\\': 15252647Seric /* it's backslash escaped */ 15352647Seric (void) strcpy(p, p + 1); 15452647Seric break; 15552647Seric 15652647Seric default: 15752647Seric /* delete preceeding white space */ 15858050Seric while (isascii(*p) && isspace(*p) && p > bp) 15952647Seric p--; 16056795Seric if ((e = strchr(++p, '\n')) != NULL) 16152647Seric (void) strcpy(p, e); 16252647Seric else 16352647Seric p[0] = p[1] = '\0'; 16452647Seric break; 16552647Seric } 16652647Seric continue; 16752647Seric } 16852647Seric 16916157Seric if (*p != '$') 17016157Seric continue; 17116157Seric 17216157Seric if (p[1] == '$') 17316157Seric { 17416157Seric /* actual dollar sign.... */ 17523111Seric (void) strcpy(p, p + 1); 17616157Seric continue; 17716157Seric } 17816157Seric 17916157Seric /* convert to macro expansion character */ 18058050Seric *p = MACROEXPAND; 18116157Seric } 18216157Seric 18316157Seric /* interpret this line */ 18464718Seric errno = 0; 18557135Seric switch (bp[0]) 1863308Seric { 1873308Seric case '\0': 1883308Seric case '#': /* comment */ 1893308Seric break; 1903308Seric 1913308Seric case 'R': /* rewriting rule */ 19257135Seric for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 1933308Seric continue; 1943308Seric 1953308Seric if (*p == '\0') 1965909Seric { 19765821Seric syserr("invalid rewrite line \"%s\" (tab expected)", bp); 1985909Seric break; 1995909Seric } 2005909Seric 2015909Seric /* allocate space for the rule header */ 2025909Seric if (rwp == NULL) 2035909Seric { 2045909Seric RewriteRules[ruleset] = rwp = 2055909Seric (struct rewrite *) xalloc(sizeof *rwp); 2065909Seric } 2073308Seric else 2083308Seric { 2095909Seric rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 2105909Seric rwp = rwp->r_next; 2115909Seric } 2125909Seric rwp->r_next = NULL; 2133308Seric 2145909Seric /* expand and save the LHS */ 2155909Seric *p = '\0'; 21657135Seric expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e); 21765066Seric rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, 21865066Seric sizeof pvpbuf, NULL); 21957589Seric nfuzzy = 0; 2205909Seric if (rwp->r_lhs != NULL) 22157589Seric { 22257589Seric register char **ap; 22357589Seric 2245909Seric rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 22557589Seric 22657589Seric /* count the number of fuzzy matches in LHS */ 22757589Seric for (ap = rwp->r_lhs; *ap != NULL; ap++) 22857589Seric { 22958148Seric char *botch; 23058148Seric 23158148Seric botch = NULL; 23258050Seric switch (**ap & 0377) 23357589Seric { 23457589Seric case MATCHZANY: 23557589Seric case MATCHANY: 23657589Seric case MATCHONE: 23757589Seric case MATCHCLASS: 23857589Seric case MATCHNCLASS: 23957589Seric nfuzzy++; 24058148Seric break; 24158148Seric 24258148Seric case MATCHREPL: 24358148Seric botch = "$0-$9"; 24458148Seric break; 24558148Seric 24658148Seric case CANONNET: 24758148Seric botch = "$#"; 24858148Seric break; 24958148Seric 25058148Seric case CANONUSER: 25158148Seric botch = "$:"; 25258148Seric break; 25358148Seric 25458148Seric case CALLSUBR: 25558148Seric botch = "$>"; 25658148Seric break; 25758148Seric 25858148Seric case CONDIF: 25958148Seric botch = "$?"; 26058148Seric break; 26158148Seric 26258148Seric case CONDELSE: 26358148Seric botch = "$|"; 26458148Seric break; 26558148Seric 26658148Seric case CONDFI: 26758148Seric botch = "$."; 26858148Seric break; 26958148Seric 27058148Seric case HOSTBEGIN: 27158148Seric botch = "$["; 27258148Seric break; 27358148Seric 27458148Seric case HOSTEND: 27558148Seric botch = "$]"; 27658148Seric break; 27758148Seric 27858148Seric case LOOKUPBEGIN: 27958148Seric botch = "$("; 28058148Seric break; 28158148Seric 28258148Seric case LOOKUPEND: 28358148Seric botch = "$)"; 28458148Seric break; 28557589Seric } 28658148Seric if (botch != NULL) 28758148Seric syserr("Inappropriate use of %s on LHS", 28858148Seric botch); 28957589Seric } 29057589Seric } 29156678Seric else 29256678Seric syserr("R line: null LHS"); 2935909Seric 2945909Seric /* expand and save the RHS */ 2955909Seric while (*++p == '\t') 2965909Seric continue; 2977231Seric q = p; 2987231Seric while (*p != '\0' && *p != '\t') 2997231Seric p++; 3007231Seric *p = '\0'; 30155012Seric expand(q, exbuf, &exbuf[sizeof exbuf], e); 30265066Seric rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, 30365066Seric sizeof pvpbuf, NULL); 3045909Seric if (rwp->r_rhs != NULL) 30557589Seric { 30657589Seric register char **ap; 30757589Seric 3085909Seric rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 30957589Seric 31057589Seric /* check no out-of-bounds replacements */ 31157589Seric nfuzzy += '0'; 31257589Seric for (ap = rwp->r_rhs; *ap != NULL; ap++) 31357589Seric { 31458148Seric char *botch; 31558148Seric 31658148Seric botch = NULL; 31758148Seric switch (**ap & 0377) 31857589Seric { 31958148Seric case MATCHREPL: 32058148Seric if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 32158148Seric { 32258148Seric syserr("replacement $%c out of bounds", 32358148Seric (*ap)[1]); 32458148Seric } 32558148Seric break; 32658148Seric 32758148Seric case MATCHZANY: 32858148Seric botch = "$*"; 32958148Seric break; 33058148Seric 33158148Seric case MATCHANY: 33258148Seric botch = "$+"; 33358148Seric break; 33458148Seric 33558148Seric case MATCHONE: 33658148Seric botch = "$-"; 33758148Seric break; 33858148Seric 33958148Seric case MATCHCLASS: 34058148Seric botch = "$="; 34158148Seric break; 34258148Seric 34358148Seric case MATCHNCLASS: 34458148Seric botch = "$~"; 34558148Seric break; 34657589Seric } 34758148Seric if (botch != NULL) 34858148Seric syserr("Inappropriate use of %s on RHS", 34958148Seric botch); 35057589Seric } 35157589Seric } 35256678Seric else 35356678Seric syserr("R line: null RHS"); 3543308Seric break; 3553308Seric 3564072Seric case 'S': /* select rewriting set */ 35764440Seric for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 35864440Seric continue; 35964440Seric if (!isascii(*p) || !isdigit(*p)) 36064440Seric { 36164440Seric syserr("invalid argument to S line: \"%.20s\"", 36264440Seric &bp[1]); 36364440Seric break; 36464440Seric } 36564440Seric ruleset = atoi(p); 3668056Seric if (ruleset >= MAXRWSETS || ruleset < 0) 3678056Seric { 3689381Seric syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 3698056Seric ruleset = 0; 3708056Seric } 3714072Seric rwp = NULL; 3724072Seric break; 3734072Seric 3743308Seric case 'D': /* macro definition */ 37564086Seric p = munchstring(&bp[2], NULL); 37664086Seric define(bp[1], newstr(p), e); 3773308Seric break; 3783308Seric 3793387Seric case 'H': /* required header line */ 38057135Seric (void) chompheader(&bp[1], TRUE, e); 3813387Seric break; 3823387Seric 3834061Seric case 'C': /* word class */ 3844432Seric /* scan the list of words and set class for all */ 38564121Seric expand(&bp[2], exbuf, &exbuf[sizeof exbuf], e); 38664121Seric for (p = exbuf; *p != '\0'; ) 3874061Seric { 3884061Seric register char *wd; 3894061Seric char delim; 3904061Seric 39158050Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 3924061Seric p++; 3934061Seric wd = p; 39458050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 3954061Seric p++; 3964061Seric delim = *p; 3974061Seric *p = '\0'; 3984061Seric if (wd[0] != '\0') 39957135Seric setclass(bp[1], wd); 4004061Seric *p = delim; 4014061Seric } 4024061Seric break; 4034061Seric 40459272Seric case 'F': /* word class from file */ 40564133Seric for (p = &bp[2]; isascii(*p) && isspace(*p); ) 40664133Seric p++; 40764133Seric if (p[0] == '-' && p[1] == 'o') 40864133Seric { 40964133Seric optional = TRUE; 41064133Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 41164133Seric p++; 41264133Seric while (isascii(*p) && isspace(*p)) 41367615Seric p++; 41464133Seric } 41564133Seric else 41664133Seric optional = FALSE; 41764133Seric file = p; 41864133Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 41964133Seric p++; 42059272Seric if (*p == '\0') 42159272Seric p = "%s"; 42259272Seric else 42359272Seric { 42459272Seric *p = '\0'; 42559272Seric while (isascii(*++p) && isspace(*p)) 42659272Seric continue; 42759272Seric } 42864133Seric fileclass(bp[1], file, p, safe, optional); 42959272Seric break; 43059272Seric 43159156Seric #ifdef XLA 43259156Seric case 'L': /* extended load average description */ 43359156Seric xla_init(&bp[1]); 43459156Seric break; 43559156Seric #endif 43659156Seric 4374096Seric case 'M': /* define mailer */ 43857135Seric makemailer(&bp[1]); 4394096Seric break; 4404096Seric 4418252Seric case 'O': /* set option */ 44258734Seric setoption(bp[1], &bp[2], safe, FALSE, e); 4438252Seric break; 4448252Seric 4458252Seric case 'P': /* set precedence */ 4468252Seric if (NumPriorities >= MAXPRIORITIES) 4478252Seric { 4488547Seric toomany('P', MAXPRIORITIES); 4498252Seric break; 4508252Seric } 45157135Seric for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 4528252Seric continue; 4538252Seric if (*p == '\0') 4548252Seric goto badline; 4558252Seric *p = '\0'; 45657135Seric Priorities[NumPriorities].pri_name = newstr(&bp[1]); 4578252Seric Priorities[NumPriorities].pri_val = atoi(++p); 4588252Seric NumPriorities++; 4598252Seric break; 4608252Seric 4618547Seric case 'T': /* trusted user(s) */ 46258161Seric /* this option is obsolete, but will be ignored */ 4638547Seric break; 4648547Seric 46552645Seric case 'V': /* configuration syntax version */ 46664440Seric for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 46764440Seric continue; 46864440Seric if (!isascii(*p) || !isdigit(*p)) 46964440Seric { 47064440Seric syserr("invalid argument to V line: \"%.20s\"", 47164440Seric &bp[1]); 47264440Seric break; 47364440Seric } 47464718Seric ConfigLevel = strtol(p, &ep, 10); 47564279Seric if (ConfigLevel >= 5) 47664279Seric { 47764279Seric /* level 5 configs have short name in $w */ 47864279Seric p = macvalue('w', e); 47964279Seric if (p != NULL && (p = strchr(p, '.')) != NULL) 48064279Seric *p = '\0'; 48164279Seric } 48264718Seric if (*ep++ == '/') 48364718Seric { 48464718Seric /* extract vendor code */ 48564718Seric for (p = ep; isascii(*p) && isalpha(*p); ) 48664718Seric p++; 48764718Seric *p = '\0'; 48864718Seric 48964718Seric if (!setvendor(ep)) 49064718Seric syserr("invalid V line vendor code: \"%s\"", 49164718Seric ep); 49264718Seric } 49352645Seric break; 49452645Seric 49553654Seric case 'K': 49657135Seric makemapentry(&bp[1]); 49753654Seric break; 49853654Seric 4993308Seric default: 5004061Seric badline: 50157135Seric syserr("unknown control line \"%s\"", bp); 5023308Seric } 50357135Seric if (bp != buf) 50457135Seric free(bp); 5053308Seric } 50652637Seric if (ferror(cf)) 50752637Seric { 50852647Seric syserr("I/O read error", cfname); 50952637Seric exit(EX_OSFILE); 51052637Seric } 51152637Seric fclose(cf); 5129381Seric FileName = NULL; 51356836Seric 514*67730Seric /* initialize host maps from local service tables */ 515*67730Seric inithostmaps(); 516*67730Seric 51757076Seric if (stab("host", ST_MAP, ST_FIND) == NULL) 51857076Seric { 51957076Seric /* user didn't initialize: set up host map */ 52057076Seric strcpy(buf, "host host"); 52166334Seric #if NAMED_BIND 52257076Seric if (ConfigLevel >= 2) 52357076Seric strcat(buf, " -a."); 52464947Seric #endif 52557076Seric makemapentry(buf); 52657076Seric } 5274096Seric } 5284096Seric /* 5298547Seric ** TOOMANY -- signal too many of some option 5308547Seric ** 5318547Seric ** Parameters: 5328547Seric ** id -- the id of the error line 5338547Seric ** maxcnt -- the maximum possible values 5348547Seric ** 5358547Seric ** Returns: 5368547Seric ** none. 5378547Seric ** 5388547Seric ** Side Effects: 5398547Seric ** gives a syserr. 5408547Seric */ 5418547Seric 5428547Seric toomany(id, maxcnt) 5438547Seric char id; 5448547Seric int maxcnt; 5458547Seric { 5469381Seric syserr("too many %c lines, %d max", id, maxcnt); 5478547Seric } 5488547Seric /* 5494432Seric ** FILECLASS -- read members of a class from a file 5504432Seric ** 5514432Seric ** Parameters: 5524432Seric ** class -- class to define. 5534432Seric ** filename -- name of file to read. 5544432Seric ** fmt -- scanf string to use for match. 55564133Seric ** safe -- if set, this is a safe read. 55664133Seric ** optional -- if set, it is not an error for the file to 55764133Seric ** not exist. 5584432Seric ** 5594432Seric ** Returns: 5604432Seric ** none 5614432Seric ** 5624432Seric ** Side Effects: 5634432Seric ** 5644432Seric ** puts all lines in filename that match a scanf into 5654432Seric ** the named class. 5664432Seric */ 5674432Seric 56864133Seric fileclass(class, filename, fmt, safe, optional) 5694432Seric int class; 5704432Seric char *filename; 5714432Seric char *fmt; 57254973Seric bool safe; 57364133Seric bool optional; 5744432Seric { 57525808Seric FILE *f; 57654973Seric struct stat stbuf; 5774432Seric char buf[MAXLINE]; 5784432Seric 57966101Seric if (tTd(37, 2)) 58066101Seric printf("fileclass(%s, fmt=%s)\n", filename, fmt); 58166101Seric 58266031Seric if (filename[0] == '|') 58366031Seric { 58466031Seric syserr("fileclass: pipes (F%c%s) not supported due to security problems", 58566031Seric class, filename); 58666031Seric return; 58766031Seric } 58854973Seric if (stat(filename, &stbuf) < 0) 58954973Seric { 59066101Seric if (tTd(37, 2)) 59166101Seric printf(" cannot stat (%s)\n", errstring(errno)); 59264133Seric if (!optional) 59364133Seric syserr("fileclass: cannot stat %s", filename); 59454973Seric return; 59554973Seric } 59654973Seric if (!S_ISREG(stbuf.st_mode)) 59754973Seric { 59854973Seric syserr("fileclass: %s not a regular file", filename); 59954973Seric return; 60054973Seric } 60154973Seric if (!safe && access(filename, R_OK) < 0) 60254973Seric { 60354973Seric syserr("fileclass: access denied on %s", filename); 60454973Seric return; 60554973Seric } 60654973Seric f = fopen(filename, "r"); 6074432Seric if (f == NULL) 6084432Seric { 60954973Seric syserr("fileclass: cannot open %s", filename); 6104432Seric return; 6114432Seric } 6124432Seric 6134432Seric while (fgets(buf, sizeof buf, f) != NULL) 6144432Seric { 6154432Seric register STAB *s; 61625808Seric register char *p; 61725808Seric # ifdef SCANF 6184432Seric char wordbuf[MAXNAME+1]; 6194432Seric 6204432Seric if (sscanf(buf, fmt, wordbuf) != 1) 6214432Seric continue; 62225808Seric p = wordbuf; 62356795Seric # else /* SCANF */ 62425808Seric p = buf; 62556795Seric # endif /* SCANF */ 62625808Seric 62725808Seric /* 62825808Seric ** Break up the match into words. 62925808Seric */ 63025808Seric 63125808Seric while (*p != '\0') 63225808Seric { 63325808Seric register char *q; 63425808Seric 63525808Seric /* strip leading spaces */ 63658050Seric while (isascii(*p) && isspace(*p)) 63725808Seric p++; 63825808Seric if (*p == '\0') 63925808Seric break; 64025808Seric 64125808Seric /* find the end of the word */ 64225808Seric q = p; 64358050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 64425808Seric p++; 64525808Seric if (*p != '\0') 64625808Seric *p++ = '\0'; 64725808Seric 64825808Seric /* enter the word in the symbol table */ 64966101Seric setclass(class, q); 65025808Seric } 6514432Seric } 6524432Seric 65354973Seric (void) fclose(f); 6544432Seric } 6554432Seric /* 6564096Seric ** MAKEMAILER -- define a new mailer. 6574096Seric ** 6584096Seric ** Parameters: 65910327Seric ** line -- description of mailer. This is in labeled 66010327Seric ** fields. The fields are: 66110327Seric ** P -- the path to the mailer 66210327Seric ** F -- the flags associated with the mailer 66310327Seric ** A -- the argv for this mailer 66410327Seric ** S -- the sender rewriting set 66510327Seric ** R -- the recipient rewriting set 66610327Seric ** E -- the eol string 66710327Seric ** The first word is the canonical name of the mailer. 6684096Seric ** 6694096Seric ** Returns: 6704096Seric ** none. 6714096Seric ** 6724096Seric ** Side Effects: 6734096Seric ** enters the mailer into the mailer table. 6744096Seric */ 6753308Seric 67621066Seric makemailer(line) 6774096Seric char *line; 6784096Seric { 6794096Seric register char *p; 6808067Seric register struct mailer *m; 6818067Seric register STAB *s; 6828067Seric int i; 68310327Seric char fcode; 68458020Seric auto char *endp; 6854096Seric extern int NextMailer; 68610327Seric extern char **makeargv(); 68710327Seric extern char *munchstring(); 68810701Seric extern long atol(); 6894096Seric 69010327Seric /* allocate a mailer and set up defaults */ 69110327Seric m = (struct mailer *) xalloc(sizeof *m); 69210327Seric bzero((char *) m, sizeof *m); 69310327Seric m->m_eol = "\n"; 69467604Seric m->m_uid = m->m_gid = 0; 69510327Seric 69610327Seric /* collect the mailer name */ 69758050Seric for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 69810327Seric continue; 69910327Seric if (*p != '\0') 70010327Seric *p++ = '\0'; 70110327Seric m->m_name = newstr(line); 70210327Seric 70310327Seric /* now scan through and assign info from the fields */ 70410327Seric while (*p != '\0') 70510327Seric { 70658333Seric auto char *delimptr; 70758333Seric 70858050Seric while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 70910327Seric p++; 71010327Seric 71110327Seric /* p now points to field code */ 71210327Seric fcode = *p; 71310327Seric while (*p != '\0' && *p != '=' && *p != ',') 71410327Seric p++; 71510327Seric if (*p++ != '=') 71610327Seric { 71752637Seric syserr("mailer %s: `=' expected", m->m_name); 71810327Seric return; 71910327Seric } 72058050Seric while (isascii(*p) && isspace(*p)) 72110327Seric p++; 72210327Seric 72310327Seric /* p now points to the field body */ 72458333Seric p = munchstring(p, &delimptr); 72510327Seric 72610327Seric /* install the field into the mailer struct */ 72710327Seric switch (fcode) 72810327Seric { 72910327Seric case 'P': /* pathname */ 73010327Seric m->m_mailer = newstr(p); 73110327Seric break; 73210327Seric 73310327Seric case 'F': /* flags */ 73410687Seric for (; *p != '\0'; p++) 73558050Seric if (!(isascii(*p) && isspace(*p))) 73652637Seric setbitn(*p, m->m_flags); 73710327Seric break; 73810327Seric 73910327Seric case 'S': /* sender rewriting ruleset */ 74010327Seric case 'R': /* recipient rewriting ruleset */ 74158020Seric i = strtol(p, &endp, 10); 74210327Seric if (i < 0 || i >= MAXRWSETS) 74310327Seric { 74410327Seric syserr("invalid rewrite set, %d max", MAXRWSETS); 74510327Seric return; 74610327Seric } 74710327Seric if (fcode == 'S') 74858020Seric m->m_sh_rwset = m->m_se_rwset = i; 74910327Seric else 75058020Seric m->m_rh_rwset = m->m_re_rwset = i; 75158020Seric 75258020Seric p = endp; 75359985Seric if (*p++ == '/') 75458020Seric { 75558020Seric i = strtol(p, NULL, 10); 75658020Seric if (i < 0 || i >= MAXRWSETS) 75758020Seric { 75858020Seric syserr("invalid rewrite set, %d max", 75958020Seric MAXRWSETS); 76058020Seric return; 76158020Seric } 76258020Seric if (fcode == 'S') 76358020Seric m->m_sh_rwset = i; 76458020Seric else 76558020Seric m->m_rh_rwset = i; 76658020Seric } 76710327Seric break; 76810327Seric 76910327Seric case 'E': /* end of line string */ 77010327Seric m->m_eol = newstr(p); 77110327Seric break; 77210327Seric 77310327Seric case 'A': /* argument vector */ 77410327Seric m->m_argv = makeargv(p); 77510327Seric break; 77610701Seric 77710701Seric case 'M': /* maximum message size */ 77810701Seric m->m_maxsize = atol(p); 77910701Seric break; 78052106Seric 78152106Seric case 'L': /* maximum line length */ 78252106Seric m->m_linelimit = atoi(p); 78352106Seric break; 78458935Seric 78558935Seric case 'D': /* working directory */ 78658935Seric m->m_execdir = newstr(p); 78758935Seric break; 78867604Seric 78967604Seric case 'U': /* user id */ 79067604Seric if (isascii(*p) && !isdigit(*p)) 79167604Seric { 79267604Seric char *q = p; 79367604Seric struct passwd *pw; 79467604Seric 79567604Seric while (isascii(*p) && isalnum(*p)) 79667604Seric p++; 79767604Seric while (isascii(*p) && isspace(*p)) 79867604Seric *p++ = '\0'; 79967604Seric if (*p != '\0') 80067604Seric *p++ = '\0'; 80167604Seric pw = getpwnam(q); 80267604Seric if (pw == NULL) 80367604Seric syserr("readcf: mailer U= flag: unknown user %s", q); 80467604Seric else 80567604Seric { 80667604Seric m->m_uid = pw->pw_uid; 80767604Seric m->m_gid = pw->pw_gid; 80867604Seric } 80967604Seric } 81067604Seric else 81167604Seric { 81267604Seric auto char *q; 81367604Seric 81467604Seric m->m_uid = strtol(p, &q, 0); 81567604Seric p = q; 81667604Seric } 81767604Seric while (isascii(*p) && isspace(*p)) 81867604Seric p++; 81967604Seric if (*p == '\0') 82067604Seric break; 82167604Seric if (isascii(*p) && !isdigit(*p)) 82267604Seric { 82367604Seric char *q = p; 82467604Seric struct group *gr; 82567604Seric 82667604Seric while (isascii(*p) && isalnum(*p)) 82767604Seric p++; 82867604Seric *p++ = '\0'; 82967604Seric gr = getgrnam(q); 83067604Seric if (gr == NULL) 83167604Seric syserr("readcf: mailer U= flag: unknown group %s", q); 83267604Seric else 83367604Seric m->m_gid = gr->gr_gid; 83467604Seric } 83567604Seric else 83667604Seric { 83767604Seric m->m_gid = strtol(p, NULL, 0); 83867604Seric } 83967604Seric break; 84010327Seric } 84110327Seric 84258333Seric p = delimptr; 84310327Seric } 84410327Seric 84552106Seric /* do some heuristic cleanup for back compatibility */ 84652106Seric if (bitnset(M_LIMITS, m->m_flags)) 84752106Seric { 84852106Seric if (m->m_linelimit == 0) 84952106Seric m->m_linelimit = SMTPLINELIM; 85055418Seric if (ConfigLevel < 2) 85152106Seric setbitn(M_7BITS, m->m_flags); 85252106Seric } 85352106Seric 85458321Seric /* do some rationality checking */ 85558321Seric if (m->m_argv == NULL) 85658321Seric { 85758321Seric syserr("M%s: A= argument required", m->m_name); 85858321Seric return; 85958321Seric } 86058321Seric if (m->m_mailer == NULL) 86158321Seric { 86258321Seric syserr("M%s: P= argument required", m->m_name); 86358321Seric return; 86458321Seric } 86558321Seric 8664096Seric if (NextMailer >= MAXMAILERS) 8674096Seric { 8689381Seric syserr("too many mailers defined (%d max)", MAXMAILERS); 8694096Seric return; 8704096Seric } 87157402Seric 87210327Seric s = stab(m->m_name, ST_MAILER, ST_ENTER); 87357402Seric if (s->s_mailer != NULL) 87457402Seric { 87557402Seric i = s->s_mailer->m_mno; 87657402Seric free(s->s_mailer); 87757402Seric } 87857402Seric else 87957402Seric { 88057402Seric i = NextMailer++; 88157402Seric } 88257402Seric Mailer[i] = s->s_mailer = m; 88357454Seric m->m_mno = i; 88410327Seric } 88510327Seric /* 88610327Seric ** MUNCHSTRING -- translate a string into internal form. 88710327Seric ** 88810327Seric ** Parameters: 88910327Seric ** p -- the string to munch. 89058333Seric ** delimptr -- if non-NULL, set to the pointer of the 89158333Seric ** field delimiter character. 89210327Seric ** 89310327Seric ** Returns: 89410327Seric ** the munched string. 89510327Seric */ 8964096Seric 89710327Seric char * 89858333Seric munchstring(p, delimptr) 89910327Seric register char *p; 90058333Seric char **delimptr; 90110327Seric { 90210327Seric register char *q; 90310327Seric bool backslash = FALSE; 90410327Seric bool quotemode = FALSE; 90510327Seric static char buf[MAXLINE]; 9064096Seric 90710327Seric for (q = buf; *p != '\0'; p++) 9084096Seric { 90910327Seric if (backslash) 91010327Seric { 91110327Seric /* everything is roughly literal */ 91210357Seric backslash = FALSE; 91310327Seric switch (*p) 91410327Seric { 91510327Seric case 'r': /* carriage return */ 91610327Seric *q++ = '\r'; 91710327Seric continue; 91810327Seric 91910327Seric case 'n': /* newline */ 92010327Seric *q++ = '\n'; 92110327Seric continue; 92210327Seric 92310327Seric case 'f': /* form feed */ 92410327Seric *q++ = '\f'; 92510327Seric continue; 92610327Seric 92710327Seric case 'b': /* backspace */ 92810327Seric *q++ = '\b'; 92910327Seric continue; 93010327Seric } 93110327Seric *q++ = *p; 93210327Seric } 93310327Seric else 93410327Seric { 93510327Seric if (*p == '\\') 93610327Seric backslash = TRUE; 93710327Seric else if (*p == '"') 93810327Seric quotemode = !quotemode; 93910327Seric else if (quotemode || *p != ',') 94010327Seric *q++ = *p; 94110327Seric else 94210327Seric break; 94310327Seric } 9444096Seric } 9454096Seric 94658333Seric if (delimptr != NULL) 94758333Seric *delimptr = p; 94810327Seric *q++ = '\0'; 94910327Seric return (buf); 95010327Seric } 95110327Seric /* 95210327Seric ** MAKEARGV -- break up a string into words 95310327Seric ** 95410327Seric ** Parameters: 95510327Seric ** p -- the string to break up. 95610327Seric ** 95710327Seric ** Returns: 95810327Seric ** a char **argv (dynamically allocated) 95910327Seric ** 96010327Seric ** Side Effects: 96110327Seric ** munges p. 96210327Seric */ 9634096Seric 96410327Seric char ** 96510327Seric makeargv(p) 96610327Seric register char *p; 96710327Seric { 96810327Seric char *q; 96910327Seric int i; 97010327Seric char **avp; 97110327Seric char *argv[MAXPV + 1]; 97210327Seric 97310327Seric /* take apart the words */ 97410327Seric i = 0; 97510327Seric while (*p != '\0' && i < MAXPV) 9764096Seric { 97710327Seric q = p; 97858050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 97910327Seric p++; 98058050Seric while (isascii(*p) && isspace(*p)) 98110327Seric *p++ = '\0'; 98210327Seric argv[i++] = newstr(q); 9834096Seric } 98410327Seric argv[i++] = NULL; 9854096Seric 98610327Seric /* now make a copy of the argv */ 98710327Seric avp = (char **) xalloc(sizeof *avp * i); 98816893Seric bcopy((char *) argv, (char *) avp, sizeof *avp * i); 98910327Seric 99010327Seric return (avp); 9913308Seric } 9923308Seric /* 9933308Seric ** PRINTRULES -- print rewrite rules (for debugging) 9943308Seric ** 9953308Seric ** Parameters: 9963308Seric ** none. 9973308Seric ** 9983308Seric ** Returns: 9993308Seric ** none. 10003308Seric ** 10013308Seric ** Side Effects: 10023308Seric ** prints rewrite rules. 10033308Seric */ 10043308Seric 10053308Seric printrules() 10063308Seric { 10073308Seric register struct rewrite *rwp; 10084072Seric register int ruleset; 10093308Seric 10104072Seric for (ruleset = 0; ruleset < 10; ruleset++) 10113308Seric { 10124072Seric if (RewriteRules[ruleset] == NULL) 10134072Seric continue; 10148067Seric printf("\n----Rule Set %d:", ruleset); 10153308Seric 10164072Seric for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 10173308Seric { 10188067Seric printf("\nLHS:"); 10198067Seric printav(rwp->r_lhs); 10208067Seric printf("RHS:"); 10218067Seric printav(rwp->r_rhs); 10223308Seric } 10233308Seric } 10243308Seric } 10254319Seric 10264096Seric /* 10278256Seric ** SETOPTION -- set global processing option 10288256Seric ** 10298256Seric ** Parameters: 10308256Seric ** opt -- option name. 10318256Seric ** val -- option value (as a text string). 103221755Seric ** safe -- set if this came from a configuration file. 103321755Seric ** Some options (if set from the command line) will 103421755Seric ** reset the user id to avoid security problems. 10358269Seric ** sticky -- if set, don't let other setoptions override 10368269Seric ** this value. 103758734Seric ** e -- the main envelope. 10388256Seric ** 10398256Seric ** Returns: 10408256Seric ** none. 10418256Seric ** 10428256Seric ** Side Effects: 10438256Seric ** Sets options as implied by the arguments. 10448256Seric */ 10458256Seric 104610687Seric static BITMAP StickyOpt; /* set if option is stuck */ 10478269Seric 104857207Seric 104966334Seric #if NAMED_BIND 105057207Seric 105157207Seric struct resolverflags 105257207Seric { 105357207Seric char *rf_name; /* name of the flag */ 105457207Seric long rf_bits; /* bits to set/clear */ 105557207Seric } ResolverFlags[] = 105657207Seric { 105757207Seric "debug", RES_DEBUG, 105857207Seric "aaonly", RES_AAONLY, 105957207Seric "usevc", RES_USEVC, 106057207Seric "primary", RES_PRIMARY, 106157207Seric "igntc", RES_IGNTC, 106257207Seric "recurse", RES_RECURSE, 106357207Seric "defnames", RES_DEFNAMES, 106457207Seric "stayopen", RES_STAYOPEN, 106557207Seric "dnsrch", RES_DNSRCH, 106665583Seric "true", 0, /* to avoid error on old syntax */ 106757207Seric NULL, 0 106857207Seric }; 106957207Seric 107057207Seric #endif 107157207Seric 107267614Seric struct optioninfo 107367614Seric { 107467614Seric char *o_name; /* long name of option */ 107567614Seric char o_code; /* short name of option */ 107667614Seric bool o_safe; /* safe for random people to use */ 107767614Seric } OptionTab[] = 107867614Seric { 107967707Seric "SevenBitInput", '7', TRUE, 108067707Seric "EightBitMode", '8', TRUE, 108167707Seric "AliasFile", 'A', FALSE, 108267707Seric "AliasWait", 'a', FALSE, 108367707Seric "BlankSub", 'B', FALSE, 108467707Seric "MinFreeBlocks", 'b', TRUE, 108567707Seric "CheckpointInterval", 'C', TRUE, 108667707Seric "HoldExpensive", 'c', FALSE, 108767707Seric "AutoRebuildAliases", 'D', FALSE, 108867707Seric "DeliveryMode", 'd', TRUE, 108967707Seric "ErrorHeader", 'E', FALSE, 109067707Seric "ErrorMode", 'e', TRUE, 109167707Seric "TempFileMode", 'F', FALSE, 109267707Seric "SaveFromLine", 'f', FALSE, 109367707Seric "MatchGECOS", 'G', FALSE, 109467707Seric "HelpFile", 'H', FALSE, 109567707Seric "MaxHopCount", 'h', FALSE, 109667707Seric "NameServerOptions", 'I', FALSE, 109767707Seric "IgnoreDots", 'i', TRUE, 109867707Seric "ForwardPath", 'J', FALSE, 109967707Seric "SendMimeErrors", 'j', TRUE, 110067707Seric "ConnectionCacheSize", 'k', FALSE, 110167707Seric "ConnectionCacheTimeout", 'K', FALSE, 110267707Seric "UseErrorsTo", 'l', FALSE, 110367707Seric "LogLevel", 'L', FALSE, 110467707Seric "MeToo", 'm', TRUE, 110567707Seric "CheckAliases", 'n', FALSE, 110667707Seric "OldStyleHeaders", 'o', TRUE, 110767707Seric "DaemonPortOptions", 'O', FALSE, 110867707Seric "PrivacyOptions", 'p', TRUE, 110967707Seric "PostmasterCopy", 'P', FALSE, 111067707Seric "QueueFactor", 'q', FALSE, 111167707Seric "QueueDirectory", 'Q', FALSE, 111267707Seric "DontPruneRoutes", 'R', FALSE, 111367711Seric "Timeouts", 'r', TRUE, 111467707Seric "StatusFile", 'S', FALSE, 111567707Seric "SuperSafe", 's', TRUE, 111667707Seric "QueueTimeout", 'T', FALSE, 111767707Seric "TimeZoneSpec", 't', FALSE, 111867707Seric "UserDatabaseSpec", 'U', FALSE, 111967707Seric "DefaultUser", 'u', FALSE, 112067707Seric "FallbackMXhost", 'V', FALSE, 112167707Seric "Verbose", 'v', TRUE, 112267707Seric "TryNullMXList", 'w', TRUE, 112367707Seric "QueueLA", 'x', FALSE, 112467707Seric "RefuseLA", 'X', FALSE, 112567707Seric "RecipientFactor", 'y', FALSE, 112667707Seric "ForkQueueRuns", 'Y', FALSE, 112767707Seric "ClassFactor", 'z', FALSE, 112867707Seric "TimeFactor", 'Z', FALSE, 112967707Seric #define O_BSP 0x80 113067707Seric "BrokenSmtpPeers", O_BSP, TRUE, 113167707Seric #define O_SQBH 0x81 113267707Seric "SortQueueByHost", O_SQBH, TRUE, 113367707Seric #define O_DNICE 0x82 113467707Seric "DeliveryNiceness", O_DNICE, TRUE, 113567707Seric #define O_MQA 0x83 113667707Seric "MinQueueAge", O_MQA, TRUE, 113767707Seric #define O_MHSA 0x84 113867707Seric "MaxHostStatAge", O_MHSA, TRUE, 113967707Seric 114067707Seric NULL, '\0', FALSE, 114167614Seric }; 114267614Seric 114367614Seric 114467614Seric 114558734Seric setoption(opt, val, safe, sticky, e) 114667614Seric u_char opt; 11478256Seric char *val; 114821755Seric bool safe; 11498269Seric bool sticky; 115058734Seric register ENVELOPE *e; 11518256Seric { 115257207Seric register char *p; 115367614Seric register struct optioninfo *o; 11548265Seric extern bool atobool(); 115512633Seric extern time_t convtime(); 115614879Seric extern int QueueLA; 115714879Seric extern int RefuseLA; 115864718Seric extern bool Warn_Q_option; 11598256Seric 116067614Seric if (opt == ' ') 116167614Seric { 116267614Seric /* full word options */ 116367614Seric 116467614Seric p = strchr(val, '='); 116567614Seric if (p == NULL) 116667614Seric p = &val[strlen(val)]; 116767614Seric while (*--p == ' ') 116867614Seric continue; 116967614Seric while (*++p == ' ') 117067614Seric *p = '\0'; 117167614Seric if (*p == '=') 117267614Seric *p++ = '\0'; 117367614Seric while (*p == ' ') 117467614Seric p++; 117567614Seric for (o = OptionTab; o->o_name != NULL; o++) 117667614Seric { 117767614Seric if (strcasecmp(o->o_name, val) == 0) 117867614Seric break; 117967614Seric } 118067614Seric if (o->o_name == NULL) 118167614Seric syserr("readcf: unknown option name %s", val); 118267614Seric opt = o->o_code; 118367614Seric val = p; 118467614Seric } 118567614Seric else 118667614Seric { 118767614Seric for (o = OptionTab; o->o_name != NULL; o++) 118867614Seric { 118967614Seric if (o->o_code == opt) 119067614Seric break; 119167614Seric } 119267614Seric } 119367614Seric 11948256Seric if (tTd(37, 1)) 119567614Seric printf("setoption %s (0x%x)=%s", 119667614Seric o->o_name == NULL ? "<unknown>" : o->o_name, 119767614Seric opt, val); 11988256Seric 11998256Seric /* 12008269Seric ** See if this option is preset for us. 12018256Seric */ 12028256Seric 120359731Seric if (!sticky && bitnset(opt, StickyOpt)) 12048269Seric { 12059341Seric if (tTd(37, 1)) 12069341Seric printf(" (ignored)\n"); 12078269Seric return; 12088269Seric } 12098269Seric 121021755Seric /* 121121755Seric ** Check to see if this option can be specified by this user. 121221755Seric */ 121321755Seric 121463787Seric if (!safe && RealUid == 0) 121521755Seric safe = TRUE; 121667614Seric if (!safe && !o->o_safe) 121721755Seric { 121839111Srick if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 121921755Seric { 122036582Sbostic if (tTd(37, 1)) 122136582Sbostic printf(" (unsafe)"); 122263787Seric if (RealUid != geteuid()) 122336582Sbostic { 122451210Seric if (tTd(37, 1)) 122551210Seric printf("(Resetting uid)"); 122663787Seric (void) setgid(RealGid); 122763787Seric (void) setuid(RealUid); 122836582Sbostic } 122921755Seric } 123021755Seric } 123151210Seric if (tTd(37, 1)) 123217985Seric printf("\n"); 12338269Seric 123467614Seric switch (opt & 0xff) 12358256Seric { 123659709Seric case '7': /* force seven-bit input */ 123767546Seric SevenBitInput = atobool(val); 123852106Seric break; 123952106Seric 124067546Seric case '8': /* handling of 8-bit input */ 124167546Seric switch (*val) 124267546Seric { 124367547Seric case 'r': /* reject 8-bit, don't convert MIME */ 124467546Seric MimeMode = 0; 124567546Seric break; 124667546Seric 124767547Seric case 'm': /* convert 8-bit, convert MIME */ 124867546Seric MimeMode = MM_CVTMIME|MM_MIME8BIT; 124967546Seric break; 125067546Seric 125167547Seric case 'j': /* "just send 8" */ 125267546Seric MimeMode = MM_PASS8BIT; 125367546Seric break; 125467546Seric 125567546Seric case 'p': /* pass 8 bit, convert MIME */ 125667546Seric MimeMode = MM_PASS8BIT|MM_CVTMIME; 125767546Seric break; 125867546Seric 125967546Seric case 's': /* strict adherence */ 126067546Seric MimeMode = MM_CVTMIME; 126167546Seric break; 126267546Seric 126367547Seric case 'a': /* encode 8 bit if available */ 126467546Seric MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 126567546Seric break; 126667546Seric 126767547Seric case 'c': /* convert 8 bit to MIME, never 7 bit */ 126867547Seric MimeMode = MM_MIME8BIT; 126967547Seric break; 127067547Seric 127167546Seric default: 127267546Seric syserr("Unknown 8-bit mode %c", *val); 127367546Seric exit(EX_USAGE); 127467546Seric } 127567546Seric break; 127667546Seric 12778256Seric case 'A': /* set default alias file */ 12789381Seric if (val[0] == '\0') 127959672Seric setalias("aliases"); 12809381Seric else 128159672Seric setalias(val); 12828256Seric break; 12838256Seric 128417474Seric case 'a': /* look N minutes for "@:@" in alias file */ 128517474Seric if (val[0] == '\0') 128664796Seric SafeAlias = 5 * 60; /* five minutes */ 128717474Seric else 128864796Seric SafeAlias = convtime(val, 'm'); 128917474Seric break; 129017474Seric 129116843Seric case 'B': /* substitution for blank character */ 129216843Seric SpaceSub = val[0]; 129316843Seric if (SpaceSub == '\0') 129416843Seric SpaceSub = ' '; 129516843Seric break; 129616843Seric 129759283Seric case 'b': /* min blocks free on queue fs/max msg size */ 129859283Seric p = strchr(val, '/'); 129959283Seric if (p != NULL) 130059283Seric { 130159283Seric *p++ = '\0'; 130259283Seric MaxMessageSize = atol(p); 130359283Seric } 130458082Seric MinBlocksFree = atol(val); 130558082Seric break; 130658082Seric 13079284Seric case 'c': /* don't connect to "expensive" mailers */ 13089381Seric NoConnect = atobool(val); 13099284Seric break; 13109284Seric 131151305Seric case 'C': /* checkpoint every N addresses */ 131251305Seric CheckpointInterval = atoi(val); 131324944Seric break; 131424944Seric 13159284Seric case 'd': /* delivery mode */ 13169284Seric switch (*val) 13178269Seric { 13189284Seric case '\0': 131958734Seric e->e_sendmode = SM_DELIVER; 13208269Seric break; 13218269Seric 132210755Seric case SM_QUEUE: /* queue only */ 132310755Seric #ifndef QUEUE 132410755Seric syserr("need QUEUE to set -odqueue"); 132556795Seric #endif /* QUEUE */ 132610755Seric /* fall through..... */ 132710755Seric 13289284Seric case SM_DELIVER: /* do everything */ 13299284Seric case SM_FORK: /* fork after verification */ 133058734Seric e->e_sendmode = *val; 13318269Seric break; 13328269Seric 13338269Seric default: 13349284Seric syserr("Unknown delivery mode %c", *val); 13358269Seric exit(EX_USAGE); 13368269Seric } 13378269Seric break; 13388269Seric 13399146Seric case 'D': /* rebuild alias database as needed */ 13409381Seric AutoRebuild = atobool(val); 13419146Seric break; 13429146Seric 134355372Seric case 'E': /* error message header/header file */ 134455379Seric if (*val != '\0') 134555379Seric ErrMsgFile = newstr(val); 134655372Seric break; 134755372Seric 13488269Seric case 'e': /* set error processing mode */ 13498269Seric switch (*val) 13508269Seric { 13519381Seric case EM_QUIET: /* be silent about it */ 13529381Seric case EM_MAIL: /* mail back */ 13539381Seric case EM_BERKNET: /* do berknet error processing */ 13549381Seric case EM_WRITE: /* write back (or mail) */ 13559381Seric case EM_PRINT: /* print errors normally (default) */ 135658734Seric e->e_errormode = *val; 13578269Seric break; 13588269Seric } 13598269Seric break; 13608269Seric 13619049Seric case 'F': /* file mode */ 136217975Seric FileMode = atooct(val) & 0777; 13639049Seric break; 13649049Seric 13658269Seric case 'f': /* save Unix-style From lines on front */ 13669381Seric SaveFrom = atobool(val); 13678269Seric break; 13688269Seric 136953735Seric case 'G': /* match recipients against GECOS field */ 137053735Seric MatchGecos = atobool(val); 137153735Seric break; 137253735Seric 13738256Seric case 'g': /* default gid */ 137464133Seric if (isascii(*val) && isdigit(*val)) 137564133Seric DefGid = atoi(val); 137664133Seric else 137764133Seric { 137864133Seric register struct group *gr; 137964133Seric 138064133Seric DefGid = -1; 138164133Seric gr = getgrnam(val); 138264133Seric if (gr == NULL) 138364133Seric syserr("readcf: option g: unknown group %s", val); 138464133Seric else 138564133Seric DefGid = gr->gr_gid; 138664133Seric } 13878256Seric break; 13888256Seric 13898256Seric case 'H': /* help file */ 13909381Seric if (val[0] == '\0') 13918269Seric HelpFile = "sendmail.hf"; 13929381Seric else 13939381Seric HelpFile = newstr(val); 13948256Seric break; 13958256Seric 139651305Seric case 'h': /* maximum hop count */ 139751305Seric MaxHopCount = atoi(val); 139851305Seric break; 139951305Seric 140035651Seric case 'I': /* use internet domain name server */ 140166334Seric #if NAMED_BIND 140257207Seric UseNameServer = TRUE; 140357207Seric for (p = val; *p != 0; ) 140457207Seric { 140557207Seric bool clearmode; 140657207Seric char *q; 140757207Seric struct resolverflags *rfp; 140857207Seric 140957207Seric while (*p == ' ') 141057207Seric p++; 141157207Seric if (*p == '\0') 141257207Seric break; 141357207Seric clearmode = FALSE; 141457207Seric if (*p == '-') 141557207Seric clearmode = TRUE; 141657207Seric else if (*p != '+') 141757207Seric p--; 141857207Seric p++; 141957207Seric q = p; 142058050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 142157207Seric p++; 142257207Seric if (*p != '\0') 142357207Seric *p++ = '\0'; 142457207Seric for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 142557207Seric { 142657207Seric if (strcasecmp(q, rfp->rf_name) == 0) 142757207Seric break; 142857207Seric } 142964923Seric if (rfp->rf_name == NULL) 143064923Seric syserr("readcf: I option value %s unrecognized", q); 143164923Seric else if (clearmode) 143257207Seric _res.options &= ~rfp->rf_bits; 143357207Seric else 143457207Seric _res.options |= rfp->rf_bits; 143557207Seric } 143657207Seric if (tTd(8, 2)) 143757207Seric printf("_res.options = %x\n", _res.options); 143857207Seric #else 143957207Seric usrerr("name server (I option) specified but BIND not compiled in"); 144057207Seric #endif 144135651Seric break; 144235651Seric 14438269Seric case 'i': /* ignore dot lines in message */ 14449381Seric IgnrDot = atobool(val); 14458269Seric break; 14468269Seric 144759730Seric case 'j': /* send errors in MIME (RFC 1341) format */ 144859730Seric SendMIMEErrors = atobool(val); 144959730Seric break; 145059730Seric 145157136Seric case 'J': /* .forward search path */ 145257136Seric ForwardPath = newstr(val); 145357136Seric break; 145457136Seric 145554967Seric case 'k': /* connection cache size */ 145654967Seric MaxMciCache = atoi(val); 145756215Seric if (MaxMciCache < 0) 145856215Seric MaxMciCache = 0; 145954967Seric break; 146054967Seric 146154967Seric case 'K': /* connection cache timeout */ 146258796Seric MciCacheTimeout = convtime(val, 'm'); 146354967Seric break; 146454967Seric 146561104Seric case 'l': /* use Errors-To: header */ 146661104Seric UseErrorsTo = atobool(val); 146761104Seric break; 146861104Seric 14698256Seric case 'L': /* log level */ 147064140Seric if (safe || LogLevel < atoi(val)) 147164140Seric LogLevel = atoi(val); 14728256Seric break; 14738256Seric 14748269Seric case 'M': /* define macro */ 14759381Seric define(val[0], newstr(&val[1]), CurEnv); 147616878Seric sticky = FALSE; 14778269Seric break; 14788269Seric 14798269Seric case 'm': /* send to me too */ 14809381Seric MeToo = atobool(val); 14818269Seric break; 14828269Seric 148325820Seric case 'n': /* validate RHS in newaliases */ 148425820Seric CheckAliases = atobool(val); 148525820Seric break; 148625820Seric 148761104Seric /* 'N' available -- was "net name" */ 148861104Seric 148958851Seric case 'O': /* daemon options */ 149058851Seric setdaemonoptions(val); 149158851Seric break; 149258851Seric 14938269Seric case 'o': /* assume old style headers */ 14949381Seric if (atobool(val)) 14959341Seric CurEnv->e_flags |= EF_OLDSTYLE; 14969341Seric else 14979341Seric CurEnv->e_flags &= ~EF_OLDSTYLE; 14988269Seric break; 14998269Seric 150058082Seric case 'p': /* select privacy level */ 150158082Seric p = val; 150258082Seric for (;;) 150358082Seric { 150458082Seric register struct prival *pv; 150558082Seric extern struct prival PrivacyValues[]; 150658082Seric 150758082Seric while (isascii(*p) && (isspace(*p) || ispunct(*p))) 150858082Seric p++; 150958082Seric if (*p == '\0') 151058082Seric break; 151158082Seric val = p; 151258082Seric while (isascii(*p) && isalnum(*p)) 151358082Seric p++; 151458082Seric if (*p != '\0') 151558082Seric *p++ = '\0'; 151658082Seric 151758082Seric for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 151858082Seric { 151958082Seric if (strcasecmp(val, pv->pv_name) == 0) 152058082Seric break; 152158082Seric } 152258886Seric if (pv->pv_name == NULL) 152358886Seric syserr("readcf: Op line: %s unrecognized", val); 152458082Seric PrivacyFlags |= pv->pv_flag; 152558082Seric } 152658082Seric break; 152758082Seric 152824944Seric case 'P': /* postmaster copy address for returned mail */ 152924944Seric PostMasterCopy = newstr(val); 153024944Seric break; 153124944Seric 153224944Seric case 'q': /* slope of queue only function */ 153324944Seric QueueFactor = atoi(val); 153424944Seric break; 153524944Seric 15368256Seric case 'Q': /* queue directory */ 15379381Seric if (val[0] == '\0') 15388269Seric QueueDir = "mqueue"; 15399381Seric else 15409381Seric QueueDir = newstr(val); 154158789Seric if (RealUid != 0 && !safe) 154264718Seric Warn_Q_option = TRUE; 15438256Seric break; 15448256Seric 154558148Seric case 'R': /* don't prune routes */ 154658148Seric DontPruneRoutes = atobool(val); 154758148Seric break; 154858148Seric 15498256Seric case 'r': /* read timeout */ 155058112Seric settimeouts(val); 15518256Seric break; 15528256Seric 15538256Seric case 'S': /* status file */ 15549381Seric if (val[0] == '\0') 15558269Seric StatFile = "sendmail.st"; 15569381Seric else 15579381Seric StatFile = newstr(val); 15588256Seric break; 15598256Seric 15608265Seric case 's': /* be super safe, even if expensive */ 15619381Seric SuperSafe = atobool(val); 15628256Seric break; 15638256Seric 15648256Seric case 'T': /* queue timeout */ 156558737Seric p = strchr(val, '/'); 156658737Seric if (p != NULL) 156758737Seric { 156858737Seric *p++ = '\0'; 1569*67730Seric TimeOuts.to_q_warning[TOC_NORMAL] = convtime(p, 'd'); 157058737Seric } 1571*67730Seric TimeOuts.to_q_return[TOC_NORMAL] = convtime(val, 'h'); 157254967Seric break; 15738256Seric 15748265Seric case 't': /* time zone name */ 157552106Seric TimeZoneSpec = newstr(val); 15768265Seric break; 15778265Seric 157850556Seric case 'U': /* location of user database */ 157951360Seric UdbSpec = newstr(val); 158050556Seric break; 158150556Seric 15828256Seric case 'u': /* set default uid */ 158364133Seric if (isascii(*val) && isdigit(*val)) 158464133Seric DefUid = atoi(val); 158564133Seric else 158664133Seric { 158764133Seric register struct passwd *pw; 158864133Seric 158964133Seric DefUid = -1; 159064133Seric pw = getpwnam(val); 159164133Seric if (pw == NULL) 159264133Seric syserr("readcf: option u: unknown user %s", val); 159364133Seric else 159464133Seric DefUid = pw->pw_uid; 159564133Seric } 159640973Sbostic setdefuser(); 15978256Seric break; 15988256Seric 159958851Seric case 'V': /* fallback MX host */ 160058851Seric FallBackMX = newstr(val); 160158851Seric break; 160258851Seric 16038269Seric case 'v': /* run in verbose mode */ 16049381Seric Verbose = atobool(val); 16058256Seric break; 16068256Seric 160763837Seric case 'w': /* if we are best MX, try host directly */ 160863837Seric TryNullMXList = atobool(val); 160963837Seric break; 161061104Seric 161161104Seric /* 'W' available -- was wizard password */ 161261104Seric 161314879Seric case 'x': /* load avg at which to auto-queue msgs */ 161414879Seric QueueLA = atoi(val); 161514879Seric break; 161614879Seric 161714879Seric case 'X': /* load avg at which to auto-reject connections */ 161814879Seric RefuseLA = atoi(val); 161914879Seric break; 162014879Seric 162124981Seric case 'y': /* work recipient factor */ 162224981Seric WkRecipFact = atoi(val); 162324981Seric break; 162424981Seric 162524981Seric case 'Y': /* fork jobs during queue runs */ 162624952Seric ForkQueueRuns = atobool(val); 162724952Seric break; 162824952Seric 162924981Seric case 'z': /* work message class factor */ 163024981Seric WkClassFact = atoi(val); 163124981Seric break; 163224981Seric 163324981Seric case 'Z': /* work time factor */ 163424981Seric WkTimeFact = atoi(val); 163524981Seric break; 163624981Seric 163767614Seric case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 163867614Seric BrokenSmtpPeers = atobool(val); 163967614Seric break; 164067614Seric 164167614Seric case O_SQBH: /* sort work queue by host first */ 164267614Seric SortQueueByHost = atobool(val); 164367614Seric break; 164467614Seric 164567707Seric case O_DNICE: /* delivery nice value */ 164667707Seric DeliveryNiceness = atoi(val); 164767707Seric break; 164867707Seric 164967707Seric case O_MQA: /* minimum queue age between deliveries */ 165067707Seric MinQueueAge = convtime(val, 'm'); 165167707Seric break; 165267707Seric 165367707Seric case O_MHSA: /* maximum age of cached host status */ 165467707Seric MaxHostStatAge = convtime(val, 'm'); 165567707Seric break; 165667707Seric 16578256Seric default: 16588256Seric break; 16598256Seric } 166016878Seric if (sticky) 166116878Seric setbitn(opt, StickyOpt); 16629188Seric return; 16638256Seric } 166410687Seric /* 166510687Seric ** SETCLASS -- set a word into a class 166610687Seric ** 166710687Seric ** Parameters: 166810687Seric ** class -- the class to put the word in. 166910687Seric ** word -- the word to enter 167010687Seric ** 167110687Seric ** Returns: 167210687Seric ** none. 167310687Seric ** 167410687Seric ** Side Effects: 167510687Seric ** puts the word into the symbol table. 167610687Seric */ 167710687Seric 167810687Seric setclass(class, word) 167910687Seric int class; 168010687Seric char *word; 168110687Seric { 168210687Seric register STAB *s; 168310687Seric 168457943Seric if (tTd(37, 8)) 168564326Seric printf("setclass(%c, %s)\n", class, word); 168610687Seric s = stab(word, ST_CLASS, ST_ENTER); 168710687Seric setbitn(class, s->s_class); 168810687Seric } 168953654Seric /* 169053654Seric ** MAKEMAPENTRY -- create a map entry 169153654Seric ** 169253654Seric ** Parameters: 169353654Seric ** line -- the config file line 169453654Seric ** 169553654Seric ** Returns: 169653654Seric ** TRUE if it successfully entered the map entry. 169753654Seric ** FALSE otherwise (usually syntax error). 169853654Seric ** 169953654Seric ** Side Effects: 170053654Seric ** Enters the map into the dictionary. 170153654Seric */ 170253654Seric 170353654Seric void 170453654Seric makemapentry(line) 170553654Seric char *line; 170653654Seric { 170753654Seric register char *p; 170853654Seric char *mapname; 170953654Seric char *classname; 171064078Seric register STAB *s; 171153654Seric STAB *class; 171253654Seric 171358050Seric for (p = line; isascii(*p) && isspace(*p); p++) 171453654Seric continue; 171558050Seric if (!(isascii(*p) && isalnum(*p))) 171653654Seric { 171753654Seric syserr("readcf: config K line: no map name"); 171853654Seric return; 171953654Seric } 172053654Seric 172153654Seric mapname = p; 172258050Seric while (isascii(*++p) && isalnum(*p)) 172353654Seric continue; 172453654Seric if (*p != '\0') 172553654Seric *p++ = '\0'; 172658050Seric while (isascii(*p) && isspace(*p)) 172753654Seric p++; 172858050Seric if (!(isascii(*p) && isalnum(*p))) 172953654Seric { 173053654Seric syserr("readcf: config K line, map %s: no map class", mapname); 173153654Seric return; 173253654Seric } 173353654Seric classname = p; 173458050Seric while (isascii(*++p) && isalnum(*p)) 173553654Seric continue; 173653654Seric if (*p != '\0') 173753654Seric *p++ = '\0'; 173858050Seric while (isascii(*p) && isspace(*p)) 173953654Seric p++; 174053654Seric 174153654Seric /* look up the class */ 174253654Seric class = stab(classname, ST_MAPCLASS, ST_FIND); 174353654Seric if (class == NULL) 174453654Seric { 174553654Seric syserr("readcf: map %s: class %s not available", mapname, classname); 174653654Seric return; 174753654Seric } 174853654Seric 174953654Seric /* enter the map */ 175064078Seric s = stab(mapname, ST_MAP, ST_ENTER); 175164078Seric s->s_map.map_class = &class->s_mapclass; 175264078Seric s->s_map.map_mname = newstr(mapname); 175353654Seric 175464078Seric if (class->s_mapclass.map_parse(&s->s_map, p)) 175564078Seric s->s_map.map_mflags |= MF_VALID; 175664078Seric 175764078Seric if (tTd(37, 5)) 175864078Seric { 175964078Seric printf("map %s, class %s, flags %x, file %s,\n", 176064078Seric s->s_map.map_mname, s->s_map.map_class->map_cname, 176164078Seric s->s_map.map_mflags, 176264078Seric s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 176364078Seric printf("\tapp %s, domain %s, rebuild %s\n", 176464078Seric s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 176564078Seric s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 176664078Seric s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 176764078Seric } 176853654Seric } 176958112Seric /* 177058112Seric ** SETTIMEOUTS -- parse and set timeout values 177158112Seric ** 177258112Seric ** Parameters: 177358112Seric ** val -- a pointer to the values. If NULL, do initial 177458112Seric ** settings. 177558112Seric ** 177658112Seric ** Returns: 177758112Seric ** none. 177858112Seric ** 177958112Seric ** Side Effects: 178058112Seric ** Initializes the TimeOuts structure 178158112Seric */ 178258112Seric 178364255Seric #define SECONDS 178458112Seric #define MINUTES * 60 178558112Seric #define HOUR * 3600 178658112Seric 178758112Seric settimeouts(val) 178858112Seric register char *val; 178958112Seric { 179058112Seric register char *p; 179158671Seric extern time_t convtime(); 179258112Seric 179358112Seric if (val == NULL) 179458112Seric { 179558112Seric TimeOuts.to_initial = (time_t) 5 MINUTES; 179658112Seric TimeOuts.to_helo = (time_t) 5 MINUTES; 179758112Seric TimeOuts.to_mail = (time_t) 10 MINUTES; 179858112Seric TimeOuts.to_rcpt = (time_t) 1 HOUR; 179958112Seric TimeOuts.to_datainit = (time_t) 5 MINUTES; 180058112Seric TimeOuts.to_datablock = (time_t) 1 HOUR; 180158112Seric TimeOuts.to_datafinal = (time_t) 1 HOUR; 180258112Seric TimeOuts.to_rset = (time_t) 5 MINUTES; 180358112Seric TimeOuts.to_quit = (time_t) 2 MINUTES; 180458112Seric TimeOuts.to_nextcommand = (time_t) 1 HOUR; 180558112Seric TimeOuts.to_miscshort = (time_t) 2 MINUTES; 180664255Seric TimeOuts.to_ident = (time_t) 30 SECONDS; 180767711Seric TimeOuts.to_fileopen = (time_t) 60 SECONDS; 180858112Seric return; 180958112Seric } 181058112Seric 181158112Seric for (;; val = p) 181258112Seric { 181358112Seric while (isascii(*val) && isspace(*val)) 181458112Seric val++; 181558112Seric if (*val == '\0') 181658112Seric break; 181758112Seric for (p = val; *p != '\0' && *p != ','; p++) 181858112Seric continue; 181958112Seric if (*p != '\0') 182058112Seric *p++ = '\0'; 182158112Seric 182258112Seric if (isascii(*val) && isdigit(*val)) 182358112Seric { 182458112Seric /* old syntax -- set everything */ 182558796Seric TimeOuts.to_mail = convtime(val, 'm'); 182658112Seric TimeOuts.to_rcpt = TimeOuts.to_mail; 182758112Seric TimeOuts.to_datainit = TimeOuts.to_mail; 182858112Seric TimeOuts.to_datablock = TimeOuts.to_mail; 182958112Seric TimeOuts.to_datafinal = TimeOuts.to_mail; 183058112Seric TimeOuts.to_nextcommand = TimeOuts.to_mail; 183158112Seric continue; 183258112Seric } 183358112Seric else 183458112Seric { 183567711Seric register char *q = strchr(val, ':'); 183658112Seric time_t to; 183758112Seric 183867711Seric if (q == NULL && (q = strchr(val, '=')) == NULL) 183958112Seric { 184058112Seric /* syntax error */ 184158112Seric continue; 184258112Seric } 184358112Seric *q++ = '\0'; 184458796Seric to = convtime(q, 'm'); 184558112Seric 184658112Seric if (strcasecmp(val, "initial") == 0) 184758112Seric TimeOuts.to_initial = to; 184858112Seric else if (strcasecmp(val, "mail") == 0) 184958112Seric TimeOuts.to_mail = to; 185058112Seric else if (strcasecmp(val, "rcpt") == 0) 185158112Seric TimeOuts.to_rcpt = to; 185258112Seric else if (strcasecmp(val, "datainit") == 0) 185358112Seric TimeOuts.to_datainit = to; 185458112Seric else if (strcasecmp(val, "datablock") == 0) 185558112Seric TimeOuts.to_datablock = to; 185658112Seric else if (strcasecmp(val, "datafinal") == 0) 185758112Seric TimeOuts.to_datafinal = to; 185858112Seric else if (strcasecmp(val, "command") == 0) 185958112Seric TimeOuts.to_nextcommand = to; 186058112Seric else if (strcasecmp(val, "rset") == 0) 186158112Seric TimeOuts.to_rset = to; 186258112Seric else if (strcasecmp(val, "helo") == 0) 186358112Seric TimeOuts.to_helo = to; 186458112Seric else if (strcasecmp(val, "quit") == 0) 186558112Seric TimeOuts.to_quit = to; 186658112Seric else if (strcasecmp(val, "misc") == 0) 186758112Seric TimeOuts.to_miscshort = to; 186864255Seric else if (strcasecmp(val, "ident") == 0) 186964255Seric TimeOuts.to_ident = to; 187067711Seric else if (strcasecmp(val, "fileopen") == 0) 187167711Seric TimeOuts.to_fileopen = to; 187267711Seric else if (strcasecmp(val, "queuewarn") == 0) 1873*67730Seric TimeOuts.to_q_warning[TOC_NORMAL] = to; 187467711Seric else if (strcasecmp(val, "queuereturn") == 0) 1875*67730Seric TimeOuts.to_q_return[TOC_NORMAL] = to; 1876*67730Seric else if (strcasecmp(val, "queuewarn.normal") == 0) 1877*67730Seric TimeOuts.to_q_warning[TOC_NORMAL] = to; 1878*67730Seric else if (strcasecmp(val, "queuereturn.normal") == 0) 1879*67730Seric TimeOuts.to_q_return[TOC_NORMAL] = to; 1880*67730Seric else if (strcasecmp(val, "queuewarn.urgent") == 0) 1881*67730Seric TimeOuts.to_q_warning[TOC_URGENT] = to; 1882*67730Seric else if (strcasecmp(val, "queuereturn.urgent") == 0) 1883*67730Seric TimeOuts.to_q_return[TOC_URGENT] = to; 1884*67730Seric else if (strcasecmp(val, "queuewarn.non-urgent") == 0) 1885*67730Seric TimeOuts.to_q_warning[TOC_NONURGENT] = to; 1886*67730Seric else if (strcasecmp(val, "queuereturn.non-urgent") == 0) 1887*67730Seric TimeOuts.to_q_return[TOC_NONURGENT] = to; 188858112Seric else 188958112Seric syserr("settimeouts: invalid timeout %s", val); 189058112Seric } 189158112Seric } 189258112Seric } 1893