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.35 (Berkeley) 08/21/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 /* initialize host maps from local service tables */ 515 inithostmaps(); 516 517 if (stab("host", ST_MAP, ST_FIND) == NULL) 518 { 519 /* user didn't initialize: set up host map */ 520 strcpy(buf, "host host"); 521 #if NAMED_BIND 522 if (ConfigLevel >= 2) 523 strcat(buf, " -a."); 524 #endif 525 makemapentry(buf); 526 } 527 } 528 /* 529 ** TOOMANY -- signal too many of some option 530 ** 531 ** Parameters: 532 ** id -- the id of the error line 533 ** maxcnt -- the maximum possible values 534 ** 535 ** Returns: 536 ** none. 537 ** 538 ** Side Effects: 539 ** gives a syserr. 540 */ 541 542 toomany(id, maxcnt) 543 char id; 544 int maxcnt; 545 { 546 syserr("too many %c lines, %d max", id, maxcnt); 547 } 548 /* 549 ** FILECLASS -- read members of a class from a file 550 ** 551 ** Parameters: 552 ** class -- class to define. 553 ** filename -- name of file to read. 554 ** fmt -- scanf string to use for match. 555 ** safe -- if set, this is a safe read. 556 ** optional -- if set, it is not an error for the file to 557 ** not exist. 558 ** 559 ** Returns: 560 ** none 561 ** 562 ** Side Effects: 563 ** 564 ** puts all lines in filename that match a scanf into 565 ** the named class. 566 */ 567 568 fileclass(class, filename, fmt, safe, optional) 569 int class; 570 char *filename; 571 char *fmt; 572 bool safe; 573 bool optional; 574 { 575 FILE *f; 576 struct stat stbuf; 577 char buf[MAXLINE]; 578 579 if (tTd(37, 2)) 580 printf("fileclass(%s, fmt=%s)\n", filename, fmt); 581 582 if (filename[0] == '|') 583 { 584 syserr("fileclass: pipes (F%c%s) not supported due to security problems", 585 class, filename); 586 return; 587 } 588 if (stat(filename, &stbuf) < 0) 589 { 590 if (tTd(37, 2)) 591 printf(" cannot stat (%s)\n", errstring(errno)); 592 if (!optional) 593 syserr("fileclass: cannot stat %s", filename); 594 return; 595 } 596 if (!S_ISREG(stbuf.st_mode)) 597 { 598 syserr("fileclass: %s not a regular file", filename); 599 return; 600 } 601 if (!safe && access(filename, R_OK) < 0) 602 { 603 syserr("fileclass: access denied on %s", filename); 604 return; 605 } 606 f = fopen(filename, "r"); 607 if (f == NULL) 608 { 609 syserr("fileclass: cannot open %s", filename); 610 return; 611 } 612 613 while (fgets(buf, sizeof buf, f) != NULL) 614 { 615 register STAB *s; 616 register char *p; 617 # ifdef SCANF 618 char wordbuf[MAXNAME+1]; 619 620 if (sscanf(buf, fmt, wordbuf) != 1) 621 continue; 622 p = wordbuf; 623 # else /* SCANF */ 624 p = buf; 625 # endif /* SCANF */ 626 627 /* 628 ** Break up the match into words. 629 */ 630 631 while (*p != '\0') 632 { 633 register char *q; 634 635 /* strip leading spaces */ 636 while (isascii(*p) && isspace(*p)) 637 p++; 638 if (*p == '\0') 639 break; 640 641 /* find the end of the word */ 642 q = p; 643 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 644 p++; 645 if (*p != '\0') 646 *p++ = '\0'; 647 648 /* enter the word in the symbol table */ 649 setclass(class, q); 650 } 651 } 652 653 (void) fclose(f); 654 } 655 /* 656 ** MAKEMAILER -- define a new mailer. 657 ** 658 ** Parameters: 659 ** line -- description of mailer. This is in labeled 660 ** fields. The fields are: 661 ** P -- the path to the mailer 662 ** F -- the flags associated with the mailer 663 ** A -- the argv for this mailer 664 ** S -- the sender rewriting set 665 ** R -- the recipient rewriting set 666 ** E -- the eol string 667 ** The first word is the canonical name of the mailer. 668 ** 669 ** Returns: 670 ** none. 671 ** 672 ** Side Effects: 673 ** enters the mailer into the mailer table. 674 */ 675 676 makemailer(line) 677 char *line; 678 { 679 register char *p; 680 register struct mailer *m; 681 register STAB *s; 682 int i; 683 char fcode; 684 auto char *endp; 685 extern int NextMailer; 686 extern char **makeargv(); 687 extern char *munchstring(); 688 extern long atol(); 689 690 /* allocate a mailer and set up defaults */ 691 m = (struct mailer *) xalloc(sizeof *m); 692 bzero((char *) m, sizeof *m); 693 m->m_eol = "\n"; 694 m->m_uid = m->m_gid = 0; 695 696 /* collect the mailer name */ 697 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 698 continue; 699 if (*p != '\0') 700 *p++ = '\0'; 701 m->m_name = newstr(line); 702 703 /* now scan through and assign info from the fields */ 704 while (*p != '\0') 705 { 706 auto char *delimptr; 707 708 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 709 p++; 710 711 /* p now points to field code */ 712 fcode = *p; 713 while (*p != '\0' && *p != '=' && *p != ',') 714 p++; 715 if (*p++ != '=') 716 { 717 syserr("mailer %s: `=' expected", m->m_name); 718 return; 719 } 720 while (isascii(*p) && isspace(*p)) 721 p++; 722 723 /* p now points to the field body */ 724 p = munchstring(p, &delimptr); 725 726 /* install the field into the mailer struct */ 727 switch (fcode) 728 { 729 case 'P': /* pathname */ 730 m->m_mailer = newstr(p); 731 break; 732 733 case 'F': /* flags */ 734 for (; *p != '\0'; p++) 735 if (!(isascii(*p) && isspace(*p))) 736 setbitn(*p, m->m_flags); 737 break; 738 739 case 'S': /* sender rewriting ruleset */ 740 case 'R': /* recipient rewriting ruleset */ 741 i = strtol(p, &endp, 10); 742 if (i < 0 || i >= MAXRWSETS) 743 { 744 syserr("invalid rewrite set, %d max", MAXRWSETS); 745 return; 746 } 747 if (fcode == 'S') 748 m->m_sh_rwset = m->m_se_rwset = i; 749 else 750 m->m_rh_rwset = m->m_re_rwset = i; 751 752 p = endp; 753 if (*p++ == '/') 754 { 755 i = strtol(p, NULL, 10); 756 if (i < 0 || i >= MAXRWSETS) 757 { 758 syserr("invalid rewrite set, %d max", 759 MAXRWSETS); 760 return; 761 } 762 if (fcode == 'S') 763 m->m_sh_rwset = i; 764 else 765 m->m_rh_rwset = i; 766 } 767 break; 768 769 case 'E': /* end of line string */ 770 m->m_eol = newstr(p); 771 break; 772 773 case 'A': /* argument vector */ 774 m->m_argv = makeargv(p); 775 break; 776 777 case 'M': /* maximum message size */ 778 m->m_maxsize = atol(p); 779 break; 780 781 case 'L': /* maximum line length */ 782 m->m_linelimit = atoi(p); 783 break; 784 785 case 'D': /* working directory */ 786 m->m_execdir = newstr(p); 787 break; 788 789 case 'U': /* user id */ 790 if (isascii(*p) && !isdigit(*p)) 791 { 792 char *q = p; 793 struct passwd *pw; 794 795 while (isascii(*p) && isalnum(*p)) 796 p++; 797 while (isascii(*p) && isspace(*p)) 798 *p++ = '\0'; 799 if (*p != '\0') 800 *p++ = '\0'; 801 pw = getpwnam(q); 802 if (pw == NULL) 803 syserr("readcf: mailer U= flag: unknown user %s", q); 804 else 805 { 806 m->m_uid = pw->pw_uid; 807 m->m_gid = pw->pw_gid; 808 } 809 } 810 else 811 { 812 auto char *q; 813 814 m->m_uid = strtol(p, &q, 0); 815 p = q; 816 } 817 while (isascii(*p) && isspace(*p)) 818 p++; 819 if (*p == '\0') 820 break; 821 if (isascii(*p) && !isdigit(*p)) 822 { 823 char *q = p; 824 struct group *gr; 825 826 while (isascii(*p) && isalnum(*p)) 827 p++; 828 *p++ = '\0'; 829 gr = getgrnam(q); 830 if (gr == NULL) 831 syserr("readcf: mailer U= flag: unknown group %s", q); 832 else 833 m->m_gid = gr->gr_gid; 834 } 835 else 836 { 837 m->m_gid = strtol(p, NULL, 0); 838 } 839 break; 840 } 841 842 p = delimptr; 843 } 844 845 /* do some heuristic cleanup for back compatibility */ 846 if (bitnset(M_LIMITS, m->m_flags)) 847 { 848 if (m->m_linelimit == 0) 849 m->m_linelimit = SMTPLINELIM; 850 if (ConfigLevel < 2) 851 setbitn(M_7BITS, m->m_flags); 852 } 853 854 /* do some rationality checking */ 855 if (m->m_argv == NULL) 856 { 857 syserr("M%s: A= argument required", m->m_name); 858 return; 859 } 860 if (m->m_mailer == NULL) 861 { 862 syserr("M%s: P= argument required", m->m_name); 863 return; 864 } 865 866 if (NextMailer >= MAXMAILERS) 867 { 868 syserr("too many mailers defined (%d max)", MAXMAILERS); 869 return; 870 } 871 872 s = stab(m->m_name, ST_MAILER, ST_ENTER); 873 if (s->s_mailer != NULL) 874 { 875 i = s->s_mailer->m_mno; 876 free(s->s_mailer); 877 } 878 else 879 { 880 i = NextMailer++; 881 } 882 Mailer[i] = s->s_mailer = m; 883 m->m_mno = i; 884 } 885 /* 886 ** MUNCHSTRING -- translate a string into internal form. 887 ** 888 ** Parameters: 889 ** p -- the string to munch. 890 ** delimptr -- if non-NULL, set to the pointer of the 891 ** field delimiter character. 892 ** 893 ** Returns: 894 ** the munched string. 895 */ 896 897 char * 898 munchstring(p, delimptr) 899 register char *p; 900 char **delimptr; 901 { 902 register char *q; 903 bool backslash = FALSE; 904 bool quotemode = FALSE; 905 static char buf[MAXLINE]; 906 907 for (q = buf; *p != '\0'; p++) 908 { 909 if (backslash) 910 { 911 /* everything is roughly literal */ 912 backslash = FALSE; 913 switch (*p) 914 { 915 case 'r': /* carriage return */ 916 *q++ = '\r'; 917 continue; 918 919 case 'n': /* newline */ 920 *q++ = '\n'; 921 continue; 922 923 case 'f': /* form feed */ 924 *q++ = '\f'; 925 continue; 926 927 case 'b': /* backspace */ 928 *q++ = '\b'; 929 continue; 930 } 931 *q++ = *p; 932 } 933 else 934 { 935 if (*p == '\\') 936 backslash = TRUE; 937 else if (*p == '"') 938 quotemode = !quotemode; 939 else if (quotemode || *p != ',') 940 *q++ = *p; 941 else 942 break; 943 } 944 } 945 946 if (delimptr != NULL) 947 *delimptr = p; 948 *q++ = '\0'; 949 return (buf); 950 } 951 /* 952 ** MAKEARGV -- break up a string into words 953 ** 954 ** Parameters: 955 ** p -- the string to break up. 956 ** 957 ** Returns: 958 ** a char **argv (dynamically allocated) 959 ** 960 ** Side Effects: 961 ** munges p. 962 */ 963 964 char ** 965 makeargv(p) 966 register char *p; 967 { 968 char *q; 969 int i; 970 char **avp; 971 char *argv[MAXPV + 1]; 972 973 /* take apart the words */ 974 i = 0; 975 while (*p != '\0' && i < MAXPV) 976 { 977 q = p; 978 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 979 p++; 980 while (isascii(*p) && isspace(*p)) 981 *p++ = '\0'; 982 argv[i++] = newstr(q); 983 } 984 argv[i++] = NULL; 985 986 /* now make a copy of the argv */ 987 avp = (char **) xalloc(sizeof *avp * i); 988 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 989 990 return (avp); 991 } 992 /* 993 ** PRINTRULES -- print rewrite rules (for debugging) 994 ** 995 ** Parameters: 996 ** none. 997 ** 998 ** Returns: 999 ** none. 1000 ** 1001 ** Side Effects: 1002 ** prints rewrite rules. 1003 */ 1004 1005 printrules() 1006 { 1007 register struct rewrite *rwp; 1008 register int ruleset; 1009 1010 for (ruleset = 0; ruleset < 10; ruleset++) 1011 { 1012 if (RewriteRules[ruleset] == NULL) 1013 continue; 1014 printf("\n----Rule Set %d:", ruleset); 1015 1016 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 1017 { 1018 printf("\nLHS:"); 1019 printav(rwp->r_lhs); 1020 printf("RHS:"); 1021 printav(rwp->r_rhs); 1022 } 1023 } 1024 } 1025 1026 /* 1027 ** SETOPTION -- set global processing option 1028 ** 1029 ** Parameters: 1030 ** opt -- option name. 1031 ** val -- option value (as a text string). 1032 ** safe -- set if this came from a configuration file. 1033 ** Some options (if set from the command line) will 1034 ** reset the user id to avoid security problems. 1035 ** sticky -- if set, don't let other setoptions override 1036 ** this value. 1037 ** e -- the main envelope. 1038 ** 1039 ** Returns: 1040 ** none. 1041 ** 1042 ** Side Effects: 1043 ** Sets options as implied by the arguments. 1044 */ 1045 1046 static BITMAP StickyOpt; /* set if option is stuck */ 1047 1048 1049 #if NAMED_BIND 1050 1051 struct resolverflags 1052 { 1053 char *rf_name; /* name of the flag */ 1054 long rf_bits; /* bits to set/clear */ 1055 } ResolverFlags[] = 1056 { 1057 "debug", RES_DEBUG, 1058 "aaonly", RES_AAONLY, 1059 "usevc", RES_USEVC, 1060 "primary", RES_PRIMARY, 1061 "igntc", RES_IGNTC, 1062 "recurse", RES_RECURSE, 1063 "defnames", RES_DEFNAMES, 1064 "stayopen", RES_STAYOPEN, 1065 "dnsrch", RES_DNSRCH, 1066 "true", 0, /* to avoid error on old syntax */ 1067 NULL, 0 1068 }; 1069 1070 #endif 1071 1072 struct optioninfo 1073 { 1074 char *o_name; /* long name of option */ 1075 char o_code; /* short name of option */ 1076 bool o_safe; /* safe for random people to use */ 1077 } OptionTab[] = 1078 { 1079 "SevenBitInput", '7', TRUE, 1080 "EightBitMode", '8', TRUE, 1081 "AliasFile", 'A', FALSE, 1082 "AliasWait", 'a', FALSE, 1083 "BlankSub", 'B', FALSE, 1084 "MinFreeBlocks", 'b', TRUE, 1085 "CheckpointInterval", 'C', TRUE, 1086 "HoldExpensive", 'c', FALSE, 1087 "AutoRebuildAliases", 'D', FALSE, 1088 "DeliveryMode", 'd', TRUE, 1089 "ErrorHeader", 'E', FALSE, 1090 "ErrorMode", 'e', TRUE, 1091 "TempFileMode", 'F', FALSE, 1092 "SaveFromLine", 'f', FALSE, 1093 "MatchGECOS", 'G', FALSE, 1094 "HelpFile", 'H', FALSE, 1095 "MaxHopCount", 'h', FALSE, 1096 "NameServerOptions", 'I', FALSE, 1097 "IgnoreDots", 'i', TRUE, 1098 "ForwardPath", 'J', FALSE, 1099 "SendMimeErrors", 'j', TRUE, 1100 "ConnectionCacheSize", 'k', FALSE, 1101 "ConnectionCacheTimeout", 'K', FALSE, 1102 "UseErrorsTo", 'l', FALSE, 1103 "LogLevel", 'L', FALSE, 1104 "MeToo", 'm', TRUE, 1105 "CheckAliases", 'n', FALSE, 1106 "OldStyleHeaders", 'o', TRUE, 1107 "DaemonPortOptions", 'O', FALSE, 1108 "PrivacyOptions", 'p', TRUE, 1109 "PostmasterCopy", 'P', FALSE, 1110 "QueueFactor", 'q', FALSE, 1111 "QueueDirectory", 'Q', FALSE, 1112 "DontPruneRoutes", 'R', FALSE, 1113 "Timeouts", 'r', TRUE, 1114 "StatusFile", 'S', FALSE, 1115 "SuperSafe", 's', TRUE, 1116 "QueueTimeout", 'T', FALSE, 1117 "TimeZoneSpec", 't', FALSE, 1118 "UserDatabaseSpec", 'U', FALSE, 1119 "DefaultUser", 'u', FALSE, 1120 "FallbackMXhost", 'V', FALSE, 1121 "Verbose", 'v', TRUE, 1122 "TryNullMXList", 'w', TRUE, 1123 "QueueLA", 'x', FALSE, 1124 "RefuseLA", 'X', FALSE, 1125 "RecipientFactor", 'y', FALSE, 1126 "ForkQueueRuns", 'Y', FALSE, 1127 "ClassFactor", 'z', FALSE, 1128 "TimeFactor", 'Z', FALSE, 1129 #define O_BSP 0x80 1130 "BrokenSmtpPeers", O_BSP, TRUE, 1131 #define O_SQBH 0x81 1132 "SortQueueByHost", O_SQBH, TRUE, 1133 #define O_DNICE 0x82 1134 "DeliveryNiceness", O_DNICE, TRUE, 1135 #define O_MQA 0x83 1136 "MinQueueAge", O_MQA, TRUE, 1137 #define O_MHSA 0x84 1138 "MaxHostStatAge", O_MHSA, TRUE, 1139 1140 NULL, '\0', FALSE, 1141 }; 1142 1143 1144 1145 setoption(opt, val, safe, sticky, e) 1146 u_char opt; 1147 char *val; 1148 bool safe; 1149 bool sticky; 1150 register ENVELOPE *e; 1151 { 1152 register char *p; 1153 register struct optioninfo *o; 1154 extern bool atobool(); 1155 extern time_t convtime(); 1156 extern int QueueLA; 1157 extern int RefuseLA; 1158 extern bool Warn_Q_option; 1159 1160 if (opt == ' ') 1161 { 1162 /* full word options */ 1163 1164 p = strchr(val, '='); 1165 if (p == NULL) 1166 p = &val[strlen(val)]; 1167 while (*--p == ' ') 1168 continue; 1169 while (*++p == ' ') 1170 *p = '\0'; 1171 if (*p == '=') 1172 *p++ = '\0'; 1173 while (*p == ' ') 1174 p++; 1175 for (o = OptionTab; o->o_name != NULL; o++) 1176 { 1177 if (strcasecmp(o->o_name, val) == 0) 1178 break; 1179 } 1180 if (o->o_name == NULL) 1181 syserr("readcf: unknown option name %s", val); 1182 opt = o->o_code; 1183 val = p; 1184 } 1185 else 1186 { 1187 for (o = OptionTab; o->o_name != NULL; o++) 1188 { 1189 if (o->o_code == opt) 1190 break; 1191 } 1192 } 1193 1194 if (tTd(37, 1)) 1195 printf("setoption %s (0x%x)=%s", 1196 o->o_name == NULL ? "<unknown>" : o->o_name, 1197 opt, val); 1198 1199 /* 1200 ** See if this option is preset for us. 1201 */ 1202 1203 if (!sticky && bitnset(opt, StickyOpt)) 1204 { 1205 if (tTd(37, 1)) 1206 printf(" (ignored)\n"); 1207 return; 1208 } 1209 1210 /* 1211 ** Check to see if this option can be specified by this user. 1212 */ 1213 1214 if (!safe && RealUid == 0) 1215 safe = TRUE; 1216 if (!safe && !o->o_safe) 1217 { 1218 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 1219 { 1220 if (tTd(37, 1)) 1221 printf(" (unsafe)"); 1222 if (RealUid != geteuid()) 1223 { 1224 if (tTd(37, 1)) 1225 printf("(Resetting uid)"); 1226 (void) setgid(RealGid); 1227 (void) setuid(RealUid); 1228 } 1229 } 1230 } 1231 if (tTd(37, 1)) 1232 printf("\n"); 1233 1234 switch (opt & 0xff) 1235 { 1236 case '7': /* force seven-bit input */ 1237 SevenBitInput = atobool(val); 1238 break; 1239 1240 case '8': /* handling of 8-bit input */ 1241 switch (*val) 1242 { 1243 case 'r': /* reject 8-bit, don't convert MIME */ 1244 MimeMode = 0; 1245 break; 1246 1247 case 'm': /* convert 8-bit, convert MIME */ 1248 MimeMode = MM_CVTMIME|MM_MIME8BIT; 1249 break; 1250 1251 case 'j': /* "just send 8" */ 1252 MimeMode = MM_PASS8BIT; 1253 break; 1254 1255 case 'p': /* pass 8 bit, convert MIME */ 1256 MimeMode = MM_PASS8BIT|MM_CVTMIME; 1257 break; 1258 1259 case 's': /* strict adherence */ 1260 MimeMode = MM_CVTMIME; 1261 break; 1262 1263 case 'a': /* encode 8 bit if available */ 1264 MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME; 1265 break; 1266 1267 case 'c': /* convert 8 bit to MIME, never 7 bit */ 1268 MimeMode = MM_MIME8BIT; 1269 break; 1270 1271 default: 1272 syserr("Unknown 8-bit mode %c", *val); 1273 exit(EX_USAGE); 1274 } 1275 break; 1276 1277 case 'A': /* set default alias file */ 1278 if (val[0] == '\0') 1279 setalias("aliases"); 1280 else 1281 setalias(val); 1282 break; 1283 1284 case 'a': /* look N minutes for "@:@" in alias file */ 1285 if (val[0] == '\0') 1286 SafeAlias = 5 * 60; /* five minutes */ 1287 else 1288 SafeAlias = convtime(val, 'm'); 1289 break; 1290 1291 case 'B': /* substitution for blank character */ 1292 SpaceSub = val[0]; 1293 if (SpaceSub == '\0') 1294 SpaceSub = ' '; 1295 break; 1296 1297 case 'b': /* min blocks free on queue fs/max msg size */ 1298 p = strchr(val, '/'); 1299 if (p != NULL) 1300 { 1301 *p++ = '\0'; 1302 MaxMessageSize = atol(p); 1303 } 1304 MinBlocksFree = atol(val); 1305 break; 1306 1307 case 'c': /* don't connect to "expensive" mailers */ 1308 NoConnect = atobool(val); 1309 break; 1310 1311 case 'C': /* checkpoint every N addresses */ 1312 CheckpointInterval = atoi(val); 1313 break; 1314 1315 case 'd': /* delivery mode */ 1316 switch (*val) 1317 { 1318 case '\0': 1319 e->e_sendmode = SM_DELIVER; 1320 break; 1321 1322 case SM_QUEUE: /* queue only */ 1323 #ifndef QUEUE 1324 syserr("need QUEUE to set -odqueue"); 1325 #endif /* QUEUE */ 1326 /* fall through..... */ 1327 1328 case SM_DELIVER: /* do everything */ 1329 case SM_FORK: /* fork after verification */ 1330 e->e_sendmode = *val; 1331 break; 1332 1333 default: 1334 syserr("Unknown delivery mode %c", *val); 1335 exit(EX_USAGE); 1336 } 1337 break; 1338 1339 case 'D': /* rebuild alias database as needed */ 1340 AutoRebuild = atobool(val); 1341 break; 1342 1343 case 'E': /* error message header/header file */ 1344 if (*val != '\0') 1345 ErrMsgFile = newstr(val); 1346 break; 1347 1348 case 'e': /* set error processing mode */ 1349 switch (*val) 1350 { 1351 case EM_QUIET: /* be silent about it */ 1352 case EM_MAIL: /* mail back */ 1353 case EM_BERKNET: /* do berknet error processing */ 1354 case EM_WRITE: /* write back (or mail) */ 1355 case EM_PRINT: /* print errors normally (default) */ 1356 e->e_errormode = *val; 1357 break; 1358 } 1359 break; 1360 1361 case 'F': /* file mode */ 1362 FileMode = atooct(val) & 0777; 1363 break; 1364 1365 case 'f': /* save Unix-style From lines on front */ 1366 SaveFrom = atobool(val); 1367 break; 1368 1369 case 'G': /* match recipients against GECOS field */ 1370 MatchGecos = atobool(val); 1371 break; 1372 1373 case 'g': /* default gid */ 1374 if (isascii(*val) && isdigit(*val)) 1375 DefGid = atoi(val); 1376 else 1377 { 1378 register struct group *gr; 1379 1380 DefGid = -1; 1381 gr = getgrnam(val); 1382 if (gr == NULL) 1383 syserr("readcf: option g: unknown group %s", val); 1384 else 1385 DefGid = gr->gr_gid; 1386 } 1387 break; 1388 1389 case 'H': /* help file */ 1390 if (val[0] == '\0') 1391 HelpFile = "sendmail.hf"; 1392 else 1393 HelpFile = newstr(val); 1394 break; 1395 1396 case 'h': /* maximum hop count */ 1397 MaxHopCount = atoi(val); 1398 break; 1399 1400 case 'I': /* use internet domain name server */ 1401 #if NAMED_BIND 1402 UseNameServer = TRUE; 1403 for (p = val; *p != 0; ) 1404 { 1405 bool clearmode; 1406 char *q; 1407 struct resolverflags *rfp; 1408 1409 while (*p == ' ') 1410 p++; 1411 if (*p == '\0') 1412 break; 1413 clearmode = FALSE; 1414 if (*p == '-') 1415 clearmode = TRUE; 1416 else if (*p != '+') 1417 p--; 1418 p++; 1419 q = p; 1420 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1421 p++; 1422 if (*p != '\0') 1423 *p++ = '\0'; 1424 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1425 { 1426 if (strcasecmp(q, rfp->rf_name) == 0) 1427 break; 1428 } 1429 if (rfp->rf_name == NULL) 1430 syserr("readcf: I option value %s unrecognized", q); 1431 else if (clearmode) 1432 _res.options &= ~rfp->rf_bits; 1433 else 1434 _res.options |= rfp->rf_bits; 1435 } 1436 if (tTd(8, 2)) 1437 printf("_res.options = %x\n", _res.options); 1438 #else 1439 usrerr("name server (I option) specified but BIND not compiled in"); 1440 #endif 1441 break; 1442 1443 case 'i': /* ignore dot lines in message */ 1444 IgnrDot = atobool(val); 1445 break; 1446 1447 case 'j': /* send errors in MIME (RFC 1341) format */ 1448 SendMIMEErrors = atobool(val); 1449 break; 1450 1451 case 'J': /* .forward search path */ 1452 ForwardPath = newstr(val); 1453 break; 1454 1455 case 'k': /* connection cache size */ 1456 MaxMciCache = atoi(val); 1457 if (MaxMciCache < 0) 1458 MaxMciCache = 0; 1459 break; 1460 1461 case 'K': /* connection cache timeout */ 1462 MciCacheTimeout = convtime(val, 'm'); 1463 break; 1464 1465 case 'l': /* use Errors-To: header */ 1466 UseErrorsTo = atobool(val); 1467 break; 1468 1469 case 'L': /* log level */ 1470 if (safe || LogLevel < atoi(val)) 1471 LogLevel = atoi(val); 1472 break; 1473 1474 case 'M': /* define macro */ 1475 define(val[0], newstr(&val[1]), CurEnv); 1476 sticky = FALSE; 1477 break; 1478 1479 case 'm': /* send to me too */ 1480 MeToo = atobool(val); 1481 break; 1482 1483 case 'n': /* validate RHS in newaliases */ 1484 CheckAliases = atobool(val); 1485 break; 1486 1487 /* 'N' available -- was "net name" */ 1488 1489 case 'O': /* daemon options */ 1490 setdaemonoptions(val); 1491 break; 1492 1493 case 'o': /* assume old style headers */ 1494 if (atobool(val)) 1495 CurEnv->e_flags |= EF_OLDSTYLE; 1496 else 1497 CurEnv->e_flags &= ~EF_OLDSTYLE; 1498 break; 1499 1500 case 'p': /* select privacy level */ 1501 p = val; 1502 for (;;) 1503 { 1504 register struct prival *pv; 1505 extern struct prival PrivacyValues[]; 1506 1507 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1508 p++; 1509 if (*p == '\0') 1510 break; 1511 val = p; 1512 while (isascii(*p) && isalnum(*p)) 1513 p++; 1514 if (*p != '\0') 1515 *p++ = '\0'; 1516 1517 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1518 { 1519 if (strcasecmp(val, pv->pv_name) == 0) 1520 break; 1521 } 1522 if (pv->pv_name == NULL) 1523 syserr("readcf: Op line: %s unrecognized", val); 1524 PrivacyFlags |= pv->pv_flag; 1525 } 1526 break; 1527 1528 case 'P': /* postmaster copy address for returned mail */ 1529 PostMasterCopy = newstr(val); 1530 break; 1531 1532 case 'q': /* slope of queue only function */ 1533 QueueFactor = atoi(val); 1534 break; 1535 1536 case 'Q': /* queue directory */ 1537 if (val[0] == '\0') 1538 QueueDir = "mqueue"; 1539 else 1540 QueueDir = newstr(val); 1541 if (RealUid != 0 && !safe) 1542 Warn_Q_option = TRUE; 1543 break; 1544 1545 case 'R': /* don't prune routes */ 1546 DontPruneRoutes = atobool(val); 1547 break; 1548 1549 case 'r': /* read timeout */ 1550 settimeouts(val); 1551 break; 1552 1553 case 'S': /* status file */ 1554 if (val[0] == '\0') 1555 StatFile = "sendmail.st"; 1556 else 1557 StatFile = newstr(val); 1558 break; 1559 1560 case 's': /* be super safe, even if expensive */ 1561 SuperSafe = atobool(val); 1562 break; 1563 1564 case 'T': /* queue timeout */ 1565 p = strchr(val, '/'); 1566 if (p != NULL) 1567 { 1568 *p++ = '\0'; 1569 TimeOuts.to_q_warning[TOC_NORMAL] = convtime(p, 'd'); 1570 } 1571 TimeOuts.to_q_return[TOC_NORMAL] = convtime(val, 'h'); 1572 break; 1573 1574 case 't': /* time zone name */ 1575 TimeZoneSpec = newstr(val); 1576 break; 1577 1578 case 'U': /* location of user database */ 1579 UdbSpec = newstr(val); 1580 break; 1581 1582 case 'u': /* set default uid */ 1583 if (isascii(*val) && isdigit(*val)) 1584 DefUid = atoi(val); 1585 else 1586 { 1587 register struct passwd *pw; 1588 1589 DefUid = -1; 1590 pw = getpwnam(val); 1591 if (pw == NULL) 1592 syserr("readcf: option u: unknown user %s", val); 1593 else 1594 DefUid = pw->pw_uid; 1595 } 1596 setdefuser(); 1597 break; 1598 1599 case 'V': /* fallback MX host */ 1600 FallBackMX = newstr(val); 1601 break; 1602 1603 case 'v': /* run in verbose mode */ 1604 Verbose = atobool(val); 1605 break; 1606 1607 case 'w': /* if we are best MX, try host directly */ 1608 TryNullMXList = atobool(val); 1609 break; 1610 1611 /* 'W' available -- was wizard password */ 1612 1613 case 'x': /* load avg at which to auto-queue msgs */ 1614 QueueLA = atoi(val); 1615 break; 1616 1617 case 'X': /* load avg at which to auto-reject connections */ 1618 RefuseLA = atoi(val); 1619 break; 1620 1621 case 'y': /* work recipient factor */ 1622 WkRecipFact = atoi(val); 1623 break; 1624 1625 case 'Y': /* fork jobs during queue runs */ 1626 ForkQueueRuns = atobool(val); 1627 break; 1628 1629 case 'z': /* work message class factor */ 1630 WkClassFact = atoi(val); 1631 break; 1632 1633 case 'Z': /* work time factor */ 1634 WkTimeFact = atoi(val); 1635 break; 1636 1637 case O_BSP: /* SMTP Peers can't handle 2-line greeting */ 1638 BrokenSmtpPeers = atobool(val); 1639 break; 1640 1641 case O_SQBH: /* sort work queue by host first */ 1642 SortQueueByHost = atobool(val); 1643 break; 1644 1645 case O_DNICE: /* delivery nice value */ 1646 DeliveryNiceness = atoi(val); 1647 break; 1648 1649 case O_MQA: /* minimum queue age between deliveries */ 1650 MinQueueAge = convtime(val, 'm'); 1651 break; 1652 1653 case O_MHSA: /* maximum age of cached host status */ 1654 MaxHostStatAge = convtime(val, 'm'); 1655 break; 1656 1657 default: 1658 break; 1659 } 1660 if (sticky) 1661 setbitn(opt, StickyOpt); 1662 return; 1663 } 1664 /* 1665 ** SETCLASS -- set a word into a class 1666 ** 1667 ** Parameters: 1668 ** class -- the class to put the word in. 1669 ** word -- the word to enter 1670 ** 1671 ** Returns: 1672 ** none. 1673 ** 1674 ** Side Effects: 1675 ** puts the word into the symbol table. 1676 */ 1677 1678 setclass(class, word) 1679 int class; 1680 char *word; 1681 { 1682 register STAB *s; 1683 1684 if (tTd(37, 8)) 1685 printf("setclass(%c, %s)\n", class, word); 1686 s = stab(word, ST_CLASS, ST_ENTER); 1687 setbitn(class, s->s_class); 1688 } 1689 /* 1690 ** MAKEMAPENTRY -- create a map entry 1691 ** 1692 ** Parameters: 1693 ** line -- the config file line 1694 ** 1695 ** Returns: 1696 ** TRUE if it successfully entered the map entry. 1697 ** FALSE otherwise (usually syntax error). 1698 ** 1699 ** Side Effects: 1700 ** Enters the map into the dictionary. 1701 */ 1702 1703 void 1704 makemapentry(line) 1705 char *line; 1706 { 1707 register char *p; 1708 char *mapname; 1709 char *classname; 1710 register STAB *s; 1711 STAB *class; 1712 1713 for (p = line; isascii(*p) && isspace(*p); p++) 1714 continue; 1715 if (!(isascii(*p) && isalnum(*p))) 1716 { 1717 syserr("readcf: config K line: no map name"); 1718 return; 1719 } 1720 1721 mapname = p; 1722 while (isascii(*++p) && isalnum(*p)) 1723 continue; 1724 if (*p != '\0') 1725 *p++ = '\0'; 1726 while (isascii(*p) && isspace(*p)) 1727 p++; 1728 if (!(isascii(*p) && isalnum(*p))) 1729 { 1730 syserr("readcf: config K line, map %s: no map class", mapname); 1731 return; 1732 } 1733 classname = p; 1734 while (isascii(*++p) && isalnum(*p)) 1735 continue; 1736 if (*p != '\0') 1737 *p++ = '\0'; 1738 while (isascii(*p) && isspace(*p)) 1739 p++; 1740 1741 /* look up the class */ 1742 class = stab(classname, ST_MAPCLASS, ST_FIND); 1743 if (class == NULL) 1744 { 1745 syserr("readcf: map %s: class %s not available", mapname, classname); 1746 return; 1747 } 1748 1749 /* enter the map */ 1750 s = stab(mapname, ST_MAP, ST_ENTER); 1751 s->s_map.map_class = &class->s_mapclass; 1752 s->s_map.map_mname = newstr(mapname); 1753 1754 if (class->s_mapclass.map_parse(&s->s_map, p)) 1755 s->s_map.map_mflags |= MF_VALID; 1756 1757 if (tTd(37, 5)) 1758 { 1759 printf("map %s, class %s, flags %x, file %s,\n", 1760 s->s_map.map_mname, s->s_map.map_class->map_cname, 1761 s->s_map.map_mflags, 1762 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file); 1763 printf("\tapp %s, domain %s, rebuild %s\n", 1764 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app, 1765 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain, 1766 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild); 1767 } 1768 } 1769 /* 1770 ** SETTIMEOUTS -- parse and set timeout values 1771 ** 1772 ** Parameters: 1773 ** val -- a pointer to the values. If NULL, do initial 1774 ** settings. 1775 ** 1776 ** Returns: 1777 ** none. 1778 ** 1779 ** Side Effects: 1780 ** Initializes the TimeOuts structure 1781 */ 1782 1783 #define SECONDS 1784 #define MINUTES * 60 1785 #define HOUR * 3600 1786 1787 settimeouts(val) 1788 register char *val; 1789 { 1790 register char *p; 1791 extern time_t convtime(); 1792 1793 if (val == NULL) 1794 { 1795 TimeOuts.to_initial = (time_t) 5 MINUTES; 1796 TimeOuts.to_helo = (time_t) 5 MINUTES; 1797 TimeOuts.to_mail = (time_t) 10 MINUTES; 1798 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1799 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1800 TimeOuts.to_datablock = (time_t) 1 HOUR; 1801 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1802 TimeOuts.to_rset = (time_t) 5 MINUTES; 1803 TimeOuts.to_quit = (time_t) 2 MINUTES; 1804 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1805 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1806 TimeOuts.to_ident = (time_t) 30 SECONDS; 1807 TimeOuts.to_fileopen = (time_t) 60 SECONDS; 1808 return; 1809 } 1810 1811 for (;; val = p) 1812 { 1813 while (isascii(*val) && isspace(*val)) 1814 val++; 1815 if (*val == '\0') 1816 break; 1817 for (p = val; *p != '\0' && *p != ','; p++) 1818 continue; 1819 if (*p != '\0') 1820 *p++ = '\0'; 1821 1822 if (isascii(*val) && isdigit(*val)) 1823 { 1824 /* old syntax -- set everything */ 1825 TimeOuts.to_mail = convtime(val, 'm'); 1826 TimeOuts.to_rcpt = TimeOuts.to_mail; 1827 TimeOuts.to_datainit = TimeOuts.to_mail; 1828 TimeOuts.to_datablock = TimeOuts.to_mail; 1829 TimeOuts.to_datafinal = TimeOuts.to_mail; 1830 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1831 continue; 1832 } 1833 else 1834 { 1835 register char *q = strchr(val, ':'); 1836 time_t to; 1837 1838 if (q == NULL && (q = strchr(val, '=')) == NULL) 1839 { 1840 /* syntax error */ 1841 continue; 1842 } 1843 *q++ = '\0'; 1844 to = convtime(q, 'm'); 1845 1846 if (strcasecmp(val, "initial") == 0) 1847 TimeOuts.to_initial = to; 1848 else if (strcasecmp(val, "mail") == 0) 1849 TimeOuts.to_mail = to; 1850 else if (strcasecmp(val, "rcpt") == 0) 1851 TimeOuts.to_rcpt = to; 1852 else if (strcasecmp(val, "datainit") == 0) 1853 TimeOuts.to_datainit = to; 1854 else if (strcasecmp(val, "datablock") == 0) 1855 TimeOuts.to_datablock = to; 1856 else if (strcasecmp(val, "datafinal") == 0) 1857 TimeOuts.to_datafinal = to; 1858 else if (strcasecmp(val, "command") == 0) 1859 TimeOuts.to_nextcommand = to; 1860 else if (strcasecmp(val, "rset") == 0) 1861 TimeOuts.to_rset = to; 1862 else if (strcasecmp(val, "helo") == 0) 1863 TimeOuts.to_helo = to; 1864 else if (strcasecmp(val, "quit") == 0) 1865 TimeOuts.to_quit = to; 1866 else if (strcasecmp(val, "misc") == 0) 1867 TimeOuts.to_miscshort = to; 1868 else if (strcasecmp(val, "ident") == 0) 1869 TimeOuts.to_ident = to; 1870 else if (strcasecmp(val, "fileopen") == 0) 1871 TimeOuts.to_fileopen = to; 1872 else if (strcasecmp(val, "queuewarn") == 0) 1873 TimeOuts.to_q_warning[TOC_NORMAL] = to; 1874 else if (strcasecmp(val, "queuereturn") == 0) 1875 TimeOuts.to_q_return[TOC_NORMAL] = to; 1876 else if (strcasecmp(val, "queuewarn.normal") == 0) 1877 TimeOuts.to_q_warning[TOC_NORMAL] = to; 1878 else if (strcasecmp(val, "queuereturn.normal") == 0) 1879 TimeOuts.to_q_return[TOC_NORMAL] = to; 1880 else if (strcasecmp(val, "queuewarn.urgent") == 0) 1881 TimeOuts.to_q_warning[TOC_URGENT] = to; 1882 else if (strcasecmp(val, "queuereturn.urgent") == 0) 1883 TimeOuts.to_q_return[TOC_URGENT] = to; 1884 else if (strcasecmp(val, "queuewarn.non-urgent") == 0) 1885 TimeOuts.to_q_warning[TOC_NONURGENT] = to; 1886 else if (strcasecmp(val, "queuereturn.non-urgent") == 0) 1887 TimeOuts.to_q_return[TOC_NONURGENT] = to; 1888 else 1889 syserr("settimeouts: invalid timeout %s", val); 1890 } 1891 } 1892 } 1893