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.41 (Berkeley) 10/15/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 extern char *munchstring(); 88 extern void makemapentry(); 89 90 FileName = cfname; 91 LineNumber = 0; 92 93 cf = fopen(cfname, "r"); 94 if (cf == NULL) 95 { 96 syserr("cannot open"); 97 exit(EX_OSFILE); 98 } 99 100 if (fstat(fileno(cf), &statb) < 0) 101 { 102 syserr("cannot fstat"); 103 exit(EX_OSFILE); 104 } 105 106 if (!S_ISREG(statb.st_mode)) 107 { 108 syserr("not a plain file"); 109 exit(EX_OSFILE); 110 } 111 112 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 113 { 114 if (OpMode == MD_DAEMON || OpMode == MD_FREEZE) 115 fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 116 FileName); 117 #ifdef LOG 118 if (LogLevel > 0) 119 syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", 120 FileName); 121 #endif 122 } 123 124 #ifdef XLA 125 xla_zero(); 126 #endif 127 128 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 129 { 130 if (bp[0] == '#') 131 { 132 if (bp != buf) 133 free(bp); 134 continue; 135 } 136 137 /* do macro expansion mappings */ 138 for (p = bp; *p != '\0'; p++) 139 { 140 if (*p == '#' && p > bp && ConfigLevel >= 3) 141 { 142 /* this is an on-line comment */ 143 register char *e; 144 145 switch (*--p & 0377) 146 { 147 case MACROEXPAND: 148 /* it's from $# -- let it go through */ 149 p++; 150 break; 151 152 case '\\': 153 /* it's backslash escaped */ 154 (void) strcpy(p, p + 1); 155 break; 156 157 default: 158 /* delete preceeding white space */ 159 while (isascii(*p) && isspace(*p) && p > bp) 160 p--; 161 if ((e = strchr(++p, '\n')) != NULL) 162 (void) strcpy(p, e); 163 else 164 p[0] = p[1] = '\0'; 165 break; 166 } 167 continue; 168 } 169 170 if (*p != '$' || p[1] == '\0') 171 continue; 172 173 if (p[1] == '$') 174 { 175 /* actual dollar sign.... */ 176 (void) strcpy(p, p + 1); 177 continue; 178 } 179 180 /* convert to macro expansion character */ 181 *p++ = MACROEXPAND; 182 183 /* convert macro name to code */ 184 *p = macid(p, &ep); 185 if (ep != p) 186 strcpy(p + 1, ep); 187 } 188 189 /* interpret this line */ 190 errno = 0; 191 switch (bp[0]) 192 { 193 case '\0': 194 case '#': /* comment */ 195 break; 196 197 case 'R': /* rewriting rule */ 198 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 199 continue; 200 201 if (*p == '\0') 202 { 203 syserr("invalid rewrite line \"%s\" (tab expected)", bp); 204 break; 205 } 206 207 /* allocate space for the rule header */ 208 if (rwp == NULL) 209 { 210 RewriteRules[ruleset] = rwp = 211 (struct rewrite *) xalloc(sizeof *rwp); 212 } 213 else 214 { 215 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 216 rwp = rwp->r_next; 217 } 218 rwp->r_next = NULL; 219 220 /* expand and save the LHS */ 221 *p = '\0'; 222 expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e); 223 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, 224 sizeof pvpbuf, NULL); 225 nfuzzy = 0; 226 if (rwp->r_lhs != NULL) 227 { 228 register char **ap; 229 230 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 231 232 /* count the number of fuzzy matches in LHS */ 233 for (ap = rwp->r_lhs; *ap != NULL; ap++) 234 { 235 char *botch; 236 237 botch = NULL; 238 switch (**ap & 0377) 239 { 240 case MATCHZANY: 241 case MATCHANY: 242 case MATCHONE: 243 case MATCHCLASS: 244 case MATCHNCLASS: 245 nfuzzy++; 246 break; 247 248 case MATCHREPL: 249 botch = "$0-$9"; 250 break; 251 252 case CANONNET: 253 botch = "$#"; 254 break; 255 256 case CANONUSER: 257 botch = "$:"; 258 break; 259 260 case CALLSUBR: 261 botch = "$>"; 262 break; 263 264 case CONDIF: 265 botch = "$?"; 266 break; 267 268 case CONDELSE: 269 botch = "$|"; 270 break; 271 272 case CONDFI: 273 botch = "$."; 274 break; 275 276 case HOSTBEGIN: 277 botch = "$["; 278 break; 279 280 case HOSTEND: 281 botch = "$]"; 282 break; 283 284 case LOOKUPBEGIN: 285 botch = "$("; 286 break; 287 288 case LOOKUPEND: 289 botch = "$)"; 290 break; 291 } 292 if (botch != NULL) 293 syserr("Inappropriate use of %s on LHS", 294 botch); 295 } 296 } 297 else 298 syserr("R line: null LHS"); 299 300 /* expand and save the RHS */ 301 while (*++p == '\t') 302 continue; 303 q = p; 304 while (*p != '\0' && *p != '\t') 305 p++; 306 *p = '\0'; 307 expand(q, exbuf, &exbuf[sizeof exbuf], e); 308 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, 309 sizeof pvpbuf, NULL); 310 if (rwp->r_rhs != NULL) 311 { 312 register char **ap; 313 314 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 315 316 /* check no out-of-bounds replacements */ 317 nfuzzy += '0'; 318 for (ap = rwp->r_rhs; *ap != NULL; ap++) 319 { 320 char *botch; 321 322 botch = NULL; 323 switch (**ap & 0377) 324 { 325 case MATCHREPL: 326 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 327 { 328 syserr("replacement $%c out of bounds", 329 (*ap)[1]); 330 } 331 break; 332 333 case MATCHZANY: 334 botch = "$*"; 335 break; 336 337 case MATCHANY: 338 botch = "$+"; 339 break; 340 341 case MATCHONE: 342 botch = "$-"; 343 break; 344 345 case MATCHCLASS: 346 botch = "$="; 347 break; 348 349 case MATCHNCLASS: 350 botch = "$~"; 351 break; 352 } 353 if (botch != NULL) 354 syserr("Inappropriate use of %s on RHS", 355 botch); 356 } 357 } 358 else 359 syserr("R line: null RHS"); 360 break; 361 362 case 'S': /* select rewriting set */ 363 for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 364 continue; 365 if (!isascii(*p) || !isdigit(*p)) 366 { 367 syserr("invalid argument to S line: \"%.20s\"", 368 &bp[1]); 369 break; 370 } 371 ruleset = atoi(p); 372 if (ruleset >= MAXRWSETS || ruleset < 0) 373 { 374 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 375 ruleset = 0; 376 } 377 rwp = NULL; 378 break; 379 380 case 'D': /* macro definition */ 381 mid = macid(&bp[1], &ep); 382 p = munchstring(ep, NULL); 383 define(mid, newstr(p), e); 384 break; 385 386 case 'H': /* required header line */ 387 (void) chompheader(&bp[1], TRUE, e); 388 break; 389 390 case 'C': /* word class */ 391 /* scan the list of words and set class for all */ 392 mid = macid(&bp[1], &ep); 393 expand(ep, exbuf, &exbuf[sizeof exbuf], e); 394 for (p = exbuf; *p != '\0'; ) 395 { 396 register char *wd; 397 char delim; 398 399 while (*p != '\0' && isascii(*p) && isspace(*p)) 400 p++; 401 wd = p; 402 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 403 p++; 404 delim = *p; 405 *p = '\0'; 406 if (wd[0] != '\0') 407 setclass(mid, wd); 408 *p = delim; 409 } 410 break; 411 412 case 'F': /* word class from file */ 413 mid = macid(&bp[1], &ep); 414 for (p = ep; isascii(*p) && isspace(*p); ) 415 p++; 416 if (p[0] == '-' && p[1] == 'o') 417 { 418 optional = TRUE; 419 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 420 p++; 421 while (isascii(*p) && isspace(*p)) 422 p++; 423 } 424 else 425 optional = FALSE; 426 file = p; 427 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 428 p++; 429 if (*p == '\0') 430 p = "%s"; 431 else 432 { 433 *p = '\0'; 434 while (isascii(*++p) && isspace(*p)) 435 continue; 436 } 437 fileclass(bp[1], file, p, safe, optional); 438 break; 439 440 #ifdef XLA 441 case 'L': /* extended load average description */ 442 xla_init(&bp[1]); 443 break; 444 #endif 445 446 case 'M': /* define mailer */ 447 makemailer(&bp[1]); 448 break; 449 450 case 'O': /* set option */ 451 setoption(bp[1], &bp[2], safe, FALSE, e); 452 break; 453 454 case 'P': /* set precedence */ 455 if (NumPriorities >= MAXPRIORITIES) 456 { 457 toomany('P', MAXPRIORITIES); 458 break; 459 } 460 for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 461 continue; 462 if (*p == '\0') 463 goto badline; 464 *p = '\0'; 465 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 466 Priorities[NumPriorities].pri_val = atoi(++p); 467 NumPriorities++; 468 break; 469 470 case 'T': /* trusted user(s) */ 471 /* this option is obsolete, but will be ignored */ 472 break; 473 474 case 'V': /* configuration syntax version */ 475 for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 476 continue; 477 if (!isascii(*p) || !isdigit(*p)) 478 { 479 syserr("invalid argument to V line: \"%.20s\"", 480 &bp[1]); 481 break; 482 } 483 ConfigLevel = strtol(p, &ep, 10); 484 if (ConfigLevel >= 5) 485 { 486 /* level 5 configs have short name in $w */ 487 p = macvalue('w', e); 488 if (p != NULL && (p = strchr(p, '.')) != NULL) 489 *p = '\0'; 490 } 491 if (*ep++ == '/') 492 { 493 /* extract vendor code */ 494 for (p = ep; isascii(*p) && isalpha(*p); ) 495 p++; 496 *p = '\0'; 497 498 if (!setvendor(ep)) 499 syserr("invalid V line vendor code: \"%s\"", 500 ep); 501 } 502 break; 503 504 case 'K': 505 makemapentry(&bp[1]); 506 break; 507 508 default: 509 badline: 510 syserr("unknown control line \"%s\"", bp); 511 } 512 if (bp != buf) 513 free(bp); 514 } 515 if (ferror(cf)) 516 { 517 syserr("I/O read error", cfname); 518 exit(EX_OSFILE); 519 } 520 fclose(cf); 521 FileName = NULL; 522 523 /* initialize host maps from local service tables */ 524 inithostmaps(); 525 526 if (stab("host", ST_MAP, ST_FIND) == NULL) 527 { 528 /* user didn't initialize: set up host map */ 529 strcpy(buf, "host host"); 530 #if NAMED_BIND 531 if (ConfigLevel >= 2) 532 strcat(buf, " -a."); 533 #endif 534 makemapentry(buf); 535 } 536 } 537 /* 538 ** TOOMANY -- signal too many of some option 539 ** 540 ** Parameters: 541 ** id -- the id of the error line 542 ** maxcnt -- the maximum possible values 543 ** 544 ** Returns: 545 ** none. 546 ** 547 ** Side Effects: 548 ** gives a syserr. 549 */ 550 551 toomany(id, maxcnt) 552 char id; 553 int maxcnt; 554 { 555 syserr("too many %c lines, %d max", id, maxcnt); 556 } 557 /* 558 ** FILECLASS -- read members of a class from a file 559 ** 560 ** Parameters: 561 ** class -- class to define. 562 ** filename -- name of file to read. 563 ** fmt -- scanf string to use for match. 564 ** safe -- if set, this is a safe read. 565 ** optional -- if set, it is not an error for the file to 566 ** not exist. 567 ** 568 ** Returns: 569 ** none 570 ** 571 ** Side Effects: 572 ** 573 ** puts all lines in filename that match a scanf into 574 ** the named class. 575 */ 576 577 fileclass(class, filename, fmt, safe, optional) 578 int class; 579 char *filename; 580 char *fmt; 581 bool safe; 582 bool optional; 583 { 584 FILE *f; 585 struct stat stbuf; 586 char buf[MAXLINE]; 587 588 if (tTd(37, 2)) 589 printf("fileclass(%s, fmt=%s)\n", filename, fmt); 590 591 if (filename[0] == '|') 592 { 593 syserr("fileclass: pipes (F%c%s) not supported due to security problems", 594 class, filename); 595 return; 596 } 597 if (stat(filename, &stbuf) < 0) 598 { 599 if (tTd(37, 2)) 600 printf(" cannot stat (%s)\n", errstring(errno)); 601 if (!optional) 602 syserr("fileclass: cannot stat %s", filename); 603 return; 604 } 605 if (!S_ISREG(stbuf.st_mode)) 606 { 607 syserr("fileclass: %s not a regular file", filename); 608 return; 609 } 610 if (!safe && access(filename, R_OK) < 0) 611 { 612 syserr("fileclass: access denied on %s", filename); 613 return; 614 } 615 f = fopen(filename, "r"); 616 if (f == NULL) 617 { 618 syserr("fileclass: cannot open %s", filename); 619 return; 620 } 621 622 while (fgets(buf, sizeof buf, f) != NULL) 623 { 624 register STAB *s; 625 register char *p; 626 # ifdef SCANF 627 char wordbuf[MAXNAME+1]; 628 629 if (sscanf(buf, fmt, wordbuf) != 1) 630 continue; 631 p = wordbuf; 632 # else /* SCANF */ 633 p = buf; 634 # endif /* SCANF */ 635 636 /* 637 ** Break up the match into words. 638 */ 639 640 while (*p != '\0') 641 { 642 register char *q; 643 644 /* strip leading spaces */ 645 while (isascii(*p) && isspace(*p)) 646 p++; 647 if (*p == '\0') 648 break; 649 650 /* find the end of the word */ 651 q = p; 652 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 653 p++; 654 if (*p != '\0') 655 *p++ = '\0'; 656 657 /* enter the word in the symbol table */ 658 setclass(class, q); 659 } 660 } 661 662 (void) fclose(f); 663 } 664 /* 665 ** MAKEMAILER -- define a new mailer. 666 ** 667 ** Parameters: 668 ** line -- description of mailer. This is in labeled 669 ** fields. The fields are: 670 ** P -- the path to the mailer 671 ** F -- the flags associated with the mailer 672 ** A -- the argv for this mailer 673 ** S -- the sender rewriting set 674 ** R -- the recipient rewriting set 675 ** E -- the eol string 676 ** The first word is the canonical name of the mailer. 677 ** 678 ** Returns: 679 ** none. 680 ** 681 ** Side Effects: 682 ** enters the mailer into the mailer table. 683 */ 684 685 makemailer(line) 686 char *line; 687 { 688 register char *p; 689 register struct mailer *m; 690 register STAB *s; 691 int i; 692 char fcode; 693 auto char *endp; 694 extern int NextMailer; 695 extern char **makeargv(); 696 extern char *munchstring(); 697 extern long atol(); 698 699 /* allocate a mailer and set up defaults */ 700 m = (struct mailer *) xalloc(sizeof *m); 701 bzero((char *) m, sizeof *m); 702 m->m_eol = "\n"; 703 m->m_uid = m->m_gid = 0; 704 705 /* collect the mailer name */ 706 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 707 continue; 708 if (*p != '\0') 709 *p++ = '\0'; 710 m->m_name = newstr(line); 711 712 /* now scan through and assign info from the fields */ 713 while (*p != '\0') 714 { 715 auto char *delimptr; 716 717 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 718 p++; 719 720 /* p now points to field code */ 721 fcode = *p; 722 while (*p != '\0' && *p != '=' && *p != ',') 723 p++; 724 if (*p++ != '=') 725 { 726 syserr("mailer %s: `=' expected", m->m_name); 727 return; 728 } 729 while (isascii(*p) && isspace(*p)) 730 p++; 731 732 /* p now points to the field body */ 733 p = munchstring(p, &delimptr); 734 735 /* install the field into the mailer struct */ 736 switch (fcode) 737 { 738 case 'P': /* pathname */ 739 m->m_mailer = newstr(p); 740 break; 741 742 case 'F': /* flags */ 743 for (; *p != '\0'; p++) 744 if (!(isascii(*p) && isspace(*p))) 745 setbitn(*p, m->m_flags); 746 break; 747 748 case 'S': /* sender rewriting ruleset */ 749 case 'R': /* recipient rewriting ruleset */ 750 i = strtol(p, &endp, 10); 751 if (i < 0 || i >= MAXRWSETS) 752 { 753 syserr("invalid rewrite set, %d max", MAXRWSETS); 754 return; 755 } 756 if (fcode == 'S') 757 m->m_sh_rwset = m->m_se_rwset = i; 758 else 759 m->m_rh_rwset = m->m_re_rwset = i; 760 761 p = endp; 762 if (*p++ == '/') 763 { 764 i = strtol(p, NULL, 10); 765 if (i < 0 || i >= MAXRWSETS) 766 { 767 syserr("invalid rewrite set, %d max", 768 MAXRWSETS); 769 return; 770 } 771 if (fcode == 'S') 772 m->m_sh_rwset = i; 773 else 774 m->m_rh_rwset = i; 775 } 776 break; 777 778 case 'E': /* end of line string */ 779 m->m_eol = newstr(p); 780 break; 781 782 case 'A': /* argument vector */ 783 m->m_argv = makeargv(p); 784 break; 785 786 case 'M': /* maximum message size */ 787 m->m_maxsize = atol(p); 788 break; 789 790 case 'L': /* maximum line length */ 791 m->m_linelimit = atoi(p); 792 break; 793 794 case 'D': /* working directory */ 795 m->m_execdir = 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 1151 NULL, '\0', FALSE, 1152 }; 1153 1154 1155 1156 setoption(opt, val, safe, sticky, e) 1157 u_char opt; 1158 char *val; 1159 bool safe; 1160 bool sticky; 1161 register ENVELOPE *e; 1162 { 1163 register char *p; 1164 register struct optioninfo *o; 1165 extern bool atobool(); 1166 extern time_t convtime(); 1167 extern int QueueLA; 1168 extern int RefuseLA; 1169 extern bool Warn_Q_option; 1170 1171 errno = 0; 1172 if (opt == ' ') 1173 { 1174 /* full word options */ 1175 struct optioninfo *sel; 1176 1177 p = strchr(val, '='); 1178 if (p == NULL) 1179 p = &val[strlen(val)]; 1180 while (*--p == ' ') 1181 continue; 1182 while (*++p == ' ') 1183 *p = '\0'; 1184 if (p == val) 1185 { 1186 syserr("readcf: null option name"); 1187 return; 1188 } 1189 if (*p == '=') 1190 *p++ = '\0'; 1191 while (*p == ' ') 1192 p++; 1193 sel = NULL; 1194 for (o = OptionTab; o->o_name != NULL; o++) 1195 { 1196 if (strncasecmp(o->o_name, val, strlen(val)) != 0) 1197 continue; 1198 if (strlen(o->o_name) == strlen(val)) 1199 { 1200 /* completely specified -- this must be it */ 1201 sel = NULL; 1202 break; 1203 } 1204 if (sel != NULL) 1205 break; 1206 sel = o; 1207 } 1208 if (sel != NULL && o->o_name == NULL) 1209 o = sel; 1210 else if (o->o_name == NULL) 1211 { 1212 syserr("readcf: unknown option name %s", val); 1213 return; 1214 } 1215 else if (sel != NULL) 1216 { 1217 syserr("readcf: ambiguous option name %s (matches %s and %s)", 1218 val, sel->o_name, o->o_name); 1219 return; 1220 } 1221 if (strlen(val) != strlen(o->o_name)) 1222 { 1223 bool oldVerbose = Verbose; 1224 1225 Verbose = TRUE; 1226 message("Option %s used as abbreviation for %s", 1227 val, o->o_name); 1228 Verbose = oldVerbose; 1229 } 1230 opt = o->o_code; 1231 val = p; 1232 } 1233 else 1234 { 1235 for (o = OptionTab; o->o_name != NULL; o++) 1236 { 1237 if (o->o_code == opt) 1238 break; 1239 } 1240 } 1241 1242 if (tTd(37, 1)) 1243 { 1244 printf(isascii(opt) && isprint(opt) ? 1245 "setoption %s (%c)=%s" : "setoption %s (0x%x)=%s", 1246 o->o_name == NULL ? "<unknown>" : o->o_name, 1247 opt, val); 1248 } 1249 1250 /* 1251 ** See if this option is preset for us. 1252 */ 1253 1254 if (!sticky && bitnset(opt, StickyOpt)) 1255 { 1256 if (tTd(37, 1)) 1257 printf(" (ignored)\n"); 1258 return; 1259 } 1260 1261 /* 1262 ** Check to see if this option can be specified by this user. 1263 */ 1264 1265 if (!safe && RealUid == 0) 1266 safe = TRUE; 1267 if (!safe && !o->o_safe) 1268 { 1269 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1270 { 1271 if (tTd(37, 1)) 1272 printf(" (unsafe)"); 1273 if (RealUid != geteuid()) 1274 { 1275 if (tTd(37, 1)) 1276 printf("(Resetting uid)"); 1277 (void) setgid(RealGid); 1278 (void) setuid(RealUid); 1279 } 1280 } 1281 } 1282 if (tTd(37, 1)) 1283 printf("\n"); 1284 1285 switch (opt & 0xff) 1286 { 1287 case '7': /* force seven-bit input */ 1288 SevenBitInput = atobool(val); 1289 break; 1290 1291 case '8': /* handling of 8-bit input */ 1292 switch (*val) 1293 { 1294 case 'r': /* reject 8-bit, don't convert MIME */ 1295 MimeMode = 0; 1296 break; 1297 1298 case 'm': /* convert 8-bit, convert MIME */ 1299 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1300 break; 1301 1302 case 'j': /* "just send 8" */ 1303 MimeMode = MM_PASS8BIT; 1304 break; 1305 1306 case 'p': /* pass 8 bit, convert MIME */ 1307 MimeMode = MM_PASS8BIT|MM_CVTMIME; 1308 break; 1309 1310 case 's': /* strict adherence */ 1311 MimeMode = MM_CVTMIME; 1312 break; 1313 1314 case 'a': /* encode 8 bit if available */ 1315 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1316 break; 1317 1318 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1319 MimeMode = MM_MIME8BIT; 1320 break; 1321 1322 default: 1323 syserr("Unknown 8-bit mode %c", *val); 1324 exit(EX_USAGE); 1325 } 1326 break; 1327 1328 case 'A': /* set default alias file */ 1329 if (val[0] == '\0') 1330 setalias("aliases"); 1331 else 1332 setalias(val); 1333 break; 1334 1335 case 'a': /* look N minutes for "@:@" in alias file */ 1336 if (val[0] == '\0') 1337 SafeAlias = 5 * 60; /* five minutes */ 1338 else 1339 SafeAlias = convtime(val, 'm'); 1340 break; 1341 1342 case 'B': /* substitution for blank character */ 1343 SpaceSub = val[0]; 1344 if (SpaceSub == '\0') 1345 SpaceSub = ' '; 1346 break; 1347 1348 case 'b': /* min blocks free on queue fs/max msg size */ 1349 p = strchr(val, '/'); 1350 if (p != NULL) 1351 { 1352 *p++ = '\0'; 1353 MaxMessageSize = atol(p); 1354 } 1355 MinBlocksFree = atol(val); 1356 break; 1357 1358 case 'c': /* don't connect to "expensive" mailers */ 1359 NoConnect = atobool(val); 1360 break; 1361 1362 case 'C': /* checkpoint every N addresses */ 1363 CheckpointInterval = atoi(val); 1364 break; 1365 1366 case 'd': /* delivery mode */ 1367 switch (*val) 1368 { 1369 case '\0': 1370 e->e_sendmode = SM_DELIVER; 1371 break; 1372 1373 case SM_QUEUE: /* queue only */ 1374 #ifndef QUEUE 1375 syserr("need QUEUE to set -odqueue"); 1376 #endif /* QUEUE */ 1377 /* fall through..... */ 1378 1379 case SM_DELIVER: /* do everything */ 1380 case SM_FORK: /* fork after verification */ 1381 e->e_sendmode = *val; 1382 break; 1383 1384 default: 1385 syserr("Unknown delivery mode %c", *val); 1386 exit(EX_USAGE); 1387 } 1388 break; 1389 1390 case 'D': /* rebuild alias database as needed */ 1391 AutoRebuild = atobool(val); 1392 break; 1393 1394 case 'E': /* error message header/header file */ 1395 if (*val != '\0') 1396 ErrMsgFile = newstr(val); 1397 break; 1398 1399 case 'e': /* set error processing mode */ 1400 switch (*val) 1401 { 1402 case EM_QUIET: /* be silent about it */ 1403 case EM_MAIL: /* mail back */ 1404 case EM_BERKNET: /* do berknet error processing */ 1405 case EM_WRITE: /* write back (or mail) */ 1406 case EM_PRINT: /* print errors normally (default) */ 1407 e->e_errormode = *val; 1408 break; 1409 } 1410 break; 1411 1412 case 'F': /* file mode */ 1413 FileMode = atooct(val) & 0777; 1414 break; 1415 1416 case 'f': /* save Unix-style From lines on front */ 1417 SaveFrom = atobool(val); 1418 break; 1419 1420 case 'G': /* match recipients against GECOS field */ 1421 MatchGecos = atobool(val); 1422 break; 1423 1424 case 'g': /* default gid */ 1425 if (isascii(*val) && isdigit(*val)) 1426 DefGid = atoi(val); 1427 else 1428 { 1429 register struct group *gr; 1430 1431 DefGid = -1; 1432 gr = getgrnam(val); 1433 if (gr == NULL) 1434 syserr("readcf: option g: unknown group %s", 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 if (isascii(*val) && isdigit(*val)) 1635 DefUid = atoi(val); 1636 else 1637 { 1638 register struct passwd *pw; 1639 1640 DefUid = -1; 1641 pw = getpwnam(val); 1642 if (pw == NULL) 1643 syserr("readcf: option u: unknown user %s", val); 1644 else 1645 DefUid = pw->pw_uid; 1646 } 1647 setdefuser(); 1648 break; 1649 1650 case 'V': /* fallback MX host */ 1651 FallBackMX = newstr(val); 1652 break; 1653 1654 case 'v': /* run in verbose mode */ 1655 Verbose = atobool(val); 1656 break; 1657 1658 case 'w': /* if we are best MX, try host directly */ 1659 TryNullMXList = atobool(val); 1660 break; 1661 1662 /* 'W' available -- was wizard password */ 1663 1664 case 'x': /* load avg at which to auto-queue msgs */ 1665 QueueLA = atoi(val); 1666 break; 1667 1668 case 'X': /* load avg at which to auto-reject connections */ 1669 RefuseLA = atoi(val); 1670 break; 1671 1672 case 'y': /* work recipient factor */ 1673 WkRecipFact = atoi(val); 1674 break; 1675 1676 case 'Y': /* fork jobs during queue runs */ 1677 ForkQueueRuns = atobool(val); 1678 break; 1679 1680 case 'z': /* work message class factor */ 1681 WkClassFact = atoi(val); 1682 break; 1683 1684 case 'Z': /* work time factor */ 1685 WkTimeFact = atoi(val); 1686 break; 1687 1688 case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 1689 BrokenSmtpPeers = atobool(val); 1690 break; 1691 1692 case O_SQBH: /* sort work queue by host first */ 1693 SortQueueByHost = atobool(val); 1694 break; 1695 1696 case O_DNICE: /* delivery nice value */ 1697 DeliveryNiceness = atoi(val); 1698 break; 1699 1700 case O_MQA: /* minimum queue age between deliveries */ 1701 MinQueueAge = convtime(val, 'm'); 1702 break; 1703 1704 case O_MHSA: /* maximum age of cached host status */ 1705 MaxHostStatAge = convtime(val, 'm'); 1706 break; 1707 1708 case O_DEFCHARSET: /* default character set for mimefying */ 1709 DefaultCharSet = newstr(val); 1710 break; 1711 1712 default: 1713 break; 1714 } 1715 if (sticky) 1716 setbitn(opt, StickyOpt); 1717 return; 1718 } 1719 /* 1720 ** SETCLASS -- set a word into a class 1721 ** 1722 ** Parameters: 1723 ** class -- the class to put the word in. 1724 ** word -- the word to enter 1725 ** 1726 ** Returns: 1727 ** none. 1728 ** 1729 ** Side Effects: 1730 ** puts the word into the symbol table. 1731 */ 1732 1733 setclass(class, word) 1734 int class; 1735 char *word; 1736 { 1737 register STAB *s; 1738 1739 if (tTd(37, 8)) 1740 printf("setclass(%c, %s)\n", class, word); 1741 s = stab(word, ST_CLASS, ST_ENTER); 1742 setbitn(class, s->s_class); 1743 } 1744 /* 1745 ** MAKEMAPENTRY -- create a map entry 1746 ** 1747 ** Parameters: 1748 ** line -- the config file line 1749 ** 1750 ** Returns: 1751 ** TRUE if it successfully entered the map entry. 1752 ** FALSE otherwise (usually syntax error). 1753 ** 1754 ** Side Effects: 1755 ** Enters the map into the dictionary. 1756 */ 1757 1758 void 1759 makemapentry(line) 1760 char *line; 1761 { 1762 register char *p; 1763 char *mapname; 1764 char *classname; 1765 register STAB *s; 1766 STAB *class; 1767 1768 for (p = line; isascii(*p) && isspace(*p); p++) 1769 continue; 1770 if (!(isascii(*p) && isalnum(*p))) 1771 { 1772 syserr("readcf: config K line: no map name"); 1773 return; 1774 } 1775 1776 mapname = p; 1777 while (isascii(*++p) && isalnum(*p)) 1778 continue; 1779 if (*p != '\0') 1780 *p++ = '\0'; 1781 while (isascii(*p) && isspace(*p)) 1782 p++; 1783 if (!(isascii(*p) && isalnum(*p))) 1784 { 1785 syserr("readcf: config K line, map %s: no map class", mapname); 1786 return; 1787 } 1788 classname = p; 1789 while (isascii(*++p) && isalnum(*p)) 1790 continue; 1791 if (*p != '\0') 1792 *p++ = '\0'; 1793 while (isascii(*p) && isspace(*p)) 1794 p++; 1795 1796 /* look up the class */ 1797 class = stab(classname, ST_MAPCLASS, ST_FIND); 1798 if (class == NULL) 1799 { 1800 syserr("readcf: map %s: class %s not available", mapname, classname); 1801 return; 1802 } 1803 1804 /* enter the map */ 1805 s = stab(mapname, ST_MAP, ST_ENTER); 1806 s->s_map.map_class = &class->s_mapclass; 1807 s->s_map.map_mname = newstr(mapname); 1808 1809 if (class->s_mapclass.map_parse(&s->s_map, p)) 1810 s->s_map.map_mflags |= MF_VALID; 1811 1812 if (tTd(37, 5)) 1813 { 1814 printf("map %s, class %s, flags %x, file %s,\n", 1815 s->s_map.map_mname, s->s_map.map_class->map_cname, 1816 s->s_map.map_mflags, 1817 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 1818 printf("\tapp %s, domain %s, rebuild %s\n", 1819 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 1820 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 1821 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 1822 } 1823 } 1824 /* 1825 ** SETTIMEOUTS -- parse and set timeout values 1826 ** 1827 ** Parameters: 1828 ** val -- a pointer to the values. If NULL, do initial 1829 ** settings. 1830 ** 1831 ** Returns: 1832 ** none. 1833 ** 1834 ** Side Effects: 1835 ** Initializes the TimeOuts structure 1836 */ 1837 1838 #define SECONDS 1839 #define MINUTES * 60 1840 #define HOUR * 3600 1841 1842 settimeouts(val) 1843 register char *val; 1844 { 1845 register char *p; 1846 extern time_t convtime(); 1847 1848 if (val == NULL) 1849 { 1850 TimeOuts.to_initial = (time_t) 5 MINUTES; 1851 TimeOuts.to_helo = (time_t) 5 MINUTES; 1852 TimeOuts.to_mail = (time_t) 10 MINUTES; 1853 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1854 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1855 TimeOuts.to_datablock = (time_t) 1 HOUR; 1856 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1857 TimeOuts.to_rset = (time_t) 5 MINUTES; 1858 TimeOuts.to_quit = (time_t) 2 MINUTES; 1859 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1860 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1861 TimeOuts.to_ident = (time_t) 30 SECONDS; 1862 TimeOuts.to_fileopen = (time_t) 60 SECONDS; 1863 return; 1864 } 1865 1866 for (;; val = p) 1867 { 1868 while (isascii(*val) && isspace(*val)) 1869 val++; 1870 if (*val == '\0') 1871 break; 1872 for (p = val; *p != '\0' && *p != ','; p++) 1873 continue; 1874 if (*p != '\0') 1875 *p++ = '\0'; 1876 1877 if (isascii(*val) && isdigit(*val)) 1878 { 1879 /* old syntax -- set everything */ 1880 TimeOuts.to_mail = convtime(val, 'm'); 1881 TimeOuts.to_rcpt = TimeOuts.to_mail; 1882 TimeOuts.to_datainit = TimeOuts.to_mail; 1883 TimeOuts.to_datablock = TimeOuts.to_mail; 1884 TimeOuts.to_datafinal = TimeOuts.to_mail; 1885 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1886 continue; 1887 } 1888 else 1889 { 1890 register char *q = strchr(val, ':'); 1891 time_t to; 1892 1893 if (q == NULL && (q = strchr(val, '=')) == NULL) 1894 { 1895 /* syntax error */ 1896 continue; 1897 } 1898 *q++ = '\0'; 1899 to = convtime(q, 'm'); 1900 1901 if (strcasecmp(val, "initial") == 0) 1902 TimeOuts.to_initial = to; 1903 else if (strcasecmp(val, "mail") == 0) 1904 TimeOuts.to_mail = to; 1905 else if (strcasecmp(val, "rcpt") == 0) 1906 TimeOuts.to_rcpt = to; 1907 else if (strcasecmp(val, "datainit") == 0) 1908 TimeOuts.to_datainit = to; 1909 else if (strcasecmp(val, "datablock") == 0) 1910 TimeOuts.to_datablock = to; 1911 else if (strcasecmp(val, "datafinal") == 0) 1912 TimeOuts.to_datafinal = to; 1913 else if (strcasecmp(val, "command") == 0) 1914 TimeOuts.to_nextcommand = to; 1915 else if (strcasecmp(val, "rset") == 0) 1916 TimeOuts.to_rset = to; 1917 else if (strcasecmp(val, "helo") == 0) 1918 TimeOuts.to_helo = to; 1919 else if (strcasecmp(val, "quit") == 0) 1920 TimeOuts.to_quit = to; 1921 else if (strcasecmp(val, "misc") == 0) 1922 TimeOuts.to_miscshort = to; 1923 else if (strcasecmp(val, "ident") == 0) 1924 TimeOuts.to_ident = to; 1925 else if (strcasecmp(val, "fileopen") == 0) 1926 TimeOuts.to_fileopen = to; 1927 else if (strcasecmp(val, "queuewarn") == 0) 1928 TimeOuts.to_q_warning[TOC_NORMAL] = to; 1929 else if (strcasecmp(val, "queuereturn") == 0) 1930 TimeOuts.to_q_return[TOC_NORMAL] = to; 1931 else if (strcasecmp(val, "queuewarn.normal") == 0) 1932 TimeOuts.to_q_warning[TOC_NORMAL] = to; 1933 else if (strcasecmp(val, "queuereturn.normal") == 0) 1934 TimeOuts.to_q_return[TOC_NORMAL] = to; 1935 else if (strcasecmp(val, "queuewarn.urgent") == 0) 1936 TimeOuts.to_q_warning[TOC_URGENT] = to; 1937 else if (strcasecmp(val, "queuereturn.urgent") == 0) 1938 TimeOuts.to_q_return[TOC_URGENT] = to; 1939 else if (strcasecmp(val, "queuewarn.non-urgent") == 0) 1940 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 1941 else if (strcasecmp(val, "queuereturn.non-urgent") == 0) 1942 TimeOuts.to_q_return[TOC_NONURGENT] = to; 1943 else 1944 syserr("settimeouts: invalid timeout %s", val); 1945 } 1946 } 1947 } 1948