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.45 (Berkeley) 11/08/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 extern bool atobool(); 1168 extern time_t convtime(); 1169 extern int QueueLA; 1170 extern int RefuseLA; 1171 extern bool Warn_Q_option; 1172 1173 errno = 0; 1174 if (opt == ' ') 1175 { 1176 /* full word options */ 1177 struct optioninfo *sel; 1178 1179 p = strchr(val, '='); 1180 if (p == NULL) 1181 p = &val[strlen(val)]; 1182 while (*--p == ' ') 1183 continue; 1184 while (*++p == ' ') 1185 *p = '\0'; 1186 if (p == val) 1187 { 1188 syserr("readcf: null option name"); 1189 return; 1190 } 1191 if (*p == '=') 1192 *p++ = '\0'; 1193 while (*p == ' ') 1194 p++; 1195 sel = NULL; 1196 for (o = OptionTab; o->o_name != NULL; o++) 1197 { 1198 if (strncasecmp(o->o_name, val, strlen(val)) != 0) 1199 continue; 1200 if (strlen(o->o_name) == strlen(val)) 1201 { 1202 /* completely specified -- this must be it */ 1203 sel = NULL; 1204 break; 1205 } 1206 if (sel != NULL) 1207 break; 1208 sel = o; 1209 } 1210 if (sel != NULL && o->o_name == NULL) 1211 o = sel; 1212 else if (o->o_name == NULL) 1213 { 1214 syserr("readcf: unknown option name %s", val); 1215 return; 1216 } 1217 else if (sel != NULL) 1218 { 1219 syserr("readcf: ambiguous option name %s (matches %s and %s)", 1220 val, sel->o_name, o->o_name); 1221 return; 1222 } 1223 if (strlen(val) != strlen(o->o_name)) 1224 { 1225 bool oldVerbose = Verbose; 1226 1227 Verbose = TRUE; 1228 message("Option %s used as abbreviation for %s", 1229 val, o->o_name); 1230 Verbose = oldVerbose; 1231 } 1232 opt = o->o_code; 1233 val = p; 1234 } 1235 else 1236 { 1237 for (o = OptionTab; o->o_name != NULL; o++) 1238 { 1239 if (o->o_code == opt) 1240 break; 1241 } 1242 } 1243 1244 if (tTd(37, 1)) 1245 { 1246 printf(isascii(opt) && isprint(opt) ? 1247 "setoption %s (%c)=%s" : "setoption %s (0x%x)=%s", 1248 o->o_name == NULL ? "<unknown>" : o->o_name, 1249 opt, val); 1250 } 1251 1252 /* 1253 ** See if this option is preset for us. 1254 */ 1255 1256 if (!sticky && bitnset(opt, StickyOpt)) 1257 { 1258 if (tTd(37, 1)) 1259 printf(" (ignored)\n"); 1260 return; 1261 } 1262 1263 /* 1264 ** Check to see if this option can be specified by this user. 1265 */ 1266 1267 if (!safe && RealUid == 0) 1268 safe = TRUE; 1269 if (!safe && !o->o_safe) 1270 { 1271 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1272 { 1273 if (tTd(37, 1)) 1274 printf(" (unsafe)"); 1275 if (RealUid != geteuid()) 1276 { 1277 if (tTd(37, 1)) 1278 printf("(Resetting uid)"); 1279 (void) setgid(RealGid); 1280 (void) setuid(RealUid); 1281 } 1282 } 1283 } 1284 if (tTd(37, 1)) 1285 printf("\n"); 1286 1287 switch (opt & 0xff) 1288 { 1289 case '7': /* force seven-bit input */ 1290 SevenBitInput = atobool(val); 1291 break; 1292 1293 case '8': /* handling of 8-bit input */ 1294 switch (*val) 1295 { 1296 case 'r': /* reject 8-bit, don't convert MIME */ 1297 MimeMode = 0; 1298 break; 1299 1300 case 'm': /* convert 8-bit, convert MIME */ 1301 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1302 break; 1303 1304 case 'j': /* "just send 8" */ 1305 MimeMode = MM_PASS8BIT; 1306 break; 1307 1308 case 'p': /* pass 8 bit, convert MIME */ 1309 MimeMode = MM_PASS8BIT|MM_CVTMIME; 1310 break; 1311 1312 case 's': /* strict adherence */ 1313 MimeMode = MM_CVTMIME; 1314 break; 1315 1316 case 'a': /* encode 8 bit if available */ 1317 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1318 break; 1319 1320 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1321 MimeMode = MM_MIME8BIT; 1322 break; 1323 1324 default: 1325 syserr("Unknown 8-bit mode %c", *val); 1326 exit(EX_USAGE); 1327 } 1328 break; 1329 1330 case 'A': /* set default alias file */ 1331 if (val[0] == '\0') 1332 setalias("aliases"); 1333 else 1334 setalias(val); 1335 break; 1336 1337 case 'a': /* look N minutes for "@:@" in alias file */ 1338 if (val[0] == '\0') 1339 SafeAlias = 5 * 60; /* five minutes */ 1340 else 1341 SafeAlias = convtime(val, 'm'); 1342 break; 1343 1344 case 'B': /* substitution for blank character */ 1345 SpaceSub = val[0]; 1346 if (SpaceSub == '\0') 1347 SpaceSub = ' '; 1348 break; 1349 1350 case 'b': /* min blocks free on queue fs/max msg size */ 1351 p = strchr(val, '/'); 1352 if (p != NULL) 1353 { 1354 *p++ = '\0'; 1355 MaxMessageSize = atol(p); 1356 } 1357 MinBlocksFree = atol(val); 1358 break; 1359 1360 case 'c': /* don't connect to "expensive" mailers */ 1361 NoConnect = atobool(val); 1362 break; 1363 1364 case 'C': /* checkpoint every N addresses */ 1365 CheckpointInterval = atoi(val); 1366 break; 1367 1368 case 'd': /* delivery mode */ 1369 switch (*val) 1370 { 1371 case '\0': 1372 e->e_sendmode = SM_DELIVER; 1373 break; 1374 1375 case SM_QUEUE: /* queue only */ 1376 #ifndef QUEUE 1377 syserr("need QUEUE to set -odqueue"); 1378 #endif /* QUEUE */ 1379 /* fall through..... */ 1380 1381 case SM_DELIVER: /* do everything */ 1382 case SM_FORK: /* fork after verification */ 1383 e->e_sendmode = *val; 1384 break; 1385 1386 default: 1387 syserr("Unknown delivery mode %c", *val); 1388 exit(EX_USAGE); 1389 } 1390 break; 1391 1392 case 'D': /* rebuild alias database as needed */ 1393 AutoRebuild = atobool(val); 1394 break; 1395 1396 case 'E': /* error message header/header file */ 1397 if (*val != '\0') 1398 ErrMsgFile = newstr(val); 1399 break; 1400 1401 case 'e': /* set error processing mode */ 1402 switch (*val) 1403 { 1404 case EM_QUIET: /* be silent about it */ 1405 case EM_MAIL: /* mail back */ 1406 case EM_BERKNET: /* do berknet error processing */ 1407 case EM_WRITE: /* write back (or mail) */ 1408 case EM_PRINT: /* print errors normally (default) */ 1409 e->e_errormode = *val; 1410 break; 1411 } 1412 break; 1413 1414 case 'F': /* file mode */ 1415 FileMode = atooct(val) & 0777; 1416 break; 1417 1418 case 'f': /* save Unix-style From lines on front */ 1419 SaveFrom = atobool(val); 1420 break; 1421 1422 case 'G': /* match recipients against GECOS field */ 1423 MatchGecos = atobool(val); 1424 break; 1425 1426 case 'g': /* default gid */ 1427 g_opt: 1428 if (isascii(*val) && isdigit(*val)) 1429 DefGid = atoi(val); 1430 else 1431 { 1432 register struct group *gr; 1433 1434 DefGid = -1; 1435 gr = getgrnam(val); 1436 if (gr == NULL) 1437 syserr("readcf: option %c: unknown group %s", 1438 opt, val); 1439 else 1440 DefGid = gr->gr_gid; 1441 } 1442 break; 1443 1444 case 'H': /* help file */ 1445 if (val[0] == '\0') 1446 HelpFile = "sendmail.hf"; 1447 else 1448 HelpFile = newstr(val); 1449 break; 1450 1451 case 'h': /* maximum hop count */ 1452 MaxHopCount = atoi(val); 1453 break; 1454 1455 case 'I': /* use internet domain name server */ 1456 #if NAMED_BIND 1457 UseNameServer = TRUE; 1458 for (p = val; *p != 0; ) 1459 { 1460 bool clearmode; 1461 char *q; 1462 struct resolverflags *rfp; 1463 1464 while (*p == ' ') 1465 p++; 1466 if (*p == '\0') 1467 break; 1468 clearmode = FALSE; 1469 if (*p == '-') 1470 clearmode = TRUE; 1471 else if (*p != '+') 1472 p--; 1473 p++; 1474 q = p; 1475 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1476 p++; 1477 if (*p != '\0') 1478 *p++ = '\0'; 1479 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1480 { 1481 if (strcasecmp(q, rfp->rf_name) == 0) 1482 break; 1483 } 1484 if (rfp->rf_name == NULL) 1485 syserr("readcf: I option value %s unrecognized", q); 1486 else if (clearmode) 1487 _res.options &= ~rfp->rf_bits; 1488 else 1489 _res.options |= rfp->rf_bits; 1490 } 1491 if (tTd(8, 2)) 1492 printf("_res.options = %x\n", _res.options); 1493 #else 1494 usrerr("name server (I option) specified but BIND not compiled in"); 1495 #endif 1496 break; 1497 1498 case 'i': /* ignore dot lines in message */ 1499 IgnrDot = atobool(val); 1500 break; 1501 1502 case 'j': /* send errors in MIME (RFC 1341) format */ 1503 SendMIMEErrors = atobool(val); 1504 break; 1505 1506 case 'J': /* .forward search path */ 1507 ForwardPath = newstr(val); 1508 break; 1509 1510 case 'k': /* connection cache size */ 1511 MaxMciCache = atoi(val); 1512 if (MaxMciCache < 0) 1513 MaxMciCache = 0; 1514 break; 1515 1516 case 'K': /* connection cache timeout */ 1517 MciCacheTimeout = convtime(val, 'm'); 1518 break; 1519 1520 case 'l': /* use Errors-To: header */ 1521 UseErrorsTo = atobool(val); 1522 break; 1523 1524 case 'L': /* log level */ 1525 if (safe || LogLevel < atoi(val)) 1526 LogLevel = atoi(val); 1527 break; 1528 1529 case 'M': /* define macro */ 1530 define(val[0], newstr(&val[1]), CurEnv); 1531 sticky = FALSE; 1532 break; 1533 1534 case 'm': /* send to me too */ 1535 MeToo = atobool(val); 1536 break; 1537 1538 case 'n': /* validate RHS in newaliases */ 1539 CheckAliases = atobool(val); 1540 break; 1541 1542 /* 'N' available -- was "net name" */ 1543 1544 case 'O': /* daemon options */ 1545 setdaemonoptions(val); 1546 break; 1547 1548 case 'o': /* assume old style headers */ 1549 if (atobool(val)) 1550 CurEnv->e_flags |= EF_OLDSTYLE; 1551 else 1552 CurEnv->e_flags &= ~EF_OLDSTYLE; 1553 break; 1554 1555 case 'p': /* select privacy level */ 1556 p = val; 1557 for (;;) 1558 { 1559 register struct prival *pv; 1560 extern struct prival PrivacyValues[]; 1561 1562 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1563 p++; 1564 if (*p == '\0') 1565 break; 1566 val = p; 1567 while (isascii(*p) && isalnum(*p)) 1568 p++; 1569 if (*p != '\0') 1570 *p++ = '\0'; 1571 1572 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1573 { 1574 if (strcasecmp(val, pv->pv_name) == 0) 1575 break; 1576 } 1577 if (pv->pv_name == NULL) 1578 syserr("readcf: Op line: %s unrecognized", val); 1579 PrivacyFlags |= pv->pv_flag; 1580 } 1581 break; 1582 1583 case 'P': /* postmaster copy address for returned mail */ 1584 PostMasterCopy = newstr(val); 1585 break; 1586 1587 case 'q': /* slope of queue only function */ 1588 QueueFactor = atoi(val); 1589 break; 1590 1591 case 'Q': /* queue directory */ 1592 if (val[0] == '\0') 1593 QueueDir = "mqueue"; 1594 else 1595 QueueDir = newstr(val); 1596 if (RealUid != 0 && !safe) 1597 Warn_Q_option = TRUE; 1598 break; 1599 1600 case 'R': /* don't prune routes */ 1601 DontPruneRoutes = atobool(val); 1602 break; 1603 1604 case 'r': /* read timeout */ 1605 settimeouts(val); 1606 break; 1607 1608 case 'S': /* status file */ 1609 if (val[0] == '\0') 1610 StatFile = "sendmail.st"; 1611 else 1612 StatFile = newstr(val); 1613 break; 1614 1615 case 's': /* be super safe, even if expensive */ 1616 SuperSafe = atobool(val); 1617 break; 1618 1619 case 'T': /* queue timeout */ 1620 p = strchr(val, '/'); 1621 if (p != NULL) 1622 { 1623 *p++ = '\0'; 1624 TimeOuts.to_q_warning[TOC_NORMAL] = convtime(p, 'd'); 1625 } 1626 TimeOuts.to_q_return[TOC_NORMAL] = convtime(val, 'h'); 1627 break; 1628 1629 case 't': /* time zone name */ 1630 TimeZoneSpec = newstr(val); 1631 break; 1632 1633 case 'U': /* location of user database */ 1634 UdbSpec = newstr(val); 1635 break; 1636 1637 case 'u': /* set default uid */ 1638 for (p = val; *p != '\0'; p++) 1639 { 1640 if (*p == '.' || *p == '/' || *p == ':') 1641 { 1642 *p++ = '\0'; 1643 break; 1644 } 1645 } 1646 if (isascii(*val) && isdigit(*val)) 1647 DefUid = atoi(val); 1648 else 1649 { 1650 register struct passwd *pw; 1651 1652 DefUid = -1; 1653 pw = getpwnam(val); 1654 if (pw == NULL) 1655 syserr("readcf: option u: unknown user %s", val); 1656 else 1657 { 1658 DefUid = pw->pw_uid; 1659 DefGid = pw->pw_gid; 1660 } 1661 } 1662 setdefuser(); 1663 1664 /* handle the group if it is there */ 1665 if (*p == '\0') 1666 break; 1667 val = p; 1668 goto g_opt; 1669 1670 case 'V': /* fallback MX host */ 1671 FallBackMX = newstr(val); 1672 break; 1673 1674 case 'v': /* run in verbose mode */ 1675 Verbose = atobool(val); 1676 break; 1677 1678 case 'w': /* if we are best MX, try host directly */ 1679 TryNullMXList = atobool(val); 1680 break; 1681 1682 /* 'W' available -- was wizard password */ 1683 1684 case 'x': /* load avg at which to auto-queue msgs */ 1685 QueueLA = atoi(val); 1686 break; 1687 1688 case 'X': /* load avg at which to auto-reject connections */ 1689 RefuseLA = atoi(val); 1690 break; 1691 1692 case 'y': /* work recipient factor */ 1693 WkRecipFact = atoi(val); 1694 break; 1695 1696 case 'Y': /* fork jobs during queue runs */ 1697 ForkQueueRuns = atobool(val); 1698 break; 1699 1700 case 'z': /* work message class factor */ 1701 WkClassFact = atoi(val); 1702 break; 1703 1704 case 'Z': /* work time factor */ 1705 WkTimeFact = atoi(val); 1706 break; 1707 1708 case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 1709 BrokenSmtpPeers = atobool(val); 1710 break; 1711 1712 case O_SQBH: /* sort work queue by host first */ 1713 SortQueueByHost = atobool(val); 1714 break; 1715 1716 case O_DNICE: /* delivery nice value */ 1717 DeliveryNiceness = atoi(val); 1718 break; 1719 1720 case O_MQA: /* minimum queue age between deliveries */ 1721 MinQueueAge = convtime(val, 'm'); 1722 break; 1723 1724 case O_MHSA: /* maximum age of cached host status */ 1725 MaxHostStatAge = convtime(val, 'm'); 1726 break; 1727 1728 case O_DEFCHARSET: /* default character set for mimefying */ 1729 DefaultCharSet = newstr(val); 1730 break; 1731 1732 case O_SSFILE: /* service switch file */ 1733 ServiceSwitchFile = newstr(val); 1734 break; 1735 1736 default: 1737 break; 1738 } 1739 if (sticky) 1740 setbitn(opt, StickyOpt); 1741 return; 1742 } 1743 /* 1744 ** SETCLASS -- set a word into a class 1745 ** 1746 ** Parameters: 1747 ** class -- the class to put the word in. 1748 ** word -- the word to enter 1749 ** 1750 ** Returns: 1751 ** none. 1752 ** 1753 ** Side Effects: 1754 ** puts the word into the symbol table. 1755 */ 1756 1757 setclass(class, word) 1758 int class; 1759 char *word; 1760 { 1761 register STAB *s; 1762 1763 if (tTd(37, 8)) 1764 printf("setclass(%c, %s)\n", class, word); 1765 s = stab(word, ST_CLASS, ST_ENTER); 1766 setbitn(class, s->s_class); 1767 } 1768 /* 1769 ** MAKEMAPENTRY -- create a map entry 1770 ** 1771 ** Parameters: 1772 ** line -- the config file line 1773 ** 1774 ** Returns: 1775 ** TRUE if it successfully entered the map entry. 1776 ** FALSE otherwise (usually syntax error). 1777 ** 1778 ** Side Effects: 1779 ** Enters the map into the dictionary. 1780 */ 1781 1782 void 1783 makemapentry(line) 1784 char *line; 1785 { 1786 register char *p; 1787 char *mapname; 1788 char *classname; 1789 register STAB *s; 1790 STAB *class; 1791 1792 for (p = line; isascii(*p) && isspace(*p); p++) 1793 continue; 1794 if (!(isascii(*p) && isalnum(*p))) 1795 { 1796 syserr("readcf: config K line: no map name"); 1797 return; 1798 } 1799 1800 mapname = p; 1801 while ((isascii(*++p) && isalnum(*p)) || *p == '.') 1802 continue; 1803 if (*p != '\0') 1804 *p++ = '\0'; 1805 while (isascii(*p) && isspace(*p)) 1806 p++; 1807 if (!(isascii(*p) && isalnum(*p))) 1808 { 1809 syserr("readcf: config K line, map %s: no map class", mapname); 1810 return; 1811 } 1812 classname = p; 1813 while (isascii(*++p) && isalnum(*p)) 1814 continue; 1815 if (*p != '\0') 1816 *p++ = '\0'; 1817 while (isascii(*p) && isspace(*p)) 1818 p++; 1819 1820 /* look up the class */ 1821 class = stab(classname, ST_MAPCLASS, ST_FIND); 1822 if (class == NULL) 1823 { 1824 syserr("readcf: map %s: class %s not available", mapname, classname); 1825 return; 1826 } 1827 1828 /* enter the map */ 1829 s = stab(mapname, ST_MAP, ST_ENTER); 1830 s->s_map.map_class = &class->s_mapclass; 1831 s->s_map.map_mname = newstr(mapname); 1832 1833 if (class->s_mapclass.map_parse(&s->s_map, p)) 1834 s->s_map.map_mflags |= MF_VALID; 1835 1836 if (tTd(37, 5)) 1837 { 1838 printf("map %s, class %s, flags %x, file %s,\n", 1839 s->s_map.map_mname, s->s_map.map_class->map_cname, 1840 s->s_map.map_mflags, 1841 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 1842 printf("\tapp %s, domain %s, rebuild %s\n", 1843 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 1844 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 1845 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 1846 } 1847 } 1848 /* 1849 ** SETTIMEOUTS -- parse and set timeout values 1850 ** 1851 ** Parameters: 1852 ** val -- a pointer to the values. If NULL, do initial 1853 ** settings. 1854 ** 1855 ** Returns: 1856 ** none. 1857 ** 1858 ** Side Effects: 1859 ** Initializes the TimeOuts structure 1860 */ 1861 1862 #define SECONDS 1863 #define MINUTES * 60 1864 #define HOUR * 3600 1865 1866 settimeouts(val) 1867 register char *val; 1868 { 1869 register char *p; 1870 extern time_t convtime(); 1871 1872 if (val == NULL) 1873 { 1874 TimeOuts.to_initial = (time_t) 5 MINUTES; 1875 TimeOuts.to_helo = (time_t) 5 MINUTES; 1876 TimeOuts.to_mail = (time_t) 10 MINUTES; 1877 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1878 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1879 TimeOuts.to_datablock = (time_t) 1 HOUR; 1880 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1881 TimeOuts.to_rset = (time_t) 5 MINUTES; 1882 TimeOuts.to_quit = (time_t) 2 MINUTES; 1883 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1884 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1885 TimeOuts.to_ident = (time_t) 30 SECONDS; 1886 TimeOuts.to_fileopen = (time_t) 60 SECONDS; 1887 return; 1888 } 1889 1890 for (;; val = p) 1891 { 1892 while (isascii(*val) && isspace(*val)) 1893 val++; 1894 if (*val == '\0') 1895 break; 1896 for (p = val; *p != '\0' && *p != ','; p++) 1897 continue; 1898 if (*p != '\0') 1899 *p++ = '\0'; 1900 1901 if (isascii(*val) && isdigit(*val)) 1902 { 1903 /* old syntax -- set everything */ 1904 TimeOuts.to_mail = convtime(val, 'm'); 1905 TimeOuts.to_rcpt = TimeOuts.to_mail; 1906 TimeOuts.to_datainit = TimeOuts.to_mail; 1907 TimeOuts.to_datablock = TimeOuts.to_mail; 1908 TimeOuts.to_datafinal = TimeOuts.to_mail; 1909 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1910 continue; 1911 } 1912 else 1913 { 1914 register char *q = strchr(val, ':'); 1915 time_t to; 1916 1917 if (q == NULL && (q = strchr(val, '=')) == NULL) 1918 { 1919 /* syntax error */ 1920 continue; 1921 } 1922 *q++ = '\0'; 1923 to = convtime(q, 'm'); 1924 1925 if (strcasecmp(val, "initial") == 0) 1926 TimeOuts.to_initial = to; 1927 else if (strcasecmp(val, "mail") == 0) 1928 TimeOuts.to_mail = to; 1929 else if (strcasecmp(val, "rcpt") == 0) 1930 TimeOuts.to_rcpt = to; 1931 else if (strcasecmp(val, "datainit") == 0) 1932 TimeOuts.to_datainit = to; 1933 else if (strcasecmp(val, "datablock") == 0) 1934 TimeOuts.to_datablock = to; 1935 else if (strcasecmp(val, "datafinal") == 0) 1936 TimeOuts.to_datafinal = to; 1937 else if (strcasecmp(val, "command") == 0) 1938 TimeOuts.to_nextcommand = to; 1939 else if (strcasecmp(val, "rset") == 0) 1940 TimeOuts.to_rset = to; 1941 else if (strcasecmp(val, "helo") == 0) 1942 TimeOuts.to_helo = to; 1943 else if (strcasecmp(val, "quit") == 0) 1944 TimeOuts.to_quit = to; 1945 else if (strcasecmp(val, "misc") == 0) 1946 TimeOuts.to_miscshort = to; 1947 else if (strcasecmp(val, "ident") == 0) 1948 TimeOuts.to_ident = to; 1949 else if (strcasecmp(val, "fileopen") == 0) 1950 TimeOuts.to_fileopen = to; 1951 else if (strcasecmp(val, "queuewarn") == 0) 1952 TimeOuts.to_q_warning[TOC_NORMAL] = to; 1953 else if (strcasecmp(val, "queuereturn") == 0) 1954 TimeOuts.to_q_return[TOC_NORMAL] = to; 1955 else if (strcasecmp(val, "queuewarn.normal") == 0) 1956 TimeOuts.to_q_warning[TOC_NORMAL] = to; 1957 else if (strcasecmp(val, "queuereturn.normal") == 0) 1958 TimeOuts.to_q_return[TOC_NORMAL] = to; 1959 else if (strcasecmp(val, "queuewarn.urgent") == 0) 1960 TimeOuts.to_q_warning[TOC_URGENT] = to; 1961 else if (strcasecmp(val, "queuereturn.urgent") == 0) 1962 TimeOuts.to_q_return[TOC_URGENT] = to; 1963 else if (strcasecmp(val, "queuewarn.non-urgent") == 0) 1964 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 1965 else if (strcasecmp(val, "queuereturn.non-urgent") == 0) 1966 TimeOuts.to_q_return[TOC_NONURGENT] = to; 1967 else 1968 syserr("settimeouts: invalid timeout %s", val); 1969 } 1970 } 1971 } 1972