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.33 (Berkeley) 08/18/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 char buf[MAXLINE]; 81 register char *p; 82 extern char **copyplist(); 83 struct stat statb; 84 char exbuf[MAXLINE]; 85 char pvpbuf[MAXLINE + MAXATOM]; 86 extern char *munchstring(); 87 extern void makemapentry(); 88 89 FileName = cfname; 90 LineNumber = 0; 91 92 cf = fopen(cfname, "r"); 93 if (cf == NULL) 94 { 95 syserr("cannot open"); 96 exit(EX_OSFILE); 97 } 98 99 if (fstat(fileno(cf), &statb) < 0) 100 { 101 syserr("cannot fstat"); 102 exit(EX_OSFILE); 103 } 104 105 if (!S_ISREG(statb.st_mode)) 106 { 107 syserr("not a plain file"); 108 exit(EX_OSFILE); 109 } 110 111 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 112 { 113 if (OpMode == MD_DAEMON || OpMode == MD_FREEZE) 114 fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 115 FileName); 116 #ifdef LOG 117 if (LogLevel > 0) 118 syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", 119 FileName); 120 #endif 121 } 122 123 #ifdef XLA 124 xla_zero(); 125 #endif 126 127 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 128 { 129 if (bp[0] == '#') 130 { 131 if (bp != buf) 132 free(bp); 133 continue; 134 } 135 136 /* map $ into \201 for macro expansion */ 137 for (p = bp; *p != '\0'; p++) 138 { 139 if (*p == '#' && p > bp && ConfigLevel >= 3) 140 { 141 /* this is an on-line comment */ 142 register char *e; 143 144 switch (*--p & 0377) 145 { 146 case MACROEXPAND: 147 /* it's from $# -- let it go through */ 148 p++; 149 break; 150 151 case '\\': 152 /* it's backslash escaped */ 153 (void) strcpy(p, p + 1); 154 break; 155 156 default: 157 /* delete preceeding white space */ 158 while (isascii(*p) && isspace(*p) && p > bp) 159 p--; 160 if ((e = strchr(++p, '\n')) != NULL) 161 (void) strcpy(p, e); 162 else 163 p[0] = p[1] = '\0'; 164 break; 165 } 166 continue; 167 } 168 169 if (*p != '$') 170 continue; 171 172 if (p[1] == '$') 173 { 174 /* actual dollar sign.... */ 175 (void) strcpy(p, p + 1); 176 continue; 177 } 178 179 /* convert to macro expansion character */ 180 *p = MACROEXPAND; 181 } 182 183 /* interpret this line */ 184 errno = 0; 185 switch (bp[0]) 186 { 187 case '\0': 188 case '#': /* comment */ 189 break; 190 191 case 'R': /* rewriting rule */ 192 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 193 continue; 194 195 if (*p == '\0') 196 { 197 syserr("invalid rewrite line \"%s\" (tab expected)", bp); 198 break; 199 } 200 201 /* allocate space for the rule header */ 202 if (rwp == NULL) 203 { 204 RewriteRules[ruleset] = rwp = 205 (struct rewrite *) xalloc(sizeof *rwp); 206 } 207 else 208 { 209 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 210 rwp = rwp->r_next; 211 } 212 rwp->r_next = NULL; 213 214 /* expand and save the LHS */ 215 *p = '\0'; 216 expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e); 217 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, 218 sizeof pvpbuf, NULL); 219 nfuzzy = 0; 220 if (rwp->r_lhs != NULL) 221 { 222 register char **ap; 223 224 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 225 226 /* count the number of fuzzy matches in LHS */ 227 for (ap = rwp->r_lhs; *ap != NULL; ap++) 228 { 229 char *botch; 230 231 botch = NULL; 232 switch (**ap & 0377) 233 { 234 case MATCHZANY: 235 case MATCHANY: 236 case MATCHONE: 237 case MATCHCLASS: 238 case MATCHNCLASS: 239 nfuzzy++; 240 break; 241 242 case MATCHREPL: 243 botch = "$0-$9"; 244 break; 245 246 case CANONNET: 247 botch = "$#"; 248 break; 249 250 case CANONUSER: 251 botch = "$:"; 252 break; 253 254 case CALLSUBR: 255 botch = "$>"; 256 break; 257 258 case CONDIF: 259 botch = "$?"; 260 break; 261 262 case CONDELSE: 263 botch = "$|"; 264 break; 265 266 case CONDFI: 267 botch = "$."; 268 break; 269 270 case HOSTBEGIN: 271 botch = "$["; 272 break; 273 274 case HOSTEND: 275 botch = "$]"; 276 break; 277 278 case LOOKUPBEGIN: 279 botch = "$("; 280 break; 281 282 case LOOKUPEND: 283 botch = "$)"; 284 break; 285 } 286 if (botch != NULL) 287 syserr("Inappropriate use of %s on LHS", 288 botch); 289 } 290 } 291 else 292 syserr("R line: null LHS"); 293 294 /* expand and save the RHS */ 295 while (*++p == '\t') 296 continue; 297 q = p; 298 while (*p != '\0' && *p != '\t') 299 p++; 300 *p = '\0'; 301 expand(q, exbuf, &exbuf[sizeof exbuf], e); 302 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, 303 sizeof pvpbuf, NULL); 304 if (rwp->r_rhs != NULL) 305 { 306 register char **ap; 307 308 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 309 310 /* check no out-of-bounds replacements */ 311 nfuzzy += '0'; 312 for (ap = rwp->r_rhs; *ap != NULL; ap++) 313 { 314 char *botch; 315 316 botch = NULL; 317 switch (**ap & 0377) 318 { 319 case MATCHREPL: 320 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 321 { 322 syserr("replacement $%c out of bounds", 323 (*ap)[1]); 324 } 325 break; 326 327 case MATCHZANY: 328 botch = "$*"; 329 break; 330 331 case MATCHANY: 332 botch = "$+"; 333 break; 334 335 case MATCHONE: 336 botch = "$-"; 337 break; 338 339 case MATCHCLASS: 340 botch = "$="; 341 break; 342 343 case MATCHNCLASS: 344 botch = "$~"; 345 break; 346 } 347 if (botch != NULL) 348 syserr("Inappropriate use of %s on RHS", 349 botch); 350 } 351 } 352 else 353 syserr("R line: null RHS"); 354 break; 355 356 case 'S': /* select rewriting set */ 357 for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 358 continue; 359 if (!isascii(*p) || !isdigit(*p)) 360 { 361 syserr("invalid argument to S line: \"%.20s\"", 362 &bp[1]); 363 break; 364 } 365 ruleset = atoi(p); 366 if (ruleset >= MAXRWSETS || ruleset < 0) 367 { 368 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 369 ruleset = 0; 370 } 371 rwp = NULL; 372 break; 373 374 case 'D': /* macro definition */ 375 p = munchstring(&bp[2], NULL); 376 define(bp[1], newstr(p), e); 377 break; 378 379 case 'H': /* required header line */ 380 (void) chompheader(&bp[1], TRUE, e); 381 break; 382 383 case 'C': /* word class */ 384 /* scan the list of words and set class for all */ 385 expand(&bp[2], exbuf, &exbuf[sizeof exbuf], e); 386 for (p = exbuf; *p != '\0'; ) 387 { 388 register char *wd; 389 char delim; 390 391 while (*p != '\0' && isascii(*p) && isspace(*p)) 392 p++; 393 wd = p; 394 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 395 p++; 396 delim = *p; 397 *p = '\0'; 398 if (wd[0] != '\0') 399 setclass(bp[1], wd); 400 *p = delim; 401 } 402 break; 403 404 case 'F': /* word class from file */ 405 for (p = &bp[2]; isascii(*p) && isspace(*p); ) 406 p++; 407 if (p[0] == '-' && p[1] == 'o') 408 { 409 optional = TRUE; 410 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 411 p++; 412 while (isascii(*p) && isspace(*p)) 413 p++; 414 } 415 else 416 optional = FALSE; 417 file = p; 418 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 419 p++; 420 if (*p == '\0') 421 p = "%s"; 422 else 423 { 424 *p = '\0'; 425 while (isascii(*++p) && isspace(*p)) 426 continue; 427 } 428 fileclass(bp[1], file, p, safe, optional); 429 break; 430 431 #ifdef XLA 432 case 'L': /* extended load average description */ 433 xla_init(&bp[1]); 434 break; 435 #endif 436 437 case 'M': /* define mailer */ 438 makemailer(&bp[1]); 439 break; 440 441 case 'O': /* set option */ 442 setoption(bp[1], &bp[2], safe, FALSE, e); 443 break; 444 445 case 'P': /* set precedence */ 446 if (NumPriorities >= MAXPRIORITIES) 447 { 448 toomany('P', MAXPRIORITIES); 449 break; 450 } 451 for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 452 continue; 453 if (*p == '\0') 454 goto badline; 455 *p = '\0'; 456 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 457 Priorities[NumPriorities].pri_val = atoi(++p); 458 NumPriorities++; 459 break; 460 461 case 'T': /* trusted user(s) */ 462 /* this option is obsolete, but will be ignored */ 463 break; 464 465 case 'V': /* configuration syntax version */ 466 for (p = &bp[1]; isascii(*p) && isspace(*p); p++) 467 continue; 468 if (!isascii(*p) || !isdigit(*p)) 469 { 470 syserr("invalid argument to V line: \"%.20s\"", 471 &bp[1]); 472 break; 473 } 474 ConfigLevel = strtol(p, &ep, 10); 475 if (ConfigLevel >= 5) 476 { 477 /* level 5 configs have short name in $w */ 478 p = macvalue('w', e); 479 if (p != NULL && (p = strchr(p, '.')) != NULL) 480 *p = '\0'; 481 } 482 if (*ep++ == '/') 483 { 484 /* extract vendor code */ 485 for (p = ep; isascii(*p) && isalpha(*p); ) 486 p++; 487 *p = '\0'; 488 489 if (!setvendor(ep)) 490 syserr("invalid V line vendor code: \"%s\"", 491 ep); 492 } 493 break; 494 495 case 'K': 496 makemapentry(&bp[1]); 497 break; 498 499 default: 500 badline: 501 syserr("unknown control line \"%s\"", bp); 502 } 503 if (bp != buf) 504 free(bp); 505 } 506 if (ferror(cf)) 507 { 508 syserr("I/O read error", cfname); 509 exit(EX_OSFILE); 510 } 511 fclose(cf); 512 FileName = NULL; 513 514 if (stab("host", ST_MAP, ST_FIND) == NULL) 515 { 516 /* user didn't initialize: set up host map */ 517 strcpy(buf, "host host"); 518 #if NAMED_BIND 519 if (ConfigLevel >= 2) 520 strcat(buf, " -a."); 521 #endif 522 makemapentry(buf); 523 } 524 } 525 /* 526 ** TOOMANY -- signal too many of some option 527 ** 528 ** Parameters: 529 ** id -- the id of the error line 530 ** maxcnt -- the maximum possible values 531 ** 532 ** Returns: 533 ** none. 534 ** 535 ** Side Effects: 536 ** gives a syserr. 537 */ 538 539 toomany(id, maxcnt) 540 char id; 541 int maxcnt; 542 { 543 syserr("too many %c lines, %d max", id, maxcnt); 544 } 545 /* 546 ** FILECLASS -- read members of a class from a file 547 ** 548 ** Parameters: 549 ** class -- class to define. 550 ** filename -- name of file to read. 551 ** fmt -- scanf string to use for match. 552 ** safe -- if set, this is a safe read. 553 ** optional -- if set, it is not an error for the file to 554 ** not exist. 555 ** 556 ** Returns: 557 ** none 558 ** 559 ** Side Effects: 560 ** 561 ** puts all lines in filename that match a scanf into 562 ** the named class. 563 */ 564 565 fileclass(class, filename, fmt, safe, optional) 566 int class; 567 char *filename; 568 char *fmt; 569 bool safe; 570 bool optional; 571 { 572 FILE *f; 573 struct stat stbuf; 574 char buf[MAXLINE]; 575 576 if (tTd(37, 2)) 577 printf("fileclass(%s, fmt=%s)\n", filename, fmt); 578 579 if (filename[0] == '|') 580 { 581 syserr("fileclass: pipes (F%c%s) not supported due to security problems", 582 class, filename); 583 return; 584 } 585 if (stat(filename, &stbuf) < 0) 586 { 587 if (tTd(37, 2)) 588 printf(" cannot stat (%s)\n", errstring(errno)); 589 if (!optional) 590 syserr("fileclass: cannot stat %s", filename); 591 return; 592 } 593 if (!S_ISREG(stbuf.st_mode)) 594 { 595 syserr("fileclass: %s not a regular file", filename); 596 return; 597 } 598 if (!safe && access(filename, R_OK) < 0) 599 { 600 syserr("fileclass: access denied on %s", filename); 601 return; 602 } 603 f = fopen(filename, "r"); 604 if (f == NULL) 605 { 606 syserr("fileclass: cannot open %s", filename); 607 return; 608 } 609 610 while (fgets(buf, sizeof buf, f) != NULL) 611 { 612 register STAB *s; 613 register char *p; 614 # ifdef SCANF 615 char wordbuf[MAXNAME+1]; 616 617 if (sscanf(buf, fmt, wordbuf) != 1) 618 continue; 619 p = wordbuf; 620 # else /* SCANF */ 621 p = buf; 622 # endif /* SCANF */ 623 624 /* 625 ** Break up the match into words. 626 */ 627 628 while (*p != '\0') 629 { 630 register char *q; 631 632 /* strip leading spaces */ 633 while (isascii(*p) && isspace(*p)) 634 p++; 635 if (*p == '\0') 636 break; 637 638 /* find the end of the word */ 639 q = p; 640 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 641 p++; 642 if (*p != '\0') 643 *p++ = '\0'; 644 645 /* enter the word in the symbol table */ 646 setclass(class, q); 647 } 648 } 649 650 (void) fclose(f); 651 } 652 /* 653 ** MAKEMAILER -- define a new mailer. 654 ** 655 ** Parameters: 656 ** line -- description of mailer. This is in labeled 657 ** fields. The fields are: 658 ** P -- the path to the mailer 659 ** F -- the flags associated with the mailer 660 ** A -- the argv for this mailer 661 ** S -- the sender rewriting set 662 ** R -- the recipient rewriting set 663 ** E -- the eol string 664 ** The first word is the canonical name of the mailer. 665 ** 666 ** Returns: 667 ** none. 668 ** 669 ** Side Effects: 670 ** enters the mailer into the mailer table. 671 */ 672 673 makemailer(line) 674 char *line; 675 { 676 register char *p; 677 register struct mailer *m; 678 register STAB *s; 679 int i; 680 char fcode; 681 auto char *endp; 682 extern int NextMailer; 683 extern char **makeargv(); 684 extern char *munchstring(); 685 extern long atol(); 686 687 /* allocate a mailer and set up defaults */ 688 m = (struct mailer *) xalloc(sizeof *m); 689 bzero((char *) m, sizeof *m); 690 m->m_eol = "\n"; 691 m->m_uid = m->m_gid = 0; 692 693 /* collect the mailer name */ 694 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 695 continue; 696 if (*p != '\0') 697 *p++ = '\0'; 698 m->m_name = newstr(line); 699 700 /* now scan through and assign info from the fields */ 701 while (*p != '\0') 702 { 703 auto char *delimptr; 704 705 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 706 p++; 707 708 /* p now points to field code */ 709 fcode = *p; 710 while (*p != '\0' && *p != '=' && *p != ',') 711 p++; 712 if (*p++ != '=') 713 { 714 syserr("mailer %s: `=' expected", m->m_name); 715 return; 716 } 717 while (isascii(*p) && isspace(*p)) 718 p++; 719 720 /* p now points to the field body */ 721 p = munchstring(p, &delimptr); 722 723 /* install the field into the mailer struct */ 724 switch (fcode) 725 { 726 case 'P': /* pathname */ 727 m->m_mailer = newstr(p); 728 break; 729 730 case 'F': /* flags */ 731 for (; *p != '\0'; p++) 732 if (!(isascii(*p) && isspace(*p))) 733 setbitn(*p, m->m_flags); 734 break; 735 736 case 'S': /* sender rewriting ruleset */ 737 case 'R': /* recipient rewriting ruleset */ 738 i = strtol(p, &endp, 10); 739 if (i < 0 || i >= MAXRWSETS) 740 { 741 syserr("invalid rewrite set, %d max", MAXRWSETS); 742 return; 743 } 744 if (fcode == 'S') 745 m->m_sh_rwset = m->m_se_rwset = i; 746 else 747 m->m_rh_rwset = m->m_re_rwset = i; 748 749 p = endp; 750 if (*p++ == '/') 751 { 752 i = strtol(p, NULL, 10); 753 if (i < 0 || i >= MAXRWSETS) 754 { 755 syserr("invalid rewrite set, %d max", 756 MAXRWSETS); 757 return; 758 } 759 if (fcode == 'S') 760 m->m_sh_rwset = i; 761 else 762 m->m_rh_rwset = i; 763 } 764 break; 765 766 case 'E': /* end of line string */ 767 m->m_eol = newstr(p); 768 break; 769 770 case 'A': /* argument vector */ 771 m->m_argv = makeargv(p); 772 break; 773 774 case 'M': /* maximum message size */ 775 m->m_maxsize = atol(p); 776 break; 777 778 case 'L': /* maximum line length */ 779 m->m_linelimit = atoi(p); 780 break; 781 782 case 'D': /* working directory */ 783 m->m_execdir = newstr(p); 784 break; 785 786 case 'U': /* user id */ 787 if (isascii(*p) && !isdigit(*p)) 788 { 789 char *q = p; 790 struct passwd *pw; 791 792 while (isascii(*p) && isalnum(*p)) 793 p++; 794 while (isascii(*p) && isspace(*p)) 795 *p++ = '\0'; 796 if (*p != '\0') 797 *p++ = '\0'; 798 pw = getpwnam(q); 799 if (pw == NULL) 800 syserr("readcf: mailer U= flag: unknown user %s", q); 801 else 802 { 803 m->m_uid = pw->pw_uid; 804 m->m_gid = pw->pw_gid; 805 } 806 } 807 else 808 { 809 auto char *q; 810 811 m->m_uid = strtol(p, &q, 0); 812 p = q; 813 } 814 while (isascii(*p) && isspace(*p)) 815 p++; 816 if (*p == '\0') 817 break; 818 if (isascii(*p) && !isdigit(*p)) 819 { 820 char *q = p; 821 struct group *gr; 822 823 while (isascii(*p) && isalnum(*p)) 824 p++; 825 *p++ = '\0'; 826 gr = getgrnam(q); 827 if (gr == NULL) 828 syserr("readcf: mailer U= flag: unknown group %s", q); 829 else 830 m->m_gid = gr->gr_gid; 831 } 832 else 833 { 834 m->m_gid = strtol(p, NULL, 0); 835 } 836 break; 837 } 838 839 p = delimptr; 840 } 841 842 /* do some heuristic cleanup for back compatibility */ 843 if (bitnset(M_LIMITS, m->m_flags)) 844 { 845 if (m->m_linelimit == 0) 846 m->m_linelimit = SMTPLINELIM; 847 if (ConfigLevel < 2) 848 setbitn(M_7BITS, m->m_flags); 849 } 850 851 /* do some rationality checking */ 852 if (m->m_argv == NULL) 853 { 854 syserr("M%s: A= argument required", m->m_name); 855 return; 856 } 857 if (m->m_mailer == NULL) 858 { 859 syserr("M%s: P= argument required", m->m_name); 860 return; 861 } 862 863 if (NextMailer >= MAXMAILERS) 864 { 865 syserr("too many mailers defined (%d max)", MAXMAILERS); 866 return; 867 } 868 869 s = stab(m->m_name, ST_MAILER, ST_ENTER); 870 if (s->s_mailer != NULL) 871 { 872 i = s->s_mailer->m_mno; 873 free(s->s_mailer); 874 } 875 else 876 { 877 i = NextMailer++; 878 } 879 Mailer[i] = s->s_mailer = m; 880 m->m_mno = i; 881 } 882 /* 883 ** MUNCHSTRING -- translate a string into internal form. 884 ** 885 ** Parameters: 886 ** p -- the string to munch. 887 ** delimptr -- if non-NULL, set to the pointer of the 888 ** field delimiter character. 889 ** 890 ** Returns: 891 ** the munched string. 892 */ 893 894 char * 895 munchstring(p, delimptr) 896 register char *p; 897 char **delimptr; 898 { 899 register char *q; 900 bool backslash = FALSE; 901 bool quotemode = FALSE; 902 static char buf[MAXLINE]; 903 904 for (q = buf; *p != '\0'; p++) 905 { 906 if (backslash) 907 { 908 /* everything is roughly literal */ 909 backslash = FALSE; 910 switch (*p) 911 { 912 case 'r': /* carriage return */ 913 *q++ = '\r'; 914 continue; 915 916 case 'n': /* newline */ 917 *q++ = '\n'; 918 continue; 919 920 case 'f': /* form feed */ 921 *q++ = '\f'; 922 continue; 923 924 case 'b': /* backspace */ 925 *q++ = '\b'; 926 continue; 927 } 928 *q++ = *p; 929 } 930 else 931 { 932 if (*p == '\\') 933 backslash = TRUE; 934 else if (*p == '"') 935 quotemode = !quotemode; 936 else if (quotemode || *p != ',') 937 *q++ = *p; 938 else 939 break; 940 } 941 } 942 943 if (delimptr != NULL) 944 *delimptr = p; 945 *q++ = '\0'; 946 return (buf); 947 } 948 /* 949 ** MAKEARGV -- break up a string into words 950 ** 951 ** Parameters: 952 ** p -- the string to break up. 953 ** 954 ** Returns: 955 ** a char **argv (dynamically allocated) 956 ** 957 ** Side Effects: 958 ** munges p. 959 */ 960 961 char ** 962 makeargv(p) 963 register char *p; 964 { 965 char *q; 966 int i; 967 char **avp; 968 char *argv[MAXPV + 1]; 969 970 /* take apart the words */ 971 i = 0; 972 while (*p != '\0' && i < MAXPV) 973 { 974 q = p; 975 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 976 p++; 977 while (isascii(*p) && isspace(*p)) 978 *p++ = '\0'; 979 argv[i++] = newstr(q); 980 } 981 argv[i++] = NULL; 982 983 /* now make a copy of the argv */ 984 avp = (char **) xalloc(sizeof *avp * i); 985 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 986 987 return (avp); 988 } 989 /* 990 ** PRINTRULES -- print rewrite rules (for debugging) 991 ** 992 ** Parameters: 993 ** none. 994 ** 995 ** Returns: 996 ** none. 997 ** 998 ** Side Effects: 999 ** prints rewrite rules. 1000 */ 1001 1002 printrules() 1003 { 1004 register struct rewrite *rwp; 1005 register int ruleset; 1006 1007 for (ruleset = 0; ruleset < 10; ruleset++) 1008 { 1009 if (RewriteRules[ruleset] == NULL) 1010 continue; 1011 printf("\n----Rule Set %d:", ruleset); 1012 1013 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 1014 { 1015 printf("\nLHS:"); 1016 printav(rwp->r_lhs); 1017 printf("RHS:"); 1018 printav(rwp->r_rhs); 1019 } 1020 } 1021 } 1022 1023 /* 1024 ** SETOPTION -- set global processing option 1025 ** 1026 ** Parameters: 1027 ** opt -- option name. 1028 ** val -- option value (as a text string). 1029 ** safe -- set if this came from a configuration file. 1030 ** Some options (if set from the command line) will 1031 ** reset the user id to avoid security problems. 1032 ** sticky -- if set, don't let other setoptions override 1033 ** this value. 1034 ** e -- the main envelope. 1035 ** 1036 ** Returns: 1037 ** none. 1038 ** 1039 ** Side Effects: 1040 ** Sets options as implied by the arguments. 1041 */ 1042 1043 static BITMAP StickyOpt; /* set if option is stuck */ 1044 1045 1046 #if NAMED_BIND 1047 1048 struct resolverflags 1049 { 1050 char *rf_name; /* name of the flag */ 1051 long rf_bits; /* bits to set/clear */ 1052 } ResolverFlags[] = 1053 { 1054 "debug", RES_DEBUG, 1055 "aaonly", RES_AAONLY, 1056 "usevc", RES_USEVC, 1057 "primary", RES_PRIMARY, 1058 "igntc", RES_IGNTC, 1059 "recurse", RES_RECURSE, 1060 "defnames", RES_DEFNAMES, 1061 "stayopen", RES_STAYOPEN, 1062 "dnsrch", RES_DNSRCH, 1063 "true", 0, /* to avoid error on old syntax */ 1064 NULL, 0 1065 }; 1066 1067 #endif 1068 1069 struct optioninfo 1070 { 1071 char *o_name; /* long name of option */ 1072 char o_code; /* short name of option */ 1073 bool o_safe; /* safe for random people to use */ 1074 } OptionTab[] = 1075 { 1076 "SevenBitInput", '7', TRUE, 1077 "EightBitMode", '8', TRUE, 1078 "AliasFile", 'A', FALSE, 1079 "AliasWait", 'a', FALSE, 1080 "BlankSub", 'B', FALSE, 1081 "MinFreeBlocks", 'b', TRUE, 1082 "CheckpointInterval", 'C', TRUE, 1083 "HoldExpensive", 'c', FALSE, 1084 "AutoRebuildAliases", 'D', FALSE, 1085 "DeliveryMode", 'd', TRUE, 1086 "ErrorHeader", 'E', FALSE, 1087 "ErrorMode", 'e', TRUE, 1088 "TempFileMode", 'F', FALSE, 1089 "SaveFromLine", 'f', FALSE, 1090 "MatchGECOS", 'G', FALSE, 1091 "HelpFile", 'H', FALSE, 1092 "MaxHopCount", 'h', FALSE, 1093 "NameServerOptions", 'I', FALSE, 1094 "IgnoreDots", 'i', TRUE, 1095 "ForwardPath", 'J', FALSE, 1096 "SendMimeErrors", 'j', TRUE, 1097 "ConnectionCacheSize", 'k', FALSE, 1098 "ConnectionCacheTimeout", 'K', FALSE, 1099 "UseErrorsTo", 'l', FALSE, 1100 "LogLevel", 'L', FALSE, 1101 "MeToo", 'm', TRUE, 1102 "CheckAliases", 'n', FALSE, 1103 "OldStyleHeaders", 'o', TRUE, 1104 "DaemonPortOptions", 'O', FALSE, 1105 "PrivacyOptions", 'p', TRUE, 1106 "PostmasterCopy", 'P', FALSE, 1107 "QueueFactor", 'q', FALSE, 1108 "QueueDirectory", 'Q', FALSE, 1109 "DontPruneRoutes", 'R', FALSE, 1110 "ReadTimeout", 'r', TRUE, 1111 "StatusFile", 'S', FALSE, 1112 "SuperSafe", 's', TRUE, 1113 "QueueTimeout", 'T', FALSE, 1114 "TimeZoneSpec", 't', FALSE, 1115 "UserDatabaseSpec", 'U', FALSE, 1116 "DefaultUser", 'u', FALSE, 1117 "FallbackMXhost", 'V', FALSE, 1118 "Verbose", 'v', TRUE, 1119 "TryNullMXList", 'w', TRUE, 1120 "QueueLA", 'x', FALSE, 1121 "RefuseLA", 'X', FALSE, 1122 "RecipientFactor", 'y', FALSE, 1123 "ForkQueueRuns", 'Y', FALSE, 1124 "ClassFactor", 'z', FALSE, 1125 "TimeFactor", 'Z', FALSE, 1126 #define O_BSP 0x80 1127 "BrokenSmtpPeers", O_BSP, TRUE, 1128 #define O_SQBH 0x81 1129 "SortQueueByHost", O_SQBH, TRUE, 1130 #define O_DNICE 0x82 1131 "DeliveryNiceness", O_DNICE, TRUE, 1132 #define O_MQA 0x83 1133 "MinQueueAge", O_MQA, TRUE, 1134 #define O_MHSA 0x84 1135 "MaxHostStatAge", O_MHSA, TRUE, 1136 1137 NULL, '\0', FALSE, 1138 }; 1139 1140 1141 1142 setoption(opt, val, safe, sticky, e) 1143 u_char opt; 1144 char *val; 1145 bool safe; 1146 bool sticky; 1147 register ENVELOPE *e; 1148 { 1149 register char *p; 1150 register struct optioninfo *o; 1151 extern bool atobool(); 1152 extern time_t convtime(); 1153 extern int QueueLA; 1154 extern int RefuseLA; 1155 extern bool Warn_Q_option; 1156 extern bool trusteduser(); 1157 1158 if (opt == ' ') 1159 { 1160 /* full word options */ 1161 1162 p = strchr(val, '='); 1163 if (p == NULL) 1164 p = &val[strlen(val)]; 1165 while (*--p == ' ') 1166 continue; 1167 while (*++p == ' ') 1168 *p = '\0'; 1169 if (*p == '=') 1170 *p++ = '\0'; 1171 while (*p == ' ') 1172 p++; 1173 for (o = OptionTab; o->o_name != NULL; o++) 1174 { 1175 if (strcasecmp(o->o_name, val) == 0) 1176 break; 1177 } 1178 if (o->o_name == NULL) 1179 syserr("readcf: unknown option name %s", val); 1180 opt = o->o_code; 1181 val = p; 1182 } 1183 else 1184 { 1185 for (o = OptionTab; o->o_name != NULL; o++) 1186 { 1187 if (o->o_code == opt) 1188 break; 1189 } 1190 } 1191 1192 if (tTd(37, 1)) 1193 printf("setoption %s (0x%x)=%s", 1194 o->o_name == NULL ? "<unknown>" : o->o_name, 1195 opt, val); 1196 1197 /* 1198 ** See if this option is preset for us. 1199 */ 1200 1201 if (!sticky && bitnset(opt, StickyOpt)) 1202 { 1203 if (tTd(37, 1)) 1204 printf(" (ignored)\n"); 1205 return; 1206 } 1207 1208 /* 1209 ** Check to see if this option can be specified by this user. 1210 */ 1211 1212 if (!safe && RealUid == 0) 1213 safe = TRUE; 1214 if (!safe && !o->o_safe) 1215 { 1216 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1217 { 1218 if (tTd(37, 1)) 1219 printf(" (unsafe)"); 1220 if (RealUid != geteuid()) 1221 { 1222 if (tTd(37, 1)) 1223 printf("(Resetting uid)"); 1224 (void) setgid(RealGid); 1225 (void) setuid(RealUid); 1226 } 1227 } 1228 } 1229 if (tTd(37, 1)) 1230 printf("\n"); 1231 1232 switch (opt & 0xff) 1233 { 1234 case '7': /* force seven-bit input */ 1235 SevenBitInput = atobool(val); 1236 break; 1237 1238 case '8': /* handling of 8-bit input */ 1239 switch (*val) 1240 { 1241 case 'r': /* reject 8-bit, don't convert MIME */ 1242 MimeMode = 0; 1243 break; 1244 1245 case 'm': /* convert 8-bit, convert MIME */ 1246 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1247 break; 1248 1249 case 'j': /* "just send 8" */ 1250 MimeMode = MM_PASS8BIT; 1251 break; 1252 1253 case 'p': /* pass 8 bit, convert MIME */ 1254 MimeMode = MM_PASS8BIT|MM_CVTMIME; 1255 break; 1256 1257 case 's': /* strict adherence */ 1258 MimeMode = MM_CVTMIME; 1259 break; 1260 1261 case 'a': /* encode 8 bit if available */ 1262 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1263 break; 1264 1265 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1266 MimeMode = MM_MIME8BIT; 1267 break; 1268 1269 default: 1270 syserr("Unknown 8-bit mode %c", *val); 1271 exit(EX_USAGE); 1272 } 1273 break; 1274 1275 case 'A': /* set default alias file */ 1276 if (val[0] == '\0') 1277 setalias("aliases"); 1278 else 1279 setalias(val); 1280 break; 1281 1282 case 'a': /* look N minutes for "@:@" in alias file */ 1283 if (val[0] == '\0') 1284 SafeAlias = 5 * 60; /* five minutes */ 1285 else 1286 SafeAlias = convtime(val, 'm'); 1287 break; 1288 1289 case 'B': /* substitution for blank character */ 1290 SpaceSub = val[0]; 1291 if (SpaceSub == '\0') 1292 SpaceSub = ' '; 1293 break; 1294 1295 case 'b': /* min blocks free on queue fs/max msg size */ 1296 p = strchr(val, '/'); 1297 if (p != NULL) 1298 { 1299 *p++ = '\0'; 1300 MaxMessageSize = atol(p); 1301 } 1302 MinBlocksFree = atol(val); 1303 break; 1304 1305 case 'c': /* don't connect to "expensive" mailers */ 1306 NoConnect = atobool(val); 1307 break; 1308 1309 case 'C': /* checkpoint every N addresses */ 1310 CheckpointInterval = atoi(val); 1311 break; 1312 1313 case 'd': /* delivery mode */ 1314 switch (*val) 1315 { 1316 case '\0': 1317 e->e_sendmode = SM_DELIVER; 1318 break; 1319 1320 case SM_QUEUE: /* queue only */ 1321 #ifndef QUEUE 1322 syserr("need QUEUE to set -odqueue"); 1323 #endif /* QUEUE */ 1324 /* fall through..... */ 1325 1326 case SM_DELIVER: /* do everything */ 1327 case SM_FORK: /* fork after verification */ 1328 e->e_sendmode = *val; 1329 break; 1330 1331 default: 1332 syserr("Unknown delivery mode %c", *val); 1333 exit(EX_USAGE); 1334 } 1335 break; 1336 1337 case 'D': /* rebuild alias database as needed */ 1338 AutoRebuild = atobool(val); 1339 break; 1340 1341 case 'E': /* error message header/header file */ 1342 if (*val != '\0') 1343 ErrMsgFile = newstr(val); 1344 break; 1345 1346 case 'e': /* set error processing mode */ 1347 switch (*val) 1348 { 1349 case EM_QUIET: /* be silent about it */ 1350 case EM_MAIL: /* mail back */ 1351 case EM_BERKNET: /* do berknet error processing */ 1352 case EM_WRITE: /* write back (or mail) */ 1353 case EM_PRINT: /* print errors normally (default) */ 1354 e->e_errormode = *val; 1355 break; 1356 } 1357 break; 1358 1359 case 'F': /* file mode */ 1360 FileMode = atooct(val) & 0777; 1361 break; 1362 1363 case 'f': /* save Unix-style From lines on front */ 1364 SaveFrom = atobool(val); 1365 break; 1366 1367 case 'G': /* match recipients against GECOS field */ 1368 MatchGecos = atobool(val); 1369 break; 1370 1371 case 'g': /* default gid */ 1372 if (isascii(*val) && isdigit(*val)) 1373 DefGid = atoi(val); 1374 else 1375 { 1376 register struct group *gr; 1377 1378 DefGid = -1; 1379 gr = getgrnam(val); 1380 if (gr == NULL) 1381 syserr("readcf: option g: unknown group %s", val); 1382 else 1383 DefGid = gr->gr_gid; 1384 } 1385 break; 1386 1387 case 'H': /* help file */ 1388 if (val[0] == '\0') 1389 HelpFile = "sendmail.hf"; 1390 else 1391 HelpFile = newstr(val); 1392 break; 1393 1394 case 'h': /* maximum hop count */ 1395 MaxHopCount = atoi(val); 1396 break; 1397 1398 case 'I': /* use internet domain name server */ 1399 #if NAMED_BIND 1400 UseNameServer = TRUE; 1401 for (p = val; *p != 0; ) 1402 { 1403 bool clearmode; 1404 char *q; 1405 struct resolverflags *rfp; 1406 1407 while (*p == ' ') 1408 p++; 1409 if (*p == '\0') 1410 break; 1411 clearmode = FALSE; 1412 if (*p == '-') 1413 clearmode = TRUE; 1414 else if (*p != '+') 1415 p--; 1416 p++; 1417 q = p; 1418 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1419 p++; 1420 if (*p != '\0') 1421 *p++ = '\0'; 1422 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1423 { 1424 if (strcasecmp(q, rfp->rf_name) == 0) 1425 break; 1426 } 1427 if (rfp->rf_name == NULL) 1428 syserr("readcf: I option value %s unrecognized", q); 1429 else if (clearmode) 1430 _res.options &= ~rfp->rf_bits; 1431 else 1432 _res.options |= rfp->rf_bits; 1433 } 1434 if (tTd(8, 2)) 1435 printf("_res.options = %x\n", _res.options); 1436 #else 1437 usrerr("name server (I option) specified but BIND not compiled in"); 1438 #endif 1439 break; 1440 1441 case 'i': /* ignore dot lines in message */ 1442 IgnrDot = atobool(val); 1443 break; 1444 1445 case 'j': /* send errors in MIME (RFC 1341) format */ 1446 SendMIMEErrors = atobool(val); 1447 break; 1448 1449 case 'J': /* .forward search path */ 1450 ForwardPath = newstr(val); 1451 break; 1452 1453 case 'k': /* connection cache size */ 1454 MaxMciCache = atoi(val); 1455 if (MaxMciCache < 0) 1456 MaxMciCache = 0; 1457 break; 1458 1459 case 'K': /* connection cache timeout */ 1460 MciCacheTimeout = convtime(val, 'm'); 1461 break; 1462 1463 case 'l': /* use Errors-To: header */ 1464 UseErrorsTo = atobool(val); 1465 break; 1466 1467 case 'L': /* log level */ 1468 if (safe || LogLevel < atoi(val)) 1469 LogLevel = atoi(val); 1470 break; 1471 1472 case 'M': /* define macro */ 1473 define(val[0], newstr(&val[1]), CurEnv); 1474 sticky = FALSE; 1475 break; 1476 1477 case 'm': /* send to me too */ 1478 MeToo = atobool(val); 1479 break; 1480 1481 case 'n': /* validate RHS in newaliases */ 1482 CheckAliases = atobool(val); 1483 break; 1484 1485 /* 'N' available -- was "net name" */ 1486 1487 case 'O': /* daemon options */ 1488 setdaemonoptions(val); 1489 break; 1490 1491 case 'o': /* assume old style headers */ 1492 if (atobool(val)) 1493 CurEnv->e_flags |= EF_OLDSTYLE; 1494 else 1495 CurEnv->e_flags &= ~EF_OLDSTYLE; 1496 break; 1497 1498 case 'p': /* select privacy level */ 1499 p = val; 1500 for (;;) 1501 { 1502 register struct prival *pv; 1503 extern struct prival PrivacyValues[]; 1504 1505 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1506 p++; 1507 if (*p == '\0') 1508 break; 1509 val = p; 1510 while (isascii(*p) && isalnum(*p)) 1511 p++; 1512 if (*p != '\0') 1513 *p++ = '\0'; 1514 1515 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1516 { 1517 if (strcasecmp(val, pv->pv_name) == 0) 1518 break; 1519 } 1520 if (pv->pv_name == NULL) 1521 syserr("readcf: Op line: %s unrecognized", val); 1522 PrivacyFlags |= pv->pv_flag; 1523 } 1524 break; 1525 1526 case 'P': /* postmaster copy address for returned mail */ 1527 PostMasterCopy = newstr(val); 1528 break; 1529 1530 case 'q': /* slope of queue only function */ 1531 QueueFactor = atoi(val); 1532 break; 1533 1534 case 'Q': /* queue directory */ 1535 if (val[0] == '\0') 1536 QueueDir = "mqueue"; 1537 else 1538 QueueDir = newstr(val); 1539 if (RealUid != 0 && !safe) 1540 Warn_Q_option = TRUE; 1541 break; 1542 1543 case 'R': /* don't prune routes */ 1544 DontPruneRoutes = atobool(val); 1545 break; 1546 1547 case 'r': /* read timeout */ 1548 settimeouts(val); 1549 break; 1550 1551 case 'S': /* status file */ 1552 if (val[0] == '\0') 1553 StatFile = "sendmail.st"; 1554 else 1555 StatFile = newstr(val); 1556 break; 1557 1558 case 's': /* be super safe, even if expensive */ 1559 SuperSafe = atobool(val); 1560 break; 1561 1562 case 'T': /* queue timeout */ 1563 p = strchr(val, '/'); 1564 if (p != NULL) 1565 { 1566 *p++ = '\0'; 1567 TimeOuts.to_q_warning = convtime(p, 'd'); 1568 } 1569 TimeOuts.to_q_return = convtime(val, 'h'); 1570 break; 1571 1572 case 't': /* time zone name */ 1573 TimeZoneSpec = newstr(val); 1574 break; 1575 1576 case 'U': /* location of user database */ 1577 UdbSpec = newstr(val); 1578 break; 1579 1580 case 'u': /* set default uid */ 1581 if (isascii(*val) && isdigit(*val)) 1582 DefUid = atoi(val); 1583 else 1584 { 1585 register struct passwd *pw; 1586 1587 DefUid = -1; 1588 pw = getpwnam(val); 1589 if (pw == NULL) 1590 syserr("readcf: option u: unknown user %s", val); 1591 else 1592 DefUid = pw->pw_uid; 1593 } 1594 setdefuser(); 1595 break; 1596 1597 case 'V': /* fallback MX host */ 1598 FallBackMX = newstr(val); 1599 break; 1600 1601 case 'v': /* run in verbose mode */ 1602 Verbose = atobool(val); 1603 break; 1604 1605 case 'w': /* if we are best MX, try host directly */ 1606 TryNullMXList = atobool(val); 1607 break; 1608 1609 /* 'W' available -- was wizard password */ 1610 1611 case 'x': /* load avg at which to auto-queue msgs */ 1612 QueueLA = atoi(val); 1613 break; 1614 1615 case 'X': /* load avg at which to auto-reject connections */ 1616 RefuseLA = atoi(val); 1617 break; 1618 1619 case 'y': /* work recipient factor */ 1620 WkRecipFact = atoi(val); 1621 break; 1622 1623 case 'Y': /* fork jobs during queue runs */ 1624 ForkQueueRuns = atobool(val); 1625 break; 1626 1627 case 'z': /* work message class factor */ 1628 WkClassFact = atoi(val); 1629 break; 1630 1631 case 'Z': /* work time factor */ 1632 WkTimeFact = atoi(val); 1633 break; 1634 1635 case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 1636 BrokenSmtpPeers = atobool(val); 1637 break; 1638 1639 case O_SQBH: /* sort work queue by host first */ 1640 SortQueueByHost = atobool(val); 1641 break; 1642 1643 case O_DNICE: /* delivery nice value */ 1644 DeliveryNiceness = atoi(val); 1645 break; 1646 1647 case O_MQA: /* minimum queue age between deliveries */ 1648 MinQueueAge = convtime(val, 'm'); 1649 break; 1650 1651 case O_MHSA: /* maximum age of cached host status */ 1652 MaxHostStatAge = convtime(val, 'm'); 1653 break; 1654 1655 default: 1656 break; 1657 } 1658 if (sticky) 1659 setbitn(opt, StickyOpt); 1660 return; 1661 } 1662 /* 1663 ** SETCLASS -- set a word into a class 1664 ** 1665 ** Parameters: 1666 ** class -- the class to put the word in. 1667 ** word -- the word to enter 1668 ** 1669 ** Returns: 1670 ** none. 1671 ** 1672 ** Side Effects: 1673 ** puts the word into the symbol table. 1674 */ 1675 1676 setclass(class, word) 1677 int class; 1678 char *word; 1679 { 1680 register STAB *s; 1681 1682 if (tTd(37, 8)) 1683 printf("setclass(%c, %s)\n", class, word); 1684 s = stab(word, ST_CLASS, ST_ENTER); 1685 setbitn(class, s->s_class); 1686 } 1687 /* 1688 ** MAKEMAPENTRY -- create a map entry 1689 ** 1690 ** Parameters: 1691 ** line -- the config file line 1692 ** 1693 ** Returns: 1694 ** TRUE if it successfully entered the map entry. 1695 ** FALSE otherwise (usually syntax error). 1696 ** 1697 ** Side Effects: 1698 ** Enters the map into the dictionary. 1699 */ 1700 1701 void 1702 makemapentry(line) 1703 char *line; 1704 { 1705 register char *p; 1706 char *mapname; 1707 char *classname; 1708 register STAB *s; 1709 STAB *class; 1710 1711 for (p = line; isascii(*p) && isspace(*p); p++) 1712 continue; 1713 if (!(isascii(*p) && isalnum(*p))) 1714 { 1715 syserr("readcf: config K line: no map name"); 1716 return; 1717 } 1718 1719 mapname = p; 1720 while (isascii(*++p) && isalnum(*p)) 1721 continue; 1722 if (*p != '\0') 1723 *p++ = '\0'; 1724 while (isascii(*p) && isspace(*p)) 1725 p++; 1726 if (!(isascii(*p) && isalnum(*p))) 1727 { 1728 syserr("readcf: config K line, map %s: no map class", mapname); 1729 return; 1730 } 1731 classname = p; 1732 while (isascii(*++p) && isalnum(*p)) 1733 continue; 1734 if (*p != '\0') 1735 *p++ = '\0'; 1736 while (isascii(*p) && isspace(*p)) 1737 p++; 1738 1739 /* look up the class */ 1740 class = stab(classname, ST_MAPCLASS, ST_FIND); 1741 if (class == NULL) 1742 { 1743 syserr("readcf: map %s: class %s not available", mapname, classname); 1744 return; 1745 } 1746 1747 /* enter the map */ 1748 s = stab(mapname, ST_MAP, ST_ENTER); 1749 s->s_map.map_class = &class->s_mapclass; 1750 s->s_map.map_mname = newstr(mapname); 1751 1752 if (class->s_mapclass.map_parse(&s->s_map, p)) 1753 s->s_map.map_mflags |= MF_VALID; 1754 1755 if (tTd(37, 5)) 1756 { 1757 printf("map %s, class %s, flags %x, file %s,\n", 1758 s->s_map.map_mname, s->s_map.map_class->map_cname, 1759 s->s_map.map_mflags, 1760 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 1761 printf("\tapp %s, domain %s, rebuild %s\n", 1762 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 1763 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 1764 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 1765 } 1766 } 1767 /* 1768 ** SETTIMEOUTS -- parse and set timeout values 1769 ** 1770 ** Parameters: 1771 ** val -- a pointer to the values. If NULL, do initial 1772 ** settings. 1773 ** 1774 ** Returns: 1775 ** none. 1776 ** 1777 ** Side Effects: 1778 ** Initializes the TimeOuts structure 1779 */ 1780 1781 #define SECONDS 1782 #define MINUTES * 60 1783 #define HOUR * 3600 1784 1785 settimeouts(val) 1786 register char *val; 1787 { 1788 register char *p; 1789 extern time_t convtime(); 1790 1791 if (val == NULL) 1792 { 1793 TimeOuts.to_initial = (time_t) 5 MINUTES; 1794 TimeOuts.to_helo = (time_t) 5 MINUTES; 1795 TimeOuts.to_mail = (time_t) 10 MINUTES; 1796 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1797 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1798 TimeOuts.to_datablock = (time_t) 1 HOUR; 1799 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1800 TimeOuts.to_rset = (time_t) 5 MINUTES; 1801 TimeOuts.to_quit = (time_t) 2 MINUTES; 1802 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1803 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1804 TimeOuts.to_ident = (time_t) 30 SECONDS; 1805 return; 1806 } 1807 1808 for (;; val = p) 1809 { 1810 while (isascii(*val) && isspace(*val)) 1811 val++; 1812 if (*val == '\0') 1813 break; 1814 for (p = val; *p != '\0' && *p != ','; p++) 1815 continue; 1816 if (*p != '\0') 1817 *p++ = '\0'; 1818 1819 if (isascii(*val) && isdigit(*val)) 1820 { 1821 /* old syntax -- set everything */ 1822 TimeOuts.to_mail = convtime(val, 'm'); 1823 TimeOuts.to_rcpt = TimeOuts.to_mail; 1824 TimeOuts.to_datainit = TimeOuts.to_mail; 1825 TimeOuts.to_datablock = TimeOuts.to_mail; 1826 TimeOuts.to_datafinal = TimeOuts.to_mail; 1827 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1828 continue; 1829 } 1830 else 1831 { 1832 register char *q = strchr(val, '='); 1833 time_t to; 1834 1835 if (q == NULL) 1836 { 1837 /* syntax error */ 1838 continue; 1839 } 1840 *q++ = '\0'; 1841 to = convtime(q, 'm'); 1842 1843 if (strcasecmp(val, "initial") == 0) 1844 TimeOuts.to_initial = to; 1845 else if (strcasecmp(val, "mail") == 0) 1846 TimeOuts.to_mail = to; 1847 else if (strcasecmp(val, "rcpt") == 0) 1848 TimeOuts.to_rcpt = to; 1849 else if (strcasecmp(val, "datainit") == 0) 1850 TimeOuts.to_datainit = to; 1851 else if (strcasecmp(val, "datablock") == 0) 1852 TimeOuts.to_datablock = to; 1853 else if (strcasecmp(val, "datafinal") == 0) 1854 TimeOuts.to_datafinal = to; 1855 else if (strcasecmp(val, "command") == 0) 1856 TimeOuts.to_nextcommand = to; 1857 else if (strcasecmp(val, "rset") == 0) 1858 TimeOuts.to_rset = to; 1859 else if (strcasecmp(val, "helo") == 0) 1860 TimeOuts.to_helo = to; 1861 else if (strcasecmp(val, "quit") == 0) 1862 TimeOuts.to_quit = to; 1863 else if (strcasecmp(val, "misc") == 0) 1864 TimeOuts.to_miscshort = to; 1865 else if (strcasecmp(val, "ident") == 0) 1866 TimeOuts.to_ident = to; 1867 else 1868 syserr("settimeouts: invalid timeout %s", val); 1869 } 1870 } 1871 } 1872