1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)readcf.c 8.48 (Berkeley) 11/20/94"; 11 #endif /* not lint */ 12 13 # include "sendmail.h" 14 # include <pwd.h> 15 # include <grp.h> 16 #if NAMED_BIND 17 # include <resolv.h> 18 #endif 19 20 /* 21 ** READCF -- read control file. 22 ** 23 ** This routine reads the control file and builds the internal 24 ** form. 25 ** 26 ** The file is formatted as a sequence of lines, each taken 27 ** atomically. The first character of each line describes how 28 ** the line is to be interpreted. The lines are: 29 ** Dxval Define macro x to have value val. 30 ** Cxword Put word into class x. 31 ** Fxfile [fmt] Read file for lines to put into 32 ** class x. Use scanf string 'fmt' 33 ** or "%s" if not present. Fmt should 34 ** only produce one string-valued result. 35 ** Hname: value Define header with field-name 'name' 36 ** and value as specified; this will be 37 ** macro expanded immediately before 38 ** use. 39 ** Sn Use rewriting set n. 40 ** Rlhs rhs Rewrite addresses that match lhs to 41 ** be rhs. 42 ** Mn arg=val... Define mailer. n is the internal name. 43 ** Args specify mailer parameters. 44 ** Oxvalue Set option x to value. 45 ** Pname=value Set precedence name to value. 46 ** Vversioncode[/vendorcode] 47 ** Version level/vendor name of 48 ** configuration syntax. 49 ** Kmapname mapclass arguments.... 50 ** Define keyed lookup of a given class. 51 ** Arguments are class dependent. 52 ** 53 ** Parameters: 54 ** cfname -- control file name. 55 ** safe -- TRUE if this is the system config file; 56 ** FALSE otherwise. 57 ** e -- the main envelope. 58 ** 59 ** Returns: 60 ** none. 61 ** 62 ** Side Effects: 63 ** Builds several internal tables. 64 */ 65 66 readcf(cfname, safe, e) 67 char *cfname; 68 bool safe; 69 register ENVELOPE *e; 70 { 71 FILE *cf; 72 int ruleset = 0; 73 char *q; 74 struct rewrite *rwp = NULL; 75 char *bp; 76 auto char *ep; 77 int nfuzzy; 78 char *file; 79 bool optional; 80 int mid; 81 char buf[MAXLINE]; 82 register char *p; 83 extern char **copyplist(); 84 struct stat statb; 85 char exbuf[MAXLINE]; 86 char pvpbuf[MAXLINE + MAXATOM]; 87 static char *null_list[1] = { NULL }; 88 extern char *munchstring(); 89 extern void makemapentry(); 90 91 FileName = cfname; 92 LineNumber = 0; 93 94 cf = fopen(cfname, "r"); 95 if (cf == NULL) 96 { 97 syserr("cannot open"); 98 exit(EX_OSFILE); 99 } 100 101 if (fstat(fileno(cf), &statb) < 0) 102 { 103 syserr("cannot fstat"); 104 exit(EX_OSFILE); 105 } 106 107 if (!S_ISREG(statb.st_mode)) 108 { 109 syserr("not a plain file"); 110 exit(EX_OSFILE); 111 } 112 113 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 114 { 115 if (OpMode == MD_DAEMON || OpMode == MD_FREEZE) 116 fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 117 FileName); 118 #ifdef LOG 119 if (LogLevel > 0) 120 syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", 121 FileName); 122 #endif 123 } 124 125 #ifdef XLA 126 xla_zero(); 127 #endif 128 129 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 130 { 131 if (bp[0] == '#') 132 { 133 if (bp != buf) 134 free(bp); 135 continue; 136 } 137 138 /* do macro expansion mappings */ 139 for (p = bp; *p != '\0'; p++) 140 { 141 if (*p == '#' && p > bp && ConfigLevel >= 3) 142 { 143 /* this is an on-line comment */ 144 register char *e; 145 146 switch (*--p & 0377) 147 { 148 case MACROEXPAND: 149 /* it's from $# -- let it go through */ 150 p++; 151 break; 152 153 case '\\': 154 /* it's backslash escaped */ 155 (void) strcpy(p, p + 1); 156 break; 157 158 default: 159 /* delete preceeding white space */ 160 while (isascii(*p) && isspace(*p) && p > bp) 161 p--; 162 if ((e = strchr(++p, '\n')) != NULL) 163 (void) strcpy(p, e); 164 else 165 p[0] = p[1] = '\0'; 166 break; 167 } 168 continue; 169 } 170 171 if (*p != '$' || p[1] == '\0') 172 continue; 173 174 if (p[1] == '$') 175 { 176 /* actual dollar sign.... */ 177 (void) strcpy(p, p + 1); 178 continue; 179 } 180 181 /* convert to macro expansion character */ 182 *p++ = MACROEXPAND; 183 184 /* convert macro name to code */ 185 *p = macid(p, &ep); 186 if (ep != p) 187 strcpy(p + 1, ep); 188 } 189 190 /* interpret this line */ 191 errno = 0; 192 switch (bp[0]) 193 { 194 case '\0': 195 case '#': /* comment */ 196 break; 197 198 case 'R': /* rewriting rule */ 199 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 200 continue; 201 202 if (*p == '\0') 203 { 204 syserr("invalid rewrite line \"%s\" (tab expected)", bp); 205 break; 206 } 207 208 /* allocate space for the rule header */ 209 if (rwp == NULL) 210 { 211 RewriteRules[ruleset] = rwp = 212 (struct rewrite *) xalloc(sizeof *rwp); 213 } 214 else 215 { 216 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 217 rwp = rwp->r_next; 218 } 219 rwp->r_next = NULL; 220 221 /* expand and save the LHS */ 222 *p = '\0'; 223 expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e); 224 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, 225 sizeof pvpbuf, NULL); 226 nfuzzy = 0; 227 if (rwp->r_lhs != NULL) 228 { 229 register char **ap; 230 231 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 232 233 /* count the number of fuzzy matches in LHS */ 234 for (ap = rwp->r_lhs; *ap != NULL; ap++) 235 { 236 char *botch; 237 238 botch = NULL; 239 switch (**ap & 0377) 240 { 241 case MATCHZANY: 242 case MATCHANY: 243 case MATCHONE: 244 case MATCHCLASS: 245 case MATCHNCLASS: 246 nfuzzy++; 247 break; 248 249 case MATCHREPL: 250 botch = "$0-$9"; 251 break; 252 253 case CANONNET: 254 botch = "$#"; 255 break; 256 257 case CANONUSER: 258 botch = "$:"; 259 break; 260 261 case CALLSUBR: 262 botch = "$>"; 263 break; 264 265 case CONDIF: 266 botch = "$?"; 267 break; 268 269 case CONDELSE: 270 botch = "$|"; 271 break; 272 273 case CONDFI: 274 botch = "$."; 275 break; 276 277 case HOSTBEGIN: 278 botch = "$["; 279 break; 280 281 case HOSTEND: 282 botch = "$]"; 283 break; 284 285 case LOOKUPBEGIN: 286 botch = "$("; 287 break; 288 289 case LOOKUPEND: 290 botch = "$)"; 291 break; 292 } 293 if (botch != NULL) 294 syserr("Inappropriate use of %s on LHS", 295 botch); 296 } 297 } 298 else 299 { 300 syserr("R line: null LHS"); 301 rwp->r_lhs = null_list; 302 } 303 304 /* expand and save the RHS */ 305 while (*++p == '\t') 306 continue; 307 q = p; 308 while (*p != '\0' && *p != '\t') 309 p++; 310 *p = '\0'; 311 expand(q, exbuf, &exbuf[sizeof exbuf], e); 312 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, 313 sizeof pvpbuf, NULL); 314 if (rwp->r_rhs != NULL) 315 { 316 register char **ap; 317 318 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 319 320 /* check no out-of-bounds replacements */ 321 nfuzzy += '0'; 322 for (ap = rwp->r_rhs; *ap != NULL; ap++) 323 { 324 char *botch; 325 326 botch = NULL; 327 switch (**ap & 0377) 328 { 329 case MATCHREPL: 330 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 331 { 332 syserr("replacement $%c out of bounds", 333 (*ap)[1]); 334 } 335 break; 336 337 case MATCHZANY: 338 botch = "$*"; 339 break; 340 341 case MATCHANY: 342 botch = "$+"; 343 break; 344 345 case MATCHONE: 346 botch = "$-"; 347 break; 348 349 case MATCHCLASS: 350 botch = "$="; 351 break; 352 353 case MATCHNCLASS: 354 botch = "$~"; 355 break; 356 } 357 if (botch != NULL) 358 syserr("Inappropriate use of %s on RHS", 359 botch); 360 } 361 } 362 else 363 { 364 syserr("R line: null RHS"); 365 rwp->r_rhs = null_list; 366 } 367 break; 368 369 case 'S': /* select rewriting set */ 370 for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 371 continue; 372 if (!isascii(*p) || !isdigit(*p)) 373 { 374 syserr("invalid argument to S line: \"%.20s\"", 375 &bp[1]); 376 break; 377 } 378 ruleset = atoi(p); 379 if (ruleset >= MAXRWSETS || ruleset < 0) 380 { 381 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 382 ruleset = 0; 383 } 384 rwp = NULL; 385 break; 386 387 case 'D': /* macro definition */ 388 mid = macid(&bp[1], &ep); 389 p = munchstring(ep, NULL); 390 define(mid, newstr(p), e); 391 break; 392 393 case 'H': /* required header line */ 394 (void) chompheader(&bp[1], TRUE, e); 395 break; 396 397 case 'C': /* word class */ 398 /* scan the list of words and set class for all */ 399 mid = macid(&bp[1], &ep); 400 expand(ep, exbuf, &exbuf[sizeof exbuf], e); 401 for (p = exbuf; *p != '\0'; ) 402 { 403 register char *wd; 404 char delim; 405 406 while (*p != '\0' && isascii(*p) && isspace(*p)) 407 p++; 408 wd = p; 409 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 410 p++; 411 delim = *p; 412 *p = '\0'; 413 if (wd[0] != '\0') 414 setclass(mid, wd); 415 *p = delim; 416 } 417 break; 418 419 case 'F': /* word class from file */ 420 mid = macid(&bp[1], &ep); 421 for (p = ep; isascii(*p) && isspace(*p); ) 422 p++; 423 if (p[0] == '-' && p[1] == 'o') 424 { 425 optional = TRUE; 426 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 427 p++; 428 while (isascii(*p) && isspace(*p)) 429 p++; 430 } 431 else 432 optional = FALSE; 433 file = p; 434 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 435 p++; 436 if (*p == '\0') 437 p = "%s"; 438 else 439 { 440 *p = '\0'; 441 while (isascii(*++p) && isspace(*p)) 442 continue; 443 } 444 fileclass(bp[1], file, p, safe, optional); 445 break; 446 447 #ifdef XLA 448 case 'L': /* extended load average description */ 449 xla_init(&bp[1]); 450 break; 451 #endif 452 453 case 'M': /* define mailer */ 454 makemailer(&bp[1]); 455 break; 456 457 case 'O': /* set option */ 458 setoption(bp[1], &bp[2], safe, FALSE, e); 459 break; 460 461 case 'P': /* set precedence */ 462 if (NumPriorities >= MAXPRIORITIES) 463 { 464 toomany('P', MAXPRIORITIES); 465 break; 466 } 467 for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 468 continue; 469 if (*p == '\0') 470 goto badline; 471 *p = '\0'; 472 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 473 Priorities[NumPriorities].pri_val = atoi(++p); 474 NumPriorities++; 475 break; 476 477 case 'T': /* trusted user(s) */ 478 /* this option is obsolete, but will be ignored */ 479 break; 480 481 case 'V': /* configuration syntax version */ 482 for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 483 continue; 484 if (!isascii(*p) || !isdigit(*p)) 485 { 486 syserr("invalid argument to V line: \"%.20s\"", 487 &bp[1]); 488 break; 489 } 490 ConfigLevel = strtol(p, &ep, 10); 491 if (ConfigLevel >= 5) 492 { 493 /* level 5 configs have short name in $w */ 494 p = macvalue('w', e); 495 if (p != NULL && (p = strchr(p, '.')) != NULL) 496 *p = '\0'; 497 } 498 if (*ep++ == '/') 499 { 500 /* extract vendor code */ 501 for (p = ep; isascii(*p) && isalpha(*p); ) 502 p++; 503 *p = '\0'; 504 505 if (!setvendor(ep)) 506 syserr("invalid V line vendor code: \"%s\"", 507 ep); 508 } 509 break; 510 511 case 'K': 512 makemapentry(&bp[1]); 513 break; 514 515 default: 516 badline: 517 syserr("unknown control line \"%s\"", bp); 518 } 519 if (bp != buf) 520 free(bp); 521 } 522 if (ferror(cf)) 523 { 524 syserr("I/O read error", cfname); 525 exit(EX_OSFILE); 526 } 527 fclose(cf); 528 FileName = NULL; 529 530 /* initialize host maps from local service tables */ 531 inithostmaps(); 532 533 /* determine if we need to do special name-server frotz */ 534 { 535 int nmaps; 536 char *maptype[MAXMAPSTACK]; 537 short mapreturn[MAXMAPACTIONS]; 538 539 nmaps = switch_map_find("hosts", maptype, mapreturn); 540 UseNameServer = FALSE; 541 if (nmaps > 0 && nmaps <= MAXMAPSTACK) 542 { 543 register int mapno; 544 545 for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++) 546 { 547 if (strcmp(maptype[mapno], "dns") == 0) 548 UseNameServer = TRUE; 549 } 550 } 551 } 552 } 553 /* 554 ** TOOMANY -- signal too many of some option 555 ** 556 ** Parameters: 557 ** id -- the id of the error line 558 ** maxcnt -- the maximum possible values 559 ** 560 ** Returns: 561 ** none. 562 ** 563 ** Side Effects: 564 ** gives a syserr. 565 */ 566 567 toomany(id, maxcnt) 568 char id; 569 int maxcnt; 570 { 571 syserr("too many %c lines, %d max", id, maxcnt); 572 } 573 /* 574 ** FILECLASS -- read members of a class from a file 575 ** 576 ** Parameters: 577 ** class -- class to define. 578 ** filename -- name of file to read. 579 ** fmt -- scanf string to use for match. 580 ** safe -- if set, this is a safe read. 581 ** optional -- if set, it is not an error for the file to 582 ** not exist. 583 ** 584 ** Returns: 585 ** none 586 ** 587 ** Side Effects: 588 ** 589 ** puts all lines in filename that match a scanf into 590 ** the named class. 591 */ 592 593 fileclass(class, filename, fmt, safe, optional) 594 int class; 595 char *filename; 596 char *fmt; 597 bool safe; 598 bool optional; 599 { 600 FILE *f; 601 struct stat stbuf; 602 char buf[MAXLINE]; 603 604 if (tTd(37, 2)) 605 printf("fileclass(%s, fmt=%s)\n", filename, fmt); 606 607 if (filename[0] == '|') 608 { 609 syserr("fileclass: pipes (F%c%s) not supported due to security problems", 610 class, filename); 611 return; 612 } 613 if (stat(filename, &stbuf) < 0) 614 { 615 if (tTd(37, 2)) 616 printf(" cannot stat (%s)\n", errstring(errno)); 617 if (!optional) 618 syserr("fileclass: cannot stat %s", filename); 619 return; 620 } 621 if (!S_ISREG(stbuf.st_mode)) 622 { 623 syserr("fileclass: %s not a regular file", filename); 624 return; 625 } 626 if (!safe && access(filename, R_OK) < 0) 627 { 628 syserr("fileclass: access denied on %s", filename); 629 return; 630 } 631 f = fopen(filename, "r"); 632 if (f == NULL) 633 { 634 syserr("fileclass: cannot open %s", filename); 635 return; 636 } 637 638 while (fgets(buf, sizeof buf, f) != NULL) 639 { 640 register STAB *s; 641 register char *p; 642 # ifdef SCANF 643 char wordbuf[MAXNAME+1]; 644 645 if (sscanf(buf, fmt, wordbuf) != 1) 646 continue; 647 p = wordbuf; 648 # else /* SCANF */ 649 p = buf; 650 # endif /* SCANF */ 651 652 /* 653 ** Break up the match into words. 654 */ 655 656 while (*p != '\0') 657 { 658 register char *q; 659 660 /* strip leading spaces */ 661 while (isascii(*p) && isspace(*p)) 662 p++; 663 if (*p == '\0') 664 break; 665 666 /* find the end of the word */ 667 q = p; 668 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 669 p++; 670 if (*p != '\0') 671 *p++ = '\0'; 672 673 /* enter the word in the symbol table */ 674 setclass(class, q); 675 } 676 } 677 678 (void) fclose(f); 679 } 680 /* 681 ** MAKEMAILER -- define a new mailer. 682 ** 683 ** Parameters: 684 ** line -- description of mailer. This is in labeled 685 ** fields. The fields are: 686 ** P -- the path to the mailer 687 ** F -- the flags associated with the mailer 688 ** A -- the argv for this mailer 689 ** S -- the sender rewriting set 690 ** R -- the recipient rewriting set 691 ** E -- the eol string 692 ** The first word is the canonical name of the mailer. 693 ** 694 ** Returns: 695 ** none. 696 ** 697 ** Side Effects: 698 ** enters the mailer into the mailer table. 699 */ 700 701 makemailer(line) 702 char *line; 703 { 704 register char *p; 705 register struct mailer *m; 706 register STAB *s; 707 int i; 708 char fcode; 709 auto char *endp; 710 extern int NextMailer; 711 extern char **makeargv(); 712 extern char *munchstring(); 713 extern long atol(); 714 715 /* allocate a mailer and set up defaults */ 716 m = (struct mailer *) xalloc(sizeof *m); 717 bzero((char *) m, sizeof *m); 718 m->m_eol = "\n"; 719 m->m_uid = m->m_gid = 0; 720 721 /* collect the mailer name */ 722 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 723 continue; 724 if (*p != '\0') 725 *p++ = '\0'; 726 m->m_name = newstr(line); 727 728 /* now scan through and assign info from the fields */ 729 while (*p != '\0') 730 { 731 auto char *delimptr; 732 733 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 734 p++; 735 736 /* p now points to field code */ 737 fcode = *p; 738 while (*p != '\0' && *p != '=' && *p != ',') 739 p++; 740 if (*p++ != '=') 741 { 742 syserr("mailer %s: `=' expected", m->m_name); 743 return; 744 } 745 while (isascii(*p) && isspace(*p)) 746 p++; 747 748 /* p now points to the field body */ 749 p = munchstring(p, &delimptr); 750 751 /* install the field into the mailer struct */ 752 switch (fcode) 753 { 754 case 'P': /* pathname */ 755 m->m_mailer = newstr(p); 756 break; 757 758 case 'F': /* flags */ 759 for (; *p != '\0'; p++) 760 if (!(isascii(*p) && isspace(*p))) 761 setbitn(*p, m->m_flags); 762 break; 763 764 case 'S': /* sender rewriting ruleset */ 765 case 'R': /* recipient rewriting ruleset */ 766 i = strtol(p, &endp, 10); 767 if (i < 0 || i >= MAXRWSETS) 768 { 769 syserr("invalid rewrite set, %d max", MAXRWSETS); 770 return; 771 } 772 if (fcode == 'S') 773 m->m_sh_rwset = m->m_se_rwset = i; 774 else 775 m->m_rh_rwset = m->m_re_rwset = i; 776 777 p = endp; 778 if (*p++ == '/') 779 { 780 i = strtol(p, NULL, 10); 781 if (i < 0 || i >= MAXRWSETS) 782 { 783 syserr("invalid rewrite set, %d max", 784 MAXRWSETS); 785 return; 786 } 787 if (fcode == 'S') 788 m->m_sh_rwset = i; 789 else 790 m->m_rh_rwset = i; 791 } 792 break; 793 794 case 'E': /* end of line string */ 795 m->m_eol = newstr(p); 796 break; 797 798 case 'A': /* argument vector */ 799 m->m_argv = makeargv(p); 800 break; 801 802 case 'M': /* maximum message size */ 803 m->m_maxsize = atol(p); 804 break; 805 806 case 'L': /* maximum line length */ 807 m->m_linelimit = atoi(p); 808 break; 809 810 case 'D': /* working directory */ 811 m->m_execdir = newstr(p); 812 break; 813 814 case 'C': /* default charset */ 815 m->m_defcharset = newstr(p); 816 break; 817 818 case 'U': /* user id */ 819 if (isascii(*p) && !isdigit(*p)) 820 { 821 char *q = p; 822 struct passwd *pw; 823 824 while (isascii(*p) && isalnum(*p)) 825 p++; 826 while (isascii(*p) && isspace(*p)) 827 *p++ = '\0'; 828 if (*p != '\0') 829 *p++ = '\0'; 830 pw = getpwnam(q); 831 if (pw == NULL) 832 syserr("readcf: mailer U= flag: unknown user %s", q); 833 else 834 { 835 m->m_uid = pw->pw_uid; 836 m->m_gid = pw->pw_gid; 837 } 838 } 839 else 840 { 841 auto char *q; 842 843 m->m_uid = strtol(p, &q, 0); 844 p = q; 845 } 846 while (isascii(*p) && isspace(*p)) 847 p++; 848 if (*p == '\0') 849 break; 850 if (isascii(*p) && !isdigit(*p)) 851 { 852 char *q = p; 853 struct group *gr; 854 855 while (isascii(*p) && isalnum(*p)) 856 p++; 857 *p++ = '\0'; 858 gr = getgrnam(q); 859 if (gr == NULL) 860 syserr("readcf: mailer U= flag: unknown group %s", q); 861 else 862 m->m_gid = gr->gr_gid; 863 } 864 else 865 { 866 m->m_gid = strtol(p, NULL, 0); 867 } 868 break; 869 } 870 871 p = delimptr; 872 } 873 874 /* do some heuristic cleanup for back compatibility */ 875 if (bitnset(M_LIMITS, m->m_flags)) 876 { 877 if (m->m_linelimit == 0) 878 m->m_linelimit = SMTPLINELIM; 879 if (ConfigLevel < 2) 880 setbitn(M_7BITS, m->m_flags); 881 } 882 883 /* do some rationality checking */ 884 if (m->m_argv == NULL) 885 { 886 syserr("M%s: A= argument required", m->m_name); 887 return; 888 } 889 if (m->m_mailer == NULL) 890 { 891 syserr("M%s: P= argument required", m->m_name); 892 return; 893 } 894 895 if (NextMailer >= MAXMAILERS) 896 { 897 syserr("too many mailers defined (%d max)", MAXMAILERS); 898 return; 899 } 900 901 s = stab(m->m_name, ST_MAILER, ST_ENTER); 902 if (s->s_mailer != NULL) 903 { 904 i = s->s_mailer->m_mno; 905 free(s->s_mailer); 906 } 907 else 908 { 909 i = NextMailer++; 910 } 911 Mailer[i] = s->s_mailer = m; 912 m->m_mno = i; 913 } 914 /* 915 ** MUNCHSTRING -- translate a string into internal form. 916 ** 917 ** Parameters: 918 ** p -- the string to munch. 919 ** delimptr -- if non-NULL, set to the pointer of the 920 ** field delimiter character. 921 ** 922 ** Returns: 923 ** the munched string. 924 */ 925 926 char * 927 munchstring(p, delimptr) 928 register char *p; 929 char **delimptr; 930 { 931 register char *q; 932 bool backslash = FALSE; 933 bool quotemode = FALSE; 934 static char buf[MAXLINE]; 935 936 for (q = buf; *p != '\0'; p++) 937 { 938 if (backslash) 939 { 940 /* everything is roughly literal */ 941 backslash = FALSE; 942 switch (*p) 943 { 944 case 'r': /* carriage return */ 945 *q++ = '\r'; 946 continue; 947 948 case 'n': /* newline */ 949 *q++ = '\n'; 950 continue; 951 952 case 'f': /* form feed */ 953 *q++ = '\f'; 954 continue; 955 956 case 'b': /* backspace */ 957 *q++ = '\b'; 958 continue; 959 } 960 *q++ = *p; 961 } 962 else 963 { 964 if (*p == '\\') 965 backslash = TRUE; 966 else if (*p == '"') 967 quotemode = !quotemode; 968 else if (quotemode || *p != ',') 969 *q++ = *p; 970 else 971 break; 972 } 973 } 974 975 if (delimptr != NULL) 976 *delimptr = p; 977 *q++ = '\0'; 978 return (buf); 979 } 980 /* 981 ** MAKEARGV -- break up a string into words 982 ** 983 ** Parameters: 984 ** p -- the string to break up. 985 ** 986 ** Returns: 987 ** a char **argv (dynamically allocated) 988 ** 989 ** Side Effects: 990 ** munges p. 991 */ 992 993 char ** 994 makeargv(p) 995 register char *p; 996 { 997 char *q; 998 int i; 999 char **avp; 1000 char *argv[MAXPV + 1]; 1001 1002 /* take apart the words */ 1003 i = 0; 1004 while (*p != '\0' && i < MAXPV) 1005 { 1006 q = p; 1007 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1008 p++; 1009 while (isascii(*p) && isspace(*p)) 1010 *p++ = '\0'; 1011 argv[i++] = newstr(q); 1012 } 1013 argv[i++] = NULL; 1014 1015 /* now make a copy of the argv */ 1016 avp = (char **) xalloc(sizeof *avp * i); 1017 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 1018 1019 return (avp); 1020 } 1021 /* 1022 ** PRINTRULES -- print rewrite rules (for debugging) 1023 ** 1024 ** Parameters: 1025 ** none. 1026 ** 1027 ** Returns: 1028 ** none. 1029 ** 1030 ** Side Effects: 1031 ** prints rewrite rules. 1032 */ 1033 1034 printrules() 1035 { 1036 register struct rewrite *rwp; 1037 register int ruleset; 1038 1039 for (ruleset = 0; ruleset < 10; ruleset++) 1040 { 1041 if (RewriteRules[ruleset] == NULL) 1042 continue; 1043 printf("\n----Rule Set %d:", ruleset); 1044 1045 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 1046 { 1047 printf("\nLHS:"); 1048 printav(rwp->r_lhs); 1049 printf("RHS:"); 1050 printav(rwp->r_rhs); 1051 } 1052 } 1053 } 1054 1055 /* 1056 ** SETOPTION -- set global processing option 1057 ** 1058 ** Parameters: 1059 ** opt -- option name. 1060 ** val -- option value (as a text string). 1061 ** safe -- set if this came from a configuration file. 1062 ** Some options (if set from the command line) will 1063 ** reset the user id to avoid security problems. 1064 ** sticky -- if set, don't let other setoptions override 1065 ** this value. 1066 ** e -- the main envelope. 1067 ** 1068 ** Returns: 1069 ** none. 1070 ** 1071 ** Side Effects: 1072 ** Sets options as implied by the arguments. 1073 */ 1074 1075 static BITMAP StickyOpt; /* set if option is stuck */ 1076 1077 1078 #if NAMED_BIND 1079 1080 struct resolverflags 1081 { 1082 char *rf_name; /* name of the flag */ 1083 long rf_bits; /* bits to set/clear */ 1084 } ResolverFlags[] = 1085 { 1086 "debug", RES_DEBUG, 1087 "aaonly", RES_AAONLY, 1088 "usevc", RES_USEVC, 1089 "primary", RES_PRIMARY, 1090 "igntc", RES_IGNTC, 1091 "recurse", RES_RECURSE, 1092 "defnames", RES_DEFNAMES, 1093 "stayopen", RES_STAYOPEN, 1094 "dnsrch", RES_DNSRCH, 1095 "true", 0, /* to avoid error on old syntax */ 1096 NULL, 0 1097 }; 1098 1099 #endif 1100 1101 struct optioninfo 1102 { 1103 char *o_name; /* long name of option */ 1104 u_char o_code; /* short name of option */ 1105 bool o_safe; /* safe for random people to use */ 1106 } OptionTab[] = 1107 { 1108 "SevenBitInput", '7', TRUE, 1109 "EightBitMode", '8', TRUE, 1110 "AliasFile", 'A', FALSE, 1111 "AliasWait", 'a', FALSE, 1112 "BlankSub", 'B', FALSE, 1113 "MinFreeBlocks", 'b', TRUE, 1114 "CheckpointInterval", 'C', TRUE, 1115 "HoldExpensive", 'c', FALSE, 1116 "AutoRebuildAliases", 'D', FALSE, 1117 "DeliveryMode", 'd', TRUE, 1118 "ErrorHeader", 'E', FALSE, 1119 "ErrorMode", 'e', TRUE, 1120 "TempFileMode", 'F', FALSE, 1121 "SaveFromLine", 'f', FALSE, 1122 "MatchGECOS", 'G', FALSE, 1123 "HelpFile", 'H', FALSE, 1124 "MaxHopCount", 'h', FALSE, 1125 "NameServerOptions", 'I', FALSE, 1126 "IgnoreDots", 'i', TRUE, 1127 "ForwardPath", 'J', FALSE, 1128 "SendMimeErrors", 'j', TRUE, 1129 "ConnectionCacheSize", 'k', FALSE, 1130 "ConnectionCacheTimeout", 'K', FALSE, 1131 "UseErrorsTo", 'l', FALSE, 1132 "LogLevel", 'L', FALSE, 1133 "MeToo", 'm', TRUE, 1134 "CheckAliases", 'n', FALSE, 1135 "OldStyleHeaders", 'o', TRUE, 1136 "DaemonPortOptions", 'O', FALSE, 1137 "PrivacyOptions", 'p', TRUE, 1138 "PostmasterCopy", 'P', FALSE, 1139 "QueueFactor", 'q', FALSE, 1140 "QueueDirectory", 'Q', FALSE, 1141 "DontPruneRoutes", 'R', FALSE, 1142 "Timeouts", 'r', TRUE, 1143 "StatusFile", 'S', FALSE, 1144 "SuperSafe", 's', TRUE, 1145 "QueueTimeout", 'T', FALSE, 1146 "TimeZoneSpec", 't', FALSE, 1147 "UserDatabaseSpec", 'U', FALSE, 1148 "DefaultUser", 'u', FALSE, 1149 "FallbackMXhost", 'V', FALSE, 1150 "Verbose", 'v', TRUE, 1151 "TryNullMXList", 'w', TRUE, 1152 "QueueLA", 'x', FALSE, 1153 "RefuseLA", 'X', FALSE, 1154 "RecipientFactor", 'y', FALSE, 1155 "ForkQueueRuns", 'Y', FALSE, 1156 "ClassFactor", 'z', FALSE, 1157 "TimeFactor", 'Z', FALSE, 1158 #define O_BSP 0x80 1159 "BrokenSmtpPeers", O_BSP, TRUE, 1160 #define O_SQBH 0x81 1161 "SortQueueByHost", O_SQBH, TRUE, 1162 #define O_DNICE 0x82 1163 "DeliveryNiceness", O_DNICE, TRUE, 1164 #define O_MQA 0x83 1165 "MinQueueAge", O_MQA, TRUE, 1166 #define O_MHSA 0x84 1167 "MaxHostStatAge", O_MHSA, TRUE, 1168 #define O_DEFCHARSET 0x85 1169 "DefaultCharSet", O_DEFCHARSET, TRUE, 1170 #define O_SSFILE 0x86 1171 "ServiceSwitchFile", O_SSFILE, FALSE, 1172 1173 NULL, '\0', FALSE, 1174 }; 1175 1176 1177 1178 setoption(opt, val, safe, sticky, e) 1179 u_char opt; 1180 char *val; 1181 bool safe; 1182 bool sticky; 1183 register ENVELOPE *e; 1184 { 1185 register char *p; 1186 register struct optioninfo *o; 1187 char *subopt; 1188 extern bool atobool(); 1189 extern time_t convtime(); 1190 extern int QueueLA; 1191 extern int RefuseLA; 1192 extern bool Warn_Q_option; 1193 1194 errno = 0; 1195 if (opt == ' ') 1196 { 1197 /* full word options */ 1198 struct optioninfo *sel; 1199 1200 p = strchr(val, '='); 1201 if (p == NULL) 1202 p = &val[strlen(val)]; 1203 while (*--p == ' ') 1204 continue; 1205 while (*++p == ' ') 1206 *p = '\0'; 1207 if (p == val) 1208 { 1209 syserr("readcf: null option name"); 1210 return; 1211 } 1212 if (*p == '=') 1213 *p++ = '\0'; 1214 while (*p == ' ') 1215 p++; 1216 subopt = strchr(val, '.'); 1217 if (subopt != NULL) 1218 *subopt++ = '\0'; 1219 sel = NULL; 1220 for (o = OptionTab; o->o_name != NULL; o++) 1221 { 1222 if (strncasecmp(o->o_name, val, strlen(val)) != 0) 1223 continue; 1224 if (strlen(o->o_name) == strlen(val)) 1225 { 1226 /* completely specified -- this must be it */ 1227 sel = NULL; 1228 break; 1229 } 1230 if (sel != NULL) 1231 break; 1232 sel = o; 1233 } 1234 if (sel != NULL && o->o_name == NULL) 1235 o = sel; 1236 else if (o->o_name == NULL) 1237 { 1238 syserr("readcf: unknown option name %s", val); 1239 return; 1240 } 1241 else if (sel != NULL) 1242 { 1243 syserr("readcf: ambiguous option name %s (matches %s and %s)", 1244 val, sel->o_name, o->o_name); 1245 return; 1246 } 1247 if (strlen(val) != strlen(o->o_name)) 1248 { 1249 bool oldVerbose = Verbose; 1250 1251 Verbose = TRUE; 1252 message("Option %s used as abbreviation for %s", 1253 val, o->o_name); 1254 Verbose = oldVerbose; 1255 } 1256 opt = o->o_code; 1257 val = p; 1258 } 1259 else 1260 { 1261 for (o = OptionTab; o->o_name != NULL; o++) 1262 { 1263 if (o->o_code == opt) 1264 break; 1265 } 1266 subopt = NULL; 1267 } 1268 1269 if (tTd(37, 1)) 1270 { 1271 printf(isascii(opt) && isprint(opt) ? 1272 "setoption %s (%c).%s=%s" : 1273 "setoption %s (0x%x).%s=%s", 1274 o->o_name == NULL ? "<unknown>" : o->o_name, 1275 opt, 1276 subopt == NULL ? "" : subopt, 1277 val); 1278 } 1279 1280 /* 1281 ** See if this option is preset for us. 1282 */ 1283 1284 if (!sticky && bitnset(opt, StickyOpt)) 1285 { 1286 if (tTd(37, 1)) 1287 printf(" (ignored)\n"); 1288 return; 1289 } 1290 1291 /* 1292 ** Check to see if this option can be specified by this user. 1293 */ 1294 1295 if (!safe && RealUid == 0) 1296 safe = TRUE; 1297 if (!safe && !o->o_safe) 1298 { 1299 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1300 { 1301 if (tTd(37, 1)) 1302 printf(" (unsafe)"); 1303 if (RealUid != geteuid()) 1304 { 1305 if (tTd(37, 1)) 1306 printf("(Resetting uid)"); 1307 (void) setgid(RealGid); 1308 (void) setuid(RealUid); 1309 } 1310 } 1311 } 1312 if (tTd(37, 1)) 1313 printf("\n"); 1314 1315 switch (opt & 0xff) 1316 { 1317 case '7': /* force seven-bit input */ 1318 SevenBitInput = atobool(val); 1319 break; 1320 1321 case '8': /* handling of 8-bit input */ 1322 switch (*val) 1323 { 1324 case 'r': /* reject 8-bit, don't convert MIME */ 1325 MimeMode = 0; 1326 break; 1327 1328 case 'm': /* convert 8-bit, convert MIME */ 1329 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1330 break; 1331 1332 case 'j': /* "just send 8" */ 1333 MimeMode = MM_PASS8BIT; 1334 break; 1335 1336 case 'p': /* pass 8 bit, convert MIME */ 1337 MimeMode = MM_PASS8BIT|MM_CVTMIME; 1338 break; 1339 1340 case 's': /* strict adherence */ 1341 MimeMode = MM_CVTMIME; 1342 break; 1343 1344 case 'a': /* encode 8 bit if available */ 1345 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1346 break; 1347 1348 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1349 MimeMode = MM_MIME8BIT; 1350 break; 1351 1352 default: 1353 syserr("Unknown 8-bit mode %c", *val); 1354 exit(EX_USAGE); 1355 } 1356 break; 1357 1358 case 'A': /* set default alias file */ 1359 if (val[0] == '\0') 1360 setalias("aliases"); 1361 else 1362 setalias(val); 1363 break; 1364 1365 case 'a': /* look N minutes for "@:@" in alias file */ 1366 if (val[0] == '\0') 1367 SafeAlias = 5 * 60; /* five minutes */ 1368 else 1369 SafeAlias = convtime(val, 'm'); 1370 break; 1371 1372 case 'B': /* substitution for blank character */ 1373 SpaceSub = val[0]; 1374 if (SpaceSub == '\0') 1375 SpaceSub = ' '; 1376 break; 1377 1378 case 'b': /* min blocks free on queue fs/max msg size */ 1379 p = strchr(val, '/'); 1380 if (p != NULL) 1381 { 1382 *p++ = '\0'; 1383 MaxMessageSize = atol(p); 1384 } 1385 MinBlocksFree = atol(val); 1386 break; 1387 1388 case 'c': /* don't connect to "expensive" mailers */ 1389 NoConnect = atobool(val); 1390 break; 1391 1392 case 'C': /* checkpoint every N addresses */ 1393 CheckpointInterval = atoi(val); 1394 break; 1395 1396 case 'd': /* delivery mode */ 1397 switch (*val) 1398 { 1399 case '\0': 1400 e->e_sendmode = SM_DELIVER; 1401 break; 1402 1403 case SM_QUEUE: /* queue only */ 1404 #ifndef QUEUE 1405 syserr("need QUEUE to set -odqueue"); 1406 #endif /* QUEUE */ 1407 /* fall through..... */ 1408 1409 case SM_DELIVER: /* do everything */ 1410 case SM_FORK: /* fork after verification */ 1411 e->e_sendmode = *val; 1412 break; 1413 1414 default: 1415 syserr("Unknown delivery mode %c", *val); 1416 exit(EX_USAGE); 1417 } 1418 break; 1419 1420 case 'D': /* rebuild alias database as needed */ 1421 AutoRebuild = atobool(val); 1422 break; 1423 1424 case 'E': /* error message header/header file */ 1425 if (*val != '\0') 1426 ErrMsgFile = newstr(val); 1427 break; 1428 1429 case 'e': /* set error processing mode */ 1430 switch (*val) 1431 { 1432 case EM_QUIET: /* be silent about it */ 1433 case EM_MAIL: /* mail back */ 1434 case EM_BERKNET: /* do berknet error processing */ 1435 case EM_WRITE: /* write back (or mail) */ 1436 case EM_PRINT: /* print errors normally (default) */ 1437 e->e_errormode = *val; 1438 break; 1439 } 1440 break; 1441 1442 case 'F': /* file mode */ 1443 FileMode = atooct(val) & 0777; 1444 break; 1445 1446 case 'f': /* save Unix-style From lines on front */ 1447 SaveFrom = atobool(val); 1448 break; 1449 1450 case 'G': /* match recipients against GECOS field */ 1451 MatchGecos = atobool(val); 1452 break; 1453 1454 case 'g': /* default gid */ 1455 g_opt: 1456 if (isascii(*val) && isdigit(*val)) 1457 DefGid = atoi(val); 1458 else 1459 { 1460 register struct group *gr; 1461 1462 DefGid = -1; 1463 gr = getgrnam(val); 1464 if (gr == NULL) 1465 syserr("readcf: option %c: unknown group %s", 1466 opt, val); 1467 else 1468 DefGid = gr->gr_gid; 1469 } 1470 break; 1471 1472 case 'H': /* help file */ 1473 if (val[0] == '\0') 1474 HelpFile = "sendmail.hf"; 1475 else 1476 HelpFile = newstr(val); 1477 break; 1478 1479 case 'h': /* maximum hop count */ 1480 MaxHopCount = atoi(val); 1481 break; 1482 1483 case 'I': /* use internet domain name server */ 1484 #if NAMED_BIND 1485 for (p = val; *p != 0; ) 1486 { 1487 bool clearmode; 1488 char *q; 1489 struct resolverflags *rfp; 1490 1491 while (*p == ' ') 1492 p++; 1493 if (*p == '\0') 1494 break; 1495 clearmode = FALSE; 1496 if (*p == '-') 1497 clearmode = TRUE; 1498 else if (*p != '+') 1499 p--; 1500 p++; 1501 q = p; 1502 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1503 p++; 1504 if (*p != '\0') 1505 *p++ = '\0'; 1506 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1507 { 1508 if (strcasecmp(q, rfp->rf_name) == 0) 1509 break; 1510 } 1511 if (rfp->rf_name == NULL) 1512 syserr("readcf: I option value %s unrecognized", q); 1513 else if (clearmode) 1514 _res.options &= ~rfp->rf_bits; 1515 else 1516 _res.options |= rfp->rf_bits; 1517 } 1518 if (tTd(8, 2)) 1519 printf("_res.options = %x\n", _res.options); 1520 #else 1521 usrerr("name server (I option) specified but BIND not compiled in"); 1522 #endif 1523 break; 1524 1525 case 'i': /* ignore dot lines in message */ 1526 IgnrDot = atobool(val); 1527 break; 1528 1529 case 'j': /* send errors in MIME (RFC 1341) format */ 1530 SendMIMEErrors = atobool(val); 1531 break; 1532 1533 case 'J': /* .forward search path */ 1534 ForwardPath = newstr(val); 1535 break; 1536 1537 case 'k': /* connection cache size */ 1538 MaxMciCache = atoi(val); 1539 if (MaxMciCache < 0) 1540 MaxMciCache = 0; 1541 break; 1542 1543 case 'K': /* connection cache timeout */ 1544 MciCacheTimeout = convtime(val, 'm'); 1545 break; 1546 1547 case 'l': /* use Errors-To: header */ 1548 UseErrorsTo = atobool(val); 1549 break; 1550 1551 case 'L': /* log level */ 1552 if (safe || LogLevel < atoi(val)) 1553 LogLevel = atoi(val); 1554 break; 1555 1556 case 'M': /* define macro */ 1557 define(val[0], newstr(&val[1]), CurEnv); 1558 sticky = FALSE; 1559 break; 1560 1561 case 'm': /* send to me too */ 1562 MeToo = atobool(val); 1563 break; 1564 1565 case 'n': /* validate RHS in newaliases */ 1566 CheckAliases = atobool(val); 1567 break; 1568 1569 /* 'N' available -- was "net name" */ 1570 1571 case 'O': /* daemon options */ 1572 setdaemonoptions(val); 1573 break; 1574 1575 case 'o': /* assume old style headers */ 1576 if (atobool(val)) 1577 CurEnv->e_flags |= EF_OLDSTYLE; 1578 else 1579 CurEnv->e_flags &= ~EF_OLDSTYLE; 1580 break; 1581 1582 case 'p': /* select privacy level */ 1583 p = val; 1584 for (;;) 1585 { 1586 register struct prival *pv; 1587 extern struct prival PrivacyValues[]; 1588 1589 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1590 p++; 1591 if (*p == '\0') 1592 break; 1593 val = p; 1594 while (isascii(*p) && isalnum(*p)) 1595 p++; 1596 if (*p != '\0') 1597 *p++ = '\0'; 1598 1599 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1600 { 1601 if (strcasecmp(val, pv->pv_name) == 0) 1602 break; 1603 } 1604 if (pv->pv_name == NULL) 1605 syserr("readcf: Op line: %s unrecognized", val); 1606 PrivacyFlags |= pv->pv_flag; 1607 } 1608 break; 1609 1610 case 'P': /* postmaster copy address for returned mail */ 1611 PostMasterCopy = newstr(val); 1612 break; 1613 1614 case 'q': /* slope of queue only function */ 1615 QueueFactor = atoi(val); 1616 break; 1617 1618 case 'Q': /* queue directory */ 1619 if (val[0] == '\0') 1620 QueueDir = "mqueue"; 1621 else 1622 QueueDir = newstr(val); 1623 if (RealUid != 0 && !safe) 1624 Warn_Q_option = TRUE; 1625 break; 1626 1627 case 'R': /* don't prune routes */ 1628 DontPruneRoutes = atobool(val); 1629 break; 1630 1631 case 'r': /* read timeout */ 1632 if (subopt == NULL) 1633 inittimeouts(val); 1634 else 1635 settimeout(subopt, val); 1636 break; 1637 1638 case 'S': /* status file */ 1639 if (val[0] == '\0') 1640 StatFile = "sendmail.st"; 1641 else 1642 StatFile = newstr(val); 1643 break; 1644 1645 case 's': /* be super safe, even if expensive */ 1646 SuperSafe = atobool(val); 1647 break; 1648 1649 case 'T': /* queue timeout */ 1650 p = strchr(val, '/'); 1651 if (p != NULL) 1652 { 1653 *p++ = '\0'; 1654 settimeout("queuewarn", p); 1655 } 1656 settimeout("queuereturn", val); 1657 break; 1658 1659 case 't': /* time zone name */ 1660 TimeZoneSpec = newstr(val); 1661 break; 1662 1663 case 'U': /* location of user database */ 1664 UdbSpec = newstr(val); 1665 break; 1666 1667 case 'u': /* set default uid */ 1668 for (p = val; *p != '\0'; p++) 1669 { 1670 if (*p == '.' || *p == '/' || *p == ':') 1671 { 1672 *p++ = '\0'; 1673 break; 1674 } 1675 } 1676 if (isascii(*val) && isdigit(*val)) 1677 DefUid = atoi(val); 1678 else 1679 { 1680 register struct passwd *pw; 1681 1682 DefUid = -1; 1683 pw = getpwnam(val); 1684 if (pw == NULL) 1685 syserr("readcf: option u: unknown user %s", val); 1686 else 1687 { 1688 DefUid = pw->pw_uid; 1689 DefGid = pw->pw_gid; 1690 } 1691 } 1692 setdefuser(); 1693 1694 /* handle the group if it is there */ 1695 if (*p == '\0') 1696 break; 1697 val = p; 1698 goto g_opt; 1699 1700 case 'V': /* fallback MX host */ 1701 FallBackMX = newstr(val); 1702 break; 1703 1704 case 'v': /* run in verbose mode */ 1705 Verbose = atobool(val); 1706 break; 1707 1708 case 'w': /* if we are best MX, try host directly */ 1709 TryNullMXList = atobool(val); 1710 break; 1711 1712 /* 'W' available -- was wizard password */ 1713 1714 case 'x': /* load avg at which to auto-queue msgs */ 1715 QueueLA = atoi(val); 1716 break; 1717 1718 case 'X': /* load avg at which to auto-reject connections */ 1719 RefuseLA = atoi(val); 1720 break; 1721 1722 case 'y': /* work recipient factor */ 1723 WkRecipFact = atoi(val); 1724 break; 1725 1726 case 'Y': /* fork jobs during queue runs */ 1727 ForkQueueRuns = atobool(val); 1728 break; 1729 1730 case 'z': /* work message class factor */ 1731 WkClassFact = atoi(val); 1732 break; 1733 1734 case 'Z': /* work time factor */ 1735 WkTimeFact = atoi(val); 1736 break; 1737 1738 case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 1739 BrokenSmtpPeers = atobool(val); 1740 break; 1741 1742 case O_SQBH: /* sort work queue by host first */ 1743 SortQueueByHost = atobool(val); 1744 break; 1745 1746 case O_DNICE: /* delivery nice value */ 1747 DeliveryNiceness = atoi(val); 1748 break; 1749 1750 case O_MQA: /* minimum queue age between deliveries */ 1751 MinQueueAge = convtime(val, 'm'); 1752 break; 1753 1754 case O_MHSA: /* maximum age of cached host status */ 1755 MaxHostStatAge = convtime(val, 'm'); 1756 break; 1757 1758 case O_DEFCHARSET: /* default character set for mimefying */ 1759 DefaultCharSet = newstr(val); 1760 break; 1761 1762 case O_SSFILE: /* service switch file */ 1763 ServiceSwitchFile = newstr(val); 1764 break; 1765 1766 default: 1767 break; 1768 } 1769 if (sticky) 1770 setbitn(opt, StickyOpt); 1771 return; 1772 } 1773 /* 1774 ** SETCLASS -- set a word into a class 1775 ** 1776 ** Parameters: 1777 ** class -- the class to put the word in. 1778 ** word -- the word to enter 1779 ** 1780 ** Returns: 1781 ** none. 1782 ** 1783 ** Side Effects: 1784 ** puts the word into the symbol table. 1785 */ 1786 1787 setclass(class, word) 1788 int class; 1789 char *word; 1790 { 1791 register STAB *s; 1792 1793 if (tTd(37, 8)) 1794 printf("setclass(%c, %s)\n", class, word); 1795 s = stab(word, ST_CLASS, ST_ENTER); 1796 setbitn(class, s->s_class); 1797 } 1798 /* 1799 ** MAKEMAPENTRY -- create a map entry 1800 ** 1801 ** Parameters: 1802 ** line -- the config file line 1803 ** 1804 ** Returns: 1805 ** TRUE if it successfully entered the map entry. 1806 ** FALSE otherwise (usually syntax error). 1807 ** 1808 ** Side Effects: 1809 ** Enters the map into the dictionary. 1810 */ 1811 1812 void 1813 makemapentry(line) 1814 char *line; 1815 { 1816 register char *p; 1817 char *mapname; 1818 char *classname; 1819 register STAB *s; 1820 STAB *class; 1821 1822 for (p = line; isascii(*p) && isspace(*p); p++) 1823 continue; 1824 if (!(isascii(*p) && isalnum(*p))) 1825 { 1826 syserr("readcf: config K line: no map name"); 1827 return; 1828 } 1829 1830 mapname = p; 1831 while ((isascii(*++p) && isalnum(*p)) || *p == '.') 1832 continue; 1833 if (*p != '\0') 1834 *p++ = '\0'; 1835 while (isascii(*p) && isspace(*p)) 1836 p++; 1837 if (!(isascii(*p) && isalnum(*p))) 1838 { 1839 syserr("readcf: config K line, map %s: no map class", mapname); 1840 return; 1841 } 1842 classname = p; 1843 while (isascii(*++p) && isalnum(*p)) 1844 continue; 1845 if (*p != '\0') 1846 *p++ = '\0'; 1847 while (isascii(*p) && isspace(*p)) 1848 p++; 1849 1850 /* look up the class */ 1851 class = stab(classname, ST_MAPCLASS, ST_FIND); 1852 if (class == NULL) 1853 { 1854 syserr("readcf: map %s: class %s not available", mapname, classname); 1855 return; 1856 } 1857 1858 /* enter the map */ 1859 s = stab(mapname, ST_MAP, ST_ENTER); 1860 s->s_map.map_class = &class->s_mapclass; 1861 s->s_map.map_mname = newstr(mapname); 1862 1863 if (class->s_mapclass.map_parse(&s->s_map, p)) 1864 s->s_map.map_mflags |= MF_VALID; 1865 1866 if (tTd(37, 5)) 1867 { 1868 printf("map %s, class %s, flags %x, file %s,\n", 1869 s->s_map.map_mname, s->s_map.map_class->map_cname, 1870 s->s_map.map_mflags, 1871 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 1872 printf("\tapp %s, domain %s, rebuild %s\n", 1873 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 1874 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 1875 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 1876 } 1877 } 1878 /* 1879 ** INITTIMEOUTS -- parse and set timeout values 1880 ** 1881 ** Parameters: 1882 ** val -- a pointer to the values. If NULL, do initial 1883 ** settings. 1884 ** 1885 ** Returns: 1886 ** none. 1887 ** 1888 ** Side Effects: 1889 ** Initializes the TimeOuts structure 1890 */ 1891 1892 #define SECONDS 1893 #define MINUTES * 60 1894 #define HOUR * 3600 1895 1896 inittimeouts(val) 1897 register char *val; 1898 { 1899 register char *p; 1900 extern time_t convtime(); 1901 1902 if (val == NULL) 1903 { 1904 TimeOuts.to_initial = (time_t) 5 MINUTES; 1905 TimeOuts.to_helo = (time_t) 5 MINUTES; 1906 TimeOuts.to_mail = (time_t) 10 MINUTES; 1907 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1908 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1909 TimeOuts.to_datablock = (time_t) 1 HOUR; 1910 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1911 TimeOuts.to_rset = (time_t) 5 MINUTES; 1912 TimeOuts.to_quit = (time_t) 2 MINUTES; 1913 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1914 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1915 TimeOuts.to_ident = (time_t) 30 SECONDS; 1916 TimeOuts.to_fileopen = (time_t) 60 SECONDS; 1917 return; 1918 } 1919 1920 for (;; val = p) 1921 { 1922 while (isascii(*val) && isspace(*val)) 1923 val++; 1924 if (*val == '\0') 1925 break; 1926 for (p = val; *p != '\0' && *p != ','; p++) 1927 continue; 1928 if (*p != '\0') 1929 *p++ = '\0'; 1930 1931 if (isascii(*val) && isdigit(*val)) 1932 { 1933 /* old syntax -- set everything */ 1934 TimeOuts.to_mail = convtime(val, 'm'); 1935 TimeOuts.to_rcpt = TimeOuts.to_mail; 1936 TimeOuts.to_datainit = TimeOuts.to_mail; 1937 TimeOuts.to_datablock = TimeOuts.to_mail; 1938 TimeOuts.to_datafinal = TimeOuts.to_mail; 1939 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1940 continue; 1941 } 1942 else 1943 { 1944 register char *q = strchr(val, ':'); 1945 1946 if (q == NULL && (q = strchr(val, '=')) == NULL) 1947 { 1948 /* syntax error */ 1949 continue; 1950 } 1951 *q++ = '\0'; 1952 settimeout(val, q); 1953 } 1954 } 1955 } 1956 /* 1957 ** SETTIMEOUT -- set an individual timeout 1958 ** 1959 ** Parameters: 1960 ** name -- the name of the timeout. 1961 ** val -- the value of the timeout. 1962 ** 1963 ** Returns: 1964 ** none. 1965 */ 1966 1967 settimeout(name, val) 1968 char *name; 1969 char *val; 1970 { 1971 register char *p; 1972 time_t to; 1973 extern time_t convtime(); 1974 1975 to = convtime(val, 'm'); 1976 p = strchr(name, '.'); 1977 if (p != NULL) 1978 *p++ = '\0'; 1979 1980 if (strcasecmp(name, "initial") == 0) 1981 TimeOuts.to_initial = to; 1982 else if (strcasecmp(name, "mail") == 0) 1983 TimeOuts.to_mail = to; 1984 else if (strcasecmp(name, "rcpt") == 0) 1985 TimeOuts.to_rcpt = to; 1986 else if (strcasecmp(name, "datainit") == 0) 1987 TimeOuts.to_datainit = to; 1988 else if (strcasecmp(name, "datablock") == 0) 1989 TimeOuts.to_datablock = to; 1990 else if (strcasecmp(name, "datafinal") == 0) 1991 TimeOuts.to_datafinal = to; 1992 else if (strcasecmp(name, "command") == 0) 1993 TimeOuts.to_nextcommand = to; 1994 else if (strcasecmp(name, "rset") == 0) 1995 TimeOuts.to_rset = to; 1996 else if (strcasecmp(name, "helo") == 0) 1997 TimeOuts.to_helo = to; 1998 else if (strcasecmp(name, "quit") == 0) 1999 TimeOuts.to_quit = to; 2000 else if (strcasecmp(name, "misc") == 0) 2001 TimeOuts.to_miscshort = to; 2002 else if (strcasecmp(name, "ident") == 0) 2003 TimeOuts.to_ident = to; 2004 else if (strcasecmp(name, "fileopen") == 0) 2005 TimeOuts.to_fileopen = to; 2006 else if (strcasecmp(name, "queuewarn") == 0) 2007 { 2008 to = convtime(val, 'h'); 2009 if (p == NULL || strcmp(p, "*") == 0) 2010 { 2011 TimeOuts.to_q_warning[TOC_NORMAL] = to; 2012 TimeOuts.to_q_warning[TOC_URGENT] = to; 2013 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 2014 } 2015 else if (strcasecmp(p, "normal") == 0) 2016 TimeOuts.to_q_warning[TOC_NORMAL] = to; 2017 else if (strcasecmp(p, "urgent") == 0) 2018 TimeOuts.to_q_warning[TOC_URGENT] = to; 2019 else if (strcasecmp(p, "non-urgent") == 0) 2020 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 2021 else 2022 syserr("settimeout: invalid queuewarn subtimeout %s", p); 2023 } 2024 else if (strcasecmp(name, "queuereturn") == 0) 2025 { 2026 to = convtime(val, 'd'); 2027 if (p == NULL || strcmp(p, "*") == 0) 2028 { 2029 TimeOuts.to_q_return[TOC_NORMAL] = to; 2030 TimeOuts.to_q_return[TOC_URGENT] = to; 2031 TimeOuts.to_q_return[TOC_NONURGENT] = to; 2032 } 2033 else if (strcasecmp(p, "normal") == 0) 2034 TimeOuts.to_q_return[TOC_NORMAL] = to; 2035 else if (strcasecmp(p, "urgent") == 0) 2036 TimeOuts.to_q_return[TOC_URGENT] = to; 2037 else if (strcasecmp(p, "non-urgent") == 0) 2038 TimeOuts.to_q_return[TOC_NONURGENT] = to; 2039 else 2040 syserr("settimeout: invalid queuereturn subtimeout %s", p); 2041 } 2042 else 2043 syserr("settimeout: invalid timeout %s", name); 2044 } 2045