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.30 (Berkeley) 08/07/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 /* codes for options that have no short name */ 1070 /* NOTE: some of these values may be in the list of "safe" options below */ 1071 #define O_BSP 0x80 /* have broken SMTP peers */ 1072 #define O_SQBH 0x81 /* sort queue by host */ 1073 1074 struct optioninfo 1075 { 1076 char *o_name; /* long name of option */ 1077 char o_code; /* short name of option */ 1078 bool o_safe; /* safe for random people to use */ 1079 } OptionTab[] = 1080 { 1081 "SevenBitInput", '7', TRUE, 1082 "AliasFile", 'A', FALSE, 1083 "AliasWait", 'a', FALSE, 1084 "BlankSub", 'B', FALSE, 1085 "MinFreeBlocks", 'b', TRUE, 1086 "CheckpointInterval", 'C', TRUE, 1087 "HoldExpensive", 'c', FALSE, 1088 "AutoRebuildAliases", 'D', FALSE, 1089 "DeliveryMode", 'd', TRUE, 1090 "ErrorHeader", 'E', FALSE, 1091 "ErrorMode", 'e', TRUE, 1092 "TempFileMode", 'F', FALSE, 1093 "SaveFromLine", 'f', FALSE, 1094 "MatchGECOS", 'G', FALSE, 1095 "HelpFile", 'H', FALSE, 1096 "MaxHopCount", 'h', FALSE, 1097 "NameServerOptions", 'I', FALSE, 1098 "IgnoreDots", 'i', TRUE, 1099 "ForwardPath", 'J', FALSE, 1100 "SendMimeErrors", 'j', TRUE, 1101 "ConnectionCacheSize", 'k', FALSE, 1102 "ConnectionCacheTimeout", 'K', FALSE, 1103 "UseErrorsTo", 'l', FALSE, 1104 "LogLevel", 'L', FALSE, 1105 "MeToo", 'm', TRUE, 1106 "CheckAliases", 'n', FALSE, 1107 "OldStyleHeaders", 'o', TRUE, 1108 "DaemonPortOptions", 'O', FALSE, 1109 "PrivacyOptions", 'p', TRUE, 1110 "PostmasterCopy", 'P', FALSE, 1111 "QueueFactor", 'q', FALSE, 1112 "QueueDirectory", 'Q', FALSE, 1113 "DontPruneRoutes", 'R', FALSE, 1114 "ReadTimeout", 'r', TRUE, 1115 "StatusFile", 'S', FALSE, 1116 "SuperSafe", 's', TRUE, 1117 "QueueTimeout", 'T', FALSE, 1118 "TimeZoneSpec", 't', FALSE, 1119 "UserDatabaseSpec", 'U', FALSE, 1120 "DefaultUser", 'u', FALSE, 1121 "FallbackMXhost", 'V', FALSE, 1122 "Verbose", 'v', TRUE, 1123 "TryNullMXList", 'w', TRUE, 1124 "QueueLA", 'x', FALSE, 1125 "RefuseLA", 'X', FALSE, 1126 "RecipientFactor", 'y', FALSE, 1127 "ForkQueueRuns", 'Y', FALSE, 1128 "ClassFactor", 'z', FALSE, 1129 "TimeFactor", 'Z', FALSE, 1130 "BrokenSmtpPeers", O_BSP, TRUE, 1131 "SortQueueByHost", O_SQBH, TRUE, 1132 NULL, '\0', FALSE, 1133 }; 1134 1135 1136 1137 setoption(opt, val, safe, sticky, e) 1138 u_char opt; 1139 char *val; 1140 bool safe; 1141 bool sticky; 1142 register ENVELOPE *e; 1143 { 1144 register char *p; 1145 register struct optioninfo *o; 1146 extern bool atobool(); 1147 extern time_t convtime(); 1148 extern int QueueLA; 1149 extern int RefuseLA; 1150 extern bool Warn_Q_option; 1151 extern bool trusteduser(); 1152 1153 if (opt == ' ') 1154 { 1155 /* full word options */ 1156 1157 p = strchr(val, '='); 1158 if (p == NULL) 1159 p = &val[strlen(val)]; 1160 while (*--p == ' ') 1161 continue; 1162 while (*++p == ' ') 1163 *p = '\0'; 1164 if (*p == '=') 1165 *p++ = '\0'; 1166 while (*p == ' ') 1167 p++; 1168 for (o = OptionTab; o->o_name != NULL; o++) 1169 { 1170 if (strcasecmp(o->o_name, val) == 0) 1171 break; 1172 } 1173 if (o->o_name == NULL) 1174 syserr("readcf: unknown option name %s", val); 1175 opt = o->o_code; 1176 val = p; 1177 } 1178 else 1179 { 1180 for (o = OptionTab; o->o_name != NULL; o++) 1181 { 1182 if (o->o_code == opt) 1183 break; 1184 } 1185 } 1186 1187 if (tTd(37, 1)) 1188 printf("setoption %s (0x%x)=%s", 1189 o->o_name == NULL ? "<unknown>" : o->o_name, 1190 opt, val); 1191 1192 /* 1193 ** See if this option is preset for us. 1194 */ 1195 1196 if (!sticky && bitnset(opt, StickyOpt)) 1197 { 1198 if (tTd(37, 1)) 1199 printf(" (ignored)\n"); 1200 return; 1201 } 1202 1203 /* 1204 ** Check to see if this option can be specified by this user. 1205 */ 1206 1207 if (!safe && RealUid == 0) 1208 safe = TRUE; 1209 if (!safe && !o->o_safe) 1210 { 1211 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1212 { 1213 if (tTd(37, 1)) 1214 printf(" (unsafe)"); 1215 if (RealUid != geteuid()) 1216 { 1217 if (tTd(37, 1)) 1218 printf("(Resetting uid)"); 1219 (void) setgid(RealGid); 1220 (void) setuid(RealUid); 1221 } 1222 } 1223 } 1224 if (tTd(37, 1)) 1225 printf("\n"); 1226 1227 switch (opt & 0xff) 1228 { 1229 case '7': /* force seven-bit input */ 1230 SevenBitInput = atobool(val); 1231 break; 1232 1233 case '8': /* handling of 8-bit input */ 1234 switch (*val) 1235 { 1236 case 'r': /* reject 8-bit, don't convert MIME */ 1237 MimeMode = 0; 1238 break; 1239 1240 case 'm': /* convert 8-bit, convert MIME */ 1241 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1242 break; 1243 1244 case 'j': /* "just send 8" */ 1245 MimeMode = MM_PASS8BIT; 1246 break; 1247 1248 case 'p': /* pass 8 bit, convert MIME */ 1249 MimeMode = MM_PASS8BIT|MM_CVTMIME; 1250 break; 1251 1252 case 's': /* strict adherence */ 1253 MimeMode = MM_CVTMIME; 1254 break; 1255 1256 case 'a': /* encode 8 bit if available */ 1257 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1258 break; 1259 1260 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1261 MimeMode = MM_MIME8BIT; 1262 break; 1263 1264 default: 1265 syserr("Unknown 8-bit mode %c", *val); 1266 exit(EX_USAGE); 1267 } 1268 break; 1269 1270 case 'A': /* set default alias file */ 1271 if (val[0] == '\0') 1272 setalias("aliases"); 1273 else 1274 setalias(val); 1275 break; 1276 1277 case 'a': /* look N minutes for "@:@" in alias file */ 1278 if (val[0] == '\0') 1279 SafeAlias = 5 * 60; /* five minutes */ 1280 else 1281 SafeAlias = convtime(val, 'm'); 1282 break; 1283 1284 case 'B': /* substitution for blank character */ 1285 SpaceSub = val[0]; 1286 if (SpaceSub == '\0') 1287 SpaceSub = ' '; 1288 break; 1289 1290 case 'b': /* min blocks free on queue fs/max msg size */ 1291 p = strchr(val, '/'); 1292 if (p != NULL) 1293 { 1294 *p++ = '\0'; 1295 MaxMessageSize = atol(p); 1296 } 1297 MinBlocksFree = atol(val); 1298 break; 1299 1300 case 'c': /* don't connect to "expensive" mailers */ 1301 NoConnect = atobool(val); 1302 break; 1303 1304 case 'C': /* checkpoint every N addresses */ 1305 CheckpointInterval = atoi(val); 1306 break; 1307 1308 case 'd': /* delivery mode */ 1309 switch (*val) 1310 { 1311 case '\0': 1312 e->e_sendmode = SM_DELIVER; 1313 break; 1314 1315 case SM_QUEUE: /* queue only */ 1316 #ifndef QUEUE 1317 syserr("need QUEUE to set -odqueue"); 1318 #endif /* QUEUE */ 1319 /* fall through..... */ 1320 1321 case SM_DELIVER: /* do everything */ 1322 case SM_FORK: /* fork after verification */ 1323 e->e_sendmode = *val; 1324 break; 1325 1326 default: 1327 syserr("Unknown delivery mode %c", *val); 1328 exit(EX_USAGE); 1329 } 1330 break; 1331 1332 case 'D': /* rebuild alias database as needed */ 1333 AutoRebuild = atobool(val); 1334 break; 1335 1336 case 'E': /* error message header/header file */ 1337 if (*val != '\0') 1338 ErrMsgFile = newstr(val); 1339 break; 1340 1341 case 'e': /* set error processing mode */ 1342 switch (*val) 1343 { 1344 case EM_QUIET: /* be silent about it */ 1345 case EM_MAIL: /* mail back */ 1346 case EM_BERKNET: /* do berknet error processing */ 1347 case EM_WRITE: /* write back (or mail) */ 1348 case EM_PRINT: /* print errors normally (default) */ 1349 e->e_errormode = *val; 1350 break; 1351 } 1352 break; 1353 1354 case 'F': /* file mode */ 1355 FileMode = atooct(val) & 0777; 1356 break; 1357 1358 case 'f': /* save Unix-style From lines on front */ 1359 SaveFrom = atobool(val); 1360 break; 1361 1362 case 'G': /* match recipients against GECOS field */ 1363 MatchGecos = atobool(val); 1364 break; 1365 1366 case 'g': /* default gid */ 1367 if (isascii(*val) && isdigit(*val)) 1368 DefGid = atoi(val); 1369 else 1370 { 1371 register struct group *gr; 1372 1373 DefGid = -1; 1374 gr = getgrnam(val); 1375 if (gr == NULL) 1376 syserr("readcf: option g: unknown group %s", val); 1377 else 1378 DefGid = gr->gr_gid; 1379 } 1380 break; 1381 1382 case 'H': /* help file */ 1383 if (val[0] == '\0') 1384 HelpFile = "sendmail.hf"; 1385 else 1386 HelpFile = newstr(val); 1387 break; 1388 1389 case 'h': /* maximum hop count */ 1390 MaxHopCount = atoi(val); 1391 break; 1392 1393 case 'I': /* use internet domain name server */ 1394 #if NAMED_BIND 1395 UseNameServer = TRUE; 1396 for (p = val; *p != 0; ) 1397 { 1398 bool clearmode; 1399 char *q; 1400 struct resolverflags *rfp; 1401 1402 while (*p == ' ') 1403 p++; 1404 if (*p == '\0') 1405 break; 1406 clearmode = FALSE; 1407 if (*p == '-') 1408 clearmode = TRUE; 1409 else if (*p != '+') 1410 p--; 1411 p++; 1412 q = p; 1413 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1414 p++; 1415 if (*p != '\0') 1416 *p++ = '\0'; 1417 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1418 { 1419 if (strcasecmp(q, rfp->rf_name) == 0) 1420 break; 1421 } 1422 if (rfp->rf_name == NULL) 1423 syserr("readcf: I option value %s unrecognized", q); 1424 else if (clearmode) 1425 _res.options &= ~rfp->rf_bits; 1426 else 1427 _res.options |= rfp->rf_bits; 1428 } 1429 if (tTd(8, 2)) 1430 printf("_res.options = %x\n", _res.options); 1431 #else 1432 usrerr("name server (I option) specified but BIND not compiled in"); 1433 #endif 1434 break; 1435 1436 case 'i': /* ignore dot lines in message */ 1437 IgnrDot = atobool(val); 1438 break; 1439 1440 case 'j': /* send errors in MIME (RFC 1341) format */ 1441 SendMIMEErrors = atobool(val); 1442 break; 1443 1444 case 'J': /* .forward search path */ 1445 ForwardPath = newstr(val); 1446 break; 1447 1448 case 'k': /* connection cache size */ 1449 MaxMciCache = atoi(val); 1450 if (MaxMciCache < 0) 1451 MaxMciCache = 0; 1452 break; 1453 1454 case 'K': /* connection cache timeout */ 1455 MciCacheTimeout = convtime(val, 'm'); 1456 break; 1457 1458 case 'l': /* use Errors-To: header */ 1459 UseErrorsTo = atobool(val); 1460 break; 1461 1462 case 'L': /* log level */ 1463 if (safe || LogLevel < atoi(val)) 1464 LogLevel = atoi(val); 1465 break; 1466 1467 case 'M': /* define macro */ 1468 define(val[0], newstr(&val[1]), CurEnv); 1469 sticky = FALSE; 1470 break; 1471 1472 case 'm': /* send to me too */ 1473 MeToo = atobool(val); 1474 break; 1475 1476 case 'n': /* validate RHS in newaliases */ 1477 CheckAliases = atobool(val); 1478 break; 1479 1480 /* 'N' available -- was "net name" */ 1481 1482 case 'O': /* daemon options */ 1483 setdaemonoptions(val); 1484 break; 1485 1486 case 'o': /* assume old style headers */ 1487 if (atobool(val)) 1488 CurEnv->e_flags |= EF_OLDSTYLE; 1489 else 1490 CurEnv->e_flags &= ~EF_OLDSTYLE; 1491 break; 1492 1493 case 'p': /* select privacy level */ 1494 p = val; 1495 for (;;) 1496 { 1497 register struct prival *pv; 1498 extern struct prival PrivacyValues[]; 1499 1500 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1501 p++; 1502 if (*p == '\0') 1503 break; 1504 val = p; 1505 while (isascii(*p) && isalnum(*p)) 1506 p++; 1507 if (*p != '\0') 1508 *p++ = '\0'; 1509 1510 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1511 { 1512 if (strcasecmp(val, pv->pv_name) == 0) 1513 break; 1514 } 1515 if (pv->pv_name == NULL) 1516 syserr("readcf: Op line: %s unrecognized", val); 1517 PrivacyFlags |= pv->pv_flag; 1518 } 1519 break; 1520 1521 case 'P': /* postmaster copy address for returned mail */ 1522 PostMasterCopy = newstr(val); 1523 break; 1524 1525 case 'q': /* slope of queue only function */ 1526 QueueFactor = atoi(val); 1527 break; 1528 1529 case 'Q': /* queue directory */ 1530 if (val[0] == '\0') 1531 QueueDir = "mqueue"; 1532 else 1533 QueueDir = newstr(val); 1534 if (RealUid != 0 && !safe) 1535 Warn_Q_option = TRUE; 1536 break; 1537 1538 case 'R': /* don't prune routes */ 1539 DontPruneRoutes = atobool(val); 1540 break; 1541 1542 case 'r': /* read timeout */ 1543 settimeouts(val); 1544 break; 1545 1546 case 'S': /* status file */ 1547 if (val[0] == '\0') 1548 StatFile = "sendmail.st"; 1549 else 1550 StatFile = newstr(val); 1551 break; 1552 1553 case 's': /* be super safe, even if expensive */ 1554 SuperSafe = atobool(val); 1555 break; 1556 1557 case 'T': /* queue timeout */ 1558 p = strchr(val, '/'); 1559 if (p != NULL) 1560 { 1561 *p++ = '\0'; 1562 TimeOuts.to_q_warning = convtime(p, 'd'); 1563 } 1564 TimeOuts.to_q_return = convtime(val, 'h'); 1565 break; 1566 1567 case 't': /* time zone name */ 1568 TimeZoneSpec = newstr(val); 1569 break; 1570 1571 case 'U': /* location of user database */ 1572 UdbSpec = newstr(val); 1573 break; 1574 1575 case 'u': /* set default uid */ 1576 if (isascii(*val) && isdigit(*val)) 1577 DefUid = atoi(val); 1578 else 1579 { 1580 register struct passwd *pw; 1581 1582 DefUid = -1; 1583 pw = getpwnam(val); 1584 if (pw == NULL) 1585 syserr("readcf: option u: unknown user %s", val); 1586 else 1587 DefUid = pw->pw_uid; 1588 } 1589 setdefuser(); 1590 break; 1591 1592 case 'V': /* fallback MX host */ 1593 FallBackMX = newstr(val); 1594 break; 1595 1596 case 'v': /* run in verbose mode */ 1597 Verbose = atobool(val); 1598 break; 1599 1600 case 'w': /* if we are best MX, try host directly */ 1601 TryNullMXList = atobool(val); 1602 break; 1603 1604 /* 'W' available -- was wizard password */ 1605 1606 case 'x': /* load avg at which to auto-queue msgs */ 1607 QueueLA = atoi(val); 1608 break; 1609 1610 case 'X': /* load avg at which to auto-reject connections */ 1611 RefuseLA = atoi(val); 1612 break; 1613 1614 case 'y': /* work recipient factor */ 1615 WkRecipFact = atoi(val); 1616 break; 1617 1618 case 'Y': /* fork jobs during queue runs */ 1619 ForkQueueRuns = atobool(val); 1620 break; 1621 1622 case 'z': /* work message class factor */ 1623 WkClassFact = atoi(val); 1624 break; 1625 1626 case 'Z': /* work time factor */ 1627 WkTimeFact = atoi(val); 1628 break; 1629 1630 case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 1631 BrokenSmtpPeers = atobool(val); 1632 break; 1633 1634 case O_SQBH: /* sort work queue by host first */ 1635 SortQueueByHost = atobool(val); 1636 break; 1637 1638 default: 1639 break; 1640 } 1641 if (sticky) 1642 setbitn(opt, StickyOpt); 1643 return; 1644 } 1645 /* 1646 ** SETCLASS -- set a word into a class 1647 ** 1648 ** Parameters: 1649 ** class -- the class to put the word in. 1650 ** word -- the word to enter 1651 ** 1652 ** Returns: 1653 ** none. 1654 ** 1655 ** Side Effects: 1656 ** puts the word into the symbol table. 1657 */ 1658 1659 setclass(class, word) 1660 int class; 1661 char *word; 1662 { 1663 register STAB *s; 1664 1665 if (tTd(37, 8)) 1666 printf("setclass(%c, %s)\n", class, word); 1667 s = stab(word, ST_CLASS, ST_ENTER); 1668 setbitn(class, s->s_class); 1669 } 1670 /* 1671 ** MAKEMAPENTRY -- create a map entry 1672 ** 1673 ** Parameters: 1674 ** line -- the config file line 1675 ** 1676 ** Returns: 1677 ** TRUE if it successfully entered the map entry. 1678 ** FALSE otherwise (usually syntax error). 1679 ** 1680 ** Side Effects: 1681 ** Enters the map into the dictionary. 1682 */ 1683 1684 void 1685 makemapentry(line) 1686 char *line; 1687 { 1688 register char *p; 1689 char *mapname; 1690 char *classname; 1691 register STAB *s; 1692 STAB *class; 1693 1694 for (p = line; isascii(*p) && isspace(*p); p++) 1695 continue; 1696 if (!(isascii(*p) && isalnum(*p))) 1697 { 1698 syserr("readcf: config K line: no map name"); 1699 return; 1700 } 1701 1702 mapname = p; 1703 while (isascii(*++p) && isalnum(*p)) 1704 continue; 1705 if (*p != '\0') 1706 *p++ = '\0'; 1707 while (isascii(*p) && isspace(*p)) 1708 p++; 1709 if (!(isascii(*p) && isalnum(*p))) 1710 { 1711 syserr("readcf: config K line, map %s: no map class", mapname); 1712 return; 1713 } 1714 classname = p; 1715 while (isascii(*++p) && isalnum(*p)) 1716 continue; 1717 if (*p != '\0') 1718 *p++ = '\0'; 1719 while (isascii(*p) && isspace(*p)) 1720 p++; 1721 1722 /* look up the class */ 1723 class = stab(classname, ST_MAPCLASS, ST_FIND); 1724 if (class == NULL) 1725 { 1726 syserr("readcf: map %s: class %s not available", mapname, classname); 1727 return; 1728 } 1729 1730 /* enter the map */ 1731 s = stab(mapname, ST_MAP, ST_ENTER); 1732 s->s_map.map_class = &class->s_mapclass; 1733 s->s_map.map_mname = newstr(mapname); 1734 1735 if (class->s_mapclass.map_parse(&s->s_map, p)) 1736 s->s_map.map_mflags |= MF_VALID; 1737 1738 if (tTd(37, 5)) 1739 { 1740 printf("map %s, class %s, flags %x, file %s,\n", 1741 s->s_map.map_mname, s->s_map.map_class->map_cname, 1742 s->s_map.map_mflags, 1743 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 1744 printf("\tapp %s, domain %s, rebuild %s\n", 1745 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 1746 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 1747 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 1748 } 1749 } 1750 /* 1751 ** SETTIMEOUTS -- parse and set timeout values 1752 ** 1753 ** Parameters: 1754 ** val -- a pointer to the values. If NULL, do initial 1755 ** settings. 1756 ** 1757 ** Returns: 1758 ** none. 1759 ** 1760 ** Side Effects: 1761 ** Initializes the TimeOuts structure 1762 */ 1763 1764 #define SECONDS 1765 #define MINUTES * 60 1766 #define HOUR * 3600 1767 1768 settimeouts(val) 1769 register char *val; 1770 { 1771 register char *p; 1772 extern time_t convtime(); 1773 1774 if (val == NULL) 1775 { 1776 TimeOuts.to_initial = (time_t) 5 MINUTES; 1777 TimeOuts.to_helo = (time_t) 5 MINUTES; 1778 TimeOuts.to_mail = (time_t) 10 MINUTES; 1779 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1780 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1781 TimeOuts.to_datablock = (time_t) 1 HOUR; 1782 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1783 TimeOuts.to_rset = (time_t) 5 MINUTES; 1784 TimeOuts.to_quit = (time_t) 2 MINUTES; 1785 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1786 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1787 TimeOuts.to_ident = (time_t) 30 SECONDS; 1788 return; 1789 } 1790 1791 for (;; val = p) 1792 { 1793 while (isascii(*val) && isspace(*val)) 1794 val++; 1795 if (*val == '\0') 1796 break; 1797 for (p = val; *p != '\0' && *p != ','; p++) 1798 continue; 1799 if (*p != '\0') 1800 *p++ = '\0'; 1801 1802 if (isascii(*val) && isdigit(*val)) 1803 { 1804 /* old syntax -- set everything */ 1805 TimeOuts.to_mail = convtime(val, 'm'); 1806 TimeOuts.to_rcpt = TimeOuts.to_mail; 1807 TimeOuts.to_datainit = TimeOuts.to_mail; 1808 TimeOuts.to_datablock = TimeOuts.to_mail; 1809 TimeOuts.to_datafinal = TimeOuts.to_mail; 1810 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1811 continue; 1812 } 1813 else 1814 { 1815 register char *q = strchr(val, '='); 1816 time_t to; 1817 1818 if (q == NULL) 1819 { 1820 /* syntax error */ 1821 continue; 1822 } 1823 *q++ = '\0'; 1824 to = convtime(q, 'm'); 1825 1826 if (strcasecmp(val, "initial") == 0) 1827 TimeOuts.to_initial = to; 1828 else if (strcasecmp(val, "mail") == 0) 1829 TimeOuts.to_mail = to; 1830 else if (strcasecmp(val, "rcpt") == 0) 1831 TimeOuts.to_rcpt = to; 1832 else if (strcasecmp(val, "datainit") == 0) 1833 TimeOuts.to_datainit = to; 1834 else if (strcasecmp(val, "datablock") == 0) 1835 TimeOuts.to_datablock = to; 1836 else if (strcasecmp(val, "datafinal") == 0) 1837 TimeOuts.to_datafinal = to; 1838 else if (strcasecmp(val, "command") == 0) 1839 TimeOuts.to_nextcommand = to; 1840 else if (strcasecmp(val, "rset") == 0) 1841 TimeOuts.to_rset = to; 1842 else if (strcasecmp(val, "helo") == 0) 1843 TimeOuts.to_helo = to; 1844 else if (strcasecmp(val, "quit") == 0) 1845 TimeOuts.to_quit = to; 1846 else if (strcasecmp(val, "misc") == 0) 1847 TimeOuts.to_miscshort = to; 1848 else if (strcasecmp(val, "ident") == 0) 1849 TimeOuts.to_ident = to; 1850 else 1851 syserr("settimeouts: invalid timeout %s", val); 1852 } 1853 } 1854 } 1855