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.39 (Berkeley) 10/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 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 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 if (isascii(*val) && isdigit(*val)) 1424 DefGid = atoi(val); 1425 else 1426 { 1427 register struct group *gr; 1428 1429 DefGid = -1; 1430 gr = getgrnam(val); 1431 if (gr == NULL) 1432 syserr("readcf: option g: unknown group %s", val); 1433 else 1434 DefGid = gr->gr_gid; 1435 } 1436 break; 1437 1438 case 'H': /* help file */ 1439 if (val[0] == '\0') 1440 HelpFile = "sendmail.hf"; 1441 else 1442 HelpFile = newstr(val); 1443 break; 1444 1445 case 'h': /* maximum hop count */ 1446 MaxHopCount = atoi(val); 1447 break; 1448 1449 case 'I': /* use internet domain name server */ 1450 #if NAMED_BIND 1451 UseNameServer = TRUE; 1452 for (p = val; *p != 0; ) 1453 { 1454 bool clearmode; 1455 char *q; 1456 struct resolverflags *rfp; 1457 1458 while (*p == ' ') 1459 p++; 1460 if (*p == '\0') 1461 break; 1462 clearmode = FALSE; 1463 if (*p == '-') 1464 clearmode = TRUE; 1465 else if (*p != '+') 1466 p--; 1467 p++; 1468 q = p; 1469 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1470 p++; 1471 if (*p != '\0') 1472 *p++ = '\0'; 1473 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1474 { 1475 if (strcasecmp(q, rfp->rf_name) == 0) 1476 break; 1477 } 1478 if (rfp->rf_name == NULL) 1479 syserr("readcf: I option value %s unrecognized", q); 1480 else if (clearmode) 1481 _res.options &= ~rfp->rf_bits; 1482 else 1483 _res.options |= rfp->rf_bits; 1484 } 1485 if (tTd(8, 2)) 1486 printf("_res.options = %x\n", _res.options); 1487 #else 1488 usrerr("name server (I option) specified but BIND not compiled in"); 1489 #endif 1490 break; 1491 1492 case 'i': /* ignore dot lines in message */ 1493 IgnrDot = atobool(val); 1494 break; 1495 1496 case 'j': /* send errors in MIME (RFC 1341) format */ 1497 SendMIMEErrors = atobool(val); 1498 break; 1499 1500 case 'J': /* .forward search path */ 1501 ForwardPath = newstr(val); 1502 break; 1503 1504 case 'k': /* connection cache size */ 1505 MaxMciCache = atoi(val); 1506 if (MaxMciCache < 0) 1507 MaxMciCache = 0; 1508 break; 1509 1510 case 'K': /* connection cache timeout */ 1511 MciCacheTimeout = convtime(val, 'm'); 1512 break; 1513 1514 case 'l': /* use Errors-To: header */ 1515 UseErrorsTo = atobool(val); 1516 break; 1517 1518 case 'L': /* log level */ 1519 if (safe || LogLevel < atoi(val)) 1520 LogLevel = atoi(val); 1521 break; 1522 1523 case 'M': /* define macro */ 1524 define(val[0], newstr(&val[1]), CurEnv); 1525 sticky = FALSE; 1526 break; 1527 1528 case 'm': /* send to me too */ 1529 MeToo = atobool(val); 1530 break; 1531 1532 case 'n': /* validate RHS in newaliases */ 1533 CheckAliases = atobool(val); 1534 break; 1535 1536 /* 'N' available -- was "net name" */ 1537 1538 case 'O': /* daemon options */ 1539 setdaemonoptions(val); 1540 break; 1541 1542 case 'o': /* assume old style headers */ 1543 if (atobool(val)) 1544 CurEnv->e_flags |= EF_OLDSTYLE; 1545 else 1546 CurEnv->e_flags &= ~EF_OLDSTYLE; 1547 break; 1548 1549 case 'p': /* select privacy level */ 1550 p = val; 1551 for (;;) 1552 { 1553 register struct prival *pv; 1554 extern struct prival PrivacyValues[]; 1555 1556 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1557 p++; 1558 if (*p == '\0') 1559 break; 1560 val = p; 1561 while (isascii(*p) && isalnum(*p)) 1562 p++; 1563 if (*p != '\0') 1564 *p++ = '\0'; 1565 1566 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1567 { 1568 if (strcasecmp(val, pv->pv_name) == 0) 1569 break; 1570 } 1571 if (pv->pv_name == NULL) 1572 syserr("readcf: Op line: %s unrecognized", val); 1573 PrivacyFlags |= pv->pv_flag; 1574 } 1575 break; 1576 1577 case 'P': /* postmaster copy address for returned mail */ 1578 PostMasterCopy = newstr(val); 1579 break; 1580 1581 case 'q': /* slope of queue only function */ 1582 QueueFactor = atoi(val); 1583 break; 1584 1585 case 'Q': /* queue directory */ 1586 if (val[0] == '\0') 1587 QueueDir = "mqueue"; 1588 else 1589 QueueDir = newstr(val); 1590 if (RealUid != 0 && !safe) 1591 Warn_Q_option = TRUE; 1592 break; 1593 1594 case 'R': /* don't prune routes */ 1595 DontPruneRoutes = atobool(val); 1596 break; 1597 1598 case 'r': /* read timeout */ 1599 settimeouts(val); 1600 break; 1601 1602 case 'S': /* status file */ 1603 if (val[0] == '\0') 1604 StatFile = "sendmail.st"; 1605 else 1606 StatFile = newstr(val); 1607 break; 1608 1609 case 's': /* be super safe, even if expensive */ 1610 SuperSafe = atobool(val); 1611 break; 1612 1613 case 'T': /* queue timeout */ 1614 p = strchr(val, '/'); 1615 if (p != NULL) 1616 { 1617 *p++ = '\0'; 1618 TimeOuts.to_q_warning[TOC_NORMAL] = convtime(p, 'd'); 1619 } 1620 TimeOuts.to_q_return[TOC_NORMAL] = convtime(val, 'h'); 1621 break; 1622 1623 case 't': /* time zone name */ 1624 TimeZoneSpec = newstr(val); 1625 break; 1626 1627 case 'U': /* location of user database */ 1628 UdbSpec = newstr(val); 1629 break; 1630 1631 case 'u': /* set default uid */ 1632 if (isascii(*val) && isdigit(*val)) 1633 DefUid = atoi(val); 1634 else 1635 { 1636 register struct passwd *pw; 1637 1638 DefUid = -1; 1639 pw = getpwnam(val); 1640 if (pw == NULL) 1641 syserr("readcf: option u: unknown user %s", val); 1642 else 1643 DefUid = pw->pw_uid; 1644 } 1645 setdefuser(); 1646 break; 1647 1648 case 'V': /* fallback MX host */ 1649 FallBackMX = newstr(val); 1650 break; 1651 1652 case 'v': /* run in verbose mode */ 1653 Verbose = atobool(val); 1654 break; 1655 1656 case 'w': /* if we are best MX, try host directly */ 1657 TryNullMXList = atobool(val); 1658 break; 1659 1660 /* 'W' available -- was wizard password */ 1661 1662 case 'x': /* load avg at which to auto-queue msgs */ 1663 QueueLA = atoi(val); 1664 break; 1665 1666 case 'X': /* load avg at which to auto-reject connections */ 1667 RefuseLA = atoi(val); 1668 break; 1669 1670 case 'y': /* work recipient factor */ 1671 WkRecipFact = atoi(val); 1672 break; 1673 1674 case 'Y': /* fork jobs during queue runs */ 1675 ForkQueueRuns = atobool(val); 1676 break; 1677 1678 case 'z': /* work message class factor */ 1679 WkClassFact = atoi(val); 1680 break; 1681 1682 case 'Z': /* work time factor */ 1683 WkTimeFact = atoi(val); 1684 break; 1685 1686 case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 1687 BrokenSmtpPeers = atobool(val); 1688 break; 1689 1690 case O_SQBH: /* sort work queue by host first */ 1691 SortQueueByHost = atobool(val); 1692 break; 1693 1694 case O_DNICE: /* delivery nice value */ 1695 DeliveryNiceness = atoi(val); 1696 break; 1697 1698 case O_MQA: /* minimum queue age between deliveries */ 1699 MinQueueAge = convtime(val, 'm'); 1700 break; 1701 1702 case O_MHSA: /* maximum age of cached host status */ 1703 MaxHostStatAge = convtime(val, 'm'); 1704 break; 1705 1706 default: 1707 break; 1708 } 1709 if (sticky) 1710 setbitn(opt, StickyOpt); 1711 return; 1712 } 1713 /* 1714 ** SETCLASS -- set a word into a class 1715 ** 1716 ** Parameters: 1717 ** class -- the class to put the word in. 1718 ** word -- the word to enter 1719 ** 1720 ** Returns: 1721 ** none. 1722 ** 1723 ** Side Effects: 1724 ** puts the word into the symbol table. 1725 */ 1726 1727 setclass(class, word) 1728 int class; 1729 char *word; 1730 { 1731 register STAB *s; 1732 1733 if (tTd(37, 8)) 1734 printf("setclass(%c, %s)\n", class, word); 1735 s = stab(word, ST_CLASS, ST_ENTER); 1736 setbitn(class, s->s_class); 1737 } 1738 /* 1739 ** MAKEMAPENTRY -- create a map entry 1740 ** 1741 ** Parameters: 1742 ** line -- the config file line 1743 ** 1744 ** Returns: 1745 ** TRUE if it successfully entered the map entry. 1746 ** FALSE otherwise (usually syntax error). 1747 ** 1748 ** Side Effects: 1749 ** Enters the map into the dictionary. 1750 */ 1751 1752 void 1753 makemapentry(line) 1754 char *line; 1755 { 1756 register char *p; 1757 char *mapname; 1758 char *classname; 1759 register STAB *s; 1760 STAB *class; 1761 1762 for (p = line; isascii(*p) && isspace(*p); p++) 1763 continue; 1764 if (!(isascii(*p) && isalnum(*p))) 1765 { 1766 syserr("readcf: config K line: no map name"); 1767 return; 1768 } 1769 1770 mapname = p; 1771 while (isascii(*++p) && isalnum(*p)) 1772 continue; 1773 if (*p != '\0') 1774 *p++ = '\0'; 1775 while (isascii(*p) && isspace(*p)) 1776 p++; 1777 if (!(isascii(*p) && isalnum(*p))) 1778 { 1779 syserr("readcf: config K line, map %s: no map class", mapname); 1780 return; 1781 } 1782 classname = p; 1783 while (isascii(*++p) && isalnum(*p)) 1784 continue; 1785 if (*p != '\0') 1786 *p++ = '\0'; 1787 while (isascii(*p) && isspace(*p)) 1788 p++; 1789 1790 /* look up the class */ 1791 class = stab(classname, ST_MAPCLASS, ST_FIND); 1792 if (class == NULL) 1793 { 1794 syserr("readcf: map %s: class %s not available", mapname, classname); 1795 return; 1796 } 1797 1798 /* enter the map */ 1799 s = stab(mapname, ST_MAP, ST_ENTER); 1800 s->s_map.map_class = &class->s_mapclass; 1801 s->s_map.map_mname = newstr(mapname); 1802 1803 if (class->s_mapclass.map_parse(&s->s_map, p)) 1804 s->s_map.map_mflags |= MF_VALID; 1805 1806 if (tTd(37, 5)) 1807 { 1808 printf("map %s, class %s, flags %x, file %s,\n", 1809 s->s_map.map_mname, s->s_map.map_class->map_cname, 1810 s->s_map.map_mflags, 1811 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 1812 printf("\tapp %s, domain %s, rebuild %s\n", 1813 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 1814 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 1815 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 1816 } 1817 } 1818 /* 1819 ** SETTIMEOUTS -- parse and set timeout values 1820 ** 1821 ** Parameters: 1822 ** val -- a pointer to the values. If NULL, do initial 1823 ** settings. 1824 ** 1825 ** Returns: 1826 ** none. 1827 ** 1828 ** Side Effects: 1829 ** Initializes the TimeOuts structure 1830 */ 1831 1832 #define SECONDS 1833 #define MINUTES * 60 1834 #define HOUR * 3600 1835 1836 settimeouts(val) 1837 register char *val; 1838 { 1839 register char *p; 1840 extern time_t convtime(); 1841 1842 if (val == NULL) 1843 { 1844 TimeOuts.to_initial = (time_t) 5 MINUTES; 1845 TimeOuts.to_helo = (time_t) 5 MINUTES; 1846 TimeOuts.to_mail = (time_t) 10 MINUTES; 1847 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1848 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1849 TimeOuts.to_datablock = (time_t) 1 HOUR; 1850 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1851 TimeOuts.to_rset = (time_t) 5 MINUTES; 1852 TimeOuts.to_quit = (time_t) 2 MINUTES; 1853 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1854 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1855 TimeOuts.to_ident = (time_t) 30 SECONDS; 1856 TimeOuts.to_fileopen = (time_t) 60 SECONDS; 1857 return; 1858 } 1859 1860 for (;; val = p) 1861 { 1862 while (isascii(*val) && isspace(*val)) 1863 val++; 1864 if (*val == '\0') 1865 break; 1866 for (p = val; *p != '\0' && *p != ','; p++) 1867 continue; 1868 if (*p != '\0') 1869 *p++ = '\0'; 1870 1871 if (isascii(*val) && isdigit(*val)) 1872 { 1873 /* old syntax -- set everything */ 1874 TimeOuts.to_mail = convtime(val, 'm'); 1875 TimeOuts.to_rcpt = TimeOuts.to_mail; 1876 TimeOuts.to_datainit = TimeOuts.to_mail; 1877 TimeOuts.to_datablock = TimeOuts.to_mail; 1878 TimeOuts.to_datafinal = TimeOuts.to_mail; 1879 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1880 continue; 1881 } 1882 else 1883 { 1884 register char *q = strchr(val, ':'); 1885 time_t to; 1886 1887 if (q == NULL && (q = strchr(val, '=')) == NULL) 1888 { 1889 /* syntax error */ 1890 continue; 1891 } 1892 *q++ = '\0'; 1893 to = convtime(q, 'm'); 1894 1895 if (strcasecmp(val, "initial") == 0) 1896 TimeOuts.to_initial = to; 1897 else if (strcasecmp(val, "mail") == 0) 1898 TimeOuts.to_mail = to; 1899 else if (strcasecmp(val, "rcpt") == 0) 1900 TimeOuts.to_rcpt = to; 1901 else if (strcasecmp(val, "datainit") == 0) 1902 TimeOuts.to_datainit = to; 1903 else if (strcasecmp(val, "datablock") == 0) 1904 TimeOuts.to_datablock = to; 1905 else if (strcasecmp(val, "datafinal") == 0) 1906 TimeOuts.to_datafinal = to; 1907 else if (strcasecmp(val, "command") == 0) 1908 TimeOuts.to_nextcommand = to; 1909 else if (strcasecmp(val, "rset") == 0) 1910 TimeOuts.to_rset = to; 1911 else if (strcasecmp(val, "helo") == 0) 1912 TimeOuts.to_helo = to; 1913 else if (strcasecmp(val, "quit") == 0) 1914 TimeOuts.to_quit = to; 1915 else if (strcasecmp(val, "misc") == 0) 1916 TimeOuts.to_miscshort = to; 1917 else if (strcasecmp(val, "ident") == 0) 1918 TimeOuts.to_ident = to; 1919 else if (strcasecmp(val, "fileopen") == 0) 1920 TimeOuts.to_fileopen = to; 1921 else if (strcasecmp(val, "queuewarn") == 0) 1922 TimeOuts.to_q_warning[TOC_NORMAL] = to; 1923 else if (strcasecmp(val, "queuereturn") == 0) 1924 TimeOuts.to_q_return[TOC_NORMAL] = to; 1925 else if (strcasecmp(val, "queuewarn.normal") == 0) 1926 TimeOuts.to_q_warning[TOC_NORMAL] = to; 1927 else if (strcasecmp(val, "queuereturn.normal") == 0) 1928 TimeOuts.to_q_return[TOC_NORMAL] = to; 1929 else if (strcasecmp(val, "queuewarn.urgent") == 0) 1930 TimeOuts.to_q_warning[TOC_URGENT] = to; 1931 else if (strcasecmp(val, "queuereturn.urgent") == 0) 1932 TimeOuts.to_q_return[TOC_URGENT] = to; 1933 else if (strcasecmp(val, "queuewarn.non-urgent") == 0) 1934 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 1935 else if (strcasecmp(val, "queuereturn.non-urgent") == 0) 1936 TimeOuts.to_q_return[TOC_NONURGENT] = to; 1937 else 1938 syserr("settimeouts: invalid timeout %s", val); 1939 } 1940 } 1941 } 1942