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