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*67769Seric static char sccsid[] = "@(#)readcf.c 8.38 (Berkeley) 09/08/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; 80*67769Seric int mid; 813308Seric char buf[MAXLINE]; 823308Seric register char *p; 833308Seric extern char **copyplist(); 8452647Seric struct stat statb; 855909Seric char exbuf[MAXLINE]; 8665066Seric char pvpbuf[MAXLINE + MAXATOM]; 8710709Seric extern char *munchstring(); 8853654Seric extern void makemapentry(); 893308Seric 9052647Seric FileName = cfname; 9152647Seric LineNumber = 0; 9252647Seric 933308Seric cf = fopen(cfname, "r"); 943308Seric if (cf == NULL) 953308Seric { 9652647Seric syserr("cannot open"); 973308Seric exit(EX_OSFILE); 983308Seric } 993308Seric 10052647Seric if (fstat(fileno(cf), &statb) < 0) 10152647Seric { 10252647Seric syserr("cannot fstat"); 10352647Seric exit(EX_OSFILE); 10452647Seric } 10552647Seric 10652647Seric if (!S_ISREG(statb.st_mode)) 10752647Seric { 10852647Seric syserr("not a plain file"); 10952647Seric exit(EX_OSFILE); 11052647Seric } 11152647Seric 11252647Seric if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 11352647Seric { 11453037Seric if (OpMode == MD_DAEMON || OpMode == MD_FREEZE) 11553037Seric fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 11653037Seric FileName); 11753037Seric #ifdef LOG 11853037Seric if (LogLevel > 0) 11953037Seric syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", 12053037Seric FileName); 12153037Seric #endif 12252647Seric } 12352647Seric 12459254Seric #ifdef XLA 12559254Seric xla_zero(); 12659254Seric #endif 12759254Seric 12857135Seric while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 1293308Seric { 13057135Seric if (bp[0] == '#') 13157135Seric { 13257135Seric if (bp != buf) 13357135Seric free(bp); 13452637Seric continue; 13557135Seric } 13652637Seric 137*67769Seric /* do macro expansion mappings */ 13857135Seric for (p = bp; *p != '\0'; p++) 13916157Seric { 14057135Seric if (*p == '#' && p > bp && ConfigLevel >= 3) 14152647Seric { 14252647Seric /* this is an on-line comment */ 14352647Seric register char *e; 14452647Seric 14558050Seric switch (*--p & 0377) 14652647Seric { 14758050Seric case MACROEXPAND: 14852647Seric /* it's from $# -- let it go through */ 14952647Seric p++; 15052647Seric break; 15152647Seric 15252647Seric case '\\': 15352647Seric /* it's backslash escaped */ 15452647Seric (void) strcpy(p, p + 1); 15552647Seric break; 15652647Seric 15752647Seric default: 15852647Seric /* delete preceeding white space */ 15958050Seric while (isascii(*p) && isspace(*p) && p > bp) 16052647Seric p--; 16156795Seric if ((e = strchr(++p, '\n')) != NULL) 16252647Seric (void) strcpy(p, e); 16352647Seric else 16452647Seric p[0] = p[1] = '\0'; 16552647Seric break; 16652647Seric } 16752647Seric continue; 16852647Seric } 16952647Seric 170*67769Seric if (*p != '$' || p[1] == '\0') 17116157Seric continue; 17216157Seric 17316157Seric if (p[1] == '$') 17416157Seric { 17516157Seric /* actual dollar sign.... */ 17623111Seric (void) strcpy(p, p + 1); 17716157Seric continue; 17816157Seric } 17916157Seric 18016157Seric /* convert to macro expansion character */ 181*67769Seric *p++ = MACROEXPAND; 182*67769Seric 183*67769Seric /* convert macro name to code */ 184*67769Seric *p = macid(p, &ep); 185*67769Seric if (ep != p) 186*67769Seric strcpy(p + 1, ep); 18716157Seric } 18816157Seric 18916157Seric /* interpret this line */ 19064718Seric errno = 0; 19157135Seric switch (bp[0]) 1923308Seric { 1933308Seric case '\0': 1943308Seric case '#': /* comment */ 1953308Seric break; 1963308Seric 1973308Seric case 'R': /* rewriting rule */ 19857135Seric for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 1993308Seric continue; 2003308Seric 2013308Seric if (*p == '\0') 2025909Seric { 20365821Seric syserr("invalid rewrite line \"%s\" (tab expected)", bp); 2045909Seric break; 2055909Seric } 2065909Seric 2075909Seric /* allocate space for the rule header */ 2085909Seric if (rwp == NULL) 2095909Seric { 2105909Seric RewriteRules[ruleset] = rwp = 2115909Seric (struct rewrite *) xalloc(sizeof *rwp); 2125909Seric } 2133308Seric else 2143308Seric { 2155909Seric rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 2165909Seric rwp = rwp->r_next; 2175909Seric } 2185909Seric rwp->r_next = NULL; 2193308Seric 2205909Seric /* expand and save the LHS */ 2215909Seric *p = '\0'; 22257135Seric expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e); 22365066Seric rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, 22465066Seric sizeof pvpbuf, NULL); 22557589Seric nfuzzy = 0; 2265909Seric if (rwp->r_lhs != NULL) 22757589Seric { 22857589Seric register char **ap; 22957589Seric 2305909Seric rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 23157589Seric 23257589Seric /* count the number of fuzzy matches in LHS */ 23357589Seric for (ap = rwp->r_lhs; *ap != NULL; ap++) 23457589Seric { 23558148Seric char *botch; 23658148Seric 23758148Seric botch = NULL; 23858050Seric switch (**ap & 0377) 23957589Seric { 24057589Seric case MATCHZANY: 24157589Seric case MATCHANY: 24257589Seric case MATCHONE: 24357589Seric case MATCHCLASS: 24457589Seric case MATCHNCLASS: 24557589Seric nfuzzy++; 24658148Seric break; 24758148Seric 24858148Seric case MATCHREPL: 24958148Seric botch = "$0-$9"; 25058148Seric break; 25158148Seric 25258148Seric case CANONNET: 25358148Seric botch = "$#"; 25458148Seric break; 25558148Seric 25658148Seric case CANONUSER: 25758148Seric botch = "$:"; 25858148Seric break; 25958148Seric 26058148Seric case CALLSUBR: 26158148Seric botch = "$>"; 26258148Seric break; 26358148Seric 26458148Seric case CONDIF: 26558148Seric botch = "$?"; 26658148Seric break; 26758148Seric 26858148Seric case CONDELSE: 26958148Seric botch = "$|"; 27058148Seric break; 27158148Seric 27258148Seric case CONDFI: 27358148Seric botch = "$."; 27458148Seric break; 27558148Seric 27658148Seric case HOSTBEGIN: 27758148Seric botch = "$["; 27858148Seric break; 27958148Seric 28058148Seric case HOSTEND: 28158148Seric botch = "$]"; 28258148Seric break; 28358148Seric 28458148Seric case LOOKUPBEGIN: 28558148Seric botch = "$("; 28658148Seric break; 28758148Seric 28858148Seric case LOOKUPEND: 28958148Seric botch = "$)"; 29058148Seric break; 29157589Seric } 29258148Seric if (botch != NULL) 29358148Seric syserr("Inappropriate use of %s on LHS", 29458148Seric botch); 29557589Seric } 29657589Seric } 29756678Seric else 29856678Seric syserr("R line: null LHS"); 2995909Seric 3005909Seric /* expand and save the RHS */ 3015909Seric while (*++p == '\t') 3025909Seric continue; 3037231Seric q = p; 3047231Seric while (*p != '\0' && *p != '\t') 3057231Seric p++; 3067231Seric *p = '\0'; 30755012Seric expand(q, exbuf, &exbuf[sizeof exbuf], e); 30865066Seric rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, 30965066Seric sizeof pvpbuf, NULL); 3105909Seric if (rwp->r_rhs != NULL) 31157589Seric { 31257589Seric register char **ap; 31357589Seric 3145909Seric rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 31557589Seric 31657589Seric /* check no out-of-bounds replacements */ 31757589Seric nfuzzy += '0'; 31857589Seric for (ap = rwp->r_rhs; *ap != NULL; ap++) 31957589Seric { 32058148Seric char *botch; 32158148Seric 32258148Seric botch = NULL; 32358148Seric switch (**ap & 0377) 32457589Seric { 32558148Seric case MATCHREPL: 32658148Seric if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 32758148Seric { 32858148Seric syserr("replacement $%c out of bounds", 32958148Seric (*ap)[1]); 33058148Seric } 33158148Seric break; 33258148Seric 33358148Seric case MATCHZANY: 33458148Seric botch = "$*"; 33558148Seric break; 33658148Seric 33758148Seric case MATCHANY: 33858148Seric botch = "$+"; 33958148Seric break; 34058148Seric 34158148Seric case MATCHONE: 34258148Seric botch = "$-"; 34358148Seric break; 34458148Seric 34558148Seric case MATCHCLASS: 34658148Seric botch = "$="; 34758148Seric break; 34858148Seric 34958148Seric case MATCHNCLASS: 35058148Seric botch = "$~"; 35158148Seric break; 35257589Seric } 35358148Seric if (botch != NULL) 35458148Seric syserr("Inappropriate use of %s on RHS", 35558148Seric botch); 35657589Seric } 35757589Seric } 35856678Seric else 35956678Seric syserr("R line: null RHS"); 3603308Seric break; 3613308Seric 3624072Seric case 'S': /* select rewriting set */ 36364440Seric for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 36464440Seric continue; 36564440Seric if (!isascii(*p) || !isdigit(*p)) 36664440Seric { 36764440Seric syserr("invalid argument to S line: \"%.20s\"", 36864440Seric &bp[1]); 36964440Seric break; 37064440Seric } 37164440Seric ruleset = atoi(p); 3728056Seric if (ruleset >= MAXRWSETS || ruleset < 0) 3738056Seric { 3749381Seric syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 3758056Seric ruleset = 0; 3768056Seric } 3774072Seric rwp = NULL; 3784072Seric break; 3794072Seric 3803308Seric case 'D': /* macro definition */ 381*67769Seric mid = macid(&bp[1], &ep); 382*67769Seric p = munchstring(ep, NULL); 383*67769Seric define(mid, newstr(p), e); 3843308Seric break; 3853308Seric 3863387Seric case 'H': /* required header line */ 38757135Seric (void) chompheader(&bp[1], TRUE, e); 3883387Seric break; 3893387Seric 3904061Seric case 'C': /* word class */ 3914432Seric /* scan the list of words and set class for all */ 392*67769Seric mid = macid(&bp[1], &ep); 393*67769Seric expand(ep, exbuf, &exbuf[sizeof exbuf], e); 39464121Seric for (p = exbuf; *p != '\0'; ) 3954061Seric { 3964061Seric register char *wd; 3974061Seric char delim; 3984061Seric 39958050Seric while (*p != '\0' && isascii(*p) && isspace(*p)) 4004061Seric p++; 4014061Seric wd = p; 40258050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 4034061Seric p++; 4044061Seric delim = *p; 4054061Seric *p = '\0'; 4064061Seric if (wd[0] != '\0') 407*67769Seric setclass(mid, wd); 4084061Seric *p = delim; 4094061Seric } 4104061Seric break; 4114061Seric 41259272Seric case 'F': /* word class from file */ 413*67769Seric mid = macid(&bp[1], &ep); 414*67769Seric for (p = ep; isascii(*p) && isspace(*p); ) 41564133Seric p++; 41664133Seric if (p[0] == '-' && p[1] == 'o') 41764133Seric { 41864133Seric optional = TRUE; 41964133Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 42064133Seric p++; 42164133Seric while (isascii(*p) && isspace(*p)) 42267615Seric p++; 42364133Seric } 42464133Seric else 42564133Seric optional = FALSE; 42664133Seric file = p; 42764133Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 42864133Seric p++; 42959272Seric if (*p == '\0') 43059272Seric p = "%s"; 43159272Seric else 43259272Seric { 43359272Seric *p = '\0'; 43459272Seric while (isascii(*++p) && isspace(*p)) 43559272Seric continue; 43659272Seric } 43764133Seric fileclass(bp[1], file, p, safe, optional); 43859272Seric break; 43959272Seric 44059156Seric #ifdef XLA 44159156Seric case 'L': /* extended load average description */ 44259156Seric xla_init(&bp[1]); 44359156Seric break; 44459156Seric #endif 44559156Seric 4464096Seric case 'M': /* define mailer */ 44757135Seric makemailer(&bp[1]); 4484096Seric break; 4494096Seric 4508252Seric case 'O': /* set option */ 45158734Seric setoption(bp[1], &bp[2], safe, FALSE, e); 4528252Seric break; 4538252Seric 4548252Seric case 'P': /* set precedence */ 4558252Seric if (NumPriorities >= MAXPRIORITIES) 4568252Seric { 4578547Seric toomany('P', MAXPRIORITIES); 4588252Seric break; 4598252Seric } 46057135Seric for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 4618252Seric continue; 4628252Seric if (*p == '\0') 4638252Seric goto badline; 4648252Seric *p = '\0'; 46557135Seric Priorities[NumPriorities].pri_name = newstr(&bp[1]); 4668252Seric Priorities[NumPriorities].pri_val = atoi(++p); 4678252Seric NumPriorities++; 4688252Seric break; 4698252Seric 4708547Seric case 'T': /* trusted user(s) */ 47158161Seric /* this option is obsolete, but will be ignored */ 4728547Seric break; 4738547Seric 47452645Seric case 'V': /* configuration syntax version */ 47564440Seric for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 47664440Seric continue; 47764440Seric if (!isascii(*p) || !isdigit(*p)) 47864440Seric { 47964440Seric syserr("invalid argument to V line: \"%.20s\"", 48064440Seric &bp[1]); 48164440Seric break; 48264440Seric } 48364718Seric ConfigLevel = strtol(p, &ep, 10); 48464279Seric if (ConfigLevel >= 5) 48564279Seric { 48664279Seric /* level 5 configs have short name in $w */ 48764279Seric p = macvalue('w', e); 48864279Seric if (p != NULL && (p = strchr(p, '.')) != NULL) 48964279Seric *p = '\0'; 49064279Seric } 49164718Seric if (*ep++ == '/') 49264718Seric { 49364718Seric /* extract vendor code */ 49464718Seric for (p = ep; isascii(*p) && isalpha(*p); ) 49564718Seric p++; 49664718Seric *p = '\0'; 49764718Seric 49864718Seric if (!setvendor(ep)) 49964718Seric syserr("invalid V line vendor code: \"%s\"", 50064718Seric ep); 50164718Seric } 50252645Seric break; 50352645Seric 50453654Seric case 'K': 50557135Seric makemapentry(&bp[1]); 50653654Seric break; 50753654Seric 5083308Seric default: 5094061Seric badline: 51057135Seric syserr("unknown control line \"%s\"", bp); 5113308Seric } 51257135Seric if (bp != buf) 51357135Seric free(bp); 5143308Seric } 51552637Seric if (ferror(cf)) 51652637Seric { 51752647Seric syserr("I/O read error", cfname); 51852637Seric exit(EX_OSFILE); 51952637Seric } 52052637Seric fclose(cf); 5219381Seric FileName = NULL; 52256836Seric 52367730Seric /* initialize host maps from local service tables */ 52467730Seric inithostmaps(); 52567730Seric 52657076Seric if (stab("host", ST_MAP, ST_FIND) == NULL) 52757076Seric { 52857076Seric /* user didn't initialize: set up host map */ 52957076Seric strcpy(buf, "host host"); 53066334Seric #if NAMED_BIND 53157076Seric if (ConfigLevel >= 2) 53257076Seric strcat(buf, " -a."); 53364947Seric #endif 53457076Seric makemapentry(buf); 53557076Seric } 5364096Seric } 5374096Seric /* 5388547Seric ** TOOMANY -- signal too many of some option 5398547Seric ** 5408547Seric ** Parameters: 5418547Seric ** id -- the id of the error line 5428547Seric ** maxcnt -- the maximum possible values 5438547Seric ** 5448547Seric ** Returns: 5458547Seric ** none. 5468547Seric ** 5478547Seric ** Side Effects: 5488547Seric ** gives a syserr. 5498547Seric */ 5508547Seric 5518547Seric toomany(id, maxcnt) 5528547Seric char id; 5538547Seric int maxcnt; 5548547Seric { 5559381Seric syserr("too many %c lines, %d max", id, maxcnt); 5568547Seric } 5578547Seric /* 5584432Seric ** FILECLASS -- read members of a class from a file 5594432Seric ** 5604432Seric ** Parameters: 5614432Seric ** class -- class to define. 5624432Seric ** filename -- name of file to read. 5634432Seric ** fmt -- scanf string to use for match. 56464133Seric ** safe -- if set, this is a safe read. 56564133Seric ** optional -- if set, it is not an error for the file to 56664133Seric ** not exist. 5674432Seric ** 5684432Seric ** Returns: 5694432Seric ** none 5704432Seric ** 5714432Seric ** Side Effects: 5724432Seric ** 5734432Seric ** puts all lines in filename that match a scanf into 5744432Seric ** the named class. 5754432Seric */ 5764432Seric 57764133Seric fileclass(class, filename, fmt, safe, optional) 5784432Seric int class; 5794432Seric char *filename; 5804432Seric char *fmt; 58154973Seric bool safe; 58264133Seric bool optional; 5834432Seric { 58425808Seric FILE *f; 58554973Seric struct stat stbuf; 5864432Seric char buf[MAXLINE]; 5874432Seric 58866101Seric if (tTd(37, 2)) 58966101Seric printf("fileclass(%s, fmt=%s)\n", filename, fmt); 59066101Seric 59166031Seric if (filename[0] == '|') 59266031Seric { 59366031Seric syserr("fileclass: pipes (F%c%s) not supported due to security problems", 59466031Seric class, filename); 59566031Seric return; 59666031Seric } 59754973Seric if (stat(filename, &stbuf) < 0) 59854973Seric { 59966101Seric if (tTd(37, 2)) 60066101Seric printf(" cannot stat (%s)\n", errstring(errno)); 60164133Seric if (!optional) 60264133Seric syserr("fileclass: cannot stat %s", filename); 60354973Seric return; 60454973Seric } 60554973Seric if (!S_ISREG(stbuf.st_mode)) 60654973Seric { 60754973Seric syserr("fileclass: %s not a regular file", filename); 60854973Seric return; 60954973Seric } 61054973Seric if (!safe && access(filename, R_OK) < 0) 61154973Seric { 61254973Seric syserr("fileclass: access denied on %s", filename); 61354973Seric return; 61454973Seric } 61554973Seric f = fopen(filename, "r"); 6164432Seric if (f == NULL) 6174432Seric { 61854973Seric syserr("fileclass: cannot open %s", filename); 6194432Seric return; 6204432Seric } 6214432Seric 6224432Seric while (fgets(buf, sizeof buf, f) != NULL) 6234432Seric { 6244432Seric register STAB *s; 62525808Seric register char *p; 62625808Seric # ifdef SCANF 6274432Seric char wordbuf[MAXNAME+1]; 6284432Seric 6294432Seric if (sscanf(buf, fmt, wordbuf) != 1) 6304432Seric continue; 63125808Seric p = wordbuf; 63256795Seric # else /* SCANF */ 63325808Seric p = buf; 63456795Seric # endif /* SCANF */ 63525808Seric 63625808Seric /* 63725808Seric ** Break up the match into words. 63825808Seric */ 63925808Seric 64025808Seric while (*p != '\0') 64125808Seric { 64225808Seric register char *q; 64325808Seric 64425808Seric /* strip leading spaces */ 64558050Seric while (isascii(*p) && isspace(*p)) 64625808Seric p++; 64725808Seric if (*p == '\0') 64825808Seric break; 64925808Seric 65025808Seric /* find the end of the word */ 65125808Seric q = p; 65258050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 65325808Seric p++; 65425808Seric if (*p != '\0') 65525808Seric *p++ = '\0'; 65625808Seric 65725808Seric /* enter the word in the symbol table */ 65866101Seric setclass(class, q); 65925808Seric } 6604432Seric } 6614432Seric 66254973Seric (void) fclose(f); 6634432Seric } 6644432Seric /* 6654096Seric ** MAKEMAILER -- define a new mailer. 6664096Seric ** 6674096Seric ** Parameters: 66810327Seric ** line -- description of mailer. This is in labeled 66910327Seric ** fields. The fields are: 67010327Seric ** P -- the path to the mailer 67110327Seric ** F -- the flags associated with the mailer 67210327Seric ** A -- the argv for this mailer 67310327Seric ** S -- the sender rewriting set 67410327Seric ** R -- the recipient rewriting set 67510327Seric ** E -- the eol string 67610327Seric ** The first word is the canonical name of the mailer. 6774096Seric ** 6784096Seric ** Returns: 6794096Seric ** none. 6804096Seric ** 6814096Seric ** Side Effects: 6824096Seric ** enters the mailer into the mailer table. 6834096Seric */ 6843308Seric 68521066Seric makemailer(line) 6864096Seric char *line; 6874096Seric { 6884096Seric register char *p; 6898067Seric register struct mailer *m; 6908067Seric register STAB *s; 6918067Seric int i; 69210327Seric char fcode; 69358020Seric auto char *endp; 6944096Seric extern int NextMailer; 69510327Seric extern char **makeargv(); 69610327Seric extern char *munchstring(); 69710701Seric extern long atol(); 6984096Seric 69910327Seric /* allocate a mailer and set up defaults */ 70010327Seric m = (struct mailer *) xalloc(sizeof *m); 70110327Seric bzero((char *) m, sizeof *m); 70210327Seric m->m_eol = "\n"; 70367604Seric m->m_uid = m->m_gid = 0; 70410327Seric 70510327Seric /* collect the mailer name */ 70658050Seric for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 70710327Seric continue; 70810327Seric if (*p != '\0') 70910327Seric *p++ = '\0'; 71010327Seric m->m_name = newstr(line); 71110327Seric 71210327Seric /* now scan through and assign info from the fields */ 71310327Seric while (*p != '\0') 71410327Seric { 71558333Seric auto char *delimptr; 71658333Seric 71758050Seric while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 71810327Seric p++; 71910327Seric 72010327Seric /* p now points to field code */ 72110327Seric fcode = *p; 72210327Seric while (*p != '\0' && *p != '=' && *p != ',') 72310327Seric p++; 72410327Seric if (*p++ != '=') 72510327Seric { 72652637Seric syserr("mailer %s: `=' expected", m->m_name); 72710327Seric return; 72810327Seric } 72958050Seric while (isascii(*p) && isspace(*p)) 73010327Seric p++; 73110327Seric 73210327Seric /* p now points to the field body */ 73358333Seric p = munchstring(p, &delimptr); 73410327Seric 73510327Seric /* install the field into the mailer struct */ 73610327Seric switch (fcode) 73710327Seric { 73810327Seric case 'P': /* pathname */ 73910327Seric m->m_mailer = newstr(p); 74010327Seric break; 74110327Seric 74210327Seric case 'F': /* flags */ 74310687Seric for (; *p != '\0'; p++) 74458050Seric if (!(isascii(*p) && isspace(*p))) 74552637Seric setbitn(*p, m->m_flags); 74610327Seric break; 74710327Seric 74810327Seric case 'S': /* sender rewriting ruleset */ 74910327Seric case 'R': /* recipient rewriting ruleset */ 75058020Seric i = strtol(p, &endp, 10); 75110327Seric if (i < 0 || i >= MAXRWSETS) 75210327Seric { 75310327Seric syserr("invalid rewrite set, %d max", MAXRWSETS); 75410327Seric return; 75510327Seric } 75610327Seric if (fcode == 'S') 75758020Seric m->m_sh_rwset = m->m_se_rwset = i; 75810327Seric else 75958020Seric m->m_rh_rwset = m->m_re_rwset = i; 76058020Seric 76158020Seric p = endp; 76259985Seric if (*p++ == '/') 76358020Seric { 76458020Seric i = strtol(p, NULL, 10); 76558020Seric if (i < 0 || i >= MAXRWSETS) 76658020Seric { 76758020Seric syserr("invalid rewrite set, %d max", 76858020Seric MAXRWSETS); 76958020Seric return; 77058020Seric } 77158020Seric if (fcode == 'S') 77258020Seric m->m_sh_rwset = i; 77358020Seric else 77458020Seric m->m_rh_rwset = i; 77558020Seric } 77610327Seric break; 77710327Seric 77810327Seric case 'E': /* end of line string */ 77910327Seric m->m_eol = newstr(p); 78010327Seric break; 78110327Seric 78210327Seric case 'A': /* argument vector */ 78310327Seric m->m_argv = makeargv(p); 78410327Seric break; 78510701Seric 78610701Seric case 'M': /* maximum message size */ 78710701Seric m->m_maxsize = atol(p); 78810701Seric break; 78952106Seric 79052106Seric case 'L': /* maximum line length */ 79152106Seric m->m_linelimit = atoi(p); 79252106Seric break; 79358935Seric 79458935Seric case 'D': /* working directory */ 79558935Seric m->m_execdir = newstr(p); 79658935Seric break; 79767604Seric 79867604Seric case 'U': /* user id */ 79967604Seric if (isascii(*p) && !isdigit(*p)) 80067604Seric { 80167604Seric char *q = p; 80267604Seric struct passwd *pw; 80367604Seric 80467604Seric while (isascii(*p) && isalnum(*p)) 80567604Seric p++; 80667604Seric while (isascii(*p) && isspace(*p)) 80767604Seric *p++ = '\0'; 80867604Seric if (*p != '\0') 80967604Seric *p++ = '\0'; 81067604Seric pw = getpwnam(q); 81167604Seric if (pw == NULL) 81267604Seric syserr("readcf: mailer U= flag: unknown user %s", q); 81367604Seric else 81467604Seric { 81567604Seric m->m_uid = pw->pw_uid; 81667604Seric m->m_gid = pw->pw_gid; 81767604Seric } 81867604Seric } 81967604Seric else 82067604Seric { 82167604Seric auto char *q; 82267604Seric 82367604Seric m->m_uid = strtol(p, &q, 0); 82467604Seric p = q; 82567604Seric } 82667604Seric while (isascii(*p) && isspace(*p)) 82767604Seric p++; 82867604Seric if (*p == '\0') 82967604Seric break; 83067604Seric if (isascii(*p) && !isdigit(*p)) 83167604Seric { 83267604Seric char *q = p; 83367604Seric struct group *gr; 83467604Seric 83567604Seric while (isascii(*p) && isalnum(*p)) 83667604Seric p++; 83767604Seric *p++ = '\0'; 83867604Seric gr = getgrnam(q); 83967604Seric if (gr == NULL) 84067604Seric syserr("readcf: mailer U= flag: unknown group %s", q); 84167604Seric else 84267604Seric m->m_gid = gr->gr_gid; 84367604Seric } 84467604Seric else 84567604Seric { 84667604Seric m->m_gid = strtol(p, NULL, 0); 84767604Seric } 84867604Seric break; 84910327Seric } 85010327Seric 85158333Seric p = delimptr; 85210327Seric } 85310327Seric 85452106Seric /* do some heuristic cleanup for back compatibility */ 85552106Seric if (bitnset(M_LIMITS, m->m_flags)) 85652106Seric { 85752106Seric if (m->m_linelimit == 0) 85852106Seric m->m_linelimit = SMTPLINELIM; 85955418Seric if (ConfigLevel < 2) 86052106Seric setbitn(M_7BITS, m->m_flags); 86152106Seric } 86252106Seric 86358321Seric /* do some rationality checking */ 86458321Seric if (m->m_argv == NULL) 86558321Seric { 86658321Seric syserr("M%s: A= argument required", m->m_name); 86758321Seric return; 86858321Seric } 86958321Seric if (m->m_mailer == NULL) 87058321Seric { 87158321Seric syserr("M%s: P= argument required", m->m_name); 87258321Seric return; 87358321Seric } 87458321Seric 8754096Seric if (NextMailer >= MAXMAILERS) 8764096Seric { 8779381Seric syserr("too many mailers defined (%d max)", MAXMAILERS); 8784096Seric return; 8794096Seric } 88057402Seric 88110327Seric s = stab(m->m_name, ST_MAILER, ST_ENTER); 88257402Seric if (s->s_mailer != NULL) 88357402Seric { 88457402Seric i = s->s_mailer->m_mno; 88557402Seric free(s->s_mailer); 88657402Seric } 88757402Seric else 88857402Seric { 88957402Seric i = NextMailer++; 89057402Seric } 89157402Seric Mailer[i] = s->s_mailer = m; 89257454Seric m->m_mno = i; 89310327Seric } 89410327Seric /* 89510327Seric ** MUNCHSTRING -- translate a string into internal form. 89610327Seric ** 89710327Seric ** Parameters: 89810327Seric ** p -- the string to munch. 89958333Seric ** delimptr -- if non-NULL, set to the pointer of the 90058333Seric ** field delimiter character. 90110327Seric ** 90210327Seric ** Returns: 90310327Seric ** the munched string. 90410327Seric */ 9054096Seric 90610327Seric char * 90758333Seric munchstring(p, delimptr) 90810327Seric register char *p; 90958333Seric char **delimptr; 91010327Seric { 91110327Seric register char *q; 91210327Seric bool backslash = FALSE; 91310327Seric bool quotemode = FALSE; 91410327Seric static char buf[MAXLINE]; 9154096Seric 91610327Seric for (q = buf; *p != '\0'; p++) 9174096Seric { 91810327Seric if (backslash) 91910327Seric { 92010327Seric /* everything is roughly literal */ 92110357Seric backslash = FALSE; 92210327Seric switch (*p) 92310327Seric { 92410327Seric case 'r': /* carriage return */ 92510327Seric *q++ = '\r'; 92610327Seric continue; 92710327Seric 92810327Seric case 'n': /* newline */ 92910327Seric *q++ = '\n'; 93010327Seric continue; 93110327Seric 93210327Seric case 'f': /* form feed */ 93310327Seric *q++ = '\f'; 93410327Seric continue; 93510327Seric 93610327Seric case 'b': /* backspace */ 93710327Seric *q++ = '\b'; 93810327Seric continue; 93910327Seric } 94010327Seric *q++ = *p; 94110327Seric } 94210327Seric else 94310327Seric { 94410327Seric if (*p == '\\') 94510327Seric backslash = TRUE; 94610327Seric else if (*p == '"') 94710327Seric quotemode = !quotemode; 94810327Seric else if (quotemode || *p != ',') 94910327Seric *q++ = *p; 95010327Seric else 95110327Seric break; 95210327Seric } 9534096Seric } 9544096Seric 95558333Seric if (delimptr != NULL) 95658333Seric *delimptr = p; 95710327Seric *q++ = '\0'; 95810327Seric return (buf); 95910327Seric } 96010327Seric /* 96110327Seric ** MAKEARGV -- break up a string into words 96210327Seric ** 96310327Seric ** Parameters: 96410327Seric ** p -- the string to break up. 96510327Seric ** 96610327Seric ** Returns: 96710327Seric ** a char **argv (dynamically allocated) 96810327Seric ** 96910327Seric ** Side Effects: 97010327Seric ** munges p. 97110327Seric */ 9724096Seric 97310327Seric char ** 97410327Seric makeargv(p) 97510327Seric register char *p; 97610327Seric { 97710327Seric char *q; 97810327Seric int i; 97910327Seric char **avp; 98010327Seric char *argv[MAXPV + 1]; 98110327Seric 98210327Seric /* take apart the words */ 98310327Seric i = 0; 98410327Seric while (*p != '\0' && i < MAXPV) 9854096Seric { 98610327Seric q = p; 98758050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 98810327Seric p++; 98958050Seric while (isascii(*p) && isspace(*p)) 99010327Seric *p++ = '\0'; 99110327Seric argv[i++] = newstr(q); 9924096Seric } 99310327Seric argv[i++] = NULL; 9944096Seric 99510327Seric /* now make a copy of the argv */ 99610327Seric avp = (char **) xalloc(sizeof *avp * i); 99716893Seric bcopy((char *) argv, (char *) avp, sizeof *avp * i); 99810327Seric 99910327Seric return (avp); 10003308Seric } 10013308Seric /* 10023308Seric ** PRINTRULES -- print rewrite rules (for debugging) 10033308Seric ** 10043308Seric ** Parameters: 10053308Seric ** none. 10063308Seric ** 10073308Seric ** Returns: 10083308Seric ** none. 10093308Seric ** 10103308Seric ** Side Effects: 10113308Seric ** prints rewrite rules. 10123308Seric */ 10133308Seric 10143308Seric printrules() 10153308Seric { 10163308Seric register struct rewrite *rwp; 10174072Seric register int ruleset; 10183308Seric 10194072Seric for (ruleset = 0; ruleset < 10; ruleset++) 10203308Seric { 10214072Seric if (RewriteRules[ruleset] == NULL) 10224072Seric continue; 10238067Seric printf("\n----Rule Set %d:", ruleset); 10243308Seric 10254072Seric for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 10263308Seric { 10278067Seric printf("\nLHS:"); 10288067Seric printav(rwp->r_lhs); 10298067Seric printf("RHS:"); 10308067Seric printav(rwp->r_rhs); 10313308Seric } 10323308Seric } 10333308Seric } 10344319Seric 10354096Seric /* 10368256Seric ** SETOPTION -- set global processing option 10378256Seric ** 10388256Seric ** Parameters: 10398256Seric ** opt -- option name. 10408256Seric ** val -- option value (as a text string). 104121755Seric ** safe -- set if this came from a configuration file. 104221755Seric ** Some options (if set from the command line) will 104321755Seric ** reset the user id to avoid security problems. 10448269Seric ** sticky -- if set, don't let other setoptions override 10458269Seric ** this value. 104658734Seric ** e -- the main envelope. 10478256Seric ** 10488256Seric ** Returns: 10498256Seric ** none. 10508256Seric ** 10518256Seric ** Side Effects: 10528256Seric ** Sets options as implied by the arguments. 10538256Seric */ 10548256Seric 105510687Seric static BITMAP StickyOpt; /* set if option is stuck */ 10568269Seric 105757207Seric 105866334Seric #if NAMED_BIND 105957207Seric 106057207Seric struct resolverflags 106157207Seric { 106257207Seric char *rf_name; /* name of the flag */ 106357207Seric long rf_bits; /* bits to set/clear */ 106457207Seric } ResolverFlags[] = 106557207Seric { 106657207Seric "debug", RES_DEBUG, 106757207Seric "aaonly", RES_AAONLY, 106857207Seric "usevc", RES_USEVC, 106957207Seric "primary", RES_PRIMARY, 107057207Seric "igntc", RES_IGNTC, 107157207Seric "recurse", RES_RECURSE, 107257207Seric "defnames", RES_DEFNAMES, 107357207Seric "stayopen", RES_STAYOPEN, 107457207Seric "dnsrch", RES_DNSRCH, 107565583Seric "true", 0, /* to avoid error on old syntax */ 107657207Seric NULL, 0 107757207Seric }; 107857207Seric 107957207Seric #endif 108057207Seric 108167614Seric struct optioninfo 108267614Seric { 108367614Seric char *o_name; /* long name of option */ 108467614Seric char o_code; /* short name of option */ 108567614Seric bool o_safe; /* safe for random people to use */ 108667614Seric } OptionTab[] = 108767614Seric { 108867707Seric "SevenBitInput", '7', TRUE, 108967707Seric "EightBitMode", '8', TRUE, 109067707Seric "AliasFile", 'A', FALSE, 109167707Seric "AliasWait", 'a', FALSE, 109267707Seric "BlankSub", 'B', FALSE, 109367707Seric "MinFreeBlocks", 'b', TRUE, 109467707Seric "CheckpointInterval", 'C', TRUE, 109567707Seric "HoldExpensive", 'c', FALSE, 109667707Seric "AutoRebuildAliases", 'D', FALSE, 109767707Seric "DeliveryMode", 'd', TRUE, 109867707Seric "ErrorHeader", 'E', FALSE, 109967707Seric "ErrorMode", 'e', TRUE, 110067707Seric "TempFileMode", 'F', FALSE, 110167707Seric "SaveFromLine", 'f', FALSE, 110267707Seric "MatchGECOS", 'G', FALSE, 110367707Seric "HelpFile", 'H', FALSE, 110467707Seric "MaxHopCount", 'h', FALSE, 110567707Seric "NameServerOptions", 'I', FALSE, 110667707Seric "IgnoreDots", 'i', TRUE, 110767707Seric "ForwardPath", 'J', FALSE, 110867707Seric "SendMimeErrors", 'j', TRUE, 110967707Seric "ConnectionCacheSize", 'k', FALSE, 111067707Seric "ConnectionCacheTimeout", 'K', FALSE, 111167707Seric "UseErrorsTo", 'l', FALSE, 111267707Seric "LogLevel", 'L', FALSE, 111367707Seric "MeToo", 'm', TRUE, 111467707Seric "CheckAliases", 'n', FALSE, 111567707Seric "OldStyleHeaders", 'o', TRUE, 111667707Seric "DaemonPortOptions", 'O', FALSE, 111767707Seric "PrivacyOptions", 'p', TRUE, 111867707Seric "PostmasterCopy", 'P', FALSE, 111967707Seric "QueueFactor", 'q', FALSE, 112067707Seric "QueueDirectory", 'Q', FALSE, 112167707Seric "DontPruneRoutes", 'R', FALSE, 112267711Seric "Timeouts", 'r', TRUE, 112367707Seric "StatusFile", 'S', FALSE, 112467707Seric "SuperSafe", 's', TRUE, 112567707Seric "QueueTimeout", 'T', FALSE, 112667707Seric "TimeZoneSpec", 't', FALSE, 112767707Seric "UserDatabaseSpec", 'U', FALSE, 112867707Seric "DefaultUser", 'u', FALSE, 112967707Seric "FallbackMXhost", 'V', FALSE, 113067707Seric "Verbose", 'v', TRUE, 113167707Seric "TryNullMXList", 'w', TRUE, 113267707Seric "QueueLA", 'x', FALSE, 113367707Seric "RefuseLA", 'X', FALSE, 113467707Seric "RecipientFactor", 'y', FALSE, 113567707Seric "ForkQueueRuns", 'Y', FALSE, 113667707Seric "ClassFactor", 'z', FALSE, 113767707Seric "TimeFactor", 'Z', FALSE, 113867707Seric #define O_BSP 0x80 113967707Seric "BrokenSmtpPeers", O_BSP, TRUE, 114067707Seric #define O_SQBH 0x81 114167707Seric "SortQueueByHost", O_SQBH, TRUE, 114267707Seric #define O_DNICE 0x82 114367707Seric "DeliveryNiceness", O_DNICE, TRUE, 114467707Seric #define O_MQA 0x83 114567707Seric "MinQueueAge", O_MQA, TRUE, 114667707Seric #define O_MHSA 0x84 114767707Seric "MaxHostStatAge", O_MHSA, TRUE, 114867707Seric 114967707Seric NULL, '\0', FALSE, 115067614Seric }; 115167614Seric 115267614Seric 115367614Seric 115458734Seric setoption(opt, val, safe, sticky, e) 115567614Seric u_char opt; 11568256Seric char *val; 115721755Seric bool safe; 11588269Seric bool sticky; 115958734Seric register ENVELOPE *e; 11608256Seric { 116157207Seric register char *p; 116267614Seric register struct optioninfo *o; 11638265Seric extern bool atobool(); 116412633Seric extern time_t convtime(); 116514879Seric extern int QueueLA; 116614879Seric extern int RefuseLA; 116764718Seric extern bool Warn_Q_option; 11688256Seric 116967736Seric errno = 0; 117067614Seric if (opt == ' ') 117167614Seric { 117267614Seric /* full word options */ 117367736Seric struct optioninfo *sel; 117467614Seric 117567614Seric p = strchr(val, '='); 117667614Seric if (p == NULL) 117767614Seric p = &val[strlen(val)]; 117867614Seric while (*--p == ' ') 117967614Seric continue; 118067614Seric while (*++p == ' ') 118167614Seric *p = '\0'; 118267731Seric if (p == val) 118367731Seric { 118467731Seric syserr("readcf: null option name"); 118567731Seric return; 118667731Seric } 118767614Seric if (*p == '=') 118867614Seric *p++ = '\0'; 118967614Seric while (*p == ' ') 119067614Seric p++; 119167736Seric sel = NULL; 119267614Seric for (o = OptionTab; o->o_name != NULL; o++) 119367614Seric { 119467736Seric if (strncasecmp(o->o_name, val, strlen(val)) != 0) 119567736Seric continue; 119667736Seric if (strlen(o->o_name) == strlen(val)) 119767736Seric { 119867736Seric /* completely specified -- this must be it */ 119967736Seric sel = NULL; 120067614Seric break; 120167736Seric } 120267736Seric if (sel != NULL) 120367736Seric break; 120467736Seric sel = o; 120567614Seric } 120667736Seric if (sel != NULL && o->o_name == NULL) 120767736Seric o = sel; 120867736Seric else if (o->o_name == NULL) 120967614Seric syserr("readcf: unknown option name %s", val); 121067736Seric else if (sel != NULL) 121167736Seric { 121267736Seric syserr("readcf: ambiguous option name %s (matches %s and %s)", 121367736Seric val, sel->o_name, o->o_name); 121467736Seric return; 121567736Seric } 121667736Seric if (strlen(val) != strlen(o->o_name)) 121767736Seric { 121867736Seric bool oldVerbose = Verbose; 121967736Seric 122067736Seric Verbose = TRUE; 122167736Seric message("Option %s used as abbreviation for %s", 122267736Seric val, o->o_name); 122367736Seric Verbose = oldVerbose; 122467736Seric } 122567614Seric opt = o->o_code; 122667614Seric val = p; 122767614Seric } 122867614Seric else 122967614Seric { 123067614Seric for (o = OptionTab; o->o_name != NULL; o++) 123167614Seric { 123267614Seric if (o->o_code == opt) 123367614Seric break; 123467614Seric } 123567614Seric } 123667614Seric 12378256Seric if (tTd(37, 1)) 123867731Seric { 123967731Seric printf(isascii(opt) && isprint(opt) ? 124067731Seric "setoption %s (%c)=%s" : "setoption %s (0x%x)=%s", 124167614Seric o->o_name == NULL ? "<unknown>" : o->o_name, 124267614Seric opt, val); 124367731Seric } 12448256Seric 12458256Seric /* 12468269Seric ** See if this option is preset for us. 12478256Seric */ 12488256Seric 124959731Seric if (!sticky && bitnset(opt, StickyOpt)) 12508269Seric { 12519341Seric if (tTd(37, 1)) 12529341Seric printf(" (ignored)\n"); 12538269Seric return; 12548269Seric } 12558269Seric 125621755Seric /* 125721755Seric ** Check to see if this option can be specified by this user. 125821755Seric */ 125921755Seric 126063787Seric if (!safe && RealUid == 0) 126121755Seric safe = TRUE; 126267614Seric if (!safe && !o->o_safe) 126321755Seric { 126439111Srick if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 126521755Seric { 126636582Sbostic if (tTd(37, 1)) 126736582Sbostic printf(" (unsafe)"); 126863787Seric if (RealUid != geteuid()) 126936582Sbostic { 127051210Seric if (tTd(37, 1)) 127151210Seric printf("(Resetting uid)"); 127263787Seric (void) setgid(RealGid); 127363787Seric (void) setuid(RealUid); 127436582Sbostic } 127521755Seric } 127621755Seric } 127751210Seric if (tTd(37, 1)) 127817985Seric printf("\n"); 12798269Seric 128067614Seric switch (opt & 0xff) 12818256Seric { 128259709Seric case '7': /* force seven-bit input */ 128367546Seric SevenBitInput = atobool(val); 128452106Seric break; 128552106Seric 128667546Seric case '8': /* handling of 8-bit input */ 128767546Seric switch (*val) 128867546Seric { 128967547Seric case 'r': /* reject 8-bit, don't convert MIME */ 129067546Seric MimeMode = 0; 129167546Seric break; 129267546Seric 129367547Seric case 'm': /* convert 8-bit, convert MIME */ 129467546Seric MimeMode = MM_CVTMIME|MM_MIME8BIT; 129567546Seric break; 129667546Seric 129767547Seric case 'j': /* "just send 8" */ 129867546Seric MimeMode = MM_PASS8BIT; 129967546Seric break; 130067546Seric 130167546Seric case 'p': /* pass 8 bit, convert MIME */ 130267546Seric MimeMode = MM_PASS8BIT|MM_CVTMIME; 130367546Seric break; 130467546Seric 130567546Seric case 's': /* strict adherence */ 130667546Seric MimeMode = MM_CVTMIME; 130767546Seric break; 130867546Seric 130967547Seric case 'a': /* encode 8 bit if available */ 131067546Seric MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 131167546Seric break; 131267546Seric 131367547Seric case 'c': /* convert 8 bit to MIME, never 7 bit */ 131467547Seric MimeMode = MM_MIME8BIT; 131567547Seric break; 131667547Seric 131767546Seric default: 131867546Seric syserr("Unknown 8-bit mode %c", *val); 131967546Seric exit(EX_USAGE); 132067546Seric } 132167546Seric break; 132267546Seric 13238256Seric case 'A': /* set default alias file */ 13249381Seric if (val[0] == '\0') 132559672Seric setalias("aliases"); 13269381Seric else 132759672Seric setalias(val); 13288256Seric break; 13298256Seric 133017474Seric case 'a': /* look N minutes for "@:@" in alias file */ 133117474Seric if (val[0] == '\0') 133264796Seric SafeAlias = 5 * 60; /* five minutes */ 133317474Seric else 133464796Seric SafeAlias = convtime(val, 'm'); 133517474Seric break; 133617474Seric 133716843Seric case 'B': /* substitution for blank character */ 133816843Seric SpaceSub = val[0]; 133916843Seric if (SpaceSub == '\0') 134016843Seric SpaceSub = ' '; 134116843Seric break; 134216843Seric 134359283Seric case 'b': /* min blocks free on queue fs/max msg size */ 134459283Seric p = strchr(val, '/'); 134559283Seric if (p != NULL) 134659283Seric { 134759283Seric *p++ = '\0'; 134859283Seric MaxMessageSize = atol(p); 134959283Seric } 135058082Seric MinBlocksFree = atol(val); 135158082Seric break; 135258082Seric 13539284Seric case 'c': /* don't connect to "expensive" mailers */ 13549381Seric NoConnect = atobool(val); 13559284Seric break; 13569284Seric 135751305Seric case 'C': /* checkpoint every N addresses */ 135851305Seric CheckpointInterval = atoi(val); 135924944Seric break; 136024944Seric 13619284Seric case 'd': /* delivery mode */ 13629284Seric switch (*val) 13638269Seric { 13649284Seric case '\0': 136558734Seric e->e_sendmode = SM_DELIVER; 13668269Seric break; 13678269Seric 136810755Seric case SM_QUEUE: /* queue only */ 136910755Seric #ifndef QUEUE 137010755Seric syserr("need QUEUE to set -odqueue"); 137156795Seric #endif /* QUEUE */ 137210755Seric /* fall through..... */ 137310755Seric 13749284Seric case SM_DELIVER: /* do everything */ 13759284Seric case SM_FORK: /* fork after verification */ 137658734Seric e->e_sendmode = *val; 13778269Seric break; 13788269Seric 13798269Seric default: 13809284Seric syserr("Unknown delivery mode %c", *val); 13818269Seric exit(EX_USAGE); 13828269Seric } 13838269Seric break; 13848269Seric 13859146Seric case 'D': /* rebuild alias database as needed */ 13869381Seric AutoRebuild = atobool(val); 13879146Seric break; 13889146Seric 138955372Seric case 'E': /* error message header/header file */ 139055379Seric if (*val != '\0') 139155379Seric ErrMsgFile = newstr(val); 139255372Seric break; 139355372Seric 13948269Seric case 'e': /* set error processing mode */ 13958269Seric switch (*val) 13968269Seric { 13979381Seric case EM_QUIET: /* be silent about it */ 13989381Seric case EM_MAIL: /* mail back */ 13999381Seric case EM_BERKNET: /* do berknet error processing */ 14009381Seric case EM_WRITE: /* write back (or mail) */ 14019381Seric case EM_PRINT: /* print errors normally (default) */ 140258734Seric e->e_errormode = *val; 14038269Seric break; 14048269Seric } 14058269Seric break; 14068269Seric 14079049Seric case 'F': /* file mode */ 140817975Seric FileMode = atooct(val) & 0777; 14099049Seric break; 14109049Seric 14118269Seric case 'f': /* save Unix-style From lines on front */ 14129381Seric SaveFrom = atobool(val); 14138269Seric break; 14148269Seric 141553735Seric case 'G': /* match recipients against GECOS field */ 141653735Seric MatchGecos = atobool(val); 141753735Seric break; 141853735Seric 14198256Seric case 'g': /* default gid */ 142064133Seric if (isascii(*val) && isdigit(*val)) 142164133Seric DefGid = atoi(val); 142264133Seric else 142364133Seric { 142464133Seric register struct group *gr; 142564133Seric 142664133Seric DefGid = -1; 142764133Seric gr = getgrnam(val); 142864133Seric if (gr == NULL) 142964133Seric syserr("readcf: option g: unknown group %s", val); 143064133Seric else 143164133Seric DefGid = gr->gr_gid; 143264133Seric } 14338256Seric break; 14348256Seric 14358256Seric case 'H': /* help file */ 14369381Seric if (val[0] == '\0') 14378269Seric HelpFile = "sendmail.hf"; 14389381Seric else 14399381Seric HelpFile = newstr(val); 14408256Seric break; 14418256Seric 144251305Seric case 'h': /* maximum hop count */ 144351305Seric MaxHopCount = atoi(val); 144451305Seric break; 144551305Seric 144635651Seric case 'I': /* use internet domain name server */ 144766334Seric #if NAMED_BIND 144857207Seric UseNameServer = TRUE; 144957207Seric for (p = val; *p != 0; ) 145057207Seric { 145157207Seric bool clearmode; 145257207Seric char *q; 145357207Seric struct resolverflags *rfp; 145457207Seric 145557207Seric while (*p == ' ') 145657207Seric p++; 145757207Seric if (*p == '\0') 145857207Seric break; 145957207Seric clearmode = FALSE; 146057207Seric if (*p == '-') 146157207Seric clearmode = TRUE; 146257207Seric else if (*p != '+') 146357207Seric p--; 146457207Seric p++; 146557207Seric q = p; 146658050Seric while (*p != '\0' && !(isascii(*p) && isspace(*p))) 146757207Seric p++; 146857207Seric if (*p != '\0') 146957207Seric *p++ = '\0'; 147057207Seric for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 147157207Seric { 147257207Seric if (strcasecmp(q, rfp->rf_name) == 0) 147357207Seric break; 147457207Seric } 147564923Seric if (rfp->rf_name == NULL) 147664923Seric syserr("readcf: I option value %s unrecognized", q); 147764923Seric else if (clearmode) 147857207Seric _res.options &= ~rfp->rf_bits; 147957207Seric else 148057207Seric _res.options |= rfp->rf_bits; 148157207Seric } 148257207Seric if (tTd(8, 2)) 148357207Seric printf("_res.options = %x\n", _res.options); 148457207Seric #else 148557207Seric usrerr("name server (I option) specified but BIND not compiled in"); 148657207Seric #endif 148735651Seric break; 148835651Seric 14898269Seric case 'i': /* ignore dot lines in message */ 14909381Seric IgnrDot = atobool(val); 14918269Seric break; 14928269Seric 149359730Seric case 'j': /* send errors in MIME (RFC 1341) format */ 149459730Seric SendMIMEErrors = atobool(val); 149559730Seric break; 149659730Seric 149757136Seric case 'J': /* .forward search path */ 149857136Seric ForwardPath = newstr(val); 149957136Seric break; 150057136Seric 150154967Seric case 'k': /* connection cache size */ 150254967Seric MaxMciCache = atoi(val); 150356215Seric if (MaxMciCache < 0) 150456215Seric MaxMciCache = 0; 150554967Seric break; 150654967Seric 150754967Seric case 'K': /* connection cache timeout */ 150858796Seric MciCacheTimeout = convtime(val, 'm'); 150954967Seric break; 151054967Seric 151161104Seric case 'l': /* use Errors-To: header */ 151261104Seric UseErrorsTo = atobool(val); 151361104Seric break; 151461104Seric 15158256Seric case 'L': /* log level */ 151664140Seric if (safe || LogLevel < atoi(val)) 151764140Seric LogLevel = atoi(val); 15188256Seric break; 15198256Seric 15208269Seric case 'M': /* define macro */ 15219381Seric define(val[0], newstr(&val[1]), CurEnv); 152216878Seric sticky = FALSE; 15238269Seric break; 15248269Seric 15258269Seric case 'm': /* send to me too */ 15269381Seric MeToo = atobool(val); 15278269Seric break; 15288269Seric 152925820Seric case 'n': /* validate RHS in newaliases */ 153025820Seric CheckAliases = atobool(val); 153125820Seric break; 153225820Seric 153361104Seric /* 'N' available -- was "net name" */ 153461104Seric 153558851Seric case 'O': /* daemon options */ 153658851Seric setdaemonoptions(val); 153758851Seric break; 153858851Seric 15398269Seric case 'o': /* assume old style headers */ 15409381Seric if (atobool(val)) 15419341Seric CurEnv->e_flags |= EF_OLDSTYLE; 15429341Seric else 15439341Seric CurEnv->e_flags &= ~EF_OLDSTYLE; 15448269Seric break; 15458269Seric 154658082Seric case 'p': /* select privacy level */ 154758082Seric p = val; 154858082Seric for (;;) 154958082Seric { 155058082Seric register struct prival *pv; 155158082Seric extern struct prival PrivacyValues[]; 155258082Seric 155358082Seric while (isascii(*p) && (isspace(*p) || ispunct(*p))) 155458082Seric p++; 155558082Seric if (*p == '\0') 155658082Seric break; 155758082Seric val = p; 155858082Seric while (isascii(*p) && isalnum(*p)) 155958082Seric p++; 156058082Seric if (*p != '\0') 156158082Seric *p++ = '\0'; 156258082Seric 156358082Seric for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 156458082Seric { 156558082Seric if (strcasecmp(val, pv->pv_name) == 0) 156658082Seric break; 156758082Seric } 156858886Seric if (pv->pv_name == NULL) 156958886Seric syserr("readcf: Op line: %s unrecognized", val); 157058082Seric PrivacyFlags |= pv->pv_flag; 157158082Seric } 157258082Seric break; 157358082Seric 157424944Seric case 'P': /* postmaster copy address for returned mail */ 157524944Seric PostMasterCopy = newstr(val); 157624944Seric break; 157724944Seric 157824944Seric case 'q': /* slope of queue only function */ 157924944Seric QueueFactor = atoi(val); 158024944Seric break; 158124944Seric 15828256Seric case 'Q': /* queue directory */ 15839381Seric if (val[0] == '\0') 15848269Seric QueueDir = "mqueue"; 15859381Seric else 15869381Seric QueueDir = newstr(val); 158758789Seric if (RealUid != 0 && !safe) 158864718Seric Warn_Q_option = TRUE; 15898256Seric break; 15908256Seric 159158148Seric case 'R': /* don't prune routes */ 159258148Seric DontPruneRoutes = atobool(val); 159358148Seric break; 159458148Seric 15958256Seric case 'r': /* read timeout */ 159658112Seric settimeouts(val); 15978256Seric break; 15988256Seric 15998256Seric case 'S': /* status file */ 16009381Seric if (val[0] == '\0') 16018269Seric StatFile = "sendmail.st"; 16029381Seric else 16039381Seric StatFile = newstr(val); 16048256Seric break; 16058256Seric 16068265Seric case 's': /* be super safe, even if expensive */ 16079381Seric SuperSafe = atobool(val); 16088256Seric break; 16098256Seric 16108256Seric case 'T': /* queue timeout */ 161158737Seric p = strchr(val, '/'); 161258737Seric if (p != NULL) 161358737Seric { 161458737Seric *p++ = '\0'; 161567730Seric TimeOuts.to_q_warning[TOC_NORMAL] = convtime(p, 'd'); 161658737Seric } 161767730Seric TimeOuts.to_q_return[TOC_NORMAL] = convtime(val, 'h'); 161854967Seric break; 16198256Seric 16208265Seric case 't': /* time zone name */ 162152106Seric TimeZoneSpec = newstr(val); 16228265Seric break; 16238265Seric 162450556Seric case 'U': /* location of user database */ 162551360Seric UdbSpec = newstr(val); 162650556Seric break; 162750556Seric 16288256Seric case 'u': /* set default uid */ 162964133Seric if (isascii(*val) && isdigit(*val)) 163064133Seric DefUid = atoi(val); 163164133Seric else 163264133Seric { 163364133Seric register struct passwd *pw; 163464133Seric 163564133Seric DefUid = -1; 163664133Seric pw = getpwnam(val); 163764133Seric if (pw == NULL) 163864133Seric syserr("readcf: option u: unknown user %s", val); 163964133Seric else 164064133Seric DefUid = pw->pw_uid; 164164133Seric } 164240973Sbostic setdefuser(); 16438256Seric break; 16448256Seric 164558851Seric case 'V': /* fallback MX host */ 164658851Seric FallBackMX = newstr(val); 164758851Seric break; 164858851Seric 16498269Seric case 'v': /* run in verbose mode */ 16509381Seric Verbose = atobool(val); 16518256Seric break; 16528256Seric 165363837Seric case 'w': /* if we are best MX, try host directly */ 165463837Seric TryNullMXList = atobool(val); 165563837Seric break; 165661104Seric 165761104Seric /* 'W' available -- was wizard password */ 165861104Seric 165914879Seric case 'x': /* load avg at which to auto-queue msgs */ 166014879Seric QueueLA = atoi(val); 166114879Seric break; 166214879Seric 166314879Seric case 'X': /* load avg at which to auto-reject connections */ 166414879Seric RefuseLA = atoi(val); 166514879Seric break; 166614879Seric 166724981Seric case 'y': /* work recipient factor */ 166824981Seric WkRecipFact = atoi(val); 166924981Seric break; 167024981Seric 167124981Seric case 'Y': /* fork jobs during queue runs */ 167224952Seric ForkQueueRuns = atobool(val); 167324952Seric break; 167424952Seric 167524981Seric case 'z': /* work message class factor */ 167624981Seric WkClassFact = atoi(val); 167724981Seric break; 167824981Seric 167924981Seric case 'Z': /* work time factor */ 168024981Seric WkTimeFact = atoi(val); 168124981Seric break; 168224981Seric 168367614Seric case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 168467614Seric BrokenSmtpPeers = atobool(val); 168567614Seric break; 168667614Seric 168767614Seric case O_SQBH: /* sort work queue by host first */ 168867614Seric SortQueueByHost = atobool(val); 168967614Seric break; 169067614Seric 169167707Seric case O_DNICE: /* delivery nice value */ 169267707Seric DeliveryNiceness = atoi(val); 169367707Seric break; 169467707Seric 169567707Seric case O_MQA: /* minimum queue age between deliveries */ 169667707Seric MinQueueAge = convtime(val, 'm'); 169767707Seric break; 169867707Seric 169967707Seric case O_MHSA: /* maximum age of cached host status */ 170067707Seric MaxHostStatAge = convtime(val, 'm'); 170167707Seric break; 170267707Seric 17038256Seric default: 17048256Seric break; 17058256Seric } 170616878Seric if (sticky) 170716878Seric setbitn(opt, StickyOpt); 17089188Seric return; 17098256Seric } 171010687Seric /* 171110687Seric ** SETCLASS -- set a word into a class 171210687Seric ** 171310687Seric ** Parameters: 171410687Seric ** class -- the class to put the word in. 171510687Seric ** word -- the word to enter 171610687Seric ** 171710687Seric ** Returns: 171810687Seric ** none. 171910687Seric ** 172010687Seric ** Side Effects: 172110687Seric ** puts the word into the symbol table. 172210687Seric */ 172310687Seric 172410687Seric setclass(class, word) 172510687Seric int class; 172610687Seric char *word; 172710687Seric { 172810687Seric register STAB *s; 172910687Seric 173057943Seric if (tTd(37, 8)) 173164326Seric printf("setclass(%c, %s)\n", class, word); 173210687Seric s = stab(word, ST_CLASS, ST_ENTER); 173310687Seric setbitn(class, s->s_class); 173410687Seric } 173553654Seric /* 173653654Seric ** MAKEMAPENTRY -- create a map entry 173753654Seric ** 173853654Seric ** Parameters: 173953654Seric ** line -- the config file line 174053654Seric ** 174153654Seric ** Returns: 174253654Seric ** TRUE if it successfully entered the map entry. 174353654Seric ** FALSE otherwise (usually syntax error). 174453654Seric ** 174553654Seric ** Side Effects: 174653654Seric ** Enters the map into the dictionary. 174753654Seric */ 174853654Seric 174953654Seric void 175053654Seric makemapentry(line) 175153654Seric char *line; 175253654Seric { 175353654Seric register char *p; 175453654Seric char *mapname; 175553654Seric char *classname; 175664078Seric register STAB *s; 175753654Seric STAB *class; 175853654Seric 175958050Seric for (p = line; isascii(*p) && isspace(*p); p++) 176053654Seric continue; 176158050Seric if (!(isascii(*p) && isalnum(*p))) 176253654Seric { 176353654Seric syserr("readcf: config K line: no map name"); 176453654Seric return; 176553654Seric } 176653654Seric 176753654Seric mapname = p; 176858050Seric while (isascii(*++p) && isalnum(*p)) 176953654Seric continue; 177053654Seric if (*p != '\0') 177153654Seric *p++ = '\0'; 177258050Seric while (isascii(*p) && isspace(*p)) 177353654Seric p++; 177458050Seric if (!(isascii(*p) && isalnum(*p))) 177553654Seric { 177653654Seric syserr("readcf: config K line, map %s: no map class", mapname); 177753654Seric return; 177853654Seric } 177953654Seric classname = p; 178058050Seric while (isascii(*++p) && isalnum(*p)) 178153654Seric continue; 178253654Seric if (*p != '\0') 178353654Seric *p++ = '\0'; 178458050Seric while (isascii(*p) && isspace(*p)) 178553654Seric p++; 178653654Seric 178753654Seric /* look up the class */ 178853654Seric class = stab(classname, ST_MAPCLASS, ST_FIND); 178953654Seric if (class == NULL) 179053654Seric { 179153654Seric syserr("readcf: map %s: class %s not available", mapname, classname); 179253654Seric return; 179353654Seric } 179453654Seric 179553654Seric /* enter the map */ 179664078Seric s = stab(mapname, ST_MAP, ST_ENTER); 179764078Seric s->s_map.map_class = &class->s_mapclass; 179864078Seric s->s_map.map_mname = newstr(mapname); 179953654Seric 180064078Seric if (class->s_mapclass.map_parse(&s->s_map, p)) 180164078Seric s->s_map.map_mflags |= MF_VALID; 180264078Seric 180364078Seric if (tTd(37, 5)) 180464078Seric { 180564078Seric printf("map %s, class %s, flags %x, file %s,\n", 180664078Seric s->s_map.map_mname, s->s_map.map_class->map_cname, 180764078Seric s->s_map.map_mflags, 180864078Seric s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 180964078Seric printf("\tapp %s, domain %s, rebuild %s\n", 181064078Seric s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 181164078Seric s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 181264078Seric s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 181364078Seric } 181453654Seric } 181558112Seric /* 181658112Seric ** SETTIMEOUTS -- parse and set timeout values 181758112Seric ** 181858112Seric ** Parameters: 181958112Seric ** val -- a pointer to the values. If NULL, do initial 182058112Seric ** settings. 182158112Seric ** 182258112Seric ** Returns: 182358112Seric ** none. 182458112Seric ** 182558112Seric ** Side Effects: 182658112Seric ** Initializes the TimeOuts structure 182758112Seric */ 182858112Seric 182964255Seric #define SECONDS 183058112Seric #define MINUTES * 60 183158112Seric #define HOUR * 3600 183258112Seric 183358112Seric settimeouts(val) 183458112Seric register char *val; 183558112Seric { 183658112Seric register char *p; 183758671Seric extern time_t convtime(); 183858112Seric 183958112Seric if (val == NULL) 184058112Seric { 184158112Seric TimeOuts.to_initial = (time_t) 5 MINUTES; 184258112Seric TimeOuts.to_helo = (time_t) 5 MINUTES; 184358112Seric TimeOuts.to_mail = (time_t) 10 MINUTES; 184458112Seric TimeOuts.to_rcpt = (time_t) 1 HOUR; 184558112Seric TimeOuts.to_datainit = (time_t) 5 MINUTES; 184658112Seric TimeOuts.to_datablock = (time_t) 1 HOUR; 184758112Seric TimeOuts.to_datafinal = (time_t) 1 HOUR; 184858112Seric TimeOuts.to_rset = (time_t) 5 MINUTES; 184958112Seric TimeOuts.to_quit = (time_t) 2 MINUTES; 185058112Seric TimeOuts.to_nextcommand = (time_t) 1 HOUR; 185158112Seric TimeOuts.to_miscshort = (time_t) 2 MINUTES; 185264255Seric TimeOuts.to_ident = (time_t) 30 SECONDS; 185367711Seric TimeOuts.to_fileopen = (time_t) 60 SECONDS; 185458112Seric return; 185558112Seric } 185658112Seric 185758112Seric for (;; val = p) 185858112Seric { 185958112Seric while (isascii(*val) && isspace(*val)) 186058112Seric val++; 186158112Seric if (*val == '\0') 186258112Seric break; 186358112Seric for (p = val; *p != '\0' && *p != ','; p++) 186458112Seric continue; 186558112Seric if (*p != '\0') 186658112Seric *p++ = '\0'; 186758112Seric 186858112Seric if (isascii(*val) && isdigit(*val)) 186958112Seric { 187058112Seric /* old syntax -- set everything */ 187158796Seric TimeOuts.to_mail = convtime(val, 'm'); 187258112Seric TimeOuts.to_rcpt = TimeOuts.to_mail; 187358112Seric TimeOuts.to_datainit = TimeOuts.to_mail; 187458112Seric TimeOuts.to_datablock = TimeOuts.to_mail; 187558112Seric TimeOuts.to_datafinal = TimeOuts.to_mail; 187658112Seric TimeOuts.to_nextcommand = TimeOuts.to_mail; 187758112Seric continue; 187858112Seric } 187958112Seric else 188058112Seric { 188167711Seric register char *q = strchr(val, ':'); 188258112Seric time_t to; 188358112Seric 188467711Seric if (q == NULL && (q = strchr(val, '=')) == NULL) 188558112Seric { 188658112Seric /* syntax error */ 188758112Seric continue; 188858112Seric } 188958112Seric *q++ = '\0'; 189058796Seric to = convtime(q, 'm'); 189158112Seric 189258112Seric if (strcasecmp(val, "initial") == 0) 189358112Seric TimeOuts.to_initial = to; 189458112Seric else if (strcasecmp(val, "mail") == 0) 189558112Seric TimeOuts.to_mail = to; 189658112Seric else if (strcasecmp(val, "rcpt") == 0) 189758112Seric TimeOuts.to_rcpt = to; 189858112Seric else if (strcasecmp(val, "datainit") == 0) 189958112Seric TimeOuts.to_datainit = to; 190058112Seric else if (strcasecmp(val, "datablock") == 0) 190158112Seric TimeOuts.to_datablock = to; 190258112Seric else if (strcasecmp(val, "datafinal") == 0) 190358112Seric TimeOuts.to_datafinal = to; 190458112Seric else if (strcasecmp(val, "command") == 0) 190558112Seric TimeOuts.to_nextcommand = to; 190658112Seric else if (strcasecmp(val, "rset") == 0) 190758112Seric TimeOuts.to_rset = to; 190858112Seric else if (strcasecmp(val, "helo") == 0) 190958112Seric TimeOuts.to_helo = to; 191058112Seric else if (strcasecmp(val, "quit") == 0) 191158112Seric TimeOuts.to_quit = to; 191258112Seric else if (strcasecmp(val, "misc") == 0) 191358112Seric TimeOuts.to_miscshort = to; 191464255Seric else if (strcasecmp(val, "ident") == 0) 191564255Seric TimeOuts.to_ident = to; 191667711Seric else if (strcasecmp(val, "fileopen") == 0) 191767711Seric TimeOuts.to_fileopen = to; 191867711Seric else if (strcasecmp(val, "queuewarn") == 0) 191967730Seric TimeOuts.to_q_warning[TOC_NORMAL] = to; 192067711Seric else if (strcasecmp(val, "queuereturn") == 0) 192167730Seric TimeOuts.to_q_return[TOC_NORMAL] = to; 192267730Seric else if (strcasecmp(val, "queuewarn.normal") == 0) 192367730Seric TimeOuts.to_q_warning[TOC_NORMAL] = to; 192467730Seric else if (strcasecmp(val, "queuereturn.normal") == 0) 192567730Seric TimeOuts.to_q_return[TOC_NORMAL] = to; 192667730Seric else if (strcasecmp(val, "queuewarn.urgent") == 0) 192767730Seric TimeOuts.to_q_warning[TOC_URGENT] = to; 192867730Seric else if (strcasecmp(val, "queuereturn.urgent") == 0) 192967730Seric TimeOuts.to_q_return[TOC_URGENT] = to; 193067730Seric else if (strcasecmp(val, "queuewarn.non-urgent") == 0) 193167730Seric TimeOuts.to_q_warning[TOC_NONURGENT] = to; 193267730Seric else if (strcasecmp(val, "queuereturn.non-urgent") == 0) 193367730Seric TimeOuts.to_q_return[TOC_NONURGENT] = to; 193458112Seric else 193558112Seric syserr("settimeouts: invalid timeout %s", val); 193658112Seric } 193758112Seric } 193858112Seric } 1939