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