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.46 (Berkeley) 11/12/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 /* 534 ** TOOMANY -- signal too many of some option 535 ** 536 ** Parameters: 537 ** id -- the id of the error line 538 ** maxcnt -- the maximum possible values 539 ** 540 ** Returns: 541 ** none. 542 ** 543 ** Side Effects: 544 ** gives a syserr. 545 */ 546 547 toomany(id, maxcnt) 548 char id; 549 int maxcnt; 550 { 551 syserr("too many %c lines, %d max", id, maxcnt); 552 } 553 /* 554 ** FILECLASS -- read members of a class from a file 555 ** 556 ** Parameters: 557 ** class -- class to define. 558 ** filename -- name of file to read. 559 ** fmt -- scanf string to use for match. 560 ** safe -- if set, this is a safe read. 561 ** optional -- if set, it is not an error for the file to 562 ** not exist. 563 ** 564 ** Returns: 565 ** none 566 ** 567 ** Side Effects: 568 ** 569 ** puts all lines in filename that match a scanf into 570 ** the named class. 571 */ 572 573 fileclass(class, filename, fmt, safe, optional) 574 int class; 575 char *filename; 576 char *fmt; 577 bool safe; 578 bool optional; 579 { 580 FILE *f; 581 struct stat stbuf; 582 char buf[MAXLINE]; 583 584 if (tTd(37, 2)) 585 printf("fileclass(%s, fmt=%s)\n", filename, fmt); 586 587 if (filename[0] == '|') 588 { 589 syserr("fileclass: pipes (F%c%s) not supported due to security problems", 590 class, filename); 591 return; 592 } 593 if (stat(filename, &stbuf) < 0) 594 { 595 if (tTd(37, 2)) 596 printf(" cannot stat (%s)\n", errstring(errno)); 597 if (!optional) 598 syserr("fileclass: cannot stat %s", filename); 599 return; 600 } 601 if (!S_ISREG(stbuf.st_mode)) 602 { 603 syserr("fileclass: %s not a regular file", filename); 604 return; 605 } 606 if (!safe && access(filename, R_OK) < 0) 607 { 608 syserr("fileclass: access denied on %s", filename); 609 return; 610 } 611 f = fopen(filename, "r"); 612 if (f == NULL) 613 { 614 syserr("fileclass: cannot open %s", filename); 615 return; 616 } 617 618 while (fgets(buf, sizeof buf, f) != NULL) 619 { 620 register STAB *s; 621 register char *p; 622 # ifdef SCANF 623 char wordbuf[MAXNAME+1]; 624 625 if (sscanf(buf, fmt, wordbuf) != 1) 626 continue; 627 p = wordbuf; 628 # else /* SCANF */ 629 p = buf; 630 # endif /* SCANF */ 631 632 /* 633 ** Break up the match into words. 634 */ 635 636 while (*p != '\0') 637 { 638 register char *q; 639 640 /* strip leading spaces */ 641 while (isascii(*p) && isspace(*p)) 642 p++; 643 if (*p == '\0') 644 break; 645 646 /* find the end of the word */ 647 q = p; 648 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 649 p++; 650 if (*p != '\0') 651 *p++ = '\0'; 652 653 /* enter the word in the symbol table */ 654 setclass(class, q); 655 } 656 } 657 658 (void) fclose(f); 659 } 660 /* 661 ** MAKEMAILER -- define a new mailer. 662 ** 663 ** Parameters: 664 ** line -- description of mailer. This is in labeled 665 ** fields. The fields are: 666 ** P -- the path to the mailer 667 ** F -- the flags associated with the mailer 668 ** A -- the argv for this mailer 669 ** S -- the sender rewriting set 670 ** R -- the recipient rewriting set 671 ** E -- the eol string 672 ** The first word is the canonical name of the mailer. 673 ** 674 ** Returns: 675 ** none. 676 ** 677 ** Side Effects: 678 ** enters the mailer into the mailer table. 679 */ 680 681 makemailer(line) 682 char *line; 683 { 684 register char *p; 685 register struct mailer *m; 686 register STAB *s; 687 int i; 688 char fcode; 689 auto char *endp; 690 extern int NextMailer; 691 extern char **makeargv(); 692 extern char *munchstring(); 693 extern long atol(); 694 695 /* allocate a mailer and set up defaults */ 696 m = (struct mailer *) xalloc(sizeof *m); 697 bzero((char *) m, sizeof *m); 698 m->m_eol = "\n"; 699 m->m_uid = m->m_gid = 0; 700 701 /* collect the mailer name */ 702 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 703 continue; 704 if (*p != '\0') 705 *p++ = '\0'; 706 m->m_name = newstr(line); 707 708 /* now scan through and assign info from the fields */ 709 while (*p != '\0') 710 { 711 auto char *delimptr; 712 713 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 714 p++; 715 716 /* p now points to field code */ 717 fcode = *p; 718 while (*p != '\0' && *p != '=' && *p != ',') 719 p++; 720 if (*p++ != '=') 721 { 722 syserr("mailer %s: `=' expected", m->m_name); 723 return; 724 } 725 while (isascii(*p) && isspace(*p)) 726 p++; 727 728 /* p now points to the field body */ 729 p = munchstring(p, &delimptr); 730 731 /* install the field into the mailer struct */ 732 switch (fcode) 733 { 734 case 'P': /* pathname */ 735 m->m_mailer = newstr(p); 736 break; 737 738 case 'F': /* flags */ 739 for (; *p != '\0'; p++) 740 if (!(isascii(*p) && isspace(*p))) 741 setbitn(*p, m->m_flags); 742 break; 743 744 case 'S': /* sender rewriting ruleset */ 745 case 'R': /* recipient rewriting ruleset */ 746 i = strtol(p, &endp, 10); 747 if (i < 0 || i >= MAXRWSETS) 748 { 749 syserr("invalid rewrite set, %d max", MAXRWSETS); 750 return; 751 } 752 if (fcode == 'S') 753 m->m_sh_rwset = m->m_se_rwset = i; 754 else 755 m->m_rh_rwset = m->m_re_rwset = i; 756 757 p = endp; 758 if (*p++ == '/') 759 { 760 i = strtol(p, NULL, 10); 761 if (i < 0 || i >= MAXRWSETS) 762 { 763 syserr("invalid rewrite set, %d max", 764 MAXRWSETS); 765 return; 766 } 767 if (fcode == 'S') 768 m->m_sh_rwset = i; 769 else 770 m->m_rh_rwset = i; 771 } 772 break; 773 774 case 'E': /* end of line string */ 775 m->m_eol = newstr(p); 776 break; 777 778 case 'A': /* argument vector */ 779 m->m_argv = makeargv(p); 780 break; 781 782 case 'M': /* maximum message size */ 783 m->m_maxsize = atol(p); 784 break; 785 786 case 'L': /* maximum line length */ 787 m->m_linelimit = atoi(p); 788 break; 789 790 case 'D': /* working directory */ 791 m->m_execdir = newstr(p); 792 break; 793 794 case 'C': /* default charset */ 795 m->m_defcharset = newstr(p); 796 break; 797 798 case 'U': /* user id */ 799 if (isascii(*p) && !isdigit(*p)) 800 { 801 char *q = p; 802 struct passwd *pw; 803 804 while (isascii(*p) && isalnum(*p)) 805 p++; 806 while (isascii(*p) && isspace(*p)) 807 *p++ = '\0'; 808 if (*p != '\0') 809 *p++ = '\0'; 810 pw = getpwnam(q); 811 if (pw == NULL) 812 syserr("readcf: mailer U= flag: unknown user %s", q); 813 else 814 { 815 m->m_uid = pw->pw_uid; 816 m->m_gid = pw->pw_gid; 817 } 818 } 819 else 820 { 821 auto char *q; 822 823 m->m_uid = strtol(p, &q, 0); 824 p = q; 825 } 826 while (isascii(*p) && isspace(*p)) 827 p++; 828 if (*p == '\0') 829 break; 830 if (isascii(*p) && !isdigit(*p)) 831 { 832 char *q = p; 833 struct group *gr; 834 835 while (isascii(*p) && isalnum(*p)) 836 p++; 837 *p++ = '\0'; 838 gr = getgrnam(q); 839 if (gr == NULL) 840 syserr("readcf: mailer U= flag: unknown group %s", q); 841 else 842 m->m_gid = gr->gr_gid; 843 } 844 else 845 { 846 m->m_gid = strtol(p, NULL, 0); 847 } 848 break; 849 } 850 851 p = delimptr; 852 } 853 854 /* do some heuristic cleanup for back compatibility */ 855 if (bitnset(M_LIMITS, m->m_flags)) 856 { 857 if (m->m_linelimit == 0) 858 m->m_linelimit = SMTPLINELIM; 859 if (ConfigLevel < 2) 860 setbitn(M_7BITS, m->m_flags); 861 } 862 863 /* do some rationality checking */ 864 if (m->m_argv == NULL) 865 { 866 syserr("M%s: A= argument required", m->m_name); 867 return; 868 } 869 if (m->m_mailer == NULL) 870 { 871 syserr("M%s: P= argument required", m->m_name); 872 return; 873 } 874 875 if (NextMailer >= MAXMAILERS) 876 { 877 syserr("too many mailers defined (%d max)", MAXMAILERS); 878 return; 879 } 880 881 s = stab(m->m_name, ST_MAILER, ST_ENTER); 882 if (s->s_mailer != NULL) 883 { 884 i = s->s_mailer->m_mno; 885 free(s->s_mailer); 886 } 887 else 888 { 889 i = NextMailer++; 890 } 891 Mailer[i] = s->s_mailer = m; 892 m->m_mno = i; 893 } 894 /* 895 ** MUNCHSTRING -- translate a string into internal form. 896 ** 897 ** Parameters: 898 ** p -- the string to munch. 899 ** delimptr -- if non-NULL, set to the pointer of the 900 ** field delimiter character. 901 ** 902 ** Returns: 903 ** the munched string. 904 */ 905 906 char * 907 munchstring(p, delimptr) 908 register char *p; 909 char **delimptr; 910 { 911 register char *q; 912 bool backslash = FALSE; 913 bool quotemode = FALSE; 914 static char buf[MAXLINE]; 915 916 for (q = buf; *p != '\0'; p++) 917 { 918 if (backslash) 919 { 920 /* everything is roughly literal */ 921 backslash = FALSE; 922 switch (*p) 923 { 924 case 'r': /* carriage return */ 925 *q++ = '\r'; 926 continue; 927 928 case 'n': /* newline */ 929 *q++ = '\n'; 930 continue; 931 932 case 'f': /* form feed */ 933 *q++ = '\f'; 934 continue; 935 936 case 'b': /* backspace */ 937 *q++ = '\b'; 938 continue; 939 } 940 *q++ = *p; 941 } 942 else 943 { 944 if (*p == '\\') 945 backslash = TRUE; 946 else if (*p == '"') 947 quotemode = !quotemode; 948 else if (quotemode || *p != ',') 949 *q++ = *p; 950 else 951 break; 952 } 953 } 954 955 if (delimptr != NULL) 956 *delimptr = p; 957 *q++ = '\0'; 958 return (buf); 959 } 960 /* 961 ** MAKEARGV -- break up a string into words 962 ** 963 ** Parameters: 964 ** p -- the string to break up. 965 ** 966 ** Returns: 967 ** a char **argv (dynamically allocated) 968 ** 969 ** Side Effects: 970 ** munges p. 971 */ 972 973 char ** 974 makeargv(p) 975 register char *p; 976 { 977 char *q; 978 int i; 979 char **avp; 980 char *argv[MAXPV + 1]; 981 982 /* take apart the words */ 983 i = 0; 984 while (*p != '\0' && i < MAXPV) 985 { 986 q = p; 987 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 988 p++; 989 while (isascii(*p) && isspace(*p)) 990 *p++ = '\0'; 991 argv[i++] = newstr(q); 992 } 993 argv[i++] = NULL; 994 995 /* now make a copy of the argv */ 996 avp = (char **) xalloc(sizeof *avp * i); 997 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 998 999 return (avp); 1000 } 1001 /* 1002 ** PRINTRULES -- print rewrite rules (for debugging) 1003 ** 1004 ** Parameters: 1005 ** none. 1006 ** 1007 ** Returns: 1008 ** none. 1009 ** 1010 ** Side Effects: 1011 ** prints rewrite rules. 1012 */ 1013 1014 printrules() 1015 { 1016 register struct rewrite *rwp; 1017 register int ruleset; 1018 1019 for (ruleset = 0; ruleset < 10; ruleset++) 1020 { 1021 if (RewriteRules[ruleset] == NULL) 1022 continue; 1023 printf("\n----Rule Set %d:", ruleset); 1024 1025 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 1026 { 1027 printf("\nLHS:"); 1028 printav(rwp->r_lhs); 1029 printf("RHS:"); 1030 printav(rwp->r_rhs); 1031 } 1032 } 1033 } 1034 1035 /* 1036 ** SETOPTION -- set global processing option 1037 ** 1038 ** Parameters: 1039 ** opt -- option name. 1040 ** val -- option value (as a text string). 1041 ** safe -- set if this came from a configuration file. 1042 ** Some options (if set from the command line) will 1043 ** reset the user id to avoid security problems. 1044 ** sticky -- if set, don't let other setoptions override 1045 ** this value. 1046 ** e -- the main envelope. 1047 ** 1048 ** Returns: 1049 ** none. 1050 ** 1051 ** Side Effects: 1052 ** Sets options as implied by the arguments. 1053 */ 1054 1055 static BITMAP StickyOpt; /* set if option is stuck */ 1056 1057 1058 #if NAMED_BIND 1059 1060 struct resolverflags 1061 { 1062 char *rf_name; /* name of the flag */ 1063 long rf_bits; /* bits to set/clear */ 1064 } ResolverFlags[] = 1065 { 1066 "debug", RES_DEBUG, 1067 "aaonly", RES_AAONLY, 1068 "usevc", RES_USEVC, 1069 "primary", RES_PRIMARY, 1070 "igntc", RES_IGNTC, 1071 "recurse", RES_RECURSE, 1072 "defnames", RES_DEFNAMES, 1073 "stayopen", RES_STAYOPEN, 1074 "dnsrch", RES_DNSRCH, 1075 "true", 0, /* to avoid error on old syntax */ 1076 NULL, 0 1077 }; 1078 1079 #endif 1080 1081 struct optioninfo 1082 { 1083 char *o_name; /* long name of option */ 1084 u_char o_code; /* short name of option */ 1085 bool o_safe; /* safe for random people to use */ 1086 } OptionTab[] = 1087 { 1088 "SevenBitInput", '7', TRUE, 1089 "EightBitMode", '8', TRUE, 1090 "AliasFile", 'A', FALSE, 1091 "AliasWait", 'a', FALSE, 1092 "BlankSub", 'B', FALSE, 1093 "MinFreeBlocks", 'b', TRUE, 1094 "CheckpointInterval", 'C', TRUE, 1095 "HoldExpensive", 'c', FALSE, 1096 "AutoRebuildAliases", 'D', FALSE, 1097 "DeliveryMode", 'd', TRUE, 1098 "ErrorHeader", 'E', FALSE, 1099 "ErrorMode", 'e', TRUE, 1100 "TempFileMode", 'F', FALSE, 1101 "SaveFromLine", 'f', FALSE, 1102 "MatchGECOS", 'G', FALSE, 1103 "HelpFile", 'H', FALSE, 1104 "MaxHopCount", 'h', FALSE, 1105 "NameServerOptions", 'I', FALSE, 1106 "IgnoreDots", 'i', TRUE, 1107 "ForwardPath", 'J', FALSE, 1108 "SendMimeErrors", 'j', TRUE, 1109 "ConnectionCacheSize", 'k', FALSE, 1110 "ConnectionCacheTimeout", 'K', FALSE, 1111 "UseErrorsTo", 'l', FALSE, 1112 "LogLevel", 'L', FALSE, 1113 "MeToo", 'm', TRUE, 1114 "CheckAliases", 'n', FALSE, 1115 "OldStyleHeaders", 'o', TRUE, 1116 "DaemonPortOptions", 'O', FALSE, 1117 "PrivacyOptions", 'p', TRUE, 1118 "PostmasterCopy", 'P', FALSE, 1119 "QueueFactor", 'q', FALSE, 1120 "QueueDirectory", 'Q', FALSE, 1121 "DontPruneRoutes", 'R', FALSE, 1122 "Timeouts", 'r', TRUE, 1123 "StatusFile", 'S', FALSE, 1124 "SuperSafe", 's', TRUE, 1125 "QueueTimeout", 'T', FALSE, 1126 "TimeZoneSpec", 't', FALSE, 1127 "UserDatabaseSpec", 'U', FALSE, 1128 "DefaultUser", 'u', FALSE, 1129 "FallbackMXhost", 'V', FALSE, 1130 "Verbose", 'v', TRUE, 1131 "TryNullMXList", 'w', TRUE, 1132 "QueueLA", 'x', FALSE, 1133 "RefuseLA", 'X', FALSE, 1134 "RecipientFactor", 'y', FALSE, 1135 "ForkQueueRuns", 'Y', FALSE, 1136 "ClassFactor", 'z', FALSE, 1137 "TimeFactor", 'Z', FALSE, 1138 #define O_BSP 0x80 1139 "BrokenSmtpPeers", O_BSP, TRUE, 1140 #define O_SQBH 0x81 1141 "SortQueueByHost", O_SQBH, TRUE, 1142 #define O_DNICE 0x82 1143 "DeliveryNiceness", O_DNICE, TRUE, 1144 #define O_MQA 0x83 1145 "MinQueueAge", O_MQA, TRUE, 1146 #define O_MHSA 0x84 1147 "MaxHostStatAge", O_MHSA, TRUE, 1148 #define O_DEFCHARSET 0x85 1149 "DefaultCharSet", O_DEFCHARSET, TRUE, 1150 #define O_SSFILE 0x86 1151 "ServiceSwitchFile", O_SSFILE, FALSE, 1152 1153 NULL, '\0', FALSE, 1154 }; 1155 1156 1157 1158 setoption(opt, val, safe, sticky, e) 1159 u_char opt; 1160 char *val; 1161 bool safe; 1162 bool sticky; 1163 register ENVELOPE *e; 1164 { 1165 register char *p; 1166 register struct optioninfo *o; 1167 char *subopt; 1168 extern bool atobool(); 1169 extern time_t convtime(); 1170 extern int QueueLA; 1171 extern int RefuseLA; 1172 extern bool Warn_Q_option; 1173 1174 errno = 0; 1175 if (opt == ' ') 1176 { 1177 /* full word options */ 1178 struct optioninfo *sel; 1179 1180 p = strchr(val, '='); 1181 if (p == NULL) 1182 p = &val[strlen(val)]; 1183 while (*--p == ' ') 1184 continue; 1185 while (*++p == ' ') 1186 *p = '\0'; 1187 if (p == val) 1188 { 1189 syserr("readcf: null option name"); 1190 return; 1191 } 1192 if (*p == '=') 1193 *p++ = '\0'; 1194 while (*p == ' ') 1195 p++; 1196 subopt = strchr(val, '.'); 1197 if (subopt != NULL) 1198 *subopt++ = '\0'; 1199 sel = NULL; 1200 for (o = OptionTab; o->o_name != NULL; o++) 1201 { 1202 if (strncasecmp(o->o_name, val, strlen(val)) != 0) 1203 continue; 1204 if (strlen(o->o_name) == strlen(val)) 1205 { 1206 /* completely specified -- this must be it */ 1207 sel = NULL; 1208 break; 1209 } 1210 if (sel != NULL) 1211 break; 1212 sel = o; 1213 } 1214 if (sel != NULL && o->o_name == NULL) 1215 o = sel; 1216 else if (o->o_name == NULL) 1217 { 1218 syserr("readcf: unknown option name %s", val); 1219 return; 1220 } 1221 else if (sel != NULL) 1222 { 1223 syserr("readcf: ambiguous option name %s (matches %s and %s)", 1224 val, sel->o_name, o->o_name); 1225 return; 1226 } 1227 if (strlen(val) != strlen(o->o_name)) 1228 { 1229 bool oldVerbose = Verbose; 1230 1231 Verbose = TRUE; 1232 message("Option %s used as abbreviation for %s", 1233 val, o->o_name); 1234 Verbose = oldVerbose; 1235 } 1236 opt = o->o_code; 1237 val = p; 1238 } 1239 else 1240 { 1241 for (o = OptionTab; o->o_name != NULL; o++) 1242 { 1243 if (o->o_code == opt) 1244 break; 1245 } 1246 subopt = NULL; 1247 } 1248 1249 if (tTd(37, 1)) 1250 { 1251 printf(isascii(opt) && isprint(opt) ? 1252 "setoption %s (%c).%s=%s" : 1253 "setoption %s (0x%x).%s=%s", 1254 o->o_name == NULL ? "<unknown>" : o->o_name, 1255 opt, 1256 subopt == NULL ? "" : subopt, 1257 val); 1258 } 1259 1260 /* 1261 ** See if this option is preset for us. 1262 */ 1263 1264 if (!sticky && bitnset(opt, StickyOpt)) 1265 { 1266 if (tTd(37, 1)) 1267 printf(" (ignored)\n"); 1268 return; 1269 } 1270 1271 /* 1272 ** Check to see if this option can be specified by this user. 1273 */ 1274 1275 if (!safe && RealUid == 0) 1276 safe = TRUE; 1277 if (!safe && !o->o_safe) 1278 { 1279 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1280 { 1281 if (tTd(37, 1)) 1282 printf(" (unsafe)"); 1283 if (RealUid != geteuid()) 1284 { 1285 if (tTd(37, 1)) 1286 printf("(Resetting uid)"); 1287 (void) setgid(RealGid); 1288 (void) setuid(RealUid); 1289 } 1290 } 1291 } 1292 if (tTd(37, 1)) 1293 printf("\n"); 1294 1295 switch (opt & 0xff) 1296 { 1297 case '7': /* force seven-bit input */ 1298 SevenBitInput = atobool(val); 1299 break; 1300 1301 case '8': /* handling of 8-bit input */ 1302 switch (*val) 1303 { 1304 case 'r': /* reject 8-bit, don't convert MIME */ 1305 MimeMode = 0; 1306 break; 1307 1308 case 'm': /* convert 8-bit, convert MIME */ 1309 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1310 break; 1311 1312 case 'j': /* "just send 8" */ 1313 MimeMode = MM_PASS8BIT; 1314 break; 1315 1316 case 'p': /* pass 8 bit, convert MIME */ 1317 MimeMode = MM_PASS8BIT|MM_CVTMIME; 1318 break; 1319 1320 case 's': /* strict adherence */ 1321 MimeMode = MM_CVTMIME; 1322 break; 1323 1324 case 'a': /* encode 8 bit if available */ 1325 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1326 break; 1327 1328 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1329 MimeMode = MM_MIME8BIT; 1330 break; 1331 1332 default: 1333 syserr("Unknown 8-bit mode %c", *val); 1334 exit(EX_USAGE); 1335 } 1336 break; 1337 1338 case 'A': /* set default alias file */ 1339 if (val[0] == '\0') 1340 setalias("aliases"); 1341 else 1342 setalias(val); 1343 break; 1344 1345 case 'a': /* look N minutes for "@:@" in alias file */ 1346 if (val[0] == '\0') 1347 SafeAlias = 5 * 60; /* five minutes */ 1348 else 1349 SafeAlias = convtime(val, 'm'); 1350 break; 1351 1352 case 'B': /* substitution for blank character */ 1353 SpaceSub = val[0]; 1354 if (SpaceSub == '\0') 1355 SpaceSub = ' '; 1356 break; 1357 1358 case 'b': /* min blocks free on queue fs/max msg size */ 1359 p = strchr(val, '/'); 1360 if (p != NULL) 1361 { 1362 *p++ = '\0'; 1363 MaxMessageSize = atol(p); 1364 } 1365 MinBlocksFree = atol(val); 1366 break; 1367 1368 case 'c': /* don't connect to "expensive" mailers */ 1369 NoConnect = atobool(val); 1370 break; 1371 1372 case 'C': /* checkpoint every N addresses */ 1373 CheckpointInterval = atoi(val); 1374 break; 1375 1376 case 'd': /* delivery mode */ 1377 switch (*val) 1378 { 1379 case '\0': 1380 e->e_sendmode = SM_DELIVER; 1381 break; 1382 1383 case SM_QUEUE: /* queue only */ 1384 #ifndef QUEUE 1385 syserr("need QUEUE to set -odqueue"); 1386 #endif /* QUEUE */ 1387 /* fall through..... */ 1388 1389 case SM_DELIVER: /* do everything */ 1390 case SM_FORK: /* fork after verification */ 1391 e->e_sendmode = *val; 1392 break; 1393 1394 default: 1395 syserr("Unknown delivery mode %c", *val); 1396 exit(EX_USAGE); 1397 } 1398 break; 1399 1400 case 'D': /* rebuild alias database as needed */ 1401 AutoRebuild = atobool(val); 1402 break; 1403 1404 case 'E': /* error message header/header file */ 1405 if (*val != '\0') 1406 ErrMsgFile = newstr(val); 1407 break; 1408 1409 case 'e': /* set error processing mode */ 1410 switch (*val) 1411 { 1412 case EM_QUIET: /* be silent about it */ 1413 case EM_MAIL: /* mail back */ 1414 case EM_BERKNET: /* do berknet error processing */ 1415 case EM_WRITE: /* write back (or mail) */ 1416 case EM_PRINT: /* print errors normally (default) */ 1417 e->e_errormode = *val; 1418 break; 1419 } 1420 break; 1421 1422 case 'F': /* file mode */ 1423 FileMode = atooct(val) & 0777; 1424 break; 1425 1426 case 'f': /* save Unix-style From lines on front */ 1427 SaveFrom = atobool(val); 1428 break; 1429 1430 case 'G': /* match recipients against GECOS field */ 1431 MatchGecos = atobool(val); 1432 break; 1433 1434 case 'g': /* default gid */ 1435 g_opt: 1436 if (isascii(*val) && isdigit(*val)) 1437 DefGid = atoi(val); 1438 else 1439 { 1440 register struct group *gr; 1441 1442 DefGid = -1; 1443 gr = getgrnam(val); 1444 if (gr == NULL) 1445 syserr("readcf: option %c: unknown group %s", 1446 opt, val); 1447 else 1448 DefGid = gr->gr_gid; 1449 } 1450 break; 1451 1452 case 'H': /* help file */ 1453 if (val[0] == '\0') 1454 HelpFile = "sendmail.hf"; 1455 else 1456 HelpFile = newstr(val); 1457 break; 1458 1459 case 'h': /* maximum hop count */ 1460 MaxHopCount = atoi(val); 1461 break; 1462 1463 case 'I': /* use internet domain name server */ 1464 #if NAMED_BIND 1465 UseNameServer = TRUE; 1466 for (p = val; *p != 0; ) 1467 { 1468 bool clearmode; 1469 char *q; 1470 struct resolverflags *rfp; 1471 1472 while (*p == ' ') 1473 p++; 1474 if (*p == '\0') 1475 break; 1476 clearmode = FALSE; 1477 if (*p == '-') 1478 clearmode = TRUE; 1479 else if (*p != '+') 1480 p--; 1481 p++; 1482 q = p; 1483 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1484 p++; 1485 if (*p != '\0') 1486 *p++ = '\0'; 1487 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1488 { 1489 if (strcasecmp(q, rfp->rf_name) == 0) 1490 break; 1491 } 1492 if (rfp->rf_name == NULL) 1493 syserr("readcf: I option value %s unrecognized", q); 1494 else if (clearmode) 1495 _res.options &= ~rfp->rf_bits; 1496 else 1497 _res.options |= rfp->rf_bits; 1498 } 1499 if (tTd(8, 2)) 1500 printf("_res.options = %x\n", _res.options); 1501 #else 1502 usrerr("name server (I option) specified but BIND not compiled in"); 1503 #endif 1504 break; 1505 1506 case 'i': /* ignore dot lines in message */ 1507 IgnrDot = atobool(val); 1508 break; 1509 1510 case 'j': /* send errors in MIME (RFC 1341) format */ 1511 SendMIMEErrors = atobool(val); 1512 break; 1513 1514 case 'J': /* .forward search path */ 1515 ForwardPath = newstr(val); 1516 break; 1517 1518 case 'k': /* connection cache size */ 1519 MaxMciCache = atoi(val); 1520 if (MaxMciCache < 0) 1521 MaxMciCache = 0; 1522 break; 1523 1524 case 'K': /* connection cache timeout */ 1525 MciCacheTimeout = convtime(val, 'm'); 1526 break; 1527 1528 case 'l': /* use Errors-To: header */ 1529 UseErrorsTo = atobool(val); 1530 break; 1531 1532 case 'L': /* log level */ 1533 if (safe || LogLevel < atoi(val)) 1534 LogLevel = atoi(val); 1535 break; 1536 1537 case 'M': /* define macro */ 1538 define(val[0], newstr(&val[1]), CurEnv); 1539 sticky = FALSE; 1540 break; 1541 1542 case 'm': /* send to me too */ 1543 MeToo = atobool(val); 1544 break; 1545 1546 case 'n': /* validate RHS in newaliases */ 1547 CheckAliases = atobool(val); 1548 break; 1549 1550 /* 'N' available -- was "net name" */ 1551 1552 case 'O': /* daemon options */ 1553 setdaemonoptions(val); 1554 break; 1555 1556 case 'o': /* assume old style headers */ 1557 if (atobool(val)) 1558 CurEnv->e_flags |= EF_OLDSTYLE; 1559 else 1560 CurEnv->e_flags &= ~EF_OLDSTYLE; 1561 break; 1562 1563 case 'p': /* select privacy level */ 1564 p = val; 1565 for (;;) 1566 { 1567 register struct prival *pv; 1568 extern struct prival PrivacyValues[]; 1569 1570 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1571 p++; 1572 if (*p == '\0') 1573 break; 1574 val = p; 1575 while (isascii(*p) && isalnum(*p)) 1576 p++; 1577 if (*p != '\0') 1578 *p++ = '\0'; 1579 1580 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1581 { 1582 if (strcasecmp(val, pv->pv_name) == 0) 1583 break; 1584 } 1585 if (pv->pv_name == NULL) 1586 syserr("readcf: Op line: %s unrecognized", val); 1587 PrivacyFlags |= pv->pv_flag; 1588 } 1589 break; 1590 1591 case 'P': /* postmaster copy address for returned mail */ 1592 PostMasterCopy = newstr(val); 1593 break; 1594 1595 case 'q': /* slope of queue only function */ 1596 QueueFactor = atoi(val); 1597 break; 1598 1599 case 'Q': /* queue directory */ 1600 if (val[0] == '\0') 1601 QueueDir = "mqueue"; 1602 else 1603 QueueDir = newstr(val); 1604 if (RealUid != 0 && !safe) 1605 Warn_Q_option = TRUE; 1606 break; 1607 1608 case 'R': /* don't prune routes */ 1609 DontPruneRoutes = atobool(val); 1610 break; 1611 1612 case 'r': /* read timeout */ 1613 if (subopt == NULL) 1614 inittimeouts(val); 1615 else 1616 settimeout(subopt, val); 1617 break; 1618 1619 case 'S': /* status file */ 1620 if (val[0] == '\0') 1621 StatFile = "sendmail.st"; 1622 else 1623 StatFile = newstr(val); 1624 break; 1625 1626 case 's': /* be super safe, even if expensive */ 1627 SuperSafe = atobool(val); 1628 break; 1629 1630 case 'T': /* queue timeout */ 1631 p = strchr(val, '/'); 1632 if (p != NULL) 1633 { 1634 *p++ = '\0'; 1635 settimeout("queuewarn", p); 1636 } 1637 settimeout("queuereturn", val); 1638 break; 1639 1640 case 't': /* time zone name */ 1641 TimeZoneSpec = newstr(val); 1642 break; 1643 1644 case 'U': /* location of user database */ 1645 UdbSpec = newstr(val); 1646 break; 1647 1648 case 'u': /* set default uid */ 1649 for (p = val; *p != '\0'; p++) 1650 { 1651 if (*p == '.' || *p == '/' || *p == ':') 1652 { 1653 *p++ = '\0'; 1654 break; 1655 } 1656 } 1657 if (isascii(*val) && isdigit(*val)) 1658 DefUid = atoi(val); 1659 else 1660 { 1661 register struct passwd *pw; 1662 1663 DefUid = -1; 1664 pw = getpwnam(val); 1665 if (pw == NULL) 1666 syserr("readcf: option u: unknown user %s", val); 1667 else 1668 { 1669 DefUid = pw->pw_uid; 1670 DefGid = pw->pw_gid; 1671 } 1672 } 1673 setdefuser(); 1674 1675 /* handle the group if it is there */ 1676 if (*p == '\0') 1677 break; 1678 val = p; 1679 goto g_opt; 1680 1681 case 'V': /* fallback MX host */ 1682 FallBackMX = newstr(val); 1683 break; 1684 1685 case 'v': /* run in verbose mode */ 1686 Verbose = atobool(val); 1687 break; 1688 1689 case 'w': /* if we are best MX, try host directly */ 1690 TryNullMXList = atobool(val); 1691 break; 1692 1693 /* 'W' available -- was wizard password */ 1694 1695 case 'x': /* load avg at which to auto-queue msgs */ 1696 QueueLA = atoi(val); 1697 break; 1698 1699 case 'X': /* load avg at which to auto-reject connections */ 1700 RefuseLA = atoi(val); 1701 break; 1702 1703 case 'y': /* work recipient factor */ 1704 WkRecipFact = atoi(val); 1705 break; 1706 1707 case 'Y': /* fork jobs during queue runs */ 1708 ForkQueueRuns = atobool(val); 1709 break; 1710 1711 case 'z': /* work message class factor */ 1712 WkClassFact = atoi(val); 1713 break; 1714 1715 case 'Z': /* work time factor */ 1716 WkTimeFact = atoi(val); 1717 break; 1718 1719 case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 1720 BrokenSmtpPeers = atobool(val); 1721 break; 1722 1723 case O_SQBH: /* sort work queue by host first */ 1724 SortQueueByHost = atobool(val); 1725 break; 1726 1727 case O_DNICE: /* delivery nice value */ 1728 DeliveryNiceness = atoi(val); 1729 break; 1730 1731 case O_MQA: /* minimum queue age between deliveries */ 1732 MinQueueAge = convtime(val, 'm'); 1733 break; 1734 1735 case O_MHSA: /* maximum age of cached host status */ 1736 MaxHostStatAge = convtime(val, 'm'); 1737 break; 1738 1739 case O_DEFCHARSET: /* default character set for mimefying */ 1740 DefaultCharSet = newstr(val); 1741 break; 1742 1743 case O_SSFILE: /* service switch file */ 1744 ServiceSwitchFile = newstr(val); 1745 break; 1746 1747 default: 1748 break; 1749 } 1750 if (sticky) 1751 setbitn(opt, StickyOpt); 1752 return; 1753 } 1754 /* 1755 ** SETCLASS -- set a word into a class 1756 ** 1757 ** Parameters: 1758 ** class -- the class to put the word in. 1759 ** word -- the word to enter 1760 ** 1761 ** Returns: 1762 ** none. 1763 ** 1764 ** Side Effects: 1765 ** puts the word into the symbol table. 1766 */ 1767 1768 setclass(class, word) 1769 int class; 1770 char *word; 1771 { 1772 register STAB *s; 1773 1774 if (tTd(37, 8)) 1775 printf("setclass(%c, %s)\n", class, word); 1776 s = stab(word, ST_CLASS, ST_ENTER); 1777 setbitn(class, s->s_class); 1778 } 1779 /* 1780 ** MAKEMAPENTRY -- create a map entry 1781 ** 1782 ** Parameters: 1783 ** line -- the config file line 1784 ** 1785 ** Returns: 1786 ** TRUE if it successfully entered the map entry. 1787 ** FALSE otherwise (usually syntax error). 1788 ** 1789 ** Side Effects: 1790 ** Enters the map into the dictionary. 1791 */ 1792 1793 void 1794 makemapentry(line) 1795 char *line; 1796 { 1797 register char *p; 1798 char *mapname; 1799 char *classname; 1800 register STAB *s; 1801 STAB *class; 1802 1803 for (p = line; isascii(*p) && isspace(*p); p++) 1804 continue; 1805 if (!(isascii(*p) && isalnum(*p))) 1806 { 1807 syserr("readcf: config K line: no map name"); 1808 return; 1809 } 1810 1811 mapname = p; 1812 while ((isascii(*++p) && isalnum(*p)) || *p == '.') 1813 continue; 1814 if (*p != '\0') 1815 *p++ = '\0'; 1816 while (isascii(*p) && isspace(*p)) 1817 p++; 1818 if (!(isascii(*p) && isalnum(*p))) 1819 { 1820 syserr("readcf: config K line, map %s: no map class", mapname); 1821 return; 1822 } 1823 classname = p; 1824 while (isascii(*++p) && isalnum(*p)) 1825 continue; 1826 if (*p != '\0') 1827 *p++ = '\0'; 1828 while (isascii(*p) && isspace(*p)) 1829 p++; 1830 1831 /* look up the class */ 1832 class = stab(classname, ST_MAPCLASS, ST_FIND); 1833 if (class == NULL) 1834 { 1835 syserr("readcf: map %s: class %s not available", mapname, classname); 1836 return; 1837 } 1838 1839 /* enter the map */ 1840 s = stab(mapname, ST_MAP, ST_ENTER); 1841 s->s_map.map_class = &class->s_mapclass; 1842 s->s_map.map_mname = newstr(mapname); 1843 1844 if (class->s_mapclass.map_parse(&s->s_map, p)) 1845 s->s_map.map_mflags |= MF_VALID; 1846 1847 if (tTd(37, 5)) 1848 { 1849 printf("map %s, class %s, flags %x, file %s,\n", 1850 s->s_map.map_mname, s->s_map.map_class->map_cname, 1851 s->s_map.map_mflags, 1852 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 1853 printf("\tapp %s, domain %s, rebuild %s\n", 1854 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 1855 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 1856 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 1857 } 1858 } 1859 /* 1860 ** INITTIMEOUTS -- parse and set timeout values 1861 ** 1862 ** Parameters: 1863 ** val -- a pointer to the values. If NULL, do initial 1864 ** settings. 1865 ** 1866 ** Returns: 1867 ** none. 1868 ** 1869 ** Side Effects: 1870 ** Initializes the TimeOuts structure 1871 */ 1872 1873 #define SECONDS 1874 #define MINUTES * 60 1875 #define HOUR * 3600 1876 1877 inittimeouts(val) 1878 register char *val; 1879 { 1880 register char *p; 1881 extern time_t convtime(); 1882 1883 if (val == NULL) 1884 { 1885 TimeOuts.to_initial = (time_t) 5 MINUTES; 1886 TimeOuts.to_helo = (time_t) 5 MINUTES; 1887 TimeOuts.to_mail = (time_t) 10 MINUTES; 1888 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1889 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1890 TimeOuts.to_datablock = (time_t) 1 HOUR; 1891 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1892 TimeOuts.to_rset = (time_t) 5 MINUTES; 1893 TimeOuts.to_quit = (time_t) 2 MINUTES; 1894 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1895 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1896 TimeOuts.to_ident = (time_t) 30 SECONDS; 1897 TimeOuts.to_fileopen = (time_t) 60 SECONDS; 1898 return; 1899 } 1900 1901 for (;; val = p) 1902 { 1903 while (isascii(*val) && isspace(*val)) 1904 val++; 1905 if (*val == '\0') 1906 break; 1907 for (p = val; *p != '\0' && *p != ','; p++) 1908 continue; 1909 if (*p != '\0') 1910 *p++ = '\0'; 1911 1912 if (isascii(*val) && isdigit(*val)) 1913 { 1914 /* old syntax -- set everything */ 1915 TimeOuts.to_mail = convtime(val, 'm'); 1916 TimeOuts.to_rcpt = TimeOuts.to_mail; 1917 TimeOuts.to_datainit = TimeOuts.to_mail; 1918 TimeOuts.to_datablock = TimeOuts.to_mail; 1919 TimeOuts.to_datafinal = TimeOuts.to_mail; 1920 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1921 continue; 1922 } 1923 else 1924 { 1925 register char *q = strchr(val, ':'); 1926 1927 if (q == NULL && (q = strchr(val, '=')) == NULL) 1928 { 1929 /* syntax error */ 1930 continue; 1931 } 1932 *q++ = '\0'; 1933 settimeout(val, q); 1934 } 1935 } 1936 } 1937 /* 1938 ** SETTIMEOUT -- set an individual timeout 1939 ** 1940 ** Parameters: 1941 ** name -- the name of the timeout. 1942 ** val -- the value of the timeout. 1943 ** 1944 ** Returns: 1945 ** none. 1946 */ 1947 1948 settimeout(name, val) 1949 char *name; 1950 char *val; 1951 { 1952 register char *p; 1953 time_t to; 1954 extern time_t convtime(); 1955 1956 to = convtime(val, 'm'); 1957 p = strchr(name, '.'); 1958 if (p != NULL) 1959 *p++ = '\0'; 1960 1961 if (strcasecmp(name, "initial") == 0) 1962 TimeOuts.to_initial = to; 1963 else if (strcasecmp(name, "mail") == 0) 1964 TimeOuts.to_mail = to; 1965 else if (strcasecmp(name, "rcpt") == 0) 1966 TimeOuts.to_rcpt = to; 1967 else if (strcasecmp(name, "datainit") == 0) 1968 TimeOuts.to_datainit = to; 1969 else if (strcasecmp(name, "datablock") == 0) 1970 TimeOuts.to_datablock = to; 1971 else if (strcasecmp(name, "datafinal") == 0) 1972 TimeOuts.to_datafinal = to; 1973 else if (strcasecmp(name, "command") == 0) 1974 TimeOuts.to_nextcommand = to; 1975 else if (strcasecmp(name, "rset") == 0) 1976 TimeOuts.to_rset = to; 1977 else if (strcasecmp(name, "helo") == 0) 1978 TimeOuts.to_helo = to; 1979 else if (strcasecmp(name, "quit") == 0) 1980 TimeOuts.to_quit = to; 1981 else if (strcasecmp(name, "misc") == 0) 1982 TimeOuts.to_miscshort = to; 1983 else if (strcasecmp(name, "ident") == 0) 1984 TimeOuts.to_ident = to; 1985 else if (strcasecmp(name, "fileopen") == 0) 1986 TimeOuts.to_fileopen = to; 1987 else if (strcasecmp(name, "queuewarn") == 0) 1988 { 1989 to = convtime(val, 'h'); 1990 if (p == NULL || strcmp(p, "*") == NULL) 1991 { 1992 TimeOuts.to_q_warning[TOC_NORMAL] = to; 1993 TimeOuts.to_q_warning[TOC_URGENT] = to; 1994 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 1995 } 1996 else if (strcasecmp(p, "normal") == 0) 1997 TimeOuts.to_q_warning[TOC_NORMAL] = to; 1998 else if (strcasecmp(p, "urgent") == 0) 1999 TimeOuts.to_q_warning[TOC_URGENT] = to; 2000 else if (strcasecmp(p, "non-urgent") == 0) 2001 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 2002 else 2003 syserr("settimeout: invalid queuewarn subtimeout %s", p); 2004 } 2005 else if (strcasecmp(name, "queuereturn") == 0) 2006 { 2007 to = convtime(val, 'd'); 2008 if (p == NULL || strcmp(p, "*") == 0) 2009 { 2010 TimeOuts.to_q_return[TOC_NORMAL] = to; 2011 TimeOuts.to_q_return[TOC_URGENT] = to; 2012 TimeOuts.to_q_return[TOC_NONURGENT] = to; 2013 } 2014 else if (strcasecmp(p, "normal") == 0) 2015 TimeOuts.to_q_return[TOC_NORMAL] = to; 2016 else if (strcasecmp(p, "urgent") == 0) 2017 TimeOuts.to_q_return[TOC_URGENT] = to; 2018 else if (strcasecmp(p, "non-urgent") == 0) 2019 TimeOuts.to_q_return[TOC_NONURGENT] = to; 2020 else 2021 syserr("settimeout: invalid queuereturn subtimeout %s", p); 2022 } 2023 else 2024 syserr("settimeout: invalid timeout %s", name); 2025 } 2026