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