1 # include "sendmail.h" 2 3 SCCSID(@(#)readcf.c 4.12 05/24/85); 4 5 /* 6 ** READCF -- read control file. 7 ** 8 ** This routine reads the control file and builds the internal 9 ** form. 10 ** 11 ** The file is formatted as a sequence of lines, each taken 12 ** atomically. The first character of each line describes how 13 ** the line is to be interpreted. The lines are: 14 ** Dxval Define macro x to have value val. 15 ** Cxword Put word into class x. 16 ** Fxfile [fmt] Read file for lines to put into 17 ** class x. Use scanf string 'fmt' 18 ** or "%s" if not present. Fmt should 19 ** only produce one string-valued result. 20 ** Hname: value Define header with field-name 'name' 21 ** and value as specified; this will be 22 ** macro expanded immediately before 23 ** use. 24 ** Sn Use rewriting set n. 25 ** Rlhs rhs Rewrite addresses that match lhs to 26 ** be rhs. 27 ** Mn p f s r a Define mailer. n - internal name, 28 ** p - pathname, f - flags, s - rewriting 29 ** ruleset for sender, s - rewriting ruleset 30 ** for recipients, a - argument vector. 31 ** Oxvalue Set option x to value. 32 ** Pname=value Set precedence name to value. 33 ** 34 ** Parameters: 35 ** cfname -- control file name. 36 ** 37 ** Returns: 38 ** none. 39 ** 40 ** Side Effects: 41 ** Builds several internal tables. 42 */ 43 44 readcf(cfname) 45 char *cfname; 46 { 47 FILE *cf; 48 int ruleset = 0; 49 char *q; 50 char **pv; 51 struct rewrite *rwp = NULL; 52 char buf[MAXLINE]; 53 register char *p; 54 extern char **prescan(); 55 extern char **copyplist(); 56 char exbuf[MAXLINE]; 57 char pvpbuf[PSBUFSIZE]; 58 extern char *fgetfolded(); 59 extern char *munchstring(); 60 61 cf = fopen(cfname, "r"); 62 if (cf == NULL) 63 { 64 syserr("cannot open %s", cfname); 65 exit(EX_OSFILE); 66 } 67 68 FileName = cfname; 69 LineNumber = 0; 70 while (fgetfolded(buf, sizeof buf, cf) != NULL) 71 { 72 /* map $ into \001 (ASCII SOH) for macro expansion */ 73 for (p = buf; *p != '\0'; p++) 74 { 75 if (*p != '$') 76 continue; 77 78 if (p[1] == '$') 79 { 80 /* actual dollar sign.... */ 81 strcpy(p, p + 1); 82 continue; 83 } 84 85 /* convert to macro expansion character */ 86 *p = '\001'; 87 } 88 89 /* interpret this line */ 90 switch (buf[0]) 91 { 92 case '\0': 93 case '#': /* comment */ 94 break; 95 96 case 'R': /* rewriting rule */ 97 for (p = &buf[1]; *p != '\0' && *p != '\t'; p++) 98 continue; 99 100 if (*p == '\0') 101 { 102 syserr("invalid rewrite line \"%s\"", buf); 103 break; 104 } 105 106 /* allocate space for the rule header */ 107 if (rwp == NULL) 108 { 109 RewriteRules[ruleset] = rwp = 110 (struct rewrite *) xalloc(sizeof *rwp); 111 } 112 else 113 { 114 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 115 rwp = rwp->r_next; 116 } 117 rwp->r_next = NULL; 118 119 /* expand and save the LHS */ 120 *p = '\0'; 121 expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv); 122 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf); 123 if (rwp->r_lhs != NULL) 124 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 125 126 /* expand and save the RHS */ 127 while (*++p == '\t') 128 continue; 129 q = p; 130 while (*p != '\0' && *p != '\t') 131 p++; 132 *p = '\0'; 133 expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv); 134 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf); 135 if (rwp->r_rhs != NULL) 136 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 137 break; 138 139 case 'S': /* select rewriting set */ 140 ruleset = atoi(&buf[1]); 141 if (ruleset >= MAXRWSETS || ruleset < 0) 142 { 143 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 144 ruleset = 0; 145 } 146 rwp = NULL; 147 break; 148 149 case 'D': /* macro definition */ 150 define(buf[1], newstr(munchstring(&buf[2])), CurEnv); 151 break; 152 153 case 'H': /* required header line */ 154 (void) chompheader(&buf[1], TRUE); 155 break; 156 157 case 'C': /* word class */ 158 case 'F': /* word class from file */ 159 /* read list of words from argument or file */ 160 if (buf[0] == 'F') 161 { 162 /* read from file */ 163 for (p = &buf[2]; *p != '\0' && !isspace(*p); p++) 164 continue; 165 if (*p == '\0') 166 p = "%s"; 167 else 168 { 169 *p = '\0'; 170 while (isspace(*++p)) 171 continue; 172 } 173 fileclass(buf[1], &buf[2], p); 174 break; 175 } 176 177 /* scan the list of words and set class for all */ 178 for (p = &buf[2]; *p != '\0'; ) 179 { 180 register char *wd; 181 char delim; 182 183 while (*p != '\0' && isspace(*p)) 184 p++; 185 wd = p; 186 while (*p != '\0' && !isspace(*p)) 187 p++; 188 delim = *p; 189 *p = '\0'; 190 if (wd[0] != '\0') 191 setclass(buf[1], wd); 192 *p = delim; 193 } 194 break; 195 196 case 'M': /* define mailer */ 197 makemailer(&buf[1]); 198 break; 199 200 case 'O': /* set option */ 201 setoption(buf[1], &buf[2], FALSE); 202 break; 203 204 case 'P': /* set precedence */ 205 if (NumPriorities >= MAXPRIORITIES) 206 { 207 toomany('P', MAXPRIORITIES); 208 break; 209 } 210 for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 211 continue; 212 if (*p == '\0') 213 goto badline; 214 *p = '\0'; 215 Priorities[NumPriorities].pri_name = newstr(&buf[1]); 216 Priorities[NumPriorities].pri_val = atoi(++p); 217 NumPriorities++; 218 break; 219 220 case 'T': /* trusted user(s) */ 221 p = &buf[1]; 222 while (*p != '\0') 223 { 224 while (isspace(*p)) 225 p++; 226 q = p; 227 while (*p != '\0' && !isspace(*p)) 228 p++; 229 if (*p != '\0') 230 *p++ = '\0'; 231 if (*q == '\0') 232 continue; 233 for (pv = TrustedUsers; *pv != NULL; pv++) 234 continue; 235 if (pv >= &TrustedUsers[MAXTRUST]) 236 { 237 toomany('T', MAXTRUST); 238 break; 239 } 240 *pv = newstr(q); 241 } 242 break; 243 244 default: 245 badline: 246 syserr("unknown control line \"%s\"", buf); 247 } 248 } 249 FileName = NULL; 250 } 251 /* 252 ** TOOMANY -- signal too many of some option 253 ** 254 ** Parameters: 255 ** id -- the id of the error line 256 ** maxcnt -- the maximum possible values 257 ** 258 ** Returns: 259 ** none. 260 ** 261 ** Side Effects: 262 ** gives a syserr. 263 */ 264 265 toomany(id, maxcnt) 266 char id; 267 int maxcnt; 268 { 269 syserr("too many %c lines, %d max", id, maxcnt); 270 } 271 /* 272 ** FILECLASS -- read members of a class from a file 273 ** 274 ** Parameters: 275 ** class -- class to define. 276 ** filename -- name of file to read. 277 ** fmt -- scanf string to use for match. 278 ** 279 ** Returns: 280 ** none 281 ** 282 ** Side Effects: 283 ** 284 ** puts all lines in filename that match a scanf into 285 ** the named class. 286 */ 287 288 fileclass(class, filename, fmt) 289 int class; 290 char *filename; 291 char *fmt; 292 { 293 register FILE *f; 294 char buf[MAXLINE]; 295 296 f = fopen(filename, "r"); 297 if (f == NULL) 298 { 299 syserr("cannot open %s", filename); 300 return; 301 } 302 303 while (fgets(buf, sizeof buf, f) != NULL) 304 { 305 register STAB *s; 306 char wordbuf[MAXNAME+1]; 307 308 if (sscanf(buf, fmt, wordbuf) != 1) 309 continue; 310 s = stab(wordbuf, ST_CLASS, ST_ENTER); 311 setbitn(class, s->s_class); 312 } 313 314 (void) fclose(f); 315 } 316 /* 317 ** MAKEMAILER -- define a new mailer. 318 ** 319 ** Parameters: 320 ** line -- description of mailer. This is in labeled 321 ** fields. The fields are: 322 ** P -- the path to the mailer 323 ** F -- the flags associated with the mailer 324 ** A -- the argv for this mailer 325 ** S -- the sender rewriting set 326 ** R -- the recipient rewriting set 327 ** E -- the eol string 328 ** The first word is the canonical name of the mailer. 329 ** 330 ** Returns: 331 ** none. 332 ** 333 ** Side Effects: 334 ** enters the mailer into the mailer table. 335 */ 336 337 makemailer(line) 338 char *line; 339 { 340 register char *p; 341 register struct mailer *m; 342 register STAB *s; 343 int i; 344 char fcode; 345 extern int NextMailer; 346 extern char **makeargv(); 347 extern char *munchstring(); 348 extern char *DelimChar; 349 extern long atol(); 350 351 /* allocate a mailer and set up defaults */ 352 m = (struct mailer *) xalloc(sizeof *m); 353 bzero((char *) m, sizeof *m); 354 m->m_mno = NextMailer; 355 m->m_eol = "\n"; 356 357 /* collect the mailer name */ 358 for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++) 359 continue; 360 if (*p != '\0') 361 *p++ = '\0'; 362 m->m_name = newstr(line); 363 364 /* now scan through and assign info from the fields */ 365 while (*p != '\0') 366 { 367 while (*p != '\0' && (*p == ',' || isspace(*p))) 368 p++; 369 370 /* p now points to field code */ 371 fcode = *p; 372 while (*p != '\0' && *p != '=' && *p != ',') 373 p++; 374 if (*p++ != '=') 375 { 376 syserr("`=' expected"); 377 return; 378 } 379 while (isspace(*p)) 380 p++; 381 382 /* p now points to the field body */ 383 p = munchstring(p); 384 385 /* install the field into the mailer struct */ 386 switch (fcode) 387 { 388 case 'P': /* pathname */ 389 m->m_mailer = newstr(p); 390 break; 391 392 case 'F': /* flags */ 393 for (; *p != '\0'; p++) 394 setbitn(*p, m->m_flags); 395 break; 396 397 case 'S': /* sender rewriting ruleset */ 398 case 'R': /* recipient rewriting ruleset */ 399 i = atoi(p); 400 if (i < 0 || i >= MAXRWSETS) 401 { 402 syserr("invalid rewrite set, %d max", MAXRWSETS); 403 return; 404 } 405 if (fcode == 'S') 406 m->m_s_rwset = i; 407 else 408 m->m_r_rwset = i; 409 break; 410 411 case 'E': /* end of line string */ 412 m->m_eol = newstr(p); 413 break; 414 415 case 'A': /* argument vector */ 416 m->m_argv = makeargv(p); 417 break; 418 419 case 'M': /* maximum message size */ 420 m->m_maxsize = atol(p); 421 break; 422 } 423 424 p = DelimChar; 425 } 426 427 /* now store the mailer away */ 428 if (NextMailer >= MAXMAILERS) 429 { 430 syserr("too many mailers defined (%d max)", MAXMAILERS); 431 return; 432 } 433 Mailer[NextMailer++] = m; 434 s = stab(m->m_name, ST_MAILER, ST_ENTER); 435 s->s_mailer = m; 436 } 437 /* 438 ** MUNCHSTRING -- translate a string into internal form. 439 ** 440 ** Parameters: 441 ** p -- the string to munch. 442 ** 443 ** Returns: 444 ** the munched string. 445 ** 446 ** Side Effects: 447 ** Sets "DelimChar" to point to the string that caused us 448 ** to stop. 449 */ 450 451 char * 452 munchstring(p) 453 register char *p; 454 { 455 register char *q; 456 bool backslash = FALSE; 457 bool quotemode = FALSE; 458 static char buf[MAXLINE]; 459 extern char *DelimChar; 460 461 for (q = buf; *p != '\0'; p++) 462 { 463 if (backslash) 464 { 465 /* everything is roughly literal */ 466 backslash = FALSE; 467 switch (*p) 468 { 469 case 'r': /* carriage return */ 470 *q++ = '\r'; 471 continue; 472 473 case 'n': /* newline */ 474 *q++ = '\n'; 475 continue; 476 477 case 'f': /* form feed */ 478 *q++ = '\f'; 479 continue; 480 481 case 'b': /* backspace */ 482 *q++ = '\b'; 483 continue; 484 } 485 *q++ = *p; 486 } 487 else 488 { 489 if (*p == '\\') 490 backslash = TRUE; 491 else if (*p == '"') 492 quotemode = !quotemode; 493 else if (quotemode || *p != ',') 494 *q++ = *p; 495 else 496 break; 497 } 498 } 499 500 DelimChar = p; 501 *q++ = '\0'; 502 return (buf); 503 } 504 /* 505 ** MAKEARGV -- break up a string into words 506 ** 507 ** Parameters: 508 ** p -- the string to break up. 509 ** 510 ** Returns: 511 ** a char **argv (dynamically allocated) 512 ** 513 ** Side Effects: 514 ** munges p. 515 */ 516 517 char ** 518 makeargv(p) 519 register char *p; 520 { 521 char *q; 522 int i; 523 char **avp; 524 char *argv[MAXPV + 1]; 525 526 /* take apart the words */ 527 i = 0; 528 while (*p != '\0' && i < MAXPV) 529 { 530 q = p; 531 while (*p != '\0' && !isspace(*p)) 532 p++; 533 while (isspace(*p)) 534 *p++ = '\0'; 535 argv[i++] = newstr(q); 536 } 537 argv[i++] = NULL; 538 539 /* now make a copy of the argv */ 540 avp = (char **) xalloc(sizeof *avp * i); 541 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 542 543 return (avp); 544 } 545 /* 546 ** PRINTRULES -- print rewrite rules (for debugging) 547 ** 548 ** Parameters: 549 ** none. 550 ** 551 ** Returns: 552 ** none. 553 ** 554 ** Side Effects: 555 ** prints rewrite rules. 556 */ 557 558 # ifdef DEBUG 559 560 printrules() 561 { 562 register struct rewrite *rwp; 563 register int ruleset; 564 565 for (ruleset = 0; ruleset < 10; ruleset++) 566 { 567 if (RewriteRules[ruleset] == NULL) 568 continue; 569 printf("\n----Rule Set %d:", ruleset); 570 571 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 572 { 573 printf("\nLHS:"); 574 printav(rwp->r_lhs); 575 printf("RHS:"); 576 printav(rwp->r_rhs); 577 } 578 } 579 } 580 581 # endif DEBUG 582 /* 583 ** SETOPTION -- set global processing option 584 ** 585 ** Parameters: 586 ** opt -- option name. 587 ** val -- option value (as a text string). 588 ** sticky -- if set, don't let other setoptions override 589 ** this value. 590 ** 591 ** Returns: 592 ** none. 593 ** 594 ** Side Effects: 595 ** Sets options as implied by the arguments. 596 */ 597 598 static BITMAP StickyOpt; /* set if option is stuck */ 599 extern char *WizWord; /* the stored wizard password */ 600 extern char *NetName; /* name of home (local) network */ 601 602 setoption(opt, val, sticky) 603 char opt; 604 char *val; 605 bool sticky; 606 { 607 extern bool atobool(); 608 extern time_t convtime(); 609 extern int QueueLA; 610 extern int RefuseLA; 611 extern bool trusteduser(); 612 extern char *username(); 613 614 # ifdef DEBUG 615 if (tTd(37, 1)) 616 printf("setoption %c=%s", opt, val); 617 # endif DEBUG 618 619 /* 620 ** See if this option is preset for us. 621 */ 622 623 if (bitnset(opt, StickyOpt)) 624 { 625 # ifdef DEBUG 626 if (tTd(37, 1)) 627 printf(" (ignored)\n"); 628 # endif DEBUG 629 return; 630 } 631 632 #ifdef DEBUG 633 if (tTd(37, 1)) 634 printf("\n"); 635 #endif DEBUG 636 637 switch (opt) 638 { 639 case 'A': /* set default alias file */ 640 if (val[0] == '\0') 641 AliasFile = "aliases"; 642 else 643 AliasFile = newstr(val); 644 break; 645 646 case 'a': /* look N minutes for "@:@" in alias file */ 647 if (val[0] == '\0') 648 SafeAlias = 5; 649 else 650 SafeAlias = atoi(val); 651 break; 652 653 case 'B': /* substitution for blank character */ 654 SpaceSub = val[0]; 655 if (SpaceSub == '\0') 656 SpaceSub = ' '; 657 break; 658 659 case 'c': /* don't connect to "expensive" mailers */ 660 NoConnect = atobool(val); 661 break; 662 663 case 'd': /* delivery mode */ 664 switch (*val) 665 { 666 case '\0': 667 SendMode = SM_DELIVER; 668 break; 669 670 case SM_QUEUE: /* queue only */ 671 #ifndef QUEUE 672 syserr("need QUEUE to set -odqueue"); 673 #endif QUEUE 674 /* fall through..... */ 675 676 case SM_DELIVER: /* do everything */ 677 case SM_FORK: /* fork after verification */ 678 SendMode = *val; 679 break; 680 681 default: 682 syserr("Unknown delivery mode %c", *val); 683 exit(EX_USAGE); 684 } 685 break; 686 687 case 'D': /* rebuild alias database as needed */ 688 AutoRebuild = atobool(val); 689 break; 690 691 case 'e': /* set error processing mode */ 692 switch (*val) 693 { 694 case EM_QUIET: /* be silent about it */ 695 case EM_MAIL: /* mail back */ 696 case EM_BERKNET: /* do berknet error processing */ 697 case EM_WRITE: /* write back (or mail) */ 698 HoldErrs = TRUE; 699 /* fall through... */ 700 701 case EM_PRINT: /* print errors normally (default) */ 702 ErrorMode = *val; 703 break; 704 } 705 break; 706 707 case 'F': /* file mode */ 708 FileMode = atooct(val) & 0777; 709 break; 710 711 case 'f': /* save Unix-style From lines on front */ 712 SaveFrom = atobool(val); 713 break; 714 715 case 'g': /* default gid */ 716 DefGid = atoi(val); 717 break; 718 719 case 'H': /* help file */ 720 if (val[0] == '\0') 721 HelpFile = "sendmail.hf"; 722 else 723 HelpFile = newstr(val); 724 break; 725 726 case 'i': /* ignore dot lines in message */ 727 IgnrDot = atobool(val); 728 break; 729 730 case 'L': /* log level */ 731 LogLevel = atoi(val); 732 break; 733 734 case 'M': /* define macro */ 735 define(val[0], newstr(&val[1]), CurEnv); 736 sticky = FALSE; 737 break; 738 739 case 'm': /* send to me too */ 740 MeToo = atobool(val); 741 break; 742 743 # ifdef DAEMON 744 case 'N': /* home (local?) network name */ 745 NetName = newstr(val); 746 break; 747 # endif DAEMON 748 749 case 'o': /* assume old style headers */ 750 if (atobool(val)) 751 CurEnv->e_flags |= EF_OLDSTYLE; 752 else 753 CurEnv->e_flags &= ~EF_OLDSTYLE; 754 break; 755 756 case 'Q': /* queue directory */ 757 if (val[0] == '\0') 758 QueueDir = "mqueue"; 759 else 760 QueueDir = newstr(val); 761 break; 762 763 case 'r': /* read timeout */ 764 ReadTimeout = convtime(val); 765 break; 766 767 case 'S': /* status file */ 768 if (val[0] == '\0') 769 StatFile = "sendmail.st"; 770 else 771 StatFile = newstr(val); 772 break; 773 774 case 's': /* be super safe, even if expensive */ 775 SuperSafe = atobool(val); 776 break; 777 778 case 'T': /* queue timeout */ 779 TimeOut = convtime(val); 780 break; 781 782 case 't': /* time zone name */ 783 # ifdef V6 784 StdTimezone = newstr(val); 785 DstTimezone = index(StdTimeZone, ','); 786 if (DstTimezone == NULL) 787 syserr("bad time zone spec"); 788 else 789 *DstTimezone++ = '\0'; 790 # endif V6 791 break; 792 793 case 'u': /* set default uid */ 794 DefUid = atoi(val); 795 break; 796 797 case 'v': /* run in verbose mode */ 798 Verbose = atobool(val); 799 break; 800 801 # ifdef DEBUG 802 case 'W': /* set the wizards password */ 803 WizWord = newstr(val); 804 break; 805 # endif DEBUG 806 807 case 'x': /* load avg at which to auto-queue msgs */ 808 QueueLA = atoi(val); 809 break; 810 811 case 'X': /* load avg at which to auto-reject connections */ 812 RefuseLA = atoi(val); 813 break; 814 815 default: 816 break; 817 } 818 if (sticky) 819 setbitn(opt, StickyOpt); 820 return; 821 } 822 /* 823 ** SETCLASS -- set a word into a class 824 ** 825 ** Parameters: 826 ** class -- the class to put the word in. 827 ** word -- the word to enter 828 ** 829 ** Returns: 830 ** none. 831 ** 832 ** Side Effects: 833 ** puts the word into the symbol table. 834 */ 835 836 setclass(class, word) 837 int class; 838 char *word; 839 { 840 register STAB *s; 841 842 s = stab(word, ST_CLASS, ST_ENTER); 843 setbitn(class, s->s_class); 844 } 845