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