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*67711Seric static char sccsid[] = "@(#)readcf.c 8.34 (Berkeley) 08/20/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 51457076Seric if (stab("host", ST_MAP, ST_FIND) == NULL) 51557076Seric { 51657076Seric /* user didn't initialize: set up host map */ 51757076Seric strcpy(buf, "host host"); 51866334Seric #if NAMED_BIND 51957076Seric if (ConfigLevel >= 2) 52057076Seric strcat(buf, " -a."); 52164947Seric #endif 52257076Seric makemapentry(buf); 52357076Seric } 5244096Seric } 5254096Seric /* 5268547Seric ** TOOMANY -- signal too many of some option 5278547Seric ** 5288547Seric ** Parameters: 5298547Seric ** id -- the id of the error line 5308547Seric ** maxcnt -- the maximum possible values 5318547Seric ** 5328547Seric ** Returns: 5338547Seric ** none. 5348547Seric ** 5358547Seric ** Side Effects: 5368547Seric ** gives a syserr. 5378547Seric */ 5388547Seric 5398547Seric toomany(id, maxcnt) 5408547Seric char id; 5418547Seric int maxcnt; 5428547Seric { 5439381Seric syserr("too many %c lines, %d max", id, maxcnt); 5448547Seric } 5458547Seric /* 5464432Seric ** FILECLASS -- read members of a class from a file 5474432Seric ** 5484432Seric ** Parameters: 5494432Seric ** class -- class to define. 5504432Seric ** filename -- name of file to read. 5514432Seric ** fmt -- scanf string to use for match. 55264133Seric ** safe -- if set, this is a safe read. 55364133Seric ** optional -- if set, it is not an error for the file to 55464133Seric ** not exist. 5554432Seric ** 5564432Seric ** Returns: 5574432Seric ** none 5584432Seric ** 5594432Seric ** Side Effects: 5604432Seric ** 5614432Seric ** puts all lines in filename that match a scanf into 5624432Seric ** the named class. 5634432Seric */ 5644432Seric 56564133Seric fileclass(class, filename, fmt, safe, optional) 5664432Seric int class; 5674432Seric char *filename; 5684432Seric char *fmt; 56954973Seric bool safe; 57064133Seric bool optional; 5714432Seric { 57225808Seric FILE *f; 57354973Seric struct stat stbuf; 5744432Seric char buf[MAXLINE]; 5754432Seric 57666101Seric if (tTd(37, 2)) 57766101Seric printf("fileclass(%s, fmt=%s)\n", filename, fmt); 57866101Seric 57966031Seric if (filename[0] == '|') 58066031Seric { 58166031Seric syserr("fileclass: pipes (F%c%s) not supported due to security problems", 58266031Seric class, filename); 58366031Seric return; 58466031Seric } 58554973Seric if (stat(filename, &stbuf) < 0) 58654973Seric { 58766101Seric if (tTd(37, 2)) 58866101Seric printf(" cannot stat (%s)\n", errstring(errno)); 58964133Seric if (!optional) 59064133Seric syserr("fileclass: cannot stat %s", filename); 59154973Seric return; 59254973Seric } 59354973Seric if (!S_ISREG(stbuf.st_mode)) 59454973Seric { 59554973Seric syserr("fileclass: %s not a regular file", filename); 59654973Seric return; 59754973Seric } 59854973Seric if (!safe && access(filename, R_OK) < 0) 59954973Seric { 60054973Seric syserr("fileclass: access denied on %s", filename); 60154973Seric return; 60254973Seric } 60354973Seric f = fopen(filename, "r"); 6044432Seric if (f == NULL) 6054432Seric { 60654973Seric syserr("fileclass: cannot open %s", filename); 6074432Seric return; 6084432Seric } 6094432Seric 6104432Seric while (fgets(buf, sizeof buf, f) != NULL) 6114432Seric { 6124432Seric register STAB *s; 61325808Seric register char *p; 61425808Seric # ifdef SCANF 6154432Seric char wordbuf[MAXNAME+1]; 6164432Seric 6174432Seric if (sscanf(buf, fmt, wordbuf) != 1) 6184432Seric continue; 61925808Seric p = wordbuf; 62056795Seric # else /* SCANF */ 62125808Seric p = buf; 62256795Seric # endif /* SCANF */ 62325808Seric 62425808Seric /* 62525808Seric ** Break up the match into words. 62625808Seric */ 62725808Seric 62825808Seric while (*p != '\0') 62925808Seric { 63025808Seric register char *q; 63125808Seric 63225808Seric /* strip leading spaces */ 63358050Seric while (isascii(*p) && isspace(*p)) 63425808Seric p++; 63525808Seric if (*p == '\0') 63625808Seric break; 63725808Seric 63825808Seric /* find the end of the word */ 63925808Seric q = p; 64058050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 64125808Seric p++; 64225808Seric if (*p != '\0') 64325808Seric *p++ = '\0'; 64425808Seric 64525808Seric /* enter the word in the symbol table */ 64666101Seric setclass(class, q); 64725808Seric } 6484432Seric } 6494432Seric 65054973Seric (void) fclose(f); 6514432Seric } 6524432Seric /* 6534096Seric ** MAKEMAILER -- define a new mailer. 6544096Seric ** 6554096Seric ** Parameters: 65610327Seric ** line -- description of mailer. This is in labeled 65710327Seric ** fields. The fields are: 65810327Seric ** P -- the path to the mailer 65910327Seric ** F -- the flags associated with the mailer 66010327Seric ** A -- the argv for this mailer 66110327Seric ** S -- the sender rewriting set 66210327Seric ** R -- the recipient rewriting set 66310327Seric ** E -- the eol string 66410327Seric ** The first word is the canonical name of the mailer. 6654096Seric ** 6664096Seric ** Returns: 6674096Seric ** none. 6684096Seric ** 6694096Seric ** Side Effects: 6704096Seric ** enters the mailer into the mailer table. 6714096Seric */ 6723308Seric 67321066Seric makemailer(line) 6744096Seric char *line; 6754096Seric { 6764096Seric register char *p; 6778067Seric register struct mailer *m; 6788067Seric register STAB *s; 6798067Seric int i; 68010327Seric char fcode; 68158020Seric auto char *endp; 6824096Seric extern int NextMailer; 68310327Seric extern char **makeargv(); 68410327Seric extern char *munchstring(); 68510701Seric extern long atol(); 6864096Seric 68710327Seric /* allocate a mailer and set up defaults */ 68810327Seric m = (struct mailer *) xalloc(sizeof *m); 68910327Seric bzero((char *) m, sizeof *m); 69010327Seric m->m_eol = "\n"; 69167604Seric m->m_uid = m->m_gid = 0; 69210327Seric 69310327Seric /* collect the mailer name */ 69458050Seric for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 69510327Seric continue; 69610327Seric if (*p != '\0') 69710327Seric *p++ = '\0'; 69810327Seric m->m_name = newstr(line); 69910327Seric 70010327Seric /* now scan through and assign info from the fields */ 70110327Seric while (*p != '\0') 70210327Seric { 70358333Seric auto char *delimptr; 70458333Seric 70558050Seric while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 70610327Seric p++; 70710327Seric 70810327Seric /* p now points to field code */ 70910327Seric fcode = *p; 71010327Seric while (*p != '\0' && *p != '=' && *p != ',') 71110327Seric p++; 71210327Seric if (*p++ != '=') 71310327Seric { 71452637Seric syserr("mailer %s: `=' expected", m->m_name); 71510327Seric return; 71610327Seric } 71758050Seric while (isascii(*p) && isspace(*p)) 71810327Seric p++; 71910327Seric 72010327Seric /* p now points to the field body */ 72158333Seric p = munchstring(p, &delimptr); 72210327Seric 72310327Seric /* install the field into the mailer struct */ 72410327Seric switch (fcode) 72510327Seric { 72610327Seric case 'P': /* pathname */ 72710327Seric m->m_mailer = newstr(p); 72810327Seric break; 72910327Seric 73010327Seric case 'F': /* flags */ 73110687Seric for (; *p != '\0'; p++) 73258050Seric if (!(isascii(*p) && isspace(*p))) 73352637Seric setbitn(*p, m->m_flags); 73410327Seric break; 73510327Seric 73610327Seric case 'S': /* sender rewriting ruleset */ 73710327Seric case 'R': /* recipient rewriting ruleset */ 73858020Seric i = strtol(p, &endp, 10); 73910327Seric if (i < 0 || i >= MAXRWSETS) 74010327Seric { 74110327Seric syserr("invalid rewrite set, %d max", MAXRWSETS); 74210327Seric return; 74310327Seric } 74410327Seric if (fcode == 'S') 74558020Seric m->m_sh_rwset = m->m_se_rwset = i; 74610327Seric else 74758020Seric m->m_rh_rwset = m->m_re_rwset = i; 74858020Seric 74958020Seric p = endp; 75059985Seric if (*p++ == '/') 75158020Seric { 75258020Seric i = strtol(p, NULL, 10); 75358020Seric if (i < 0 || i >= MAXRWSETS) 75458020Seric { 75558020Seric syserr("invalid rewrite set, %d max", 75658020Seric MAXRWSETS); 75758020Seric return; 75858020Seric } 75958020Seric if (fcode == 'S') 76058020Seric m->m_sh_rwset = i; 76158020Seric else 76258020Seric m->m_rh_rwset = i; 76358020Seric } 76410327Seric break; 76510327Seric 76610327Seric case 'E': /* end of line string */ 76710327Seric m->m_eol = newstr(p); 76810327Seric break; 76910327Seric 77010327Seric case 'A': /* argument vector */ 77110327Seric m->m_argv = makeargv(p); 77210327Seric break; 77310701Seric 77410701Seric case 'M': /* maximum message size */ 77510701Seric m->m_maxsize = atol(p); 77610701Seric break; 77752106Seric 77852106Seric case 'L': /* maximum line length */ 77952106Seric m->m_linelimit = atoi(p); 78052106Seric break; 78158935Seric 78258935Seric case 'D': /* working directory */ 78358935Seric m->m_execdir = newstr(p); 78458935Seric break; 78567604Seric 78667604Seric case 'U': /* user id */ 78767604Seric if (isascii(*p) && !isdigit(*p)) 78867604Seric { 78967604Seric char *q = p; 79067604Seric struct passwd *pw; 79167604Seric 79267604Seric while (isascii(*p) && isalnum(*p)) 79367604Seric p++; 79467604Seric while (isascii(*p) && isspace(*p)) 79567604Seric *p++ = '\0'; 79667604Seric if (*p != '\0') 79767604Seric *p++ = '\0'; 79867604Seric pw = getpwnam(q); 79967604Seric if (pw == NULL) 80067604Seric syserr("readcf: mailer U= flag: unknown user %s", q); 80167604Seric else 80267604Seric { 80367604Seric m->m_uid = pw->pw_uid; 80467604Seric m->m_gid = pw->pw_gid; 80567604Seric } 80667604Seric } 80767604Seric else 80867604Seric { 80967604Seric auto char *q; 81067604Seric 81167604Seric m->m_uid = strtol(p, &q, 0); 81267604Seric p = q; 81367604Seric } 81467604Seric while (isascii(*p) && isspace(*p)) 81567604Seric p++; 81667604Seric if (*p == '\0') 81767604Seric break; 81867604Seric if (isascii(*p) && !isdigit(*p)) 81967604Seric { 82067604Seric char *q = p; 82167604Seric struct group *gr; 82267604Seric 82367604Seric while (isascii(*p) && isalnum(*p)) 82467604Seric p++; 82567604Seric *p++ = '\0'; 82667604Seric gr = getgrnam(q); 82767604Seric if (gr == NULL) 82867604Seric syserr("readcf: mailer U= flag: unknown group %s", q); 82967604Seric else 83067604Seric m->m_gid = gr->gr_gid; 83167604Seric } 83267604Seric else 83367604Seric { 83467604Seric m->m_gid = strtol(p, NULL, 0); 83567604Seric } 83667604Seric break; 83710327Seric } 83810327Seric 83958333Seric p = delimptr; 84010327Seric } 84110327Seric 84252106Seric /* do some heuristic cleanup for back compatibility */ 84352106Seric if (bitnset(M_LIMITS, m->m_flags)) 84452106Seric { 84552106Seric if (m->m_linelimit == 0) 84652106Seric m->m_linelimit = SMTPLINELIM; 84755418Seric if (ConfigLevel < 2) 84852106Seric setbitn(M_7BITS, m->m_flags); 84952106Seric } 85052106Seric 85158321Seric /* do some rationality checking */ 85258321Seric if (m->m_argv == NULL) 85358321Seric { 85458321Seric syserr("M%s: A= argument required", m->m_name); 85558321Seric return; 85658321Seric } 85758321Seric if (m->m_mailer == NULL) 85858321Seric { 85958321Seric syserr("M%s: P= argument required", m->m_name); 86058321Seric return; 86158321Seric } 86258321Seric 8634096Seric if (NextMailer >= MAXMAILERS) 8644096Seric { 8659381Seric syserr("too many mailers defined (%d max)", MAXMAILERS); 8664096Seric return; 8674096Seric } 86857402Seric 86910327Seric s = stab(m->m_name, ST_MAILER, ST_ENTER); 87057402Seric if (s->s_mailer != NULL) 87157402Seric { 87257402Seric i = s->s_mailer->m_mno; 87357402Seric free(s->s_mailer); 87457402Seric } 87557402Seric else 87657402Seric { 87757402Seric i = NextMailer++; 87857402Seric } 87957402Seric Mailer[i] = s->s_mailer = m; 88057454Seric m->m_mno = i; 88110327Seric } 88210327Seric /* 88310327Seric ** MUNCHSTRING -- translate a string into internal form. 88410327Seric ** 88510327Seric ** Parameters: 88610327Seric ** p -- the string to munch. 88758333Seric ** delimptr -- if non-NULL, set to the pointer of the 88858333Seric ** field delimiter character. 88910327Seric ** 89010327Seric ** Returns: 89110327Seric ** the munched string. 89210327Seric */ 8934096Seric 89410327Seric char * 89558333Seric munchstring(p, delimptr) 89610327Seric register char *p; 89758333Seric char **delimptr; 89810327Seric { 89910327Seric register char *q; 90010327Seric bool backslash = FALSE; 90110327Seric bool quotemode = FALSE; 90210327Seric static char buf[MAXLINE]; 9034096Seric 90410327Seric for (q = buf; *p != '\0'; p++) 9054096Seric { 90610327Seric if (backslash) 90710327Seric { 90810327Seric /* everything is roughly literal */ 90910357Seric backslash = FALSE; 91010327Seric switch (*p) 91110327Seric { 91210327Seric case 'r': /* carriage return */ 91310327Seric *q++ = '\r'; 91410327Seric continue; 91510327Seric 91610327Seric case 'n': /* newline */ 91710327Seric *q++ = '\n'; 91810327Seric continue; 91910327Seric 92010327Seric case 'f': /* form feed */ 92110327Seric *q++ = '\f'; 92210327Seric continue; 92310327Seric 92410327Seric case 'b': /* backspace */ 92510327Seric *q++ = '\b'; 92610327Seric continue; 92710327Seric } 92810327Seric *q++ = *p; 92910327Seric } 93010327Seric else 93110327Seric { 93210327Seric if (*p == '\\') 93310327Seric backslash = TRUE; 93410327Seric else if (*p == '"') 93510327Seric quotemode = !quotemode; 93610327Seric else if (quotemode || *p != ',') 93710327Seric *q++ = *p; 93810327Seric else 93910327Seric break; 94010327Seric } 9414096Seric } 9424096Seric 94358333Seric if (delimptr != NULL) 94458333Seric *delimptr = p; 94510327Seric *q++ = '\0'; 94610327Seric return (buf); 94710327Seric } 94810327Seric /* 94910327Seric ** MAKEARGV -- break up a string into words 95010327Seric ** 95110327Seric ** Parameters: 95210327Seric ** p -- the string to break up. 95310327Seric ** 95410327Seric ** Returns: 95510327Seric ** a char **argv (dynamically allocated) 95610327Seric ** 95710327Seric ** Side Effects: 95810327Seric ** munges p. 95910327Seric */ 9604096Seric 96110327Seric char ** 96210327Seric makeargv(p) 96310327Seric register char *p; 96410327Seric { 96510327Seric char *q; 96610327Seric int i; 96710327Seric char **avp; 96810327Seric char *argv[MAXPV + 1]; 96910327Seric 97010327Seric /* take apart the words */ 97110327Seric i = 0; 97210327Seric while (*p != '\0' && i < MAXPV) 9734096Seric { 97410327Seric q = p; 97558050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 97610327Seric p++; 97758050Seric while (isascii(*p) && isspace(*p)) 97810327Seric *p++ = '\0'; 97910327Seric argv[i++] = newstr(q); 9804096Seric } 98110327Seric argv[i++] = NULL; 9824096Seric 98310327Seric /* now make a copy of the argv */ 98410327Seric avp = (char **) xalloc(sizeof *avp * i); 98516893Seric bcopy((char *) argv, (char *) avp, sizeof *avp * i); 98610327Seric 98710327Seric return (avp); 9883308Seric } 9893308Seric /* 9903308Seric ** PRINTRULES -- print rewrite rules (for debugging) 9913308Seric ** 9923308Seric ** Parameters: 9933308Seric ** none. 9943308Seric ** 9953308Seric ** Returns: 9963308Seric ** none. 9973308Seric ** 9983308Seric ** Side Effects: 9993308Seric ** prints rewrite rules. 10003308Seric */ 10013308Seric 10023308Seric printrules() 10033308Seric { 10043308Seric register struct rewrite *rwp; 10054072Seric register int ruleset; 10063308Seric 10074072Seric for (ruleset = 0; ruleset < 10; ruleset++) 10083308Seric { 10094072Seric if (RewriteRules[ruleset] == NULL) 10104072Seric continue; 10118067Seric printf("\n----Rule Set %d:", ruleset); 10123308Seric 10134072Seric for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 10143308Seric { 10158067Seric printf("\nLHS:"); 10168067Seric printav(rwp->r_lhs); 10178067Seric printf("RHS:"); 10188067Seric printav(rwp->r_rhs); 10193308Seric } 10203308Seric } 10213308Seric } 10224319Seric 10234096Seric /* 10248256Seric ** SETOPTION -- set global processing option 10258256Seric ** 10268256Seric ** Parameters: 10278256Seric ** opt -- option name. 10288256Seric ** val -- option value (as a text string). 102921755Seric ** safe -- set if this came from a configuration file. 103021755Seric ** Some options (if set from the command line) will 103121755Seric ** reset the user id to avoid security problems. 10328269Seric ** sticky -- if set, don't let other setoptions override 10338269Seric ** this value. 103458734Seric ** e -- the main envelope. 10358256Seric ** 10368256Seric ** Returns: 10378256Seric ** none. 10388256Seric ** 10398256Seric ** Side Effects: 10408256Seric ** Sets options as implied by the arguments. 10418256Seric */ 10428256Seric 104310687Seric static BITMAP StickyOpt; /* set if option is stuck */ 10448269Seric 104557207Seric 104666334Seric #if NAMED_BIND 104757207Seric 104857207Seric struct resolverflags 104957207Seric { 105057207Seric char *rf_name; /* name of the flag */ 105157207Seric long rf_bits; /* bits to set/clear */ 105257207Seric } ResolverFlags[] = 105357207Seric { 105457207Seric "debug", RES_DEBUG, 105557207Seric "aaonly", RES_AAONLY, 105657207Seric "usevc", RES_USEVC, 105757207Seric "primary", RES_PRIMARY, 105857207Seric "igntc", RES_IGNTC, 105957207Seric "recurse", RES_RECURSE, 106057207Seric "defnames", RES_DEFNAMES, 106157207Seric "stayopen", RES_STAYOPEN, 106257207Seric "dnsrch", RES_DNSRCH, 106365583Seric "true", 0, /* to avoid error on old syntax */ 106457207Seric NULL, 0 106557207Seric }; 106657207Seric 106757207Seric #endif 106857207Seric 106967614Seric struct optioninfo 107067614Seric { 107167614Seric char *o_name; /* long name of option */ 107267614Seric char o_code; /* short name of option */ 107367614Seric bool o_safe; /* safe for random people to use */ 107467614Seric } OptionTab[] = 107567614Seric { 107667707Seric "SevenBitInput", '7', TRUE, 107767707Seric "EightBitMode", '8', TRUE, 107867707Seric "AliasFile", 'A', FALSE, 107967707Seric "AliasWait", 'a', FALSE, 108067707Seric "BlankSub", 'B', FALSE, 108167707Seric "MinFreeBlocks", 'b', TRUE, 108267707Seric "CheckpointInterval", 'C', TRUE, 108367707Seric "HoldExpensive", 'c', FALSE, 108467707Seric "AutoRebuildAliases", 'D', FALSE, 108567707Seric "DeliveryMode", 'd', TRUE, 108667707Seric "ErrorHeader", 'E', FALSE, 108767707Seric "ErrorMode", 'e', TRUE, 108867707Seric "TempFileMode", 'F', FALSE, 108967707Seric "SaveFromLine", 'f', FALSE, 109067707Seric "MatchGECOS", 'G', FALSE, 109167707Seric "HelpFile", 'H', FALSE, 109267707Seric "MaxHopCount", 'h', FALSE, 109367707Seric "NameServerOptions", 'I', FALSE, 109467707Seric "IgnoreDots", 'i', TRUE, 109567707Seric "ForwardPath", 'J', FALSE, 109667707Seric "SendMimeErrors", 'j', TRUE, 109767707Seric "ConnectionCacheSize", 'k', FALSE, 109867707Seric "ConnectionCacheTimeout", 'K', FALSE, 109967707Seric "UseErrorsTo", 'l', FALSE, 110067707Seric "LogLevel", 'L', FALSE, 110167707Seric "MeToo", 'm', TRUE, 110267707Seric "CheckAliases", 'n', FALSE, 110367707Seric "OldStyleHeaders", 'o', TRUE, 110467707Seric "DaemonPortOptions", 'O', FALSE, 110567707Seric "PrivacyOptions", 'p', TRUE, 110667707Seric "PostmasterCopy", 'P', FALSE, 110767707Seric "QueueFactor", 'q', FALSE, 110867707Seric "QueueDirectory", 'Q', FALSE, 110967707Seric "DontPruneRoutes", 'R', FALSE, 1110*67711Seric "Timeouts", 'r', TRUE, 111167707Seric "StatusFile", 'S', FALSE, 111267707Seric "SuperSafe", 's', TRUE, 111367707Seric "QueueTimeout", 'T', FALSE, 111467707Seric "TimeZoneSpec", 't', FALSE, 111567707Seric "UserDatabaseSpec", 'U', FALSE, 111667707Seric "DefaultUser", 'u', FALSE, 111767707Seric "FallbackMXhost", 'V', FALSE, 111867707Seric "Verbose", 'v', TRUE, 111967707Seric "TryNullMXList", 'w', TRUE, 112067707Seric "QueueLA", 'x', FALSE, 112167707Seric "RefuseLA", 'X', FALSE, 112267707Seric "RecipientFactor", 'y', FALSE, 112367707Seric "ForkQueueRuns", 'Y', FALSE, 112467707Seric "ClassFactor", 'z', FALSE, 112567707Seric "TimeFactor", 'Z', FALSE, 112667707Seric #define O_BSP 0x80 112767707Seric "BrokenSmtpPeers", O_BSP, TRUE, 112867707Seric #define O_SQBH 0x81 112967707Seric "SortQueueByHost", O_SQBH, TRUE, 113067707Seric #define O_DNICE 0x82 113167707Seric "DeliveryNiceness", O_DNICE, TRUE, 113267707Seric #define O_MQA 0x83 113367707Seric "MinQueueAge", O_MQA, TRUE, 113467707Seric #define O_MHSA 0x84 113567707Seric "MaxHostStatAge", O_MHSA, TRUE, 113667707Seric 113767707Seric NULL, '\0', FALSE, 113867614Seric }; 113967614Seric 114067614Seric 114167614Seric 114258734Seric setoption(opt, val, safe, sticky, e) 114367614Seric u_char opt; 11448256Seric char *val; 114521755Seric bool safe; 11468269Seric bool sticky; 114758734Seric register ENVELOPE *e; 11488256Seric { 114957207Seric register char *p; 115067614Seric register struct optioninfo *o; 11518265Seric extern bool atobool(); 115212633Seric extern time_t convtime(); 115314879Seric extern int QueueLA; 115414879Seric extern int RefuseLA; 115564718Seric extern bool Warn_Q_option; 115617474Seric extern bool trusteduser(); 11578256Seric 115867614Seric if (opt == ' ') 115967614Seric { 116067614Seric /* full word options */ 116167614Seric 116267614Seric p = strchr(val, '='); 116367614Seric if (p == NULL) 116467614Seric p = &val[strlen(val)]; 116567614Seric while (*--p == ' ') 116667614Seric continue; 116767614Seric while (*++p == ' ') 116867614Seric *p = '\0'; 116967614Seric if (*p == '=') 117067614Seric *p++ = '\0'; 117167614Seric while (*p == ' ') 117267614Seric p++; 117367614Seric for (o = OptionTab; o->o_name != NULL; o++) 117467614Seric { 117567614Seric if (strcasecmp(o->o_name, val) == 0) 117667614Seric break; 117767614Seric } 117867614Seric if (o->o_name == NULL) 117967614Seric syserr("readcf: unknown option name %s", val); 118067614Seric opt = o->o_code; 118167614Seric val = p; 118267614Seric } 118367614Seric else 118467614Seric { 118567614Seric for (o = OptionTab; o->o_name != NULL; o++) 118667614Seric { 118767614Seric if (o->o_code == opt) 118867614Seric break; 118967614Seric } 119067614Seric } 119167614Seric 11928256Seric if (tTd(37, 1)) 119367614Seric printf("setoption %s (0x%x)=%s", 119467614Seric o->o_name == NULL ? "<unknown>" : o->o_name, 119567614Seric opt, val); 11968256Seric 11978256Seric /* 11988269Seric ** See if this option is preset for us. 11998256Seric */ 12008256Seric 120159731Seric if (!sticky && bitnset(opt, StickyOpt)) 12028269Seric { 12039341Seric if (tTd(37, 1)) 12049341Seric printf(" (ignored)\n"); 12058269Seric return; 12068269Seric } 12078269Seric 120821755Seric /* 120921755Seric ** Check to see if this option can be specified by this user. 121021755Seric */ 121121755Seric 121263787Seric if (!safe && RealUid == 0) 121321755Seric safe = TRUE; 121467614Seric if (!safe && !o->o_safe) 121521755Seric { 121639111Srick if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 121721755Seric { 121836582Sbostic if (tTd(37, 1)) 121936582Sbostic printf(" (unsafe)"); 122063787Seric if (RealUid != geteuid()) 122136582Sbostic { 122251210Seric if (tTd(37, 1)) 122351210Seric printf("(Resetting uid)"); 122463787Seric (void) setgid(RealGid); 122563787Seric (void) setuid(RealUid); 122636582Sbostic } 122721755Seric } 122821755Seric } 122951210Seric if (tTd(37, 1)) 123017985Seric printf("\n"); 12318269Seric 123267614Seric switch (opt & 0xff) 12338256Seric { 123459709Seric case '7': /* force seven-bit input */ 123567546Seric SevenBitInput = atobool(val); 123652106Seric break; 123752106Seric 123867546Seric case '8': /* handling of 8-bit input */ 123967546Seric switch (*val) 124067546Seric { 124167547Seric case 'r': /* reject 8-bit, don't convert MIME */ 124267546Seric MimeMode = 0; 124367546Seric break; 124467546Seric 124567547Seric case 'm': /* convert 8-bit, convert MIME */ 124667546Seric MimeMode = MM_CVTMIME|MM_MIME8BIT; 124767546Seric break; 124867546Seric 124967547Seric case 'j': /* "just send 8" */ 125067546Seric MimeMode = MM_PASS8BIT; 125167546Seric break; 125267546Seric 125367546Seric case 'p': /* pass 8 bit, convert MIME */ 125467546Seric MimeMode = MM_PASS8BIT|MM_CVTMIME; 125567546Seric break; 125667546Seric 125767546Seric case 's': /* strict adherence */ 125867546Seric MimeMode = MM_CVTMIME; 125967546Seric break; 126067546Seric 126167547Seric case 'a': /* encode 8 bit if available */ 126267546Seric MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 126367546Seric break; 126467546Seric 126567547Seric case 'c': /* convert 8 bit to MIME, never 7 bit */ 126667547Seric MimeMode = MM_MIME8BIT; 126767547Seric break; 126867547Seric 126967546Seric default: 127067546Seric syserr("Unknown 8-bit mode %c", *val); 127167546Seric exit(EX_USAGE); 127267546Seric } 127367546Seric break; 127467546Seric 12758256Seric case 'A': /* set default alias file */ 12769381Seric if (val[0] == '\0') 127759672Seric setalias("aliases"); 12789381Seric else 127959672Seric setalias(val); 12808256Seric break; 12818256Seric 128217474Seric case 'a': /* look N minutes for "@:@" in alias file */ 128317474Seric if (val[0] == '\0') 128464796Seric SafeAlias = 5 * 60; /* five minutes */ 128517474Seric else 128664796Seric SafeAlias = convtime(val, 'm'); 128717474Seric break; 128817474Seric 128916843Seric case 'B': /* substitution for blank character */ 129016843Seric SpaceSub = val[0]; 129116843Seric if (SpaceSub == '\0') 129216843Seric SpaceSub = ' '; 129316843Seric break; 129416843Seric 129559283Seric case 'b': /* min blocks free on queue fs/max msg size */ 129659283Seric p = strchr(val, '/'); 129759283Seric if (p != NULL) 129859283Seric { 129959283Seric *p++ = '\0'; 130059283Seric MaxMessageSize = atol(p); 130159283Seric } 130258082Seric MinBlocksFree = atol(val); 130358082Seric break; 130458082Seric 13059284Seric case 'c': /* don't connect to "expensive" mailers */ 13069381Seric NoConnect = atobool(val); 13079284Seric break; 13089284Seric 130951305Seric case 'C': /* checkpoint every N addresses */ 131051305Seric CheckpointInterval = atoi(val); 131124944Seric break; 131224944Seric 13139284Seric case 'd': /* delivery mode */ 13149284Seric switch (*val) 13158269Seric { 13169284Seric case '\0': 131758734Seric e->e_sendmode = SM_DELIVER; 13188269Seric break; 13198269Seric 132010755Seric case SM_QUEUE: /* queue only */ 132110755Seric #ifndef QUEUE 132210755Seric syserr("need QUEUE to set -odqueue"); 132356795Seric #endif /* QUEUE */ 132410755Seric /* fall through..... */ 132510755Seric 13269284Seric case SM_DELIVER: /* do everything */ 13279284Seric case SM_FORK: /* fork after verification */ 132858734Seric e->e_sendmode = *val; 13298269Seric break; 13308269Seric 13318269Seric default: 13329284Seric syserr("Unknown delivery mode %c", *val); 13338269Seric exit(EX_USAGE); 13348269Seric } 13358269Seric break; 13368269Seric 13379146Seric case 'D': /* rebuild alias database as needed */ 13389381Seric AutoRebuild = atobool(val); 13399146Seric break; 13409146Seric 134155372Seric case 'E': /* error message header/header file */ 134255379Seric if (*val != '\0') 134355379Seric ErrMsgFile = newstr(val); 134455372Seric break; 134555372Seric 13468269Seric case 'e': /* set error processing mode */ 13478269Seric switch (*val) 13488269Seric { 13499381Seric case EM_QUIET: /* be silent about it */ 13509381Seric case EM_MAIL: /* mail back */ 13519381Seric case EM_BERKNET: /* do berknet error processing */ 13529381Seric case EM_WRITE: /* write back (or mail) */ 13539381Seric case EM_PRINT: /* print errors normally (default) */ 135458734Seric e->e_errormode = *val; 13558269Seric break; 13568269Seric } 13578269Seric break; 13588269Seric 13599049Seric case 'F': /* file mode */ 136017975Seric FileMode = atooct(val) & 0777; 13619049Seric break; 13629049Seric 13638269Seric case 'f': /* save Unix-style From lines on front */ 13649381Seric SaveFrom = atobool(val); 13658269Seric break; 13668269Seric 136753735Seric case 'G': /* match recipients against GECOS field */ 136853735Seric MatchGecos = atobool(val); 136953735Seric break; 137053735Seric 13718256Seric case 'g': /* default gid */ 137264133Seric if (isascii(*val) && isdigit(*val)) 137364133Seric DefGid = atoi(val); 137464133Seric else 137564133Seric { 137664133Seric register struct group *gr; 137764133Seric 137864133Seric DefGid = -1; 137964133Seric gr = getgrnam(val); 138064133Seric if (gr == NULL) 138164133Seric syserr("readcf: option g: unknown group %s", val); 138264133Seric else 138364133Seric DefGid = gr->gr_gid; 138464133Seric } 13858256Seric break; 13868256Seric 13878256Seric case 'H': /* help file */ 13889381Seric if (val[0] == '\0') 13898269Seric HelpFile = "sendmail.hf"; 13909381Seric else 13919381Seric HelpFile = newstr(val); 13928256Seric break; 13938256Seric 139451305Seric case 'h': /* maximum hop count */ 139551305Seric MaxHopCount = atoi(val); 139651305Seric break; 139751305Seric 139835651Seric case 'I': /* use internet domain name server */ 139966334Seric #if NAMED_BIND 140057207Seric UseNameServer = TRUE; 140157207Seric for (p = val; *p != 0; ) 140257207Seric { 140357207Seric bool clearmode; 140457207Seric char *q; 140557207Seric struct resolverflags *rfp; 140657207Seric 140757207Seric while (*p == ' ') 140857207Seric p++; 140957207Seric if (*p == '\0') 141057207Seric break; 141157207Seric clearmode = FALSE; 141257207Seric if (*p == '-') 141357207Seric clearmode = TRUE; 141457207Seric else if (*p != '+') 141557207Seric p--; 141657207Seric p++; 141757207Seric q = p; 141858050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 141957207Seric p++; 142057207Seric if (*p != '\0') 142157207Seric *p++ = '\0'; 142257207Seric for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 142357207Seric { 142457207Seric if (strcasecmp(q, rfp->rf_name) == 0) 142557207Seric break; 142657207Seric } 142764923Seric if (rfp->rf_name == NULL) 142864923Seric syserr("readcf: I option value %s unrecognized", q); 142964923Seric else if (clearmode) 143057207Seric _res.options &= ~rfp->rf_bits; 143157207Seric else 143257207Seric _res.options |= rfp->rf_bits; 143357207Seric } 143457207Seric if (tTd(8, 2)) 143557207Seric printf("_res.options = %x\n", _res.options); 143657207Seric #else 143757207Seric usrerr("name server (I option) specified but BIND not compiled in"); 143857207Seric #endif 143935651Seric break; 144035651Seric 14418269Seric case 'i': /* ignore dot lines in message */ 14429381Seric IgnrDot = atobool(val); 14438269Seric break; 14448269Seric 144559730Seric case 'j': /* send errors in MIME (RFC 1341) format */ 144659730Seric SendMIMEErrors = atobool(val); 144759730Seric break; 144859730Seric 144957136Seric case 'J': /* .forward search path */ 145057136Seric ForwardPath = newstr(val); 145157136Seric break; 145257136Seric 145354967Seric case 'k': /* connection cache size */ 145454967Seric MaxMciCache = atoi(val); 145556215Seric if (MaxMciCache < 0) 145656215Seric MaxMciCache = 0; 145754967Seric break; 145854967Seric 145954967Seric case 'K': /* connection cache timeout */ 146058796Seric MciCacheTimeout = convtime(val, 'm'); 146154967Seric break; 146254967Seric 146361104Seric case 'l': /* use Errors-To: header */ 146461104Seric UseErrorsTo = atobool(val); 146561104Seric break; 146661104Seric 14678256Seric case 'L': /* log level */ 146864140Seric if (safe || LogLevel < atoi(val)) 146964140Seric LogLevel = atoi(val); 14708256Seric break; 14718256Seric 14728269Seric case 'M': /* define macro */ 14739381Seric define(val[0], newstr(&val[1]), CurEnv); 147416878Seric sticky = FALSE; 14758269Seric break; 14768269Seric 14778269Seric case 'm': /* send to me too */ 14789381Seric MeToo = atobool(val); 14798269Seric break; 14808269Seric 148125820Seric case 'n': /* validate RHS in newaliases */ 148225820Seric CheckAliases = atobool(val); 148325820Seric break; 148425820Seric 148561104Seric /* 'N' available -- was "net name" */ 148661104Seric 148758851Seric case 'O': /* daemon options */ 148858851Seric setdaemonoptions(val); 148958851Seric break; 149058851Seric 14918269Seric case 'o': /* assume old style headers */ 14929381Seric if (atobool(val)) 14939341Seric CurEnv->e_flags |= EF_OLDSTYLE; 14949341Seric else 14959341Seric CurEnv->e_flags &= ~EF_OLDSTYLE; 14968269Seric break; 14978269Seric 149858082Seric case 'p': /* select privacy level */ 149958082Seric p = val; 150058082Seric for (;;) 150158082Seric { 150258082Seric register struct prival *pv; 150358082Seric extern struct prival PrivacyValues[]; 150458082Seric 150558082Seric while (isascii(*p) && (isspace(*p) || ispunct(*p))) 150658082Seric p++; 150758082Seric if (*p == '\0') 150858082Seric break; 150958082Seric val = p; 151058082Seric while (isascii(*p) && isalnum(*p)) 151158082Seric p++; 151258082Seric if (*p != '\0') 151358082Seric *p++ = '\0'; 151458082Seric 151558082Seric for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 151658082Seric { 151758082Seric if (strcasecmp(val, pv->pv_name) == 0) 151858082Seric break; 151958082Seric } 152058886Seric if (pv->pv_name == NULL) 152158886Seric syserr("readcf: Op line: %s unrecognized", val); 152258082Seric PrivacyFlags |= pv->pv_flag; 152358082Seric } 152458082Seric break; 152558082Seric 152624944Seric case 'P': /* postmaster copy address for returned mail */ 152724944Seric PostMasterCopy = newstr(val); 152824944Seric break; 152924944Seric 153024944Seric case 'q': /* slope of queue only function */ 153124944Seric QueueFactor = atoi(val); 153224944Seric break; 153324944Seric 15348256Seric case 'Q': /* queue directory */ 15359381Seric if (val[0] == '\0') 15368269Seric QueueDir = "mqueue"; 15379381Seric else 15389381Seric QueueDir = newstr(val); 153958789Seric if (RealUid != 0 && !safe) 154064718Seric Warn_Q_option = TRUE; 15418256Seric break; 15428256Seric 154358148Seric case 'R': /* don't prune routes */ 154458148Seric DontPruneRoutes = atobool(val); 154558148Seric break; 154658148Seric 15478256Seric case 'r': /* read timeout */ 154858112Seric settimeouts(val); 15498256Seric break; 15508256Seric 15518256Seric case 'S': /* status file */ 15529381Seric if (val[0] == '\0') 15538269Seric StatFile = "sendmail.st"; 15549381Seric else 15559381Seric StatFile = newstr(val); 15568256Seric break; 15578256Seric 15588265Seric case 's': /* be super safe, even if expensive */ 15599381Seric SuperSafe = atobool(val); 15608256Seric break; 15618256Seric 15628256Seric case 'T': /* queue timeout */ 156358737Seric p = strchr(val, '/'); 156458737Seric if (p != NULL) 156558737Seric { 156658737Seric *p++ = '\0'; 156758796Seric TimeOuts.to_q_warning = convtime(p, 'd'); 156858737Seric } 156958796Seric TimeOuts.to_q_return = convtime(val, 'h'); 157054967Seric break; 15718256Seric 15728265Seric case 't': /* time zone name */ 157352106Seric TimeZoneSpec = newstr(val); 15748265Seric break; 15758265Seric 157650556Seric case 'U': /* location of user database */ 157751360Seric UdbSpec = newstr(val); 157850556Seric break; 157950556Seric 15808256Seric case 'u': /* set default uid */ 158164133Seric if (isascii(*val) && isdigit(*val)) 158264133Seric DefUid = atoi(val); 158364133Seric else 158464133Seric { 158564133Seric register struct passwd *pw; 158664133Seric 158764133Seric DefUid = -1; 158864133Seric pw = getpwnam(val); 158964133Seric if (pw == NULL) 159064133Seric syserr("readcf: option u: unknown user %s", val); 159164133Seric else 159264133Seric DefUid = pw->pw_uid; 159364133Seric } 159440973Sbostic setdefuser(); 15958256Seric break; 15968256Seric 159758851Seric case 'V': /* fallback MX host */ 159858851Seric FallBackMX = newstr(val); 159958851Seric break; 160058851Seric 16018269Seric case 'v': /* run in verbose mode */ 16029381Seric Verbose = atobool(val); 16038256Seric break; 16048256Seric 160563837Seric case 'w': /* if we are best MX, try host directly */ 160663837Seric TryNullMXList = atobool(val); 160763837Seric break; 160861104Seric 160961104Seric /* 'W' available -- was wizard password */ 161061104Seric 161114879Seric case 'x': /* load avg at which to auto-queue msgs */ 161214879Seric QueueLA = atoi(val); 161314879Seric break; 161414879Seric 161514879Seric case 'X': /* load avg at which to auto-reject connections */ 161614879Seric RefuseLA = atoi(val); 161714879Seric break; 161814879Seric 161924981Seric case 'y': /* work recipient factor */ 162024981Seric WkRecipFact = atoi(val); 162124981Seric break; 162224981Seric 162324981Seric case 'Y': /* fork jobs during queue runs */ 162424952Seric ForkQueueRuns = atobool(val); 162524952Seric break; 162624952Seric 162724981Seric case 'z': /* work message class factor */ 162824981Seric WkClassFact = atoi(val); 162924981Seric break; 163024981Seric 163124981Seric case 'Z': /* work time factor */ 163224981Seric WkTimeFact = atoi(val); 163324981Seric break; 163424981Seric 163567614Seric case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 163667614Seric BrokenSmtpPeers = atobool(val); 163767614Seric break; 163867614Seric 163967614Seric case O_SQBH: /* sort work queue by host first */ 164067614Seric SortQueueByHost = atobool(val); 164167614Seric break; 164267614Seric 164367707Seric case O_DNICE: /* delivery nice value */ 164467707Seric DeliveryNiceness = atoi(val); 164567707Seric break; 164667707Seric 164767707Seric case O_MQA: /* minimum queue age between deliveries */ 164867707Seric MinQueueAge = convtime(val, 'm'); 164967707Seric break; 165067707Seric 165167707Seric case O_MHSA: /* maximum age of cached host status */ 165267707Seric MaxHostStatAge = convtime(val, 'm'); 165367707Seric break; 165467707Seric 16558256Seric default: 16568256Seric break; 16578256Seric } 165816878Seric if (sticky) 165916878Seric setbitn(opt, StickyOpt); 16609188Seric return; 16618256Seric } 166210687Seric /* 166310687Seric ** SETCLASS -- set a word into a class 166410687Seric ** 166510687Seric ** Parameters: 166610687Seric ** class -- the class to put the word in. 166710687Seric ** word -- the word to enter 166810687Seric ** 166910687Seric ** Returns: 167010687Seric ** none. 167110687Seric ** 167210687Seric ** Side Effects: 167310687Seric ** puts the word into the symbol table. 167410687Seric */ 167510687Seric 167610687Seric setclass(class, word) 167710687Seric int class; 167810687Seric char *word; 167910687Seric { 168010687Seric register STAB *s; 168110687Seric 168257943Seric if (tTd(37, 8)) 168364326Seric printf("setclass(%c, %s)\n", class, word); 168410687Seric s = stab(word, ST_CLASS, ST_ENTER); 168510687Seric setbitn(class, s->s_class); 168610687Seric } 168753654Seric /* 168853654Seric ** MAKEMAPENTRY -- create a map entry 168953654Seric ** 169053654Seric ** Parameters: 169153654Seric ** line -- the config file line 169253654Seric ** 169353654Seric ** Returns: 169453654Seric ** TRUE if it successfully entered the map entry. 169553654Seric ** FALSE otherwise (usually syntax error). 169653654Seric ** 169753654Seric ** Side Effects: 169853654Seric ** Enters the map into the dictionary. 169953654Seric */ 170053654Seric 170153654Seric void 170253654Seric makemapentry(line) 170353654Seric char *line; 170453654Seric { 170553654Seric register char *p; 170653654Seric char *mapname; 170753654Seric char *classname; 170864078Seric register STAB *s; 170953654Seric STAB *class; 171053654Seric 171158050Seric for (p = line; isascii(*p) && isspace(*p); p++) 171253654Seric continue; 171358050Seric if (!(isascii(*p) && isalnum(*p))) 171453654Seric { 171553654Seric syserr("readcf: config K line: no map name"); 171653654Seric return; 171753654Seric } 171853654Seric 171953654Seric mapname = p; 172058050Seric while (isascii(*++p) && isalnum(*p)) 172153654Seric continue; 172253654Seric if (*p != '\0') 172353654Seric *p++ = '\0'; 172458050Seric while (isascii(*p) && isspace(*p)) 172553654Seric p++; 172658050Seric if (!(isascii(*p) && isalnum(*p))) 172753654Seric { 172853654Seric syserr("readcf: config K line, map %s: no map class", mapname); 172953654Seric return; 173053654Seric } 173153654Seric classname = p; 173258050Seric while (isascii(*++p) && isalnum(*p)) 173353654Seric continue; 173453654Seric if (*p != '\0') 173553654Seric *p++ = '\0'; 173658050Seric while (isascii(*p) && isspace(*p)) 173753654Seric p++; 173853654Seric 173953654Seric /* look up the class */ 174053654Seric class = stab(classname, ST_MAPCLASS, ST_FIND); 174153654Seric if (class == NULL) 174253654Seric { 174353654Seric syserr("readcf: map %s: class %s not available", mapname, classname); 174453654Seric return; 174553654Seric } 174653654Seric 174753654Seric /* enter the map */ 174864078Seric s = stab(mapname, ST_MAP, ST_ENTER); 174964078Seric s->s_map.map_class = &class->s_mapclass; 175064078Seric s->s_map.map_mname = newstr(mapname); 175153654Seric 175264078Seric if (class->s_mapclass.map_parse(&s->s_map, p)) 175364078Seric s->s_map.map_mflags |= MF_VALID; 175464078Seric 175564078Seric if (tTd(37, 5)) 175664078Seric { 175764078Seric printf("map %s, class %s, flags %x, file %s,\n", 175864078Seric s->s_map.map_mname, s->s_map.map_class->map_cname, 175964078Seric s->s_map.map_mflags, 176064078Seric s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 176164078Seric printf("\tapp %s, domain %s, rebuild %s\n", 176264078Seric s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 176364078Seric s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 176464078Seric s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 176564078Seric } 176653654Seric } 176758112Seric /* 176858112Seric ** SETTIMEOUTS -- parse and set timeout values 176958112Seric ** 177058112Seric ** Parameters: 177158112Seric ** val -- a pointer to the values. If NULL, do initial 177258112Seric ** settings. 177358112Seric ** 177458112Seric ** Returns: 177558112Seric ** none. 177658112Seric ** 177758112Seric ** Side Effects: 177858112Seric ** Initializes the TimeOuts structure 177958112Seric */ 178058112Seric 178164255Seric #define SECONDS 178258112Seric #define MINUTES * 60 178358112Seric #define HOUR * 3600 178458112Seric 178558112Seric settimeouts(val) 178658112Seric register char *val; 178758112Seric { 178858112Seric register char *p; 178958671Seric extern time_t convtime(); 179058112Seric 179158112Seric if (val == NULL) 179258112Seric { 179358112Seric TimeOuts.to_initial = (time_t) 5 MINUTES; 179458112Seric TimeOuts.to_helo = (time_t) 5 MINUTES; 179558112Seric TimeOuts.to_mail = (time_t) 10 MINUTES; 179658112Seric TimeOuts.to_rcpt = (time_t) 1 HOUR; 179758112Seric TimeOuts.to_datainit = (time_t) 5 MINUTES; 179858112Seric TimeOuts.to_datablock = (time_t) 1 HOUR; 179958112Seric TimeOuts.to_datafinal = (time_t) 1 HOUR; 180058112Seric TimeOuts.to_rset = (time_t) 5 MINUTES; 180158112Seric TimeOuts.to_quit = (time_t) 2 MINUTES; 180258112Seric TimeOuts.to_nextcommand = (time_t) 1 HOUR; 180358112Seric TimeOuts.to_miscshort = (time_t) 2 MINUTES; 180464255Seric TimeOuts.to_ident = (time_t) 30 SECONDS; 1805*67711Seric TimeOuts.to_fileopen = (time_t) 60 SECONDS; 180658112Seric return; 180758112Seric } 180858112Seric 180958112Seric for (;; val = p) 181058112Seric { 181158112Seric while (isascii(*val) && isspace(*val)) 181258112Seric val++; 181358112Seric if (*val == '\0') 181458112Seric break; 181558112Seric for (p = val; *p != '\0' && *p != ','; p++) 181658112Seric continue; 181758112Seric if (*p != '\0') 181858112Seric *p++ = '\0'; 181958112Seric 182058112Seric if (isascii(*val) && isdigit(*val)) 182158112Seric { 182258112Seric /* old syntax -- set everything */ 182358796Seric TimeOuts.to_mail = convtime(val, 'm'); 182458112Seric TimeOuts.to_rcpt = TimeOuts.to_mail; 182558112Seric TimeOuts.to_datainit = TimeOuts.to_mail; 182658112Seric TimeOuts.to_datablock = TimeOuts.to_mail; 182758112Seric TimeOuts.to_datafinal = TimeOuts.to_mail; 182858112Seric TimeOuts.to_nextcommand = TimeOuts.to_mail; 182958112Seric continue; 183058112Seric } 183158112Seric else 183258112Seric { 1833*67711Seric register char *q = strchr(val, ':'); 183458112Seric time_t to; 183558112Seric 1836*67711Seric if (q == NULL && (q = strchr(val, '=')) == NULL) 183758112Seric { 183858112Seric /* syntax error */ 183958112Seric continue; 184058112Seric } 184158112Seric *q++ = '\0'; 184258796Seric to = convtime(q, 'm'); 184358112Seric 184458112Seric if (strcasecmp(val, "initial") == 0) 184558112Seric TimeOuts.to_initial = to; 184658112Seric else if (strcasecmp(val, "mail") == 0) 184758112Seric TimeOuts.to_mail = to; 184858112Seric else if (strcasecmp(val, "rcpt") == 0) 184958112Seric TimeOuts.to_rcpt = to; 185058112Seric else if (strcasecmp(val, "datainit") == 0) 185158112Seric TimeOuts.to_datainit = to; 185258112Seric else if (strcasecmp(val, "datablock") == 0) 185358112Seric TimeOuts.to_datablock = to; 185458112Seric else if (strcasecmp(val, "datafinal") == 0) 185558112Seric TimeOuts.to_datafinal = to; 185658112Seric else if (strcasecmp(val, "command") == 0) 185758112Seric TimeOuts.to_nextcommand = to; 185858112Seric else if (strcasecmp(val, "rset") == 0) 185958112Seric TimeOuts.to_rset = to; 186058112Seric else if (strcasecmp(val, "helo") == 0) 186158112Seric TimeOuts.to_helo = to; 186258112Seric else if (strcasecmp(val, "quit") == 0) 186358112Seric TimeOuts.to_quit = to; 186458112Seric else if (strcasecmp(val, "misc") == 0) 186558112Seric TimeOuts.to_miscshort = to; 186664255Seric else if (strcasecmp(val, "ident") == 0) 186764255Seric TimeOuts.to_ident = to; 1868*67711Seric else if (strcasecmp(val, "fileopen") == 0) 1869*67711Seric TimeOuts.to_fileopen = to; 1870*67711Seric else if (strcasecmp(val, "queuewarn") == 0) 1871*67711Seric TimeOuts.to_q_warning = to; 1872*67711Seric else if (strcasecmp(val, "queuereturn") == 0) 1873*67711Seric TimeOuts.to_q_return = to; 187458112Seric else 187558112Seric syserr("settimeouts: invalid timeout %s", val); 187658112Seric } 187758112Seric } 187858112Seric } 1879