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*67615Seric static char sccsid[] = "@(#)readcf.c 8.31 (Berkeley) 08/07/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)) 413*67615Seric 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 /* codes for options that have no short name */ 107067614Seric /* NOTE: some of these values may be in the list of "safe" options below */ 107167614Seric #define O_BSP 0x80 /* have broken SMTP peers */ 107267614Seric #define O_SQBH 0x81 /* sort queue by host */ 107367614Seric 107467614Seric struct optioninfo 107567614Seric { 107667614Seric char *o_name; /* long name of option */ 107767614Seric char o_code; /* short name of option */ 107867614Seric bool o_safe; /* safe for random people to use */ 107967614Seric } OptionTab[] = 108067614Seric { 108167614Seric "SevenBitInput", '7', TRUE, 108267614Seric "AliasFile", 'A', FALSE, 108367614Seric "AliasWait", 'a', FALSE, 108467614Seric "BlankSub", 'B', FALSE, 108567614Seric "MinFreeBlocks", 'b', TRUE, 108667614Seric "CheckpointInterval", 'C', TRUE, 108767614Seric "HoldExpensive", 'c', FALSE, 108867614Seric "AutoRebuildAliases", 'D', FALSE, 108967614Seric "DeliveryMode", 'd', TRUE, 109067614Seric "ErrorHeader", 'E', FALSE, 109167614Seric "ErrorMode", 'e', TRUE, 109267614Seric "TempFileMode", 'F', FALSE, 109367614Seric "SaveFromLine", 'f', FALSE, 109467614Seric "MatchGECOS", 'G', FALSE, 109567614Seric "HelpFile", 'H', FALSE, 109667614Seric "MaxHopCount", 'h', FALSE, 109767614Seric "NameServerOptions", 'I', FALSE, 109867614Seric "IgnoreDots", 'i', TRUE, 109967614Seric "ForwardPath", 'J', FALSE, 110067614Seric "SendMimeErrors", 'j', TRUE, 110167614Seric "ConnectionCacheSize", 'k', FALSE, 110267614Seric "ConnectionCacheTimeout", 'K', FALSE, 110367614Seric "UseErrorsTo", 'l', FALSE, 110467614Seric "LogLevel", 'L', FALSE, 110567614Seric "MeToo", 'm', TRUE, 110667614Seric "CheckAliases", 'n', FALSE, 110767614Seric "OldStyleHeaders", 'o', TRUE, 110867614Seric "DaemonPortOptions", 'O', FALSE, 110967614Seric "PrivacyOptions", 'p', TRUE, 111067614Seric "PostmasterCopy", 'P', FALSE, 111167614Seric "QueueFactor", 'q', FALSE, 111267614Seric "QueueDirectory", 'Q', FALSE, 111367614Seric "DontPruneRoutes", 'R', FALSE, 111467614Seric "ReadTimeout", 'r', TRUE, 111567614Seric "StatusFile", 'S', FALSE, 111667614Seric "SuperSafe", 's', TRUE, 111767614Seric "QueueTimeout", 'T', FALSE, 111867614Seric "TimeZoneSpec", 't', FALSE, 111967614Seric "UserDatabaseSpec", 'U', FALSE, 112067614Seric "DefaultUser", 'u', FALSE, 112167614Seric "FallbackMXhost", 'V', FALSE, 112267614Seric "Verbose", 'v', TRUE, 112367614Seric "TryNullMXList", 'w', TRUE, 112467614Seric "QueueLA", 'x', FALSE, 112567614Seric "RefuseLA", 'X', FALSE, 112667614Seric "RecipientFactor", 'y', FALSE, 112767614Seric "ForkQueueRuns", 'Y', FALSE, 112867614Seric "ClassFactor", 'z', FALSE, 112967614Seric "TimeFactor", 'Z', FALSE, 113067614Seric "BrokenSmtpPeers", O_BSP, TRUE, 113167614Seric "SortQueueByHost", O_SQBH, TRUE, 113267614Seric NULL, '\0', FALSE, 113367614Seric }; 113467614Seric 113567614Seric 113667614Seric 113758734Seric setoption(opt, val, safe, sticky, e) 113867614Seric u_char opt; 11398256Seric char *val; 114021755Seric bool safe; 11418269Seric bool sticky; 114258734Seric register ENVELOPE *e; 11438256Seric { 114457207Seric register char *p; 114567614Seric register struct optioninfo *o; 11468265Seric extern bool atobool(); 114712633Seric extern time_t convtime(); 114814879Seric extern int QueueLA; 114914879Seric extern int RefuseLA; 115064718Seric extern bool Warn_Q_option; 115117474Seric extern bool trusteduser(); 11528256Seric 115367614Seric if (opt == ' ') 115467614Seric { 115567614Seric /* full word options */ 115667614Seric 115767614Seric p = strchr(val, '='); 115867614Seric if (p == NULL) 115967614Seric p = &val[strlen(val)]; 116067614Seric while (*--p == ' ') 116167614Seric continue; 116267614Seric while (*++p == ' ') 116367614Seric *p = '\0'; 116467614Seric if (*p == '=') 116567614Seric *p++ = '\0'; 116667614Seric while (*p == ' ') 116767614Seric p++; 116867614Seric for (o = OptionTab; o->o_name != NULL; o++) 116967614Seric { 117067614Seric if (strcasecmp(o->o_name, val) == 0) 117167614Seric break; 117267614Seric } 117367614Seric if (o->o_name == NULL) 117467614Seric syserr("readcf: unknown option name %s", val); 117567614Seric opt = o->o_code; 117667614Seric val = p; 117767614Seric } 117867614Seric else 117967614Seric { 118067614Seric for (o = OptionTab; o->o_name != NULL; o++) 118167614Seric { 118267614Seric if (o->o_code == opt) 118367614Seric break; 118467614Seric } 118567614Seric } 118667614Seric 11878256Seric if (tTd(37, 1)) 118867614Seric printf("setoption %s (0x%x)=%s", 118967614Seric o->o_name == NULL ? "<unknown>" : o->o_name, 119067614Seric opt, val); 11918256Seric 11928256Seric /* 11938269Seric ** See if this option is preset for us. 11948256Seric */ 11958256Seric 119659731Seric if (!sticky && bitnset(opt, StickyOpt)) 11978269Seric { 11989341Seric if (tTd(37, 1)) 11999341Seric printf(" (ignored)\n"); 12008269Seric return; 12018269Seric } 12028269Seric 120321755Seric /* 120421755Seric ** Check to see if this option can be specified by this user. 120521755Seric */ 120621755Seric 120763787Seric if (!safe && RealUid == 0) 120821755Seric safe = TRUE; 120967614Seric if (!safe && !o->o_safe) 121021755Seric { 121139111Srick if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 121221755Seric { 121336582Sbostic if (tTd(37, 1)) 121436582Sbostic printf(" (unsafe)"); 121563787Seric if (RealUid != geteuid()) 121636582Sbostic { 121751210Seric if (tTd(37, 1)) 121851210Seric printf("(Resetting uid)"); 121963787Seric (void) setgid(RealGid); 122063787Seric (void) setuid(RealUid); 122136582Sbostic } 122221755Seric } 122321755Seric } 122451210Seric if (tTd(37, 1)) 122517985Seric printf("\n"); 12268269Seric 122767614Seric switch (opt & 0xff) 12288256Seric { 122959709Seric case '7': /* force seven-bit input */ 123067546Seric SevenBitInput = atobool(val); 123152106Seric break; 123252106Seric 123367546Seric case '8': /* handling of 8-bit input */ 123467546Seric switch (*val) 123567546Seric { 123667547Seric case 'r': /* reject 8-bit, don't convert MIME */ 123767546Seric MimeMode = 0; 123867546Seric break; 123967546Seric 124067547Seric case 'm': /* convert 8-bit, convert MIME */ 124167546Seric MimeMode = MM_CVTMIME|MM_MIME8BIT; 124267546Seric break; 124367546Seric 124467547Seric case 'j': /* "just send 8" */ 124567546Seric MimeMode = MM_PASS8BIT; 124667546Seric break; 124767546Seric 124867546Seric case 'p': /* pass 8 bit, convert MIME */ 124967546Seric MimeMode = MM_PASS8BIT|MM_CVTMIME; 125067546Seric break; 125167546Seric 125267546Seric case 's': /* strict adherence */ 125367546Seric MimeMode = MM_CVTMIME; 125467546Seric break; 125567546Seric 125667547Seric case 'a': /* encode 8 bit if available */ 125767546Seric MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 125867546Seric break; 125967546Seric 126067547Seric case 'c': /* convert 8 bit to MIME, never 7 bit */ 126167547Seric MimeMode = MM_MIME8BIT; 126267547Seric break; 126367547Seric 126467546Seric default: 126567546Seric syserr("Unknown 8-bit mode %c", *val); 126667546Seric exit(EX_USAGE); 126767546Seric } 126867546Seric break; 126967546Seric 12708256Seric case 'A': /* set default alias file */ 12719381Seric if (val[0] == '\0') 127259672Seric setalias("aliases"); 12739381Seric else 127459672Seric setalias(val); 12758256Seric break; 12768256Seric 127717474Seric case 'a': /* look N minutes for "@:@" in alias file */ 127817474Seric if (val[0] == '\0') 127964796Seric SafeAlias = 5 * 60; /* five minutes */ 128017474Seric else 128164796Seric SafeAlias = convtime(val, 'm'); 128217474Seric break; 128317474Seric 128416843Seric case 'B': /* substitution for blank character */ 128516843Seric SpaceSub = val[0]; 128616843Seric if (SpaceSub == '\0') 128716843Seric SpaceSub = ' '; 128816843Seric break; 128916843Seric 129059283Seric case 'b': /* min blocks free on queue fs/max msg size */ 129159283Seric p = strchr(val, '/'); 129259283Seric if (p != NULL) 129359283Seric { 129459283Seric *p++ = '\0'; 129559283Seric MaxMessageSize = atol(p); 129659283Seric } 129758082Seric MinBlocksFree = atol(val); 129858082Seric break; 129958082Seric 13009284Seric case 'c': /* don't connect to "expensive" mailers */ 13019381Seric NoConnect = atobool(val); 13029284Seric break; 13039284Seric 130451305Seric case 'C': /* checkpoint every N addresses */ 130551305Seric CheckpointInterval = atoi(val); 130624944Seric break; 130724944Seric 13089284Seric case 'd': /* delivery mode */ 13099284Seric switch (*val) 13108269Seric { 13119284Seric case '\0': 131258734Seric e->e_sendmode = SM_DELIVER; 13138269Seric break; 13148269Seric 131510755Seric case SM_QUEUE: /* queue only */ 131610755Seric #ifndef QUEUE 131710755Seric syserr("need QUEUE to set -odqueue"); 131856795Seric #endif /* QUEUE */ 131910755Seric /* fall through..... */ 132010755Seric 13219284Seric case SM_DELIVER: /* do everything */ 13229284Seric case SM_FORK: /* fork after verification */ 132358734Seric e->e_sendmode = *val; 13248269Seric break; 13258269Seric 13268269Seric default: 13279284Seric syserr("Unknown delivery mode %c", *val); 13288269Seric exit(EX_USAGE); 13298269Seric } 13308269Seric break; 13318269Seric 13329146Seric case 'D': /* rebuild alias database as needed */ 13339381Seric AutoRebuild = atobool(val); 13349146Seric break; 13359146Seric 133655372Seric case 'E': /* error message header/header file */ 133755379Seric if (*val != '\0') 133855379Seric ErrMsgFile = newstr(val); 133955372Seric break; 134055372Seric 13418269Seric case 'e': /* set error processing mode */ 13428269Seric switch (*val) 13438269Seric { 13449381Seric case EM_QUIET: /* be silent about it */ 13459381Seric case EM_MAIL: /* mail back */ 13469381Seric case EM_BERKNET: /* do berknet error processing */ 13479381Seric case EM_WRITE: /* write back (or mail) */ 13489381Seric case EM_PRINT: /* print errors normally (default) */ 134958734Seric e->e_errormode = *val; 13508269Seric break; 13518269Seric } 13528269Seric break; 13538269Seric 13549049Seric case 'F': /* file mode */ 135517975Seric FileMode = atooct(val) & 0777; 13569049Seric break; 13579049Seric 13588269Seric case 'f': /* save Unix-style From lines on front */ 13599381Seric SaveFrom = atobool(val); 13608269Seric break; 13618269Seric 136253735Seric case 'G': /* match recipients against GECOS field */ 136353735Seric MatchGecos = atobool(val); 136453735Seric break; 136553735Seric 13668256Seric case 'g': /* default gid */ 136764133Seric if (isascii(*val) && isdigit(*val)) 136864133Seric DefGid = atoi(val); 136964133Seric else 137064133Seric { 137164133Seric register struct group *gr; 137264133Seric 137364133Seric DefGid = -1; 137464133Seric gr = getgrnam(val); 137564133Seric if (gr == NULL) 137664133Seric syserr("readcf: option g: unknown group %s", val); 137764133Seric else 137864133Seric DefGid = gr->gr_gid; 137964133Seric } 13808256Seric break; 13818256Seric 13828256Seric case 'H': /* help file */ 13839381Seric if (val[0] == '\0') 13848269Seric HelpFile = "sendmail.hf"; 13859381Seric else 13869381Seric HelpFile = newstr(val); 13878256Seric break; 13888256Seric 138951305Seric case 'h': /* maximum hop count */ 139051305Seric MaxHopCount = atoi(val); 139151305Seric break; 139251305Seric 139335651Seric case 'I': /* use internet domain name server */ 139466334Seric #if NAMED_BIND 139557207Seric UseNameServer = TRUE; 139657207Seric for (p = val; *p != 0; ) 139757207Seric { 139857207Seric bool clearmode; 139957207Seric char *q; 140057207Seric struct resolverflags *rfp; 140157207Seric 140257207Seric while (*p == ' ') 140357207Seric p++; 140457207Seric if (*p == '\0') 140557207Seric break; 140657207Seric clearmode = FALSE; 140757207Seric if (*p == '-') 140857207Seric clearmode = TRUE; 140957207Seric else if (*p != '+') 141057207Seric p--; 141157207Seric p++; 141257207Seric q = p; 141358050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 141457207Seric p++; 141557207Seric if (*p != '\0') 141657207Seric *p++ = '\0'; 141757207Seric for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 141857207Seric { 141957207Seric if (strcasecmp(q, rfp->rf_name) == 0) 142057207Seric break; 142157207Seric } 142264923Seric if (rfp->rf_name == NULL) 142364923Seric syserr("readcf: I option value %s unrecognized", q); 142464923Seric else if (clearmode) 142557207Seric _res.options &= ~rfp->rf_bits; 142657207Seric else 142757207Seric _res.options |= rfp->rf_bits; 142857207Seric } 142957207Seric if (tTd(8, 2)) 143057207Seric printf("_res.options = %x\n", _res.options); 143157207Seric #else 143257207Seric usrerr("name server (I option) specified but BIND not compiled in"); 143357207Seric #endif 143435651Seric break; 143535651Seric 14368269Seric case 'i': /* ignore dot lines in message */ 14379381Seric IgnrDot = atobool(val); 14388269Seric break; 14398269Seric 144059730Seric case 'j': /* send errors in MIME (RFC 1341) format */ 144159730Seric SendMIMEErrors = atobool(val); 144259730Seric break; 144359730Seric 144457136Seric case 'J': /* .forward search path */ 144557136Seric ForwardPath = newstr(val); 144657136Seric break; 144757136Seric 144854967Seric case 'k': /* connection cache size */ 144954967Seric MaxMciCache = atoi(val); 145056215Seric if (MaxMciCache < 0) 145156215Seric MaxMciCache = 0; 145254967Seric break; 145354967Seric 145454967Seric case 'K': /* connection cache timeout */ 145558796Seric MciCacheTimeout = convtime(val, 'm'); 145654967Seric break; 145754967Seric 145861104Seric case 'l': /* use Errors-To: header */ 145961104Seric UseErrorsTo = atobool(val); 146061104Seric break; 146161104Seric 14628256Seric case 'L': /* log level */ 146364140Seric if (safe || LogLevel < atoi(val)) 146464140Seric LogLevel = atoi(val); 14658256Seric break; 14668256Seric 14678269Seric case 'M': /* define macro */ 14689381Seric define(val[0], newstr(&val[1]), CurEnv); 146916878Seric sticky = FALSE; 14708269Seric break; 14718269Seric 14728269Seric case 'm': /* send to me too */ 14739381Seric MeToo = atobool(val); 14748269Seric break; 14758269Seric 147625820Seric case 'n': /* validate RHS in newaliases */ 147725820Seric CheckAliases = atobool(val); 147825820Seric break; 147925820Seric 148061104Seric /* 'N' available -- was "net name" */ 148161104Seric 148258851Seric case 'O': /* daemon options */ 148358851Seric setdaemonoptions(val); 148458851Seric break; 148558851Seric 14868269Seric case 'o': /* assume old style headers */ 14879381Seric if (atobool(val)) 14889341Seric CurEnv->e_flags |= EF_OLDSTYLE; 14899341Seric else 14909341Seric CurEnv->e_flags &= ~EF_OLDSTYLE; 14918269Seric break; 14928269Seric 149358082Seric case 'p': /* select privacy level */ 149458082Seric p = val; 149558082Seric for (;;) 149658082Seric { 149758082Seric register struct prival *pv; 149858082Seric extern struct prival PrivacyValues[]; 149958082Seric 150058082Seric while (isascii(*p) && (isspace(*p) || ispunct(*p))) 150158082Seric p++; 150258082Seric if (*p == '\0') 150358082Seric break; 150458082Seric val = p; 150558082Seric while (isascii(*p) && isalnum(*p)) 150658082Seric p++; 150758082Seric if (*p != '\0') 150858082Seric *p++ = '\0'; 150958082Seric 151058082Seric for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 151158082Seric { 151258082Seric if (strcasecmp(val, pv->pv_name) == 0) 151358082Seric break; 151458082Seric } 151558886Seric if (pv->pv_name == NULL) 151658886Seric syserr("readcf: Op line: %s unrecognized", val); 151758082Seric PrivacyFlags |= pv->pv_flag; 151858082Seric } 151958082Seric break; 152058082Seric 152124944Seric case 'P': /* postmaster copy address for returned mail */ 152224944Seric PostMasterCopy = newstr(val); 152324944Seric break; 152424944Seric 152524944Seric case 'q': /* slope of queue only function */ 152624944Seric QueueFactor = atoi(val); 152724944Seric break; 152824944Seric 15298256Seric case 'Q': /* queue directory */ 15309381Seric if (val[0] == '\0') 15318269Seric QueueDir = "mqueue"; 15329381Seric else 15339381Seric QueueDir = newstr(val); 153458789Seric if (RealUid != 0 && !safe) 153564718Seric Warn_Q_option = TRUE; 15368256Seric break; 15378256Seric 153858148Seric case 'R': /* don't prune routes */ 153958148Seric DontPruneRoutes = atobool(val); 154058148Seric break; 154158148Seric 15428256Seric case 'r': /* read timeout */ 154358112Seric settimeouts(val); 15448256Seric break; 15458256Seric 15468256Seric case 'S': /* status file */ 15479381Seric if (val[0] == '\0') 15488269Seric StatFile = "sendmail.st"; 15499381Seric else 15509381Seric StatFile = newstr(val); 15518256Seric break; 15528256Seric 15538265Seric case 's': /* be super safe, even if expensive */ 15549381Seric SuperSafe = atobool(val); 15558256Seric break; 15568256Seric 15578256Seric case 'T': /* queue timeout */ 155858737Seric p = strchr(val, '/'); 155958737Seric if (p != NULL) 156058737Seric { 156158737Seric *p++ = '\0'; 156258796Seric TimeOuts.to_q_warning = convtime(p, 'd'); 156358737Seric } 156458796Seric TimeOuts.to_q_return = convtime(val, 'h'); 156554967Seric break; 15668256Seric 15678265Seric case 't': /* time zone name */ 156852106Seric TimeZoneSpec = newstr(val); 15698265Seric break; 15708265Seric 157150556Seric case 'U': /* location of user database */ 157251360Seric UdbSpec = newstr(val); 157350556Seric break; 157450556Seric 15758256Seric case 'u': /* set default uid */ 157664133Seric if (isascii(*val) && isdigit(*val)) 157764133Seric DefUid = atoi(val); 157864133Seric else 157964133Seric { 158064133Seric register struct passwd *pw; 158164133Seric 158264133Seric DefUid = -1; 158364133Seric pw = getpwnam(val); 158464133Seric if (pw == NULL) 158564133Seric syserr("readcf: option u: unknown user %s", val); 158664133Seric else 158764133Seric DefUid = pw->pw_uid; 158864133Seric } 158940973Sbostic setdefuser(); 15908256Seric break; 15918256Seric 159258851Seric case 'V': /* fallback MX host */ 159358851Seric FallBackMX = newstr(val); 159458851Seric break; 159558851Seric 15968269Seric case 'v': /* run in verbose mode */ 15979381Seric Verbose = atobool(val); 15988256Seric break; 15998256Seric 160063837Seric case 'w': /* if we are best MX, try host directly */ 160163837Seric TryNullMXList = atobool(val); 160263837Seric break; 160361104Seric 160461104Seric /* 'W' available -- was wizard password */ 160561104Seric 160614879Seric case 'x': /* load avg at which to auto-queue msgs */ 160714879Seric QueueLA = atoi(val); 160814879Seric break; 160914879Seric 161014879Seric case 'X': /* load avg at which to auto-reject connections */ 161114879Seric RefuseLA = atoi(val); 161214879Seric break; 161314879Seric 161424981Seric case 'y': /* work recipient factor */ 161524981Seric WkRecipFact = atoi(val); 161624981Seric break; 161724981Seric 161824981Seric case 'Y': /* fork jobs during queue runs */ 161924952Seric ForkQueueRuns = atobool(val); 162024952Seric break; 162124952Seric 162224981Seric case 'z': /* work message class factor */ 162324981Seric WkClassFact = atoi(val); 162424981Seric break; 162524981Seric 162624981Seric case 'Z': /* work time factor */ 162724981Seric WkTimeFact = atoi(val); 162824981Seric break; 162924981Seric 163067614Seric case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 163167614Seric BrokenSmtpPeers = atobool(val); 163267614Seric break; 163367614Seric 163467614Seric case O_SQBH: /* sort work queue by host first */ 163567614Seric SortQueueByHost = atobool(val); 163667614Seric break; 163767614Seric 16388256Seric default: 16398256Seric break; 16408256Seric } 164116878Seric if (sticky) 164216878Seric setbitn(opt, StickyOpt); 16439188Seric return; 16448256Seric } 164510687Seric /* 164610687Seric ** SETCLASS -- set a word into a class 164710687Seric ** 164810687Seric ** Parameters: 164910687Seric ** class -- the class to put the word in. 165010687Seric ** word -- the word to enter 165110687Seric ** 165210687Seric ** Returns: 165310687Seric ** none. 165410687Seric ** 165510687Seric ** Side Effects: 165610687Seric ** puts the word into the symbol table. 165710687Seric */ 165810687Seric 165910687Seric setclass(class, word) 166010687Seric int class; 166110687Seric char *word; 166210687Seric { 166310687Seric register STAB *s; 166410687Seric 166557943Seric if (tTd(37, 8)) 166664326Seric printf("setclass(%c, %s)\n", class, word); 166710687Seric s = stab(word, ST_CLASS, ST_ENTER); 166810687Seric setbitn(class, s->s_class); 166910687Seric } 167053654Seric /* 167153654Seric ** MAKEMAPENTRY -- create a map entry 167253654Seric ** 167353654Seric ** Parameters: 167453654Seric ** line -- the config file line 167553654Seric ** 167653654Seric ** Returns: 167753654Seric ** TRUE if it successfully entered the map entry. 167853654Seric ** FALSE otherwise (usually syntax error). 167953654Seric ** 168053654Seric ** Side Effects: 168153654Seric ** Enters the map into the dictionary. 168253654Seric */ 168353654Seric 168453654Seric void 168553654Seric makemapentry(line) 168653654Seric char *line; 168753654Seric { 168853654Seric register char *p; 168953654Seric char *mapname; 169053654Seric char *classname; 169164078Seric register STAB *s; 169253654Seric STAB *class; 169353654Seric 169458050Seric for (p = line; isascii(*p) && isspace(*p); p++) 169553654Seric continue; 169658050Seric if (!(isascii(*p) && isalnum(*p))) 169753654Seric { 169853654Seric syserr("readcf: config K line: no map name"); 169953654Seric return; 170053654Seric } 170153654Seric 170253654Seric mapname = p; 170358050Seric while (isascii(*++p) && isalnum(*p)) 170453654Seric continue; 170553654Seric if (*p != '\0') 170653654Seric *p++ = '\0'; 170758050Seric while (isascii(*p) && isspace(*p)) 170853654Seric p++; 170958050Seric if (!(isascii(*p) && isalnum(*p))) 171053654Seric { 171153654Seric syserr("readcf: config K line, map %s: no map class", mapname); 171253654Seric return; 171353654Seric } 171453654Seric classname = p; 171558050Seric while (isascii(*++p) && isalnum(*p)) 171653654Seric continue; 171753654Seric if (*p != '\0') 171853654Seric *p++ = '\0'; 171958050Seric while (isascii(*p) && isspace(*p)) 172053654Seric p++; 172153654Seric 172253654Seric /* look up the class */ 172353654Seric class = stab(classname, ST_MAPCLASS, ST_FIND); 172453654Seric if (class == NULL) 172553654Seric { 172653654Seric syserr("readcf: map %s: class %s not available", mapname, classname); 172753654Seric return; 172853654Seric } 172953654Seric 173053654Seric /* enter the map */ 173164078Seric s = stab(mapname, ST_MAP, ST_ENTER); 173264078Seric s->s_map.map_class = &class->s_mapclass; 173364078Seric s->s_map.map_mname = newstr(mapname); 173453654Seric 173564078Seric if (class->s_mapclass.map_parse(&s->s_map, p)) 173664078Seric s->s_map.map_mflags |= MF_VALID; 173764078Seric 173864078Seric if (tTd(37, 5)) 173964078Seric { 174064078Seric printf("map %s, class %s, flags %x, file %s,\n", 174164078Seric s->s_map.map_mname, s->s_map.map_class->map_cname, 174264078Seric s->s_map.map_mflags, 174364078Seric s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 174464078Seric printf("\tapp %s, domain %s, rebuild %s\n", 174564078Seric s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 174664078Seric s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 174764078Seric s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 174864078Seric } 174953654Seric } 175058112Seric /* 175158112Seric ** SETTIMEOUTS -- parse and set timeout values 175258112Seric ** 175358112Seric ** Parameters: 175458112Seric ** val -- a pointer to the values. If NULL, do initial 175558112Seric ** settings. 175658112Seric ** 175758112Seric ** Returns: 175858112Seric ** none. 175958112Seric ** 176058112Seric ** Side Effects: 176158112Seric ** Initializes the TimeOuts structure 176258112Seric */ 176358112Seric 176464255Seric #define SECONDS 176558112Seric #define MINUTES * 60 176658112Seric #define HOUR * 3600 176758112Seric 176858112Seric settimeouts(val) 176958112Seric register char *val; 177058112Seric { 177158112Seric register char *p; 177258671Seric extern time_t convtime(); 177358112Seric 177458112Seric if (val == NULL) 177558112Seric { 177658112Seric TimeOuts.to_initial = (time_t) 5 MINUTES; 177758112Seric TimeOuts.to_helo = (time_t) 5 MINUTES; 177858112Seric TimeOuts.to_mail = (time_t) 10 MINUTES; 177958112Seric TimeOuts.to_rcpt = (time_t) 1 HOUR; 178058112Seric TimeOuts.to_datainit = (time_t) 5 MINUTES; 178158112Seric TimeOuts.to_datablock = (time_t) 1 HOUR; 178258112Seric TimeOuts.to_datafinal = (time_t) 1 HOUR; 178358112Seric TimeOuts.to_rset = (time_t) 5 MINUTES; 178458112Seric TimeOuts.to_quit = (time_t) 2 MINUTES; 178558112Seric TimeOuts.to_nextcommand = (time_t) 1 HOUR; 178658112Seric TimeOuts.to_miscshort = (time_t) 2 MINUTES; 178764255Seric TimeOuts.to_ident = (time_t) 30 SECONDS; 178858112Seric return; 178958112Seric } 179058112Seric 179158112Seric for (;; val = p) 179258112Seric { 179358112Seric while (isascii(*val) && isspace(*val)) 179458112Seric val++; 179558112Seric if (*val == '\0') 179658112Seric break; 179758112Seric for (p = val; *p != '\0' && *p != ','; p++) 179858112Seric continue; 179958112Seric if (*p != '\0') 180058112Seric *p++ = '\0'; 180158112Seric 180258112Seric if (isascii(*val) && isdigit(*val)) 180358112Seric { 180458112Seric /* old syntax -- set everything */ 180558796Seric TimeOuts.to_mail = convtime(val, 'm'); 180658112Seric TimeOuts.to_rcpt = TimeOuts.to_mail; 180758112Seric TimeOuts.to_datainit = TimeOuts.to_mail; 180858112Seric TimeOuts.to_datablock = TimeOuts.to_mail; 180958112Seric TimeOuts.to_datafinal = TimeOuts.to_mail; 181058112Seric TimeOuts.to_nextcommand = TimeOuts.to_mail; 181158112Seric continue; 181258112Seric } 181358112Seric else 181458112Seric { 181558112Seric register char *q = strchr(val, '='); 181658112Seric time_t to; 181758112Seric 181858112Seric if (q == NULL) 181958112Seric { 182058112Seric /* syntax error */ 182158112Seric continue; 182258112Seric } 182358112Seric *q++ = '\0'; 182458796Seric to = convtime(q, 'm'); 182558112Seric 182658112Seric if (strcasecmp(val, "initial") == 0) 182758112Seric TimeOuts.to_initial = to; 182858112Seric else if (strcasecmp(val, "mail") == 0) 182958112Seric TimeOuts.to_mail = to; 183058112Seric else if (strcasecmp(val, "rcpt") == 0) 183158112Seric TimeOuts.to_rcpt = to; 183258112Seric else if (strcasecmp(val, "datainit") == 0) 183358112Seric TimeOuts.to_datainit = to; 183458112Seric else if (strcasecmp(val, "datablock") == 0) 183558112Seric TimeOuts.to_datablock = to; 183658112Seric else if (strcasecmp(val, "datafinal") == 0) 183758112Seric TimeOuts.to_datafinal = to; 183858112Seric else if (strcasecmp(val, "command") == 0) 183958112Seric TimeOuts.to_nextcommand = to; 184058112Seric else if (strcasecmp(val, "rset") == 0) 184158112Seric TimeOuts.to_rset = to; 184258112Seric else if (strcasecmp(val, "helo") == 0) 184358112Seric TimeOuts.to_helo = to; 184458112Seric else if (strcasecmp(val, "quit") == 0) 184558112Seric TimeOuts.to_quit = to; 184658112Seric else if (strcasecmp(val, "misc") == 0) 184758112Seric TimeOuts.to_miscshort = to; 184864255Seric else if (strcasecmp(val, "ident") == 0) 184964255Seric TimeOuts.to_ident = to; 185058112Seric else 185158112Seric syserr("settimeouts: invalid timeout %s", val); 185258112Seric } 185358112Seric } 185458112Seric } 1855