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*67731Seric static char sccsid[] = "@(#)readcf.c 8.36 (Berkeley) 08/22/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 51467730Seric /* initialize host maps from local service tables */ 51567730Seric inithostmaps(); 51667730Seric 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'; 1171*67731Seric if (p == val) 1172*67731Seric { 1173*67731Seric syserr("readcf: null option name"); 1174*67731Seric return; 1175*67731Seric } 117667614Seric if (*p == '=') 117767614Seric *p++ = '\0'; 117867614Seric while (*p == ' ') 117967614Seric p++; 118067614Seric for (o = OptionTab; o->o_name != NULL; o++) 118167614Seric { 118267614Seric if (strcasecmp(o->o_name, val) == 0) 118367614Seric break; 118467614Seric } 118567614Seric if (o->o_name == NULL) 118667614Seric syserr("readcf: unknown option name %s", val); 118767614Seric opt = o->o_code; 118867614Seric val = p; 118967614Seric } 119067614Seric else 119167614Seric { 119267614Seric for (o = OptionTab; o->o_name != NULL; o++) 119367614Seric { 119467614Seric if (o->o_code == opt) 119567614Seric break; 119667614Seric } 119767614Seric } 119867614Seric 11998256Seric if (tTd(37, 1)) 1200*67731Seric { 1201*67731Seric printf(isascii(opt) && isprint(opt) ? 1202*67731Seric "setoption %s (%c)=%s" : "setoption %s (0x%x)=%s", 120367614Seric o->o_name == NULL ? "<unknown>" : o->o_name, 120467614Seric opt, val); 1205*67731Seric } 12068256Seric 12078256Seric /* 12088269Seric ** See if this option is preset for us. 12098256Seric */ 12108256Seric 121159731Seric if (!sticky && bitnset(opt, StickyOpt)) 12128269Seric { 12139341Seric if (tTd(37, 1)) 12149341Seric printf(" (ignored)\n"); 12158269Seric return; 12168269Seric } 12178269Seric 121821755Seric /* 121921755Seric ** Check to see if this option can be specified by this user. 122021755Seric */ 122121755Seric 122263787Seric if (!safe && RealUid == 0) 122321755Seric safe = TRUE; 122467614Seric if (!safe && !o->o_safe) 122521755Seric { 122639111Srick if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 122721755Seric { 122836582Sbostic if (tTd(37, 1)) 122936582Sbostic printf(" (unsafe)"); 123063787Seric if (RealUid != geteuid()) 123136582Sbostic { 123251210Seric if (tTd(37, 1)) 123351210Seric printf("(Resetting uid)"); 123463787Seric (void) setgid(RealGid); 123563787Seric (void) setuid(RealUid); 123636582Sbostic } 123721755Seric } 123821755Seric } 123951210Seric if (tTd(37, 1)) 124017985Seric printf("\n"); 12418269Seric 124267614Seric switch (opt & 0xff) 12438256Seric { 124459709Seric case '7': /* force seven-bit input */ 124567546Seric SevenBitInput = atobool(val); 124652106Seric break; 124752106Seric 124867546Seric case '8': /* handling of 8-bit input */ 124967546Seric switch (*val) 125067546Seric { 125167547Seric case 'r': /* reject 8-bit, don't convert MIME */ 125267546Seric MimeMode = 0; 125367546Seric break; 125467546Seric 125567547Seric case 'm': /* convert 8-bit, convert MIME */ 125667546Seric MimeMode = MM_CVTMIME|MM_MIME8BIT; 125767546Seric break; 125867546Seric 125967547Seric case 'j': /* "just send 8" */ 126067546Seric MimeMode = MM_PASS8BIT; 126167546Seric break; 126267546Seric 126367546Seric case 'p': /* pass 8 bit, convert MIME */ 126467546Seric MimeMode = MM_PASS8BIT|MM_CVTMIME; 126567546Seric break; 126667546Seric 126767546Seric case 's': /* strict adherence */ 126867546Seric MimeMode = MM_CVTMIME; 126967546Seric break; 127067546Seric 127167547Seric case 'a': /* encode 8 bit if available */ 127267546Seric MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 127367546Seric break; 127467546Seric 127567547Seric case 'c': /* convert 8 bit to MIME, never 7 bit */ 127667547Seric MimeMode = MM_MIME8BIT; 127767547Seric break; 127867547Seric 127967546Seric default: 128067546Seric syserr("Unknown 8-bit mode %c", *val); 128167546Seric exit(EX_USAGE); 128267546Seric } 128367546Seric break; 128467546Seric 12858256Seric case 'A': /* set default alias file */ 12869381Seric if (val[0] == '\0') 128759672Seric setalias("aliases"); 12889381Seric else 128959672Seric setalias(val); 12908256Seric break; 12918256Seric 129217474Seric case 'a': /* look N minutes for "@:@" in alias file */ 129317474Seric if (val[0] == '\0') 129464796Seric SafeAlias = 5 * 60; /* five minutes */ 129517474Seric else 129664796Seric SafeAlias = convtime(val, 'm'); 129717474Seric break; 129817474Seric 129916843Seric case 'B': /* substitution for blank character */ 130016843Seric SpaceSub = val[0]; 130116843Seric if (SpaceSub == '\0') 130216843Seric SpaceSub = ' '; 130316843Seric break; 130416843Seric 130559283Seric case 'b': /* min blocks free on queue fs/max msg size */ 130659283Seric p = strchr(val, '/'); 130759283Seric if (p != NULL) 130859283Seric { 130959283Seric *p++ = '\0'; 131059283Seric MaxMessageSize = atol(p); 131159283Seric } 131258082Seric MinBlocksFree = atol(val); 131358082Seric break; 131458082Seric 13159284Seric case 'c': /* don't connect to "expensive" mailers */ 13169381Seric NoConnect = atobool(val); 13179284Seric break; 13189284Seric 131951305Seric case 'C': /* checkpoint every N addresses */ 132051305Seric CheckpointInterval = atoi(val); 132124944Seric break; 132224944Seric 13239284Seric case 'd': /* delivery mode */ 13249284Seric switch (*val) 13258269Seric { 13269284Seric case '\0': 132758734Seric e->e_sendmode = SM_DELIVER; 13288269Seric break; 13298269Seric 133010755Seric case SM_QUEUE: /* queue only */ 133110755Seric #ifndef QUEUE 133210755Seric syserr("need QUEUE to set -odqueue"); 133356795Seric #endif /* QUEUE */ 133410755Seric /* fall through..... */ 133510755Seric 13369284Seric case SM_DELIVER: /* do everything */ 13379284Seric case SM_FORK: /* fork after verification */ 133858734Seric e->e_sendmode = *val; 13398269Seric break; 13408269Seric 13418269Seric default: 13429284Seric syserr("Unknown delivery mode %c", *val); 13438269Seric exit(EX_USAGE); 13448269Seric } 13458269Seric break; 13468269Seric 13479146Seric case 'D': /* rebuild alias database as needed */ 13489381Seric AutoRebuild = atobool(val); 13499146Seric break; 13509146Seric 135155372Seric case 'E': /* error message header/header file */ 135255379Seric if (*val != '\0') 135355379Seric ErrMsgFile = newstr(val); 135455372Seric break; 135555372Seric 13568269Seric case 'e': /* set error processing mode */ 13578269Seric switch (*val) 13588269Seric { 13599381Seric case EM_QUIET: /* be silent about it */ 13609381Seric case EM_MAIL: /* mail back */ 13619381Seric case EM_BERKNET: /* do berknet error processing */ 13629381Seric case EM_WRITE: /* write back (or mail) */ 13639381Seric case EM_PRINT: /* print errors normally (default) */ 136458734Seric e->e_errormode = *val; 13658269Seric break; 13668269Seric } 13678269Seric break; 13688269Seric 13699049Seric case 'F': /* file mode */ 137017975Seric FileMode = atooct(val) & 0777; 13719049Seric break; 13729049Seric 13738269Seric case 'f': /* save Unix-style From lines on front */ 13749381Seric SaveFrom = atobool(val); 13758269Seric break; 13768269Seric 137753735Seric case 'G': /* match recipients against GECOS field */ 137853735Seric MatchGecos = atobool(val); 137953735Seric break; 138053735Seric 13818256Seric case 'g': /* default gid */ 138264133Seric if (isascii(*val) && isdigit(*val)) 138364133Seric DefGid = atoi(val); 138464133Seric else 138564133Seric { 138664133Seric register struct group *gr; 138764133Seric 138864133Seric DefGid = -1; 138964133Seric gr = getgrnam(val); 139064133Seric if (gr == NULL) 139164133Seric syserr("readcf: option g: unknown group %s", val); 139264133Seric else 139364133Seric DefGid = gr->gr_gid; 139464133Seric } 13958256Seric break; 13968256Seric 13978256Seric case 'H': /* help file */ 13989381Seric if (val[0] == '\0') 13998269Seric HelpFile = "sendmail.hf"; 14009381Seric else 14019381Seric HelpFile = newstr(val); 14028256Seric break; 14038256Seric 140451305Seric case 'h': /* maximum hop count */ 140551305Seric MaxHopCount = atoi(val); 140651305Seric break; 140751305Seric 140835651Seric case 'I': /* use internet domain name server */ 140966334Seric #if NAMED_BIND 141057207Seric UseNameServer = TRUE; 141157207Seric for (p = val; *p != 0; ) 141257207Seric { 141357207Seric bool clearmode; 141457207Seric char *q; 141557207Seric struct resolverflags *rfp; 141657207Seric 141757207Seric while (*p == ' ') 141857207Seric p++; 141957207Seric if (*p == '\0') 142057207Seric break; 142157207Seric clearmode = FALSE; 142257207Seric if (*p == '-') 142357207Seric clearmode = TRUE; 142457207Seric else if (*p != '+') 142557207Seric p--; 142657207Seric p++; 142757207Seric q = p; 142858050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 142957207Seric p++; 143057207Seric if (*p != '\0') 143157207Seric *p++ = '\0'; 143257207Seric for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 143357207Seric { 143457207Seric if (strcasecmp(q, rfp->rf_name) == 0) 143557207Seric break; 143657207Seric } 143764923Seric if (rfp->rf_name == NULL) 143864923Seric syserr("readcf: I option value %s unrecognized", q); 143964923Seric else if (clearmode) 144057207Seric _res.options &= ~rfp->rf_bits; 144157207Seric else 144257207Seric _res.options |= rfp->rf_bits; 144357207Seric } 144457207Seric if (tTd(8, 2)) 144557207Seric printf("_res.options = %x\n", _res.options); 144657207Seric #else 144757207Seric usrerr("name server (I option) specified but BIND not compiled in"); 144857207Seric #endif 144935651Seric break; 145035651Seric 14518269Seric case 'i': /* ignore dot lines in message */ 14529381Seric IgnrDot = atobool(val); 14538269Seric break; 14548269Seric 145559730Seric case 'j': /* send errors in MIME (RFC 1341) format */ 145659730Seric SendMIMEErrors = atobool(val); 145759730Seric break; 145859730Seric 145957136Seric case 'J': /* .forward search path */ 146057136Seric ForwardPath = newstr(val); 146157136Seric break; 146257136Seric 146354967Seric case 'k': /* connection cache size */ 146454967Seric MaxMciCache = atoi(val); 146556215Seric if (MaxMciCache < 0) 146656215Seric MaxMciCache = 0; 146754967Seric break; 146854967Seric 146954967Seric case 'K': /* connection cache timeout */ 147058796Seric MciCacheTimeout = convtime(val, 'm'); 147154967Seric break; 147254967Seric 147361104Seric case 'l': /* use Errors-To: header */ 147461104Seric UseErrorsTo = atobool(val); 147561104Seric break; 147661104Seric 14778256Seric case 'L': /* log level */ 147864140Seric if (safe || LogLevel < atoi(val)) 147964140Seric LogLevel = atoi(val); 14808256Seric break; 14818256Seric 14828269Seric case 'M': /* define macro */ 14839381Seric define(val[0], newstr(&val[1]), CurEnv); 148416878Seric sticky = FALSE; 14858269Seric break; 14868269Seric 14878269Seric case 'm': /* send to me too */ 14889381Seric MeToo = atobool(val); 14898269Seric break; 14908269Seric 149125820Seric case 'n': /* validate RHS in newaliases */ 149225820Seric CheckAliases = atobool(val); 149325820Seric break; 149425820Seric 149561104Seric /* 'N' available -- was "net name" */ 149661104Seric 149758851Seric case 'O': /* daemon options */ 149858851Seric setdaemonoptions(val); 149958851Seric break; 150058851Seric 15018269Seric case 'o': /* assume old style headers */ 15029381Seric if (atobool(val)) 15039341Seric CurEnv->e_flags |= EF_OLDSTYLE; 15049341Seric else 15059341Seric CurEnv->e_flags &= ~EF_OLDSTYLE; 15068269Seric break; 15078269Seric 150858082Seric case 'p': /* select privacy level */ 150958082Seric p = val; 151058082Seric for (;;) 151158082Seric { 151258082Seric register struct prival *pv; 151358082Seric extern struct prival PrivacyValues[]; 151458082Seric 151558082Seric while (isascii(*p) && (isspace(*p) || ispunct(*p))) 151658082Seric p++; 151758082Seric if (*p == '\0') 151858082Seric break; 151958082Seric val = p; 152058082Seric while (isascii(*p) && isalnum(*p)) 152158082Seric p++; 152258082Seric if (*p != '\0') 152358082Seric *p++ = '\0'; 152458082Seric 152558082Seric for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 152658082Seric { 152758082Seric if (strcasecmp(val, pv->pv_name) == 0) 152858082Seric break; 152958082Seric } 153058886Seric if (pv->pv_name == NULL) 153158886Seric syserr("readcf: Op line: %s unrecognized", val); 153258082Seric PrivacyFlags |= pv->pv_flag; 153358082Seric } 153458082Seric break; 153558082Seric 153624944Seric case 'P': /* postmaster copy address for returned mail */ 153724944Seric PostMasterCopy = newstr(val); 153824944Seric break; 153924944Seric 154024944Seric case 'q': /* slope of queue only function */ 154124944Seric QueueFactor = atoi(val); 154224944Seric break; 154324944Seric 15448256Seric case 'Q': /* queue directory */ 15459381Seric if (val[0] == '\0') 15468269Seric QueueDir = "mqueue"; 15479381Seric else 15489381Seric QueueDir = newstr(val); 154958789Seric if (RealUid != 0 && !safe) 155064718Seric Warn_Q_option = TRUE; 15518256Seric break; 15528256Seric 155358148Seric case 'R': /* don't prune routes */ 155458148Seric DontPruneRoutes = atobool(val); 155558148Seric break; 155658148Seric 15578256Seric case 'r': /* read timeout */ 155858112Seric settimeouts(val); 15598256Seric break; 15608256Seric 15618256Seric case 'S': /* status file */ 15629381Seric if (val[0] == '\0') 15638269Seric StatFile = "sendmail.st"; 15649381Seric else 15659381Seric StatFile = newstr(val); 15668256Seric break; 15678256Seric 15688265Seric case 's': /* be super safe, even if expensive */ 15699381Seric SuperSafe = atobool(val); 15708256Seric break; 15718256Seric 15728256Seric case 'T': /* queue timeout */ 157358737Seric p = strchr(val, '/'); 157458737Seric if (p != NULL) 157558737Seric { 157658737Seric *p++ = '\0'; 157767730Seric TimeOuts.to_q_warning[TOC_NORMAL] = convtime(p, 'd'); 157858737Seric } 157967730Seric TimeOuts.to_q_return[TOC_NORMAL] = convtime(val, 'h'); 158054967Seric break; 15818256Seric 15828265Seric case 't': /* time zone name */ 158352106Seric TimeZoneSpec = newstr(val); 15848265Seric break; 15858265Seric 158650556Seric case 'U': /* location of user database */ 158751360Seric UdbSpec = newstr(val); 158850556Seric break; 158950556Seric 15908256Seric case 'u': /* set default uid */ 159164133Seric if (isascii(*val) && isdigit(*val)) 159264133Seric DefUid = atoi(val); 159364133Seric else 159464133Seric { 159564133Seric register struct passwd *pw; 159664133Seric 159764133Seric DefUid = -1; 159864133Seric pw = getpwnam(val); 159964133Seric if (pw == NULL) 160064133Seric syserr("readcf: option u: unknown user %s", val); 160164133Seric else 160264133Seric DefUid = pw->pw_uid; 160364133Seric } 160440973Sbostic setdefuser(); 16058256Seric break; 16068256Seric 160758851Seric case 'V': /* fallback MX host */ 160858851Seric FallBackMX = newstr(val); 160958851Seric break; 161058851Seric 16118269Seric case 'v': /* run in verbose mode */ 16129381Seric Verbose = atobool(val); 16138256Seric break; 16148256Seric 161563837Seric case 'w': /* if we are best MX, try host directly */ 161663837Seric TryNullMXList = atobool(val); 161763837Seric break; 161861104Seric 161961104Seric /* 'W' available -- was wizard password */ 162061104Seric 162114879Seric case 'x': /* load avg at which to auto-queue msgs */ 162214879Seric QueueLA = atoi(val); 162314879Seric break; 162414879Seric 162514879Seric case 'X': /* load avg at which to auto-reject connections */ 162614879Seric RefuseLA = atoi(val); 162714879Seric break; 162814879Seric 162924981Seric case 'y': /* work recipient factor */ 163024981Seric WkRecipFact = atoi(val); 163124981Seric break; 163224981Seric 163324981Seric case 'Y': /* fork jobs during queue runs */ 163424952Seric ForkQueueRuns = atobool(val); 163524952Seric break; 163624952Seric 163724981Seric case 'z': /* work message class factor */ 163824981Seric WkClassFact = atoi(val); 163924981Seric break; 164024981Seric 164124981Seric case 'Z': /* work time factor */ 164224981Seric WkTimeFact = atoi(val); 164324981Seric break; 164424981Seric 164567614Seric case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 164667614Seric BrokenSmtpPeers = atobool(val); 164767614Seric break; 164867614Seric 164967614Seric case O_SQBH: /* sort work queue by host first */ 165067614Seric SortQueueByHost = atobool(val); 165167614Seric break; 165267614Seric 165367707Seric case O_DNICE: /* delivery nice value */ 165467707Seric DeliveryNiceness = atoi(val); 165567707Seric break; 165667707Seric 165767707Seric case O_MQA: /* minimum queue age between deliveries */ 165867707Seric MinQueueAge = convtime(val, 'm'); 165967707Seric break; 166067707Seric 166167707Seric case O_MHSA: /* maximum age of cached host status */ 166267707Seric MaxHostStatAge = convtime(val, 'm'); 166367707Seric break; 166467707Seric 16658256Seric default: 16668256Seric break; 16678256Seric } 166816878Seric if (sticky) 166916878Seric setbitn(opt, StickyOpt); 16709188Seric return; 16718256Seric } 167210687Seric /* 167310687Seric ** SETCLASS -- set a word into a class 167410687Seric ** 167510687Seric ** Parameters: 167610687Seric ** class -- the class to put the word in. 167710687Seric ** word -- the word to enter 167810687Seric ** 167910687Seric ** Returns: 168010687Seric ** none. 168110687Seric ** 168210687Seric ** Side Effects: 168310687Seric ** puts the word into the symbol table. 168410687Seric */ 168510687Seric 168610687Seric setclass(class, word) 168710687Seric int class; 168810687Seric char *word; 168910687Seric { 169010687Seric register STAB *s; 169110687Seric 169257943Seric if (tTd(37, 8)) 169364326Seric printf("setclass(%c, %s)\n", class, word); 169410687Seric s = stab(word, ST_CLASS, ST_ENTER); 169510687Seric setbitn(class, s->s_class); 169610687Seric } 169753654Seric /* 169853654Seric ** MAKEMAPENTRY -- create a map entry 169953654Seric ** 170053654Seric ** Parameters: 170153654Seric ** line -- the config file line 170253654Seric ** 170353654Seric ** Returns: 170453654Seric ** TRUE if it successfully entered the map entry. 170553654Seric ** FALSE otherwise (usually syntax error). 170653654Seric ** 170753654Seric ** Side Effects: 170853654Seric ** Enters the map into the dictionary. 170953654Seric */ 171053654Seric 171153654Seric void 171253654Seric makemapentry(line) 171353654Seric char *line; 171453654Seric { 171553654Seric register char *p; 171653654Seric char *mapname; 171753654Seric char *classname; 171864078Seric register STAB *s; 171953654Seric STAB *class; 172053654Seric 172158050Seric for (p = line; isascii(*p) && isspace(*p); p++) 172253654Seric continue; 172358050Seric if (!(isascii(*p) && isalnum(*p))) 172453654Seric { 172553654Seric syserr("readcf: config K line: no map name"); 172653654Seric return; 172753654Seric } 172853654Seric 172953654Seric mapname = p; 173058050Seric while (isascii(*++p) && isalnum(*p)) 173153654Seric continue; 173253654Seric if (*p != '\0') 173353654Seric *p++ = '\0'; 173458050Seric while (isascii(*p) && isspace(*p)) 173553654Seric p++; 173658050Seric if (!(isascii(*p) && isalnum(*p))) 173753654Seric { 173853654Seric syserr("readcf: config K line, map %s: no map class", mapname); 173953654Seric return; 174053654Seric } 174153654Seric classname = p; 174258050Seric while (isascii(*++p) && isalnum(*p)) 174353654Seric continue; 174453654Seric if (*p != '\0') 174553654Seric *p++ = '\0'; 174658050Seric while (isascii(*p) && isspace(*p)) 174753654Seric p++; 174853654Seric 174953654Seric /* look up the class */ 175053654Seric class = stab(classname, ST_MAPCLASS, ST_FIND); 175153654Seric if (class == NULL) 175253654Seric { 175353654Seric syserr("readcf: map %s: class %s not available", mapname, classname); 175453654Seric return; 175553654Seric } 175653654Seric 175753654Seric /* enter the map */ 175864078Seric s = stab(mapname, ST_MAP, ST_ENTER); 175964078Seric s->s_map.map_class = &class->s_mapclass; 176064078Seric s->s_map.map_mname = newstr(mapname); 176153654Seric 176264078Seric if (class->s_mapclass.map_parse(&s->s_map, p)) 176364078Seric s->s_map.map_mflags |= MF_VALID; 176464078Seric 176564078Seric if (tTd(37, 5)) 176664078Seric { 176764078Seric printf("map %s, class %s, flags %x, file %s,\n", 176864078Seric s->s_map.map_mname, s->s_map.map_class->map_cname, 176964078Seric s->s_map.map_mflags, 177064078Seric s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 177164078Seric printf("\tapp %s, domain %s, rebuild %s\n", 177264078Seric s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 177364078Seric s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 177464078Seric s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 177564078Seric } 177653654Seric } 177758112Seric /* 177858112Seric ** SETTIMEOUTS -- parse and set timeout values 177958112Seric ** 178058112Seric ** Parameters: 178158112Seric ** val -- a pointer to the values. If NULL, do initial 178258112Seric ** settings. 178358112Seric ** 178458112Seric ** Returns: 178558112Seric ** none. 178658112Seric ** 178758112Seric ** Side Effects: 178858112Seric ** Initializes the TimeOuts structure 178958112Seric */ 179058112Seric 179164255Seric #define SECONDS 179258112Seric #define MINUTES * 60 179358112Seric #define HOUR * 3600 179458112Seric 179558112Seric settimeouts(val) 179658112Seric register char *val; 179758112Seric { 179858112Seric register char *p; 179958671Seric extern time_t convtime(); 180058112Seric 180158112Seric if (val == NULL) 180258112Seric { 180358112Seric TimeOuts.to_initial = (time_t) 5 MINUTES; 180458112Seric TimeOuts.to_helo = (time_t) 5 MINUTES; 180558112Seric TimeOuts.to_mail = (time_t) 10 MINUTES; 180658112Seric TimeOuts.to_rcpt = (time_t) 1 HOUR; 180758112Seric TimeOuts.to_datainit = (time_t) 5 MINUTES; 180858112Seric TimeOuts.to_datablock = (time_t) 1 HOUR; 180958112Seric TimeOuts.to_datafinal = (time_t) 1 HOUR; 181058112Seric TimeOuts.to_rset = (time_t) 5 MINUTES; 181158112Seric TimeOuts.to_quit = (time_t) 2 MINUTES; 181258112Seric TimeOuts.to_nextcommand = (time_t) 1 HOUR; 181358112Seric TimeOuts.to_miscshort = (time_t) 2 MINUTES; 181464255Seric TimeOuts.to_ident = (time_t) 30 SECONDS; 181567711Seric TimeOuts.to_fileopen = (time_t) 60 SECONDS; 181658112Seric return; 181758112Seric } 181858112Seric 181958112Seric for (;; val = p) 182058112Seric { 182158112Seric while (isascii(*val) && isspace(*val)) 182258112Seric val++; 182358112Seric if (*val == '\0') 182458112Seric break; 182558112Seric for (p = val; *p != '\0' && *p != ','; p++) 182658112Seric continue; 182758112Seric if (*p != '\0') 182858112Seric *p++ = '\0'; 182958112Seric 183058112Seric if (isascii(*val) && isdigit(*val)) 183158112Seric { 183258112Seric /* old syntax -- set everything */ 183358796Seric TimeOuts.to_mail = convtime(val, 'm'); 183458112Seric TimeOuts.to_rcpt = TimeOuts.to_mail; 183558112Seric TimeOuts.to_datainit = TimeOuts.to_mail; 183658112Seric TimeOuts.to_datablock = TimeOuts.to_mail; 183758112Seric TimeOuts.to_datafinal = TimeOuts.to_mail; 183858112Seric TimeOuts.to_nextcommand = TimeOuts.to_mail; 183958112Seric continue; 184058112Seric } 184158112Seric else 184258112Seric { 184367711Seric register char *q = strchr(val, ':'); 184458112Seric time_t to; 184558112Seric 184667711Seric if (q == NULL && (q = strchr(val, '=')) == NULL) 184758112Seric { 184858112Seric /* syntax error */ 184958112Seric continue; 185058112Seric } 185158112Seric *q++ = '\0'; 185258796Seric to = convtime(q, 'm'); 185358112Seric 185458112Seric if (strcasecmp(val, "initial") == 0) 185558112Seric TimeOuts.to_initial = to; 185658112Seric else if (strcasecmp(val, "mail") == 0) 185758112Seric TimeOuts.to_mail = to; 185858112Seric else if (strcasecmp(val, "rcpt") == 0) 185958112Seric TimeOuts.to_rcpt = to; 186058112Seric else if (strcasecmp(val, "datainit") == 0) 186158112Seric TimeOuts.to_datainit = to; 186258112Seric else if (strcasecmp(val, "datablock") == 0) 186358112Seric TimeOuts.to_datablock = to; 186458112Seric else if (strcasecmp(val, "datafinal") == 0) 186558112Seric TimeOuts.to_datafinal = to; 186658112Seric else if (strcasecmp(val, "command") == 0) 186758112Seric TimeOuts.to_nextcommand = to; 186858112Seric else if (strcasecmp(val, "rset") == 0) 186958112Seric TimeOuts.to_rset = to; 187058112Seric else if (strcasecmp(val, "helo") == 0) 187158112Seric TimeOuts.to_helo = to; 187258112Seric else if (strcasecmp(val, "quit") == 0) 187358112Seric TimeOuts.to_quit = to; 187458112Seric else if (strcasecmp(val, "misc") == 0) 187558112Seric TimeOuts.to_miscshort = to; 187664255Seric else if (strcasecmp(val, "ident") == 0) 187764255Seric TimeOuts.to_ident = to; 187867711Seric else if (strcasecmp(val, "fileopen") == 0) 187967711Seric TimeOuts.to_fileopen = to; 188067711Seric else if (strcasecmp(val, "queuewarn") == 0) 188167730Seric TimeOuts.to_q_warning[TOC_NORMAL] = to; 188267711Seric else if (strcasecmp(val, "queuereturn") == 0) 188367730Seric TimeOuts.to_q_return[TOC_NORMAL] = to; 188467730Seric else if (strcasecmp(val, "queuewarn.normal") == 0) 188567730Seric TimeOuts.to_q_warning[TOC_NORMAL] = to; 188667730Seric else if (strcasecmp(val, "queuereturn.normal") == 0) 188767730Seric TimeOuts.to_q_return[TOC_NORMAL] = to; 188867730Seric else if (strcasecmp(val, "queuewarn.urgent") == 0) 188967730Seric TimeOuts.to_q_warning[TOC_URGENT] = to; 189067730Seric else if (strcasecmp(val, "queuereturn.urgent") == 0) 189167730Seric TimeOuts.to_q_return[TOC_URGENT] = to; 189267730Seric else if (strcasecmp(val, "queuewarn.non-urgent") == 0) 189367730Seric TimeOuts.to_q_warning[TOC_NONURGENT] = to; 189467730Seric else if (strcasecmp(val, "queuereturn.non-urgent") == 0) 189567730Seric TimeOuts.to_q_return[TOC_NONURGENT] = to; 189658112Seric else 189758112Seric syserr("settimeouts: invalid timeout %s", val); 189858112Seric } 189958112Seric } 190058112Seric } 1901