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